zoukankan      html  css  js  c++  java
  • OpenGL 之 Compute Shader(通用计算并行加速)

      平常我们使用的Shader有顶点着色器、几何着色器、片段着色器,这几个都是为光栅化图形渲染服务的,OpenGL 4.3之后新出了一个Compute Shader,用于通用计算并行加速,现在对其进行介绍。

      

      介绍Compute Shader之前需要先介绍一下ImageTexture

        普通的Texture在GLSL中只能进行读取(sampler采样获取数据),写入则必须在Fragment Shader中写入帧缓冲绑定的附件Texture当前像素中,不能随意指定位置写入,并且不能同时读写同一张纹理(我试过不行,有博客同样说不行,应该是不行吧)。

      1、生成Texture

    void WKS::ImageTexture::setupTexture() {
        glGenTextures(1, &this->textureID);
        glBindTexture(GL_TEXTURE_2D, this->textureID);
        glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, width, height);
        // turn off filtering and wrap modes
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
        glBindTexture(GL_TEXTURE_2D, 0);
    }

      注意,要是用 glTexStorage2D()生成固定大小纹理,不能使用glTexImage2D()

      2、生成ImageTexture

    glBindImageTexture(0, this->inputTexture, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);

      inputTexture对应1、中生成的Texture纹理ID。第一个参数是ImageTexture绑定点,与texture纹理绑定点应该不重合。

      3、GLSL中声明

    layout (rgba32f, binding = 0) uniform image2D input_image;

      补充:ImageTexture底层是Texture,那么在Host上可以进行访问

        a、初始化,传入数据

    void WKS::ImageTexture::Transfer2Texture(float* data) {
        glBindTexture(GL_TEXTURE_2D, this->textureID);
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_FLOAT, data);
    }

        b、读取数据

    float* WKS::Texture::GetTextureData(GLuint width, GLuint height, GLuint channels, GLuint texID) {
        float* data = new float[width * height * channels];
        glBindTexture(GL_TEXTURE_2D, texID);
        if(channels==1)    glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_FLOAT, data);
        if(channels==3) glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_FLOAT, data);
        if (channels == 4) glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, data);
        glBindTexture(GL_TEXTURE_2D, 0);
        return data;
    }

      现在来介绍Compute Shader

    #version 430 core
    layout (local_size_x=16, local_size_y=16) in;
    
    uniform float v[4];
    
    layout (rgba32f, binding = 0) uniform image2D input_image;
    layout (rgba32f, binding = 1) uniform image2D output_image;
    
    shared vec4 mat_shared[16][16];
    
    void main(void)
    {
        ivec2 pos=ivec2(gl_GlobalInvocationID.xy);
        mat_shared[pos.x][pos.y]=imageLoad(input_image,pos);
        barrier();
        vec4 data=mat_shared[pos.x][pos.y];
        data.r=v[0]+data.r;
        data.g=v[1]+data.g;
        data.b=v[2]+data.b;
        data.a=v[3]+data.a;
        imageStore(output_image,pos.xy,data);
    }

      

      计算由一个一个计算单元完成,layout (local_size_x=16, local_size_y=16) in; 是表示本地工作组的由16*16的计算单元组成,本地工作组可以共享Shadered变量。
      多个本地工作组构成全局工作组,由:
    glDispatchCompute(1, 1, 1);

      启动计算,参数表示全局工作组的维度(以本地工作组为单位),(1,1,1)表示只有一个本地工作组。

      注意:Compute Shader 只有一个阶段(渲染一般是vertex+fragment 2个阶段),编译类型选择GL_COMPUTE_SHADER

    Shader(const char* computePath) :programId(0)
        {
            std::vector<ShaderFile> fileVec;
            fileVec.push_back(ShaderFile(GL_COMPUTE_SHADER, computePath));
            loadFromFile(fileVec);
        }

      

      示例:

      对一个4*4的vec4矩阵的所有元素加上vec4(0, 0.1,0.2,0.3)

      初始化:

    void SceneRendering::setupAddData() {
        int num = 4 * 4 * 4;
        this->inputData = new float[num];
        for (int i = 0; i < num; i++) inputData[i] = i;
        for (int i = 0; i < 4; i++) v[i] = i*0.1f;
        shader_add = new Shader("./Shader/add.comp");
        WKS::ImageTexture* texturePtr = new WKS::ImageTexture(4, 4);
        this->inputTexture = texturePtr->GetTextureID();
        this->outputTexture = (new WKS::ImageTexture(4, 4))->GetTextureID();
        texturePtr->Transfer2Texture(inputData);
    }

      调用Compute Shader:

    void SceneRendering::performCompute() {
        this->shader_add->use();
        this->shader_add->setVecN("v", 4, v);
        glBindImageTexture(0, this->inputTexture, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
        glBindImageTexture(1, this->outputTexture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
        glDispatchCompute(1, 1, 1);
        glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
        glFinish();
    }

      主函数调用,结果输出:

       glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 我们现在不使用模板缓冲//Compute Shader
        this->performCompute();
        float* data = WKS::Texture::GetTextureData(4, 4, 4, this->outputTexture);
        int index = 0;
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                std::cout << "(" <<data[index]<<","<<data[index+1]<<","<<data[index+2]<<","<<data[index+3]<< ")" << " ";
                index += 4;
            }
            std::cout << std::endl;
        }
        std::cout<< std::endl;
        free(data);

      图片:

      

      

      

  • 相关阅读:
    你敢说自己了解单例模式?
    关于线程池,那些你还不知道的事
    Dubbo透传traceId/logid的一种思路
    当BeanUtils遇到泛型
    Oval框架如何校验枚举类型的一种思路
    HttpClient(4.5.x)正确的使用姿势
    HttpClient官方sample代码的深入分析(连接池)
    Jaxb如何优雅的处理CData
    JAXB性能优化
    Jaxb对xml报文头的小修小改
  • 原文地址:https://www.cnblogs.com/chen9510/p/12000320.html
Copyright © 2011-2022 走看看