上一篇写了OpenGL基础理论
最近趁着空余时间把OpenGL纹理学习了,然后结合CameraX做了下滤镜效果,还是打算记录下学习效果。
关于纹理的介绍可以学习这篇文章
一、CameraRender
数据源使用CameraX,没有Camera2代码的复杂,CameraX的使用比较简单,之前写的这篇文章有过CameraX的介绍使用,这里不做过多赘述了。
public class CameraRender implements GLSurfaceView.Renderer, Preview.OnPreviewOutputUpdateListener, SurfaceTexture.OnFrameAvailableListener {
private static final String TAG = "CameraRender";
private CameraHelper cameraHelper;
private CameraView cameraView;
private SurfaceTexture mCameraTexture;
private int[] textures;
float[] mtx = new float[16];
private CameraFilter screenFilter;
public CameraRender(CameraView cameraView) {
this.cameraView = cameraView;
LifecycleOwner lifecycleOwner = (LifecycleOwner) cameraView.getContext();
cameraHelper = new CameraHelper(lifecycleOwner, this);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
textures = new int[1];
//让SurfaceTexture与Gpu共享一个数据源 0-31
mCameraTexture.attachToGLContext(textures[0]);
//监听摄像头数据回调
mCameraTexture.setOnFrameAvailableListener(this);
screenFilter = new CameraFilter(cameraView.getContext());
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//保存宽高
screenFilter.setSize(width,height); //初始化完成
}
@Override
public void onDrawFrame(GL10 gl) {
// Log.e(TAG, "线程: " + Thread.currentThread().getName());
// 更新摄像头的数据,给了gpu
mCameraTexture.updateTexImage();
// 每次updateTexImage()被调用时,纹理矩阵都可能发生变化。所以,每次texture image被更新时,getTransformMatrix ()也应该被调用。
mCameraTexture.getTransformMatrix(mtx);
screenFilter.setTransformMatrix(mtx);
// 开始绘制
screenFilter.onDraw(textures[0]);
}
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
//一帧一帧回调时,手动渲染
cameraView.requestRender();
}
@Override
public void onUpdated(Preview.PreviewOutput output) {
// 摄像头预览到的数据 在这里
mCameraTexture = output.getSurfaceTexture();
}
}
二、顶点坐标和纹理坐标
//顶点坐标
float[] VERTEX = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f
};
//纹理坐标
float[] TEXTURE = {
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f
};
两个坐标需要对应上,不然显示出来的图像可能显示不正常,因为用OpenGL渲染就是把Camera的图像贴到纹理坐标上,所以要对应上,上面是固定的
三、顶点着色器
//把顶点坐标给这个变量, 确定要画画的形状
//字节定义的 4个 数组 矩阵
attribute vec4 vPosition;//
//运行在GPU中
//接收纹理坐标,接收采样器采样图片的坐标 camera
attribute vec4 vCoord;
//oepngl camera opengl坐标和camera坐标不一样,这里需要转换下,vMatrix是通过getTransformMatrix获取的
uniform mat4 vMatrix;
//传给片元着色器 像素点
varying vec2 aCoord;
void main(){
// gpu需要渲染的,什么图像、形状
gl_Position=vPosition;
aCoord= (vMatrix * vCoord).xy;
}
四、片元着色器
#extension GL_OES_EGL_image_external : require
//必须 写的 固定的 意思 用采样器
//所有float类型数据的精度是lowp
precision mediump float;
varying vec2 aCoord;
//采样器 uniform static
uniform samplerExternalOES vTexture;
void main(){
//Opengl 自带函数
vec4 rgba = texture2D(vTexture,aCoord);
// 灰色 滤镜
float color=(rgba.r + rgba.g + rgba.b) / 3.0;
vec4 tempColor=vec4(color,color,color,1);
gl_FragColor=tempColor;
}
最后的滤镜效果,还有其他的,这里不一一列举了
五、着色器编译和创建缓存区
public AbstractFilter(Context context, int vertexSharderId, int fragSharderId) {
//GPU中创建一个缓冲区,提供给CPU,然后数据通过这个放到缓冲区
vertexBuffer = ByteBuffer.allocateDirect(4 * 4 * 2)
.order(ByteOrder.nativeOrder()) //重新整理下内存
.asFloatBuffer();
vertexBuffer.clear();
vertexBuffer.put(VERTEX);
textureBuffer = ByteBuffer.allocateDirect(4 * 4 * 2)
.order(ByteOrder.nativeOrder()) //重新整理下内存
.asFloatBuffer();
textureBuffer.clear();
textureBuffer.put(TEXTURE);
String vertexSharder = OpenGLUtils.readRawTextFile(context, vertexSharderId);
String fragSharder = OpenGLUtils.readRawTextFile(context, fragSharderId);
//给gpu使用的
program = OpenGLUtils.loadProgram(vertexSharder, fragSharder);
vPosition = GLES20.glGetAttribLocation(program, "vPosition");//0
//接收纹理坐标,接收采样器采样图片的坐标
vCoord = GLES20.glGetAttribLocation(program, "vCoord");//1
//采样点的坐标
vTexture = GLES20.glGetUniformLocation(program, "vTexture");
}
GPU中数据不能直接操作,这里拿到索引间接操作。并且会创建一个缓冲区,提供给CPU,然后数据通过ByteBuffer放到缓冲区,提供给GPU使用。
六、绘制
//摄像头数据,开始渲染
public int onDraw(int texture) {
// View 的大小
GLES20.glViewport(0, 0, mWidth, mHeight);
// 使用程序
GLES20.glUseProgram(program);
// 从索引为0的地方读
vertexBuffer.position(0);
// index 指定要修改的通用顶点属性的索引。
// size 指定每个通用顶点属性的组件数。
// type 指定数组中每个组件的数据类型。
// 接受符号常量GL_FLOAT GL_BYTE,GL_UNSIGNED_BYTE,GL_SHORT,GL_UNSIGNED_SHORT或GL_FIXED。 初始值为GL_FLOAT。
// normalized 指定在访问定点数据值时是应将其标准化(GL_TRUE)还是直接转换为定点值(GL_FALSE)。
// stride 步长
// 使句柄和数据绑定
GLES20.glVertexAttribPointer(vPosition, 2, GL_FLOAT, false, 0, vertexBuffer);
// vPosition生效
GLES20.glEnableVertexAttribArray(vPosition);
textureBuffer.position(0);
GLES20.glVertexAttribPointer(vCoord, 2, GL_FLOAT, false, 0, textureBuffer);
//CPU传数据到GPU,默认情况下着色器无法读取到这个数据。 需要我们启用一下才可以读取
GLES20.glEnableVertexAttribArray(vCoord);
// 激活图层
GLES20.glActiveTexture(GL_TEXTURE0);
// 生成一个采样
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
GLES20.glUniform1i(vTexture, GL_TEXTURE0);
beforeDraw();
// 通知渲染, 没有调用fbo,就渲染到屏幕中了
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
return texture;
}
OpenGL代码写好了,大部分就不用改动了,需要改动的也只是特效部分,也就是着色器。