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);

  圖片:

  

  

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM