最近在做一个项目,需要用到compute shader来进行复杂化的渲染,能够提高渲染性能。
在实践过程中,发现了几个问题,需要在使用中注意。
1、由于项目中用到了samplerExternalOES格式,所以利用了一种方法samplerExternalOES--->framebuffer(image2D)--->compute shader处理-->image2D-->vect/frament渲染。
更正:compute shader中支持samplerExternalOES扩展格式,使用方法需要在shader中layout(binding = 1)的方式,不能用glGetUniformLocation的方式获取。
layout (binding = 1) uniform samplerExternalOES colorAndDepthTexture;
2、在onDrawFrame的时候,viewport需要指定到跟你输出的image的大小(width,height)相同。否则生成的纹理有异样。
3、在onDrawFrame的时候,绑定framebuffer之后,需要进行clear,否则输出的image为空。
GLES32.glViewport(0,0, width, height);
GLES32.glUseProgram(mProgram);
GLES32.glBindFramebuffer(GLES32.GL_FRAMEBUFFER, mCsFrameBufferID);
GLES32.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
GLES32.glClear(GLES32.GL_COLOR_BUFFER_BIT | GLES32.GL_DEPTH_BUFFER_BIT);
4、在compute shader中,image2D需要使用layout (rgba8, binding = 1)格式进行绑定,使用binding指定位置序列,不能使用glGetUniformLocation的方式获取位置序号。
//compute shader:
layout (rgba8, binding = 1) uniform readonly image2D colorAndDepthTexture;
layout (rgba8, binding = 2) uniform writeonly image2D outputImage;
//渲染代码
GLES32.glBindImageTexture(1, mCsImageIDs[0], 0, false, 0, GLES32.GL_READ_ONLY, GLES32.GL_RGBA8);
GLES32.glBindImageTexture(2, mCsImageIDs[1], 0, false, 0, GLES32.GL_WRITE_ONLY, GLES32.GL_RGBA8);
这些都是我实践中碰到的问题,下面展示一个获取摄像头预览增加特效的一个例子。
compute shander:
#version 310 es
#extension GL_OES_EGL_image_external_essl3 : enable
layout(rgba8, binding = 1) uniform readonly image2D inTexture;
layout(rgba8, binding = 2) uniform writeonly image2D outTexture;
layout(location = 3) uniform ivec2 alignmentMin;
layout(location = 4) uniform ivec2 alignmentMax;
layout(location = 5) uniform int colorScale;
layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
void main()
{
ivec2 storePos = ivec2(gl_GlobalInvocationID.xy);
vec4 texColor = imageLoad(inTexture, storePos).rgba;
if(storePos.x > alignmentMin.x && storePos.x < alignmentMax.x) {
if(storePos.y > alignmentMin.y && storePos.y < alignmentMax.y) {
if(colorScale == 0) {
float newPixel = .299f * texColor.r + .587f * texColor.g + .114f * texColor.b;
imageStore(outTexture, storePos, vec4(newPixel, newPixel, newPixel, texColor.a));
} else {
imageStore(outTexture, storePos, vec4(1.5f * texColor.r, 0.1f * texColor.g, 0.3f * texColor.b, texColor.a));
}
} else {
if(colorScale == 0) {
imageStore(outTexture, storePos, texColor);
} else {
imageStore(outTexture, storePos, vec4(0.3f * texColor.r, 1.5f * texColor.g, 0.3f * texColor.b, texColor.a));
}
}
} else {
if(colorScale == 0) {
imageStore(outTexture, storePos, texColor);
} else {
imageStore(outTexture, storePos, vec4(0.3f * texColor.r, 0.1f * texColor.g, 1.5f * texColor.b, texColor.a));
}
}
}
Render代码:
private void initTex() {
textures = new int[1];
GLES31.glGenTextures(1, textures, 0);
GLES31.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0]);
GLES31.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES31.GL_TEXTURE_WRAP_S, GLES31.GL_CLAMP_TO_EDGE);
GLES31.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES31.GL_TEXTURE_WRAP_T, GLES31.GL_CLAMP_TO_EDGE);
GLES31.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES31.GL_TEXTURE_MIN_FILTER, GLES31.GL_NEAREST);
GLES31.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES31.GL_TEXTURE_MAG_FILTER, GLES31.GL_NEAREST);
texture2D = new int[2];
GLES31.glGenTextures(2, texture2D, 0);
GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, texture2D[0]);
GLES31.glTexParameteri(GLES31.GL_TEXTURE_2D, GLES31.GL_TEXTURE_WRAP_S, GLES31.GL_CLAMP_TO_EDGE);
GLES31.glTexParameteri(GLES31.GL_TEXTURE_2D, GLES31.GL_TEXTURE_WRAP_T, GLES31.GL_CLAMP_TO_EDGE);
GLES31.glTexParameteri(GLES31.GL_TEXTURE_2D, GLES31.GL_TEXTURE_MIN_FILTER, GLES31.GL_NEAREST);
GLES31.glTexParameteri(GLES31.GL_TEXTURE_2D, GLES31.GL_TEXTURE_MAG_FILTER, GLES31.GL_NEAREST);
GLES31.glTexStorage2D(GLES31.GL_TEXTURE_2D, 1, GLES31.GL_RGBA8, mPreviewSize.getWidth(), mPreviewSize.getHeight());
GLES31.glTexSubImage2D(GLES31.GL_TEXTURE_2D, 0, 0, 0, mPreviewSize.getWidth(), mPreviewSize.getHeight(), GLES31.GL_RGBA, GLES31.GL_UNSIGNED_BYTE, null);
GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, texture2D[1]);
GLES31.glTexParameteri(GLES31.GL_TEXTURE_2D, GLES31.GL_TEXTURE_WRAP_S, GLES31.GL_CLAMP_TO_EDGE);
GLES31.glTexParameteri(GLES31.GL_TEXTURE_2D, GLES31.GL_TEXTURE_WRAP_T, GLES31.GL_CLAMP_TO_EDGE);
GLES31.glTexParameteri(GLES31.GL_TEXTURE_2D, GLES31.GL_TEXTURE_MIN_FILTER, GLES31.GL_NEAREST);
GLES31.glTexParameteri(GLES31.GL_TEXTURE_2D, GLES31.GL_TEXTURE_MAG_FILTER, GLES31.GL_NEAREST);
GLES31.glTexStorage2D(GLES31.GL_TEXTURE_2D, 1, GLES31.GL_RGBA8, mPreviewSize.getWidth(), mPreviewSize.getHeight());
GLES31.glTexSubImage2D(GLES31.GL_TEXTURE_2D, 0, 0, 0, mPreviewSize.getWidth(), mPreviewSize.getHeight(), GLES31.GL_RGBA, GLES31.GL_UNSIGNED_BYTE, null);
frameBuffer = new int[1];
GLES31.glGenFramebuffers(1, frameBuffer, 0);
renderBuffer = new int[1];
GLES31.glGenRenderbuffers(1, renderBuffer, 0);
GLES31.glActiveTexture(GLES31.GL_TEXTURE1);
GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, texture2D[0]);
GLES31.glBindRenderbuffer(GLES31.GL_RENDERBUFFER, renderBuffer[0]);
GLES31.glRenderbufferStorage(GLES31.GL_RENDERBUFFER, GLES31.GL_DEPTH_COMPONENT16, mPreviewSize.getWidth(), mPreviewSize.getHeight());
GLES31.glBindFramebuffer(GLES31.GL_FRAMEBUFFER, frameBuffer[0]);
GLES31.glFramebufferTexture2D(GLES31.GL_FRAMEBUFFER, GLES31.GL_COLOR_ATTACHMENT0, GLES31.GL_TEXTURE_2D, texture2D[0], 0);
GLES31.glFramebufferRenderbuffer(GLES31.GL_FRAMEBUFFER, GLES31.GL_DEPTH_ATTACHMENT, GLES31.GL_RENDERBUFFER, renderBuffer[0]);
GLES20.glBindFramebuffer(GLES31.GL_FRAMEBUFFER, 0);
}
public void onDrawFrame ( GL10 unused ) {
if ( !mGLInit ) return;
//FPS Counter
// long currentTime = System.currentTimeMillis();
// ++frameCounter;
//
// if (currentTime - lastTime >= 1000) {
// System.out.println("Frames per second: " + frameCounter);
// frameCounter = 0;
// lastTime = currentTime;
// }
GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT);
synchronized(this) {
if (mUpdateST) {
mSTexture.updateTexImage();
mUpdateST = false;
}
}
GLES31.glUseProgram(renderProgram);
GLES31.glBindFramebuffer(GLES31.GL_FRAMEBUFFER, frameBuffer[0]);
int positionHandler = GLES31.glGetAttribLocation(renderProgram, "aPosition");
int texCoordHandler = GLES31.glGetAttribLocation ( renderProgram, "aTexCoord" );
GLES31.glVertexAttribPointer(positionHandler, 2, GLES31.GL_FLOAT, false, 4 * 2, vertexBuffer);
GLES31.glVertexAttribPointer(texCoordHandler, 2, GLES31.GL_FLOAT, false, 4 * 2, tempTexCoord);
GLES31.glEnableVertexAttribArray(positionHandler);
GLES31.glEnableVertexAttribArray(texCoordHandler);
GLES31.glActiveTexture(GLES31.GL_TEXTURE0);
GLES31.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0]);
GLES31.glUniform1i(GLES31.glGetUniformLocation(renderProgram, "sTexture"), 0);
GLES31.glUniform1i(GLES31.glGetUniformLocation(renderProgram, "isOffScreen"), 1);
GLES31.glDrawArrays(GLES31.GL_TRIANGLE_STRIP, 0, 4);
GLES31.glFlush();
GLES31.glFinish();
GLES31.glBindFramebuffer(GLES31.GL_FRAMEBUFFER, 0);
GLES31.glUseProgram(0);
GLES31.glUseProgram(computeProgram);
GLES31.glBindImageTexture(1, texture2D[0], 0, false, 0, GLES31.GL_READ_ONLY, GLES31.GL_RGBA8);
GLES31.glBindImageTexture(2, texture2D[1], 0, false, 0, GLES31.GL_WRITE_ONLY, GLES31.GL_RGBA8);
GLES31.glUniform2i(GLES31.glGetUniformLocation(computeProgram, "alignmentMin"), aligmentsMin[0], aligmentsMin[1]);
GLES31.glUniform2i(GLES31.glGetUniformLocation(computeProgram, "alignmentMax"), aligmentsMax[0], aligmentsMax[1]);
if(computeShaderMode == ComputeShader.ColorScaler) {
GLES31.glUniform1i(GLES31.glGetUniformLocation(computeProgram, "colorScale"), colorScale);
} else if (computeShaderMode == ComputeShader.ColorQuantizer) {
GLES31.glBindBufferBase(GLES31.GL_SHADER_STORAGE_BUFFER, 5, centroidBuffer[0]);
}
GLES31.glDispatchCompute(mPreviewSize.getWidth() / 8, mPreviewSize.getHeight() / 8, 1);
GLES31.glMemoryBarrier(GLES31.GL_ALL_SHADER_BITS);
if (computeShaderMode == ComputeShader.ColorQuantizer) {
GLES31.glBindBufferBase(GLES31.GL_SHADER_STORAGE_BUFFER, 5, 0);
}
GLES31.glUseProgram(renderProgram);
positionHandler = GLES31.glGetAttribLocation(renderProgram, "aPosition");
texCoordHandler = GLES31.glGetAttribLocation ( renderProgram, "aTexCoord" );
GLES31.glVertexAttribPointer(positionHandler, 2, GLES31.GL_FLOAT, false, 4 * 2, vertexBuffer);
GLES31.glVertexAttribPointer(texCoordHandler, 2, GLES31.GL_FLOAT, false, 4 * 2, texCoords);
GLES31.glEnableVertexAttribArray(positionHandler);
GLES31.glEnableVertexAttribArray(texCoordHandler);
GLES31.glActiveTexture(GLES31.GL_TEXTURE2);
GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, texture2D[1]);
GLES31.glUniform1i(GLES31.glGetUniformLocation(renderProgram, "inTexture"), 2);
GLES31.glUniform1i(GLES31.glGetUniformLocation(renderProgram, "isOffScreen"), 0);
GLES31.glDrawArrays(GLES31.GL_TRIANGLE_STRIP, 0, 4);
GLES31.glFlush();
}
参考文献:
https://github.com/The-Shader/Android-GPU
https://www.khronos.org/assets/uploads/developers/library/2014-siggraph-bof/KITE-BOF_Aug14.pdf
https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/05%20Framebuffers/