OpenGL之计算着色器(Compute Shader)注解

一、前言

关于计算着色器,我也是刚试验成功,所以接下来我也讲不出什么长篇大论,概念什么的百度一下到处都是,我这边只讲讲百度没有的填坑经历吧。

二、计算着色器的语法解释

先附上一个计算着色器的代码段:

#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);//隔断作用,为了保证计算着色器中纹理像素全部写入完成才进行下一步

        //....
        //....

        //之后可以使用别的着色器,绑定上面得到纹理,就实现了计算着色器的价值

四、总结

网上笔记属实有点少,而且都是零零碎碎的,要么就是讲概念。还好自己没有放弃,摸索出来见到阳光的感觉真好。

结尾:希望我的文章能给有缘人带来帮助,多交流,你我共同进步!

你可能感兴趣的:(OpenGL探索之路,着色器,opengl)