37.2 OpenGL实战之美颜
矩形绘制
VertexShader.glsl
// 简单vertexShader, 负责告诉GPU图像顶点的位置
// vPosition定义的是4维变量, 又应用输入
attribute vec4 vPosition;
void main() {
gl_Position = vPosition;
}
FragmentShader.glsl
// 简单FragmentShader, 负责告诉GPU某个位置的颜色值
// 定义Fragment Shader使用的浮点数的精度为中等精度
precision mediump float;
void main() {
gl_FragColor = vec4(1, 0, 0, 1);
}
- 创建
GLSurfaceView.Renderer
并定义顶点
public class TestRenderer implements GLSurfaceView.Renderer {
private final float[] VERTEXS = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f
};
private final short[] INDEX = {
0, 1, 2,
0, 2, 3
};
private FloatBuffer mVertexBuffer = ByteBuffer.allocateDirect(VERTEXS.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(VERTEXS);
private ShortBuffer mShortBuffer = ByteBuffer.allocateDirect(INDEX.length * 2)
.order(ByteOrder.nativeOrder())
.asShortBuffer()
.put(INDEX);
private String mVertexShader;
private String mFragmentShader;
private int mProgram;
public TestRenderer(String vertexShader, String fragmentShader) {
mVertexBuffer.position(0);
mShortBuffer.position(0);
this.mVertexShader = vertexShader;
this.mFragmentShader = fragmentShader;
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
// 渲染区域
GLES20.glViewport(0, 0, width, height);
}
}
- 创建初始化
shader
, 并创建着色器程序
public static int initShader(int type, String glsl) {
// 创建shader
int shader = GLES20.glCreateShader(type);
// 加载shader源码
GLES20.glShaderSource(shader, glsl);
// 编译shader.
GLES20.glCompileShader(shader);
return shader;
}
public static int createProgram(String vertexShaderSl, String fragmentShaderSl) {
int program = GLES20.glCreateProgram();
int vertexShader = initShader(GLES20.GL_VERTEX_SHADER, vertexShaderSl);
int fragmentShader = initShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderSl);
// 将顶点shader挂载到program
GLES20.glAttachShader(program, vertexShader);
GLES20.glAttachShader(program, fragmentShader);
// 连接shader到program
GLES20.glLinkProgram(program);
// 设置GL上下文使用program程序
GLES20.glUseProgram(program);
return program;
}
// 在Renderer#onSurfaceCreated中调用初始化
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(0, 0, 0, 0);
mProgram = GLUtils.createProgram(mVertexShader, mFragmentShader);
int positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
GLES20.glEnableVertexAttribArray(positionHandle);
// 告诉OpenGL解释顶点
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 12, mVertexBuffer);
}
- 调用绘制
// 在Renderer#onDrawFrame调用GLES20.glDrawElements进行绘制
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// 以顶点信息绘制矩形
GLES20.glDrawElements(GLES20.GL_TRIANGLES, INDEX.length, GLES20.GL_UNSIGNED_SHORT, mShortBuffer);
}
纹理绘制
VertexShader.glsl
attribute vec4 vPosition;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main() {
gl_Position = vPosition;
v_texCoord = a_texCoord;
}
FragmentShader.glsl
precision mediump float;
varying vec2 v_texCoord;
uniform sampler2D s_texture;
void main() {
gl_FragColor = texture2D(s_texture, v_texCoord);
}
- 创建和绑定纹理
public static int[] generateTexture() {
int[] texNames = new int[1];
// 创建纹理
GLES20.glGenTextures(1, texNames, 0);
// 将纹理与编号绑定, 每次切换纹理都需要调用, 重新绑定
GLES20.glBindTexture(GLES20.GL_TEXTURE, texNames[0]);
// 设置纹理拉伸参数, 当纹理比窗口小时, 进行线性拉伸(GL_LINEAR)
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
// 设置纹理超过[0,1]范围, 纹理如何填充
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
return texNames;
}
- 将
Bitmap
转纹理
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
mTexName = GLUtils.generateTexture()[0];
android.opengl.GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mTextureBitmap, 0);
mTextureBitmap.recycle();
GLES20.glClearColor(0, 0, 0, 0);
mProgram = GLUtils.createProgram(mVertexShader, mFragmentShader);
int positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
GLES20.glEnableVertexAttribArray(positionHandle);
// 告诉OpenGL解释顶点
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 0, mVertexBuffer);
int texCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_texCoord");
GLES20.glEnableVertexAttribArray(texCoordHandle);
GLES20.glVertexAttribPointer(texCoordHandle, 2, GLES20.GL_FLOAT, false, 0, mTextureBuffer);
}
- 渲染
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// 激活GL_TEXTURE0纹理
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, INDEX.length, GLES20.GL_UNSIGNED_SHORT, mShortBuffer);
}
相机预览
VertexShader.glsl
attribute vec4 vPosition;
attribute vec4 inputTextureCoordinate;
uniform mat4 textureTransform;
varying vec2 textureCoordinate;
void main() {
textureCoordinate = (textureTransform * inputTextureCoordinate).xy;
gl_Position = vPosition;
}
FragmentShader.glsl
// 由于Camera使用的是OES纹理, 这里必须声明
#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 textureCoordinate;
uniform samplerExternalOES inputImageTexture;
void main() {
gl_FragColor = texture2D(inputImageTexture, textureCoordinate);
}
- 打开相机预览,
Camera
与SurfaceTexture
绑定, 并把SurfaceTexture
和纹理绑定, 即帮输入源绑定到纹理上
public void startCamera(SurfaceTexture surfaceTexture) {
// 打开摄像头, (0-后置, 1-前置)
mCamera = Camera.open();
try {
// 设置预览纹理
mCamera.setPreviewTexture(surfaceTexture);
Camera.Parameters parameters = mCamera.getParameters();
List<Camera.Size> cameraSize = parameters.getSupportedPreviewSizes();
Camera.Size size = cameraSize.get(0);
parameters.setPreviewSize(size.width, size.height);
mCamera.setParameters(parameters);
mCamera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
mVertexBuffer.position(0);
mIndexBuffer.position(0);
mVertexShader = getStringShader("test_1_camera_vs.glsl");
mFragmentShader = getStringShader("test_1_camera_fs.glsl");
int program = GLUtils.createProgram(mVertexShader, mFragmentShader);
int positionHandle = GLES20.glGetAttribLocation(program, "vPosition");
GLES20.glEnableVertexAttribArray(positionHandle);
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 12, mVertexBuffer);
int inputTextureCoordHandle = GLES20.glGetAttribLocation(program, "inputTextureCoordinate");
GLES20.glEnableVertexAttribArray(inputTextureCoordHandle);
GLES20.glVertexAttribPointer(inputTextureCoordHandle, 2, GLES20.GL_FLOAT, false, 8, mTextureBuffer);
transformHandle = GLES20.glGetUniformLocation(program, "textureTransform");
// SurfaceTexture和纹理绑定
int tex[] = new int[1];
GLES20.glGenTextures(1, tex,0);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, tex[0]);
GLES20.glActiveTexture(tex[0]);
mSurfaceTexture = new SurfaceTexture(tex[0]);
mSurfaceTexture.setOnFrameAvailableListener(this);
cameraUtils = new CameraUtils();
// 启动相机
cameraUtils.startCamera(mSurfaceTexture);
}
- 在
SurfaceTexture.OnFrameAvailableListener#onFrameAvailable
中调用GLSurfaceView#requestRender()
, Camera采集时更新并渲染
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
requestRender();
}
- 渲染
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
mSurfaceTexture.updateTexImage();
mSurfaceTexture.getTransformMatrix(mtx);
GLES20.glUniformMatrix4fv(transformHandle, 1, false, mtx, 0);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, INDEX.length, GLES20.GL_UNSIGNED_SHORT, mIndexBuffer);
}
滤镜
- 黑白滤镜, 调整图像的RGB值, 使得通道RGB在[0, 255]且值相当, 即可给RGB通道赋予权重, 且权重值等于1
#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 textureCoordinate;
uniform samplerExternalOES inputImageTexture;
const highp vec3 W = vec3(0.300, 0.587, 0.115);
void main() {
vec3 imageColor = texture2D(inputImageTexture, textureCoordinate).rgb;
float value = dot(W, imageColor);
gl_FragColor = vec4(value, value, value, 1);
}
- 模糊滤镜, 即缩小临近点的差值, 将周围像素和当前像素混合平均, 即将
fragmentShader
调整rgb通道即可
#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 textureCoordinate;
uniform samplerExternalOES inputImageTexture;
const highp vec3 W = vec3(0.300, 0.587, 0.115);
vec2 blurCoordinates[4];
void main() {
blurCoordinates[0] = textureCoordinate.xy + vec2(0.0, -1.0 / 1920.0);
blurCoordinates[1] = textureCoordinate.xy + vec2(0.0, 1.0 / 1920.0);
blurCoordinates[2] = textureCoordinate.xy + vec2(-1.0 / 1080.0, 0.0);
blurCoordinates[3] = textureCoordinate.xy + vec2(1.0 / 1080.0, 0.0);
vec4 color = texture2D(inputImageTexture, textureCoordinate);
for (int i = 0; i < 4; i++) {
color += texture2D(inputImageTexture, blurCoordinates[i]);
}
gl_FragColor = color / 5.0;
}