• 作者:老汪软件技巧
  • 发表时间:2024-09-07 11:06
  • 浏览量:

最新大型开源项目-云游戏,云桌面系统,欢迎关注

GammaRay源码地址

本项目代码仓库

点击这里

1. 着色器的构成

前面的章节,简单用了一下着色器,着色器其实是一种类似C语言的编程语言,主要区别与C的是添加了一些与矩阵,向量运算相关的操作, 现在看一下它的构成:

#version 版本号
in type in_variable_name;
in type in_variable_name;
out type out_variable_name;
uniform type uniform_name;
int main()
{
  一些处理
  out_variable_name = value;
}

上面是一个着色器的基本结构,详细解释如下:

vecn	包含n个float分量的默认向量
bvecn	包含n个bool分量的向量
ivecn	包含n个int分量的向量
uvecn	包含n个unsigned int分量的向量
dvecn	包含n个double分量的向量

int attrib_size;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &attrib_size);

#version 330 core
void main()
{
    gl_FragColor = vec4(1,1,1,1);
}

等效于:

#version 330 core
out vec4 color;
void main()
{
    color = vec4(1,1,1,1);
}

而不必一定使用内置的gl_FragColor。

2.让我们矩形根据时间变色,但颜色单一

有了上面的知识,我们可以得到方法,定义一个unifrom的值,让他随着时间变化,然后赋值给我们片段着色器的输出,那么就让矩形的颜色得到了变化。首先修改我们的片段着色器:

#version 330 core
uniform vec3 color;
void main()
{
    gl_FragColor = vec4(color, 1.0f);
}

添加了一个vec3类型的uniform变量,然后将它赋值给了最终的输出。C++端的循环中,我们要把数值给它设置进去。

float red_value = (float)glm::sin(glfwGetTime());
glm::vec3 color(red_value, red_value/2, red_value/2);
shader->SetUniform3fv("color", color);

首先,颜色值是从0到1的,所以用了一个sin函数来取值,glfwGetTime获得的是一个连续增长的运行时间,正好可以使用。

其次,构造了一个vec3类型的变量,并把rgb的值稍作区别。glm是一个数学库,可以帮助我们计算向量,矩阵等操作,其类型也与着色器中的类型对应。

最后,将uniform的color值传给OpenGL,输出到屏幕上。注意:shader->SetUniform3fv("color", color);这句代码是我们为了使用方便而封装的,他的实现也非常简单,如下:

桌面云开源__开源云桌面系统

GLFunc glUniform3f(GetUniformLocation(name), vec.x, vec.y, vec.z);

GetUniformLocation(name)可以理解为获取变量的句柄,然后把值赋给它,这也显示出了它是一个三维向量。关于它最简单的理解就是JNI中,java层使用保存底层的指针,然后传递下去的时候,能在底层强转为该类型。

最后得到绘制效果(是动态的),我们这里展示一张图片,颜色从深变浅,再变深:

3. 每个顶点都是不同的颜色

要实现这个功能,那么每个点的颜色,必然像顶点的位置一样,属于这个点的属性,是它独有的,那么我们可以将它们写在顶点位置的后面:

float vertices[] = {
	//点点位置              //顶点的颜色
	0.5f, 0.5f, 0.0f,       1.0, 0.0, 0.0,
	0.5f, -0.5f, 0.0f,      0.0, 1.0, 0.0,
	-0.5f, -0.5f, 0.0f,     0.0, 0.0, 1.0,
	-0.5f, 0.5f, 0.0f,      0.9, 0.6, 0.8,
};

然后修改一下,顶点着色器,让他接收这个颜色属性:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
out vec3 outColor;
void main()
{
    gl_Position = vec4(aPos, 1.0);
    outColor = aColor;
}

我们声明了一个输入性变量aColor来接收它,location使用1。当然location在允许范围内随意使用,范围就是上面查到的GL_MAX_VERTEX_ATTRIBS个数,我们还是顺序使用,也方便检查。

那我们的颜色属性,怎么传递给OpenGL呢?

我们需要告诉OpenGL,如何使用上面的顶点缓冲,修改的地方见注释:

//添加了一个stride,表示每隔这个stride字节的数目,为一组属性
int stride = sizeof(float) * 6;
//激活location为0的属性
glEnableVertexAttribArray(0);
//告诉OpenGL怎么使用这些数据,注意stride与最后一个参数,是0,也就是说每隔stride字节,起始点为0的数据是顶点的位置
glVertexAttribPointer(0, 3, GL_FLOAT, false, stride, (void*)0);
glEnableVertexAttribArray(1);
//每隔stride字节,起始点为3*sizeof(float)的数据为颜色
glVertexAttribPointer(1, 3, GL_FLOAT, false, stride, (void*)(3*sizeof(float)));

具体的图示可以参看下面的图:

然后直接将这个颜色值用一个输出变量输出给下一个阶段,也就是片段着色器。

在片段着色器中,用相同名字的变量接收它,当然类型要变成输入,也就是in。

#version 330 core
in vec3 outColor;
void main()
{
    gl_FragColor = vec4(outColor, 1.0f);
}

最后执行的结果如下:

我们输入了四个顶点的颜色,但是其他大部分的像素颜色是怎么来的呢?其实是经过插值得到的,具体参看如下的图:

由此着色完成。