37.2 OpenGL实战之美颜

矩形绘制

  1. VertexShader.glsl
// 简单vertexShader, 负责告诉GPU图像顶点的位置
// vPosition定义的是4维变量, 又应用输入
attribute vec4 vPosition;

void main() {
    gl_Position = vPosition;
}
  1. FragmentShader.glsl
// 简单FragmentShader, 负责告诉GPU某个位置的颜色值
// 定义Fragment Shader使用的浮点数的精度为中等精度
precision mediump float;

void main() {
    gl_FragColor = vec4(1, 0, 0, 1);
}
  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);
    }
}

  1. 创建初始化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);
   }

  1. 调用绘制
  // 在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);
  }

纹理绘制

  1. VertexShader.glsl
  attribute vec4 vPosition;
  attribute vec2 a_texCoord;

  varying vec2 v_texCoord;

  void main() {
      gl_Position = vPosition;
      v_texCoord = a_texCoord;
  }
  1. FragmentShader.glsl
  precision mediump float;
  varying vec2 v_texCoord;

  uniform sampler2D s_texture;

  void main() {
    gl_FragColor = texture2D(s_texture, v_texCoord);
  }
  1. 创建和绑定纹理
  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;
    }
  1. 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);

 }

  1. 渲染
    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);
    }

相机预览

  1. VertexShader.glsl

attribute vec4 vPosition;
attribute vec4 inputTextureCoordinate;

uniform mat4 textureTransform;

varying vec2 textureCoordinate;

void main() {
    textureCoordinate = (textureTransform * inputTextureCoordinate).xy;
    gl_Position = vPosition;
}

  1. 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);
}
  1. 打开相机预览, CameraSurfaceTexture绑定, 并把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);
    }
  1. SurfaceTexture.OnFrameAvailableListener#onFrameAvailable中调用GLSurfaceView#requestRender(), Camera采集时更新并渲染
  public void onFrameAvailable(SurfaceTexture surfaceTexture) {
       requestRender();
   }
  1. 渲染
  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;
}

美颜特效

OpenGL调试与优化

参考