OpenGL开发-第4章-着色器,增加颜色

代码仓库

点击这里

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

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

  • 1.1 版本号,即OpenGL的运行版本。
  • 1.2 输入类型 in , 其type与C语言基本一致,int, float, double,bool等,增加了重要的组合类型,vec和mat,即向量和矩阵。vec和mat都是类型,具体的使用都是带有具体的维度,比如vec3,mat3,mat4,代表三位向量,3x3矩阵,4x4的矩阵,每个元素都是float类型。如果想要其他类型,比如int类型的向量,可用ivec3,ivec4等。具体查看:
    n可取1,2,3,4
vecn	包含n个float分量的默认向量
bvecn	包含n个bool分量的向量
ivecn	包含n个int分量的向量
uvecn	包含n个unsigned int分量的向量
dvecn	包含n个double分量的向量
  • 1.3 in 声明的变量可以输入多少个呢?每个显卡都有自己的支持的上限,比如我的AMD WX4100专业卡支持29个,可以通过代码查询,但一般足够使用。
int attrib_size;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &attrib_size);
  • 1.4 out声明的输出有多少呢?这个与着色器的类型有关系,顶点着色器,几何着色器可以输出多个到下一个阶段(片段着色器)使用,但是片段着色器只能输出一个,就是颜色,也就是下面的片段着色器代码:
#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。

  • 1.5 uniform型变量,简单理解就是全局变量。我们知道,假如有三万个顶点,顶点着色器就要执行三万遍,顶点的属性(位置,颜色,纹理坐标)大部分都不相同,都是当前顶点自己独有的,而uniform类型的数据,则是被这三万次执行共享的,是一个相同的值。其他类型的着色器也是相同的道理。
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);
}

最后执行的结果如下:

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

由此着色完成。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值