一、前言
关于计算着色器,我也是刚试验成功,所以接下来我也讲不出什么长篇大论,概念什么的百度一下到处都是,我这边只讲讲百度没有的填坑经历吧。
二、计算着色器的语法解释
先附上一个计算着色器的代码段:
#version 430
layout(local_size_x = 1, local_size_y = 1) in;
layout(rgba8,binding = 0) restrict readonly uniform image2D input_image;
layout(rgba8,binding = 1) restrict writeonly uniform image2D out_image;
void main() {
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
vec4 p = imageLoad(input_image, pos.xy);
barrier();
vec4 color = vec4(0.0,0.0,0.0,1.0);
//此处可以做一些操作,颜色值改变之类的,我这边示例只做了个提取绿色通道图像
color.g = p.g;
imageStore( out_image,
ivec2(gl_GlobalInvocationID.xy),
color);
}
关于计算着色器的内置变量啥的知识,请自行百度。接下来我们逐行解释着色器里写了啥,虽然粗暴了点,但是我觉得很直接。
首先版本号,OpenGL 4.3才开始支持计算着色器,ES是3.1才开始支持计算着色器。
layout(local_size_x = 1, local_size_y = 1) in; -----local_size_x和local_size_y表示的是着色器执行一次的尺寸范围。由于我这里是对纹理做处理,那么这个范围都设置为1,表示的是每次执行针对的是单个像素。类似片元着色器,片元着色器执行一次也是针对纹理上任意一个像素。如果条件允许可以将范围改大,节省次数,比如做高斯模糊的时候,可以将范围设置为卷积核的大小。
ivec2(gl_GlobalInvocationID.xy) ---是当前工作组执行到的位置,粗糙点理解就是当前的纹理像素位置。
imageLoad(input_image, pos.xy) ---通过纹理像素位置获取像素值。
barrier() ---阻断操作,作用是等在当前执行范围(即设置的local_size_x和local_size_y)内的所有像素都执行完上一步操作,才往下执行。由于这里范围设置的是1x1,所以在这里是不需要的。写出来是为了解释它的作用。
接下来是做一些操作,比如片元着色器能做的,都可以做,自行尝试。
imageStore( out_image,ivec2(gl_GlobalInvocationID.xy),color); ----将颜色保存到导出纹理的相同位置,就是保存处理后的结果。
二、计算着色器用途
上面已经写了一个类似片元着色器的用途,由于计算着色器性能突出,其实还可以结合SSBO做一些数据处理,比如顶点的细分,类似几何着色器的工作,将处理后的顶点传给顶点着色器。其主要作用就是处理。
三、附上读写纹理绑定和使用部分的代码
纹理的创建和绑定:
int uTexture[2];
int width, height, nrChannels;
stbi_set_flip_vertically_on_load(true); //告诉stb_image.h在y轴上翻转加载的纹理。
unsigned char* data = stbi_load("TestImg2.jpeg", &ComWidth, &ComHeight, &nrChannels, 0);//通过stb_image.h加载纹理图
glGenTextures(1, &uTexture[0]);
glBindTexture(GL_TEXTURE_2D, uTexture[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, ComWidth, ComHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glBindImageTexture(0, uTexture[0], 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8); //第一个参数表示着色器里面的bingding值
glBindTexture(GL_TEXTURE_2D, 0);
stbi_image_free(data);
glGenTextures(1, &uTexture[1]);
glBindTexture(GL_TEXTURE_2D, uTexture[1]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, ComWidth, ComHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glBindImageTexture(1, uTexture[1], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8);//第一个参数表示着色器里面的bingding值
glBindTexture(GL_TEXTURE_2D, 0);
如果对纹理不熟,可以参考之前的文章-多纹理绑定着色器
计算着色器的创建链接部分:
const char* computeshaderStr = "computeShader.comp";
GLuint computeShader = LoadShader(GL_COMPUTE_SHADER, computeshaderStr);//和普通着色器一样加载,就是类型不一样,设置为GL_COMPUTE_SHADER
GLuint programObject = glCreateProgram();
if (programObject == 0)
return false;
glAttachShader(programObject, computeShader);
glLinkProgram(programObject);
和普通着色器差不多,类型设置为GL_COMPUTE_SHADER。
计算着色器的使用部分:
glUseProgram(programObject);//使用计算着色器
//绑定之前创建的读,写纹理
glBindImageTexture(0, uTexture[0], 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8);
glBindImageTexture(1, uTexture[1], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8);
glDispatchCompute(ComWidth, ComHeight, 1);//设置工作组排列,正常参数是需要分别除以着色器里设置的local_size_x和local_size_y的,由于设置的是1,所以省略了
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);//隔断作用,为了保证计算着色器中纹理像素全部写入完成才进行下一步
//....
//....
//之后可以使用别的着色器,绑定上面得到纹理,就实现了计算着色器的价值
四、总结
网上笔记属实有点少,而且都是零零碎碎的,要么就是讲概念。还好自己没有放弃,摸索出来见到阳光的感觉真好。
结尾:希望我的文章能给有缘人带来帮助,多交流,你我共同进步!