opengl es利用compute shader进行渲染探索实践

最近在做一个项目,需要用到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/

https://www.khronos.org/opengl/wiki/Compute_Shader

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
OpenGL Compute ShaderOpenGL 4.3引入的一种新型Shader,它主要用于通用计算(GPGPU)任务而不是图形渲染。它可以在GPU上执行高度并行的计算任务,例如物理模拟、图像处理和机器学习等。Compute Shader与其他Shader不同之处在于它没有渲染管道的输入和输出,并且可以从CPU上下文中直接调用。它还可以读写各种类型的缓冲区和纹理,使其非常灵活和强大。 Compute Shader的使用步骤如下: 1.创建Compute Shader对象:使用glCreateShader函数创建Compute Shader对象。 2.编译Compute Shader代码:使用glShaderSource和glCompileShader函数将Compute Shader代码编译为OpenGL可识别的二进制格式。 3.创建Compute Program对象:使用glCreateProgram函数创建Compute Program对象。 4.将Compute Shader附加到Compute Program对象上:使用glAttachShader函数将Compute Shader附加到Compute Program对象上。 5.链接Compute Program对象:使用glLinkProgram函数将Compute Program对象链接到OpenGL渲染管道。 6.使用Compute Shader:使用glUseProgram函数激活Compute Program对象,并通过glDispatchCompute函数调用Compute Shader。 7.清理资源:使用glDeleteShader和glDeleteProgram函数删除Compute ShaderCompute Program对象。 下面是一个简单的Compute Shader示例代码: ```glsl #version 430 layout(local_size_x = 16, local_size_y = 16) in; layout(std430, binding = 0) buffer InputBuffer { float data[]; }; layout(std430, binding = 1) buffer OutputBuffer { float result[]; }; void main() { uint idx = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * gl_NumWorkGroups.x * gl_WorkGroupSize.x; result[idx] = data[idx] * data[idx]; } ``` 这个Compute Shader使用输入缓冲区和输出缓冲区,对输入缓冲区的每个元素进行平方运算,并将结果存储在输出缓冲区中。在主函数中,使用gl_GlobalInvocationID获取全局线程ID,计算出要处理的输入元素的索引,并在输出缓冲区中存储计算结果。 最后,通过调用glDispatchCompute函数启动Compute Shader。该函数需要指定调度的工作组数量,以及每个工作组中线程的数量。在这个示例中,我们使用16x16的工作组,并将其应用于输入缓冲区的所有元素。 ```c++ glDispatchCompute(numGroupsX, numGroupsY, numGroupsZ); ``` 以上就是OpenGL Compute Shader的原理与使用方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XR风云

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值