王学岗————opengl黑白效果与分屏(第32,33节课)
32节课
1,直播推流之前要做美颜和特效。
2,opengl是运行在GPU
3,左侧是cpu架构,右侧是GPU架构

渲染约等于计算,GPU有很多ALU单元,擅长渲染。
4,openGL 的几个阶段
1)确定形状
2)矢量图形像素化
3)上颜色
4)根据颜色,进行显示
第一步(顶点程序)和第三步(片元程序)需要程序员实现。
代码
#extension GL_OES_EGL_image_external : require
precision lowp float;
//片元程序,变量名要和camera_vert中一样
varying vec2 aCoord;
//定义采样器 内置变量
uniform samplerExternalOES vTexture;
//运行在GPU,
void main() {
// 分屏的写法,上下分屏float x= aCoord.x;
// 分几屏就除以几float a = 1.0/3.0;if(x2.0*a){x -= 1.0/3.0;}// 分屏的写法// texture2D是自带的采样器,根据对应的图层采样对应的像素值。vTexture这里就是第0个图层,vec4 rgba = texture2D(vTexture,vec2(x,aCoord.y));// gl_FragColor内置的变量.在这里可以对颜色进行处理
// 黑白效果的写法
// float color = (rgba.r+rgba.g+rgba.b)/3.0;
// gl_FragColor = vec4(color,color,color,rgba.a);gl_FragColor=rgba;
}左右分屏的写法
//float y= aCoord.y;
//if(y<0.5)
//{
// y+=0.25;
//
//}else{
// y -= 0.25;
//}texture2D是自带的采样器,根据对应的图层采样对应的像素值。
//vec4 rgba = texture2D(vTexture,vec2(aCoord.x,y));
//顶点程序,确定形状(形状在cpu已经定义好了)
//声明变量, vec4代表四个顶点坐标 (vec是坐标的意思)
attribute vec4 vPosition;attribute vec4 vCoord;uniform mat4 vMatrix;
//纹理坐标系
varying vec2 aCoord;
void main() {//gl_position是内置变量,顶点位置坐标,一旦赋值,形状就确定了。gl_Position=vPosition;//矩阵变换,摄像头变成横着。aCoord是片元程序(raw文件)中的变量aCoord= (vMatrix*vCoord).xy;
}
package com.maniu.openglbbc;import android.os.HandlerThread;
import android.util.Size;import androidx.camera.core.CameraX;
import androidx.camera.core.Preview;
import androidx.camera.core.PreviewConfig;
import androidx.lifecycle.LifecycleOwner;public class CameraHelper {private HandlerThread handlerThread;private CameraX.LensFacing currentFacing = CameraX.LensFacing.BACK;private Preview.OnPreviewOutputUpdateListener listener;public CameraHelper(LifecycleOwner lifecycleOwner, Preview.OnPreviewOutputUpdateListener listener) {this.listener = listener;handlerThread = new HandlerThread("Analyze-thread");handlerThread.start();//getPreView(),数据丢到GPU而不是CPU,ImageAnalysi会把数据丢到cpu,而且是yuv数据CameraX.bindToLifecycle(lifecycleOwner, getPreView());}private Preview getPreView() {// 分辨率并不是最终的分辨率,CameraX会自动根据设备的支持情况,结合你的参数,设置一个最为接近的分辨率PreviewConfig previewConfig = new PreviewConfig.Builder().setTargetResolution(new Size(640, 480)).setLensFacing(currentFacing) //前置或者后置摄像头.build();Preview preview = new Preview(previewConfig);//需要预览输出的时候,就回调这个方法。preview.setOnPreviewOutputUpdateListener(listener);return preview;}}
package com.maniu.openglbbc;import android.content.Context;
import android.graphics.SurfaceTexture;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;import androidx.camera.core.Preview;
import androidx.lifecycle.LifecycleOwner;import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;//把摄像头的数据渲染到GLSurfaceView
public class CameraView extends GLSurfaceView implements Preview.OnPreviewOutputUpdateListener, GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener {private SurfaceTexture mCameraTexure;ScreenFilter screenFilter;
// 摄像机直接把数据放到GPU内存,这个变量就是数据存放的地方,对cpu没有意义,但对GPU意义重大private int textures=0;public CameraView(Context context) {super(context);}public CameraView(Context context, AttributeSet attrs) {super(context, attrs);
// 使用的版本setEGLContextClientVersion(2);setRenderer(this);//手动渲染setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);initCamera();}private void initCamera() {CameraHelper cameraHelper = new CameraHelper((LifecycleOwner) getContext(),this);}//摄像头打开会回调onUpdated@Overridepublic void onUpdated(Preview.PreviewOutput output) {//camera数据要显示到我们自定义的CameraView控件。但现在两者并无关系//我们要让这两者发生关联。mCameraTexure= output.getSurfaceTexture();}
//监听Surface的创建过程,创建成功的时候就渲染//GLSurfaceView准备好了,就绑定//设置了 setRenderer(this);就会回调onSurfaceCreated()。@Overridepublic void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {//绑定数据,mCameraTexure.attachToGLContext(textures);//onFrameAvailablemCameraTexure.setOnFrameAvailableListener(this);screenFilter = new ScreenFilter( getContext());}// 因为设置了监听(mCameraTexure.setOnFrameAvailableListener(this)),// 一旦摄像头有数据的时候就会回调onFrameAvailable()@Overridepublic void onFrameAvailable(SurfaceTexture surfaceTexture) {
// 手动渲染,需要调用requestRender(),
//requestRender()一旦调用,就会调用onDrawFrame()。//如果是自动渲染16毫秒渲染一次,我们不用自动渲染//摄像头每捕获一次,就渲染一次requestRender();}@Overridepublic void onSurfaceChanged(GL10 gl10, int i, int i1) {}
// 当摄像头捕捉一帧画面的时候调用,把camerax捕获的数据渲染到我们自定义的CameraView。@Overridepublic void onDrawFrame(GL10 gl10) {//确定形状,上色//获取到最新数据。摄像头捕获的数据更新到GPU内存mCameraTexure.updateTexImage();
// 利用openGL渲染float[] mtx = new float[16];
// 得到纠正的矩阵,防止拉伸mCameraTexure.getTransformMatrix(mtx);
// 摄像头捕获一帧数据的时候screenFilter.onDraw(getWidth(), getHeight(),mtx, );}}
package com.maniu.openglbbc;import android.content.Context;
import android.opengl.GLES20;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;public class ScreenFilter {int program;//根据世界坐标系画//两个三角形,OpenGL会把它当作六个点。两个点是共用的。//注意点的顺序,不能换//数据在GPU,//这就是我们说的形状,需要把形状从cpu传给GPUfloat[] VERTEX = {-1.0f, -1.0f,1.0f, -1.0f,-1.0f, 1.0f,1.0f, 1.0f};//输出,android 坐标系,要和世界坐标系一一对应。float[] TEXTURE = {0.0f, 0.0f,1.0f, 0.0f,0.0f, 1.0f,1.0f, 1.0f};private int vPosition;private int vCoord;private int vTexture;private int vMatrix;FloatBuffer vertexBuffer;FloatBuffer textureBuffer; // 纹理坐标public ScreenFilter(Context context) {//读取资源文件String vertexSharder = readRawTextFile(context, R.raw.camera_vert);
// 类似把Java源码打包成jar
// 创建顶点程序,而不是片源程序。vShader是程序地址int vShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
// 加载代码GLES20.glShaderSource(vShader, vertexSharder);
// 编译,会把代码编译成可执行的GLES20.glCompileShader(vShader);int[] status = new int[1];
// 查看程序是否编译成功GLES20.glGetShaderiv(vShader, GLES20.GL_COMPILE_STATUS, status, 0);if (status[0] != GLES20.GL_TRUE) {
// 失败抛异常throw new IllegalStateException("load vertex shader:" + GLES20.glGetShaderInfoLog(vShader));}String fragSharder = readRawTextFile(context, R.raw.camera_frag);int fShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);GLES20.glShaderSource(fShader, fragSharder);GLES20.glCompileShader(fShader);status = new int[1];GLES20.glGetShaderiv(fShader, GLES20.GL_COMPILE_STATUS, status, 0);if (status[0] != GLES20.GL_TRUE) {throw new IllegalStateException("load fragment shader:" + GLES20.glGetShaderInfoLog(fShader));}
// 创建一个总程序program = GLES20.glCreateProgram();// 加载顶点程序GLES20.glAttachShader(program, vShader);// 加载片元程序GLES20.glAttachShader(program, fShader);
// 是程序处于激活状态GLES20.glLinkProgram(program);
// 在CPU中创建4 * 2 * 4内存大小的通道,4个点,每个点两个坐标,每个坐标的值是4个字节,所以是4 * 2 * 4
// order对创建好的内存排序
// asFloatBuffer()转化为float类型vertexBuffer = ByteBuffer.allocateDirect(4 * 2 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();vertexBuffer.clear();//现在数据还在cpu,我们这里已经建立一个通道,需要把VERTEX这个点传到GPU的vPosition//但不需要再初始化的时候传,再OpenGL构建的时候传,即onDraw()方法//openGL四个步骤,确定形状,栅格化,渲染,显示vertexBuffer.put(VERTEX);//把VERTEX这个点传到GPU的vPositiontextureBuffer = ByteBuffer.allocateDirect(4 * 4 * 2).order(ByteOrder.nativeOrder()).asFloatBuffer();textureBuffer.clear();textureBuffer.put(TEXTURE);//用完了一定要释放掉,后来补充的,可能有问题GLES20.glDeleteShader(vShader);GLES20.glDeleteShader(fShader);}//摄像头捕获的形状是2G的,形状不需要自己画,我们只需要把摄像头捕获的数据采样。
// 有数据就渲染一次,public void onDraw(int mWidth, int mHeight, float[] mtx, int textures) {
// 把vertexBuffer(里面有VERTEX点的信息)和vPosition关联起来
// GPU做好准备
// 告诉OpenGL要画的宽高范围是多少GLES20.glViewport(0, 0, mWidth, mHeight);
// 使用program程序GLES20.glUseProgram(program);
// OnDraw()方法会调用很多次,第一次读完后,position会跑到最后的位置,这里重新定位到第一位vertexBuffer.position(0);textureBuffer.position(0);
// 定位到GPU变量地址,返回GPU的地址,vPosition,vCoord,vTexture()再raw文件中定义vPosition = GLES20.glGetAttribLocation(program, "vPosition");vCoord = GLES20.glGetAttribLocation(program, "vCoord");//tUniform指的是片元vTexture = GLES20.glGetUniformLocation(program, "vTexture");
// 矩阵变换vMatrix = GLES20.glGetUniformLocation(program, "vMatrix");
// 传顶点属性到GPU,将CPU中的vertexBuffer数据传给GPU的vPosition
// vPosition:GPU的变量地址
// 2:每个坐标有几个值组成,我们这里是2D,所以是两个点,如果是3d,这里就传3
// GLES20.GL_FLOAT:坐标类型,我们这里是浮点型GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 0, vertexBuffer);
// 告诉GPU启动变量GLES20.glEnableVertexAttribArray(vPosition);
// 纹理坐标系GLES20.glVertexAttribPointer(vCoord, 2, GLES20.GL_FLOAT, false, 0, textureBuffer);GLES20.glEnableVertexAttribArray(vCoord);//camerax(摄像头)捕获的数据放在GPU中,通过textures获取到GPU的数据,//数据找到了,我们需要使用采样器,好比ps中吸取颜色的工具,不断的把数据吸取出来
// 激活第0个图层,采样根据图层采样GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
// 绑定采样器,GLES20.GL_TEXTURE0 表示图层,textures表示摄像头数据存放的地方
// 把摄像头捕获的数据放到图层,这样就不需要操作textures了,只需操作图层就行了。GLES20.glBindTexture(GLES20.GL_TEXTURE0, textures);
// 使用图层。传值给片元的vTexture,这里不需要ByteBuffer,
// 片元传值比顶点传值不一样,没那么复杂。
// 定位到GPU片元程序的变量vTexture。0表示使用第0个图层
// vTexture去第0个图层采样就可以了。GLES20.glUniform1i(vTexture, 0);
// 防止拉伸GLES20.glUniformMatrix4fv(vMatrix, 1, false, mtx, 0);
// 通知GPU进行渲染。
// GLES20.GL_TRIANGLE_STRIP三角形,因为渲染都是三个顶点,
// 从第0个坐标开始渲染,渲染四个坐标GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);}public String readRawTextFile(Context context, int rawId) {InputStream is = context.getResources().openRawResource(rawId);BufferedReader br = new BufferedReader(new InputStreamReader(is));String line;StringBuilder sb = new StringBuilder();try {while ((line = br.readLine()) != null) {sb.append(line);sb.append("\n");}} catch (Exception e) {e.printStackTrace();}try {br.close();} catch (IOException e) {e.printStackTrace();}return sb.toString();}}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
