您的位置:首页 > 编程语言 > Qt开发

Qt OpenGL:学习现代3D图形编程之二,玩转色彩

2016-03-25 20:35 441 查看
除了给三角形赋予单一的颜色,我们还有两种方法来改变三角形表面的颜色,一种是利用片段的位置来计算颜色,另一种是利用每个顶点的数据来计算颜色。

一、利用片段位置计算颜色

片段的数据包括片段在屏幕上的位置,因此如果我们想要改变三角形表面的颜色,可以访问片段着色器中的数据,并计算片段最终的颜色。 gl_FragCoord是片段着色器独有的内置变量,它是个三维向量,包含x、y、z,其中x和y表示窗口坐标,z表示片段的深度值。需要注意的是,窗口坐标以窗口的左下角为原点,所以三角形底部片段的y值要小于顶部片段的y值。下面这个片段着色器使得片段的颜色基于它在窗口中的y值变化。500.0f是窗口的高度,main()函数中第一行的除法将y值转化到[0,1]范围,此时0表示窗口的底部,1表示窗口的顶部。第二行使用[0,1]之间的值来计算两种颜色之间的线性混合。mix函数是OpenGL着色器语言内置的标准函数,类似mix的很多函数都是向量化的,也就是说这些函数的参数可以是向量,当这些参数为向量时,向量的维数必须相同。mix的第三个参数必须是[0,1]之间的值,当它为0时,函数返回第一个参数,当它为1时,函数返回第二个参数,否则函数返回第一个参数和第二个参数的线性混合,该混合基于第三个参数。

const std::string strFragmentShader(
"#version 330\n"
"out vec4 outputColor;\n"
"void main()\n"
"{\n"
"   float lerpValue = gl_FragCoord.y / 500.0f;\n"
"   outputColor = mix(vec4(1.0f, 1.0f, 1.0f, 1.0f), vec4(0.2f, 0.2f, 0.2f, 1.0f), lerpValue);\n"
"}\n"
);


渲染后的效果是,当y靠近底部是,三角形接近白色;当y靠近顶部时,三角形接近黑色。如下图所示。



参考链接:http://alfonse.bitbucket.org/oldtut/Basics/Tutorial%2002.html

源码链接:http://download.csdn.net/detail/caoshangpa/9473453

二、利用顶点数据计算颜色

1.顶点属性

在片段着色器中使用片段位置虽然有效,但不是控制三角形颜色的最好方法,一个更好的方法是给每个顶点赋予一个准确的颜色。下面数组每行表示一个顶点的四维向量,因为每个float占用四个字节,所以每个向量占用16个字节。前三个向量为一组,表示三角形的三个顶点;后三个向量为另一组,表示三角形三个顶点的颜色。我们其实在内存中有两个数组,它们相互毗邻,一个数组在内存中的地址为&vertexData[0],另一个数组在内存中的地址为&vertexData[12]。

const float vertexData[] = {

0.0f, 0.5f, 0.0f, 1.0f,

0.5f, -0.366f, 0.0f, 1.0f,

-0.5f, -0.366f, 0.0f, 1.0f,

1.0f, 0.0f, 0.0f, 1.0f,

0.0f, 1.0f, 0.0f, 1.0f,

0.0f, 0.0f, 1.0f, 1.0f,

};

2.顶点着色器

这里我们定义了一个输出变量theColor,它用于将数据输出顶点着色器,这是通过关键字out来实现的。关键字smooth是插值限定符。顶点着色器只执行三次,产生三个位置输出(gl_Position)和三个颜色输出(theColor)。这三个位置构成一个三角形,该三角形接着被光栅化,产生大量(成千上万)片段。

const std::string strVertexShader(
"#version 330\n"
"layout(location = 0) in vec4 position;\n"
"layout(location = 1) in vec4 color;\n"
"smooth out vec4 theColor;\n"
"void main()\n"
"{\n"
"   gl_Position = position;\n"
"   theColor = color;\n"
"}\n"
);


下图是顶点着色器的数据流图。



3.片段着色器

第二行定义了一个输入变量theColor,它与顶点着色器中的输出变量名字相同,这样片段着色器就可以从顶点着色器获取反馈信息。片段着色器执行次数与光栅化三角形时产生的片段数有关,有多少个片段,就执行多少次。theColor只从顶点着色器获取了三个颜色,那么这三个颜色是如果传递给各个片段的呢?答案是片段插值,每个片段获得的颜色是这三个颜色的混合,片段离三角形的哪个顶点越近,这个顶点的颜色在混合时所占的比重就越大。smooth定义了插值的方式,其他的两个插值限定符分别是noperspective和flat。使用smooth时,三角形表明的颜色会均匀变化;使用noperspective时,除非渲染过程复杂,否则看不出与smooth的区别;使用flat时,实际上关闭了插值,每个片段简单的使用顶点着色器输出的第一个颜色。需要注意的是,顶点着色器和片段着色器中相同变量的插值限定符必须相同。

const std::string strFragmentShader(
"#version 330\n"
"smooth in vec4 theColor;\n"
"out vec4 outputColor;\n"
"void main()\n"
"{\n"
"   outputColor = theColor;\n"
"}\n"
);


4.显示到屏幕

注意与《画一个三角形》中不同的地方。

void display()
{
//指定OpenGL清理屏幕是将要使用的颜色,这里为黑色。
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
//开始清理屏幕,GL_COLOR_BUFFER_BIT表示清理将影响颜色缓冲区,清理时使用上面指定的颜色。
glClear(GL_COLOR_BUFFER_BIT);
//告知OpenGL渲染的时候需要调用应用程序对象。
glUseProgram(theProgram);
//下面三行设置三角形的坐标,它们告知OpenGL三角形在缓冲区中的顶点位置和顶点颜色。
//获取已经初始化的缓冲区对象。
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
//启用缓冲区对象中的数据,参数指定要修改的顶点属性的索引值。在缓冲区对象中有位置和颜色两个数组,我们
//必须告诉OpenGL如何取到这两组数据。这两组数据对应两个顶点属性,在顶点着色器中属性位置是layout(location=0)
//和layout(location=1),因此,属性的索引值需要用glEnableVertexAttribArray函数指定两次。
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
//尽管这个函数包含“Pointer”,但是它不处理指针,它用来对缓冲区对象中的顶点属性进行设定。
//参数1指定要修改的顶点属性的索引值;参数2指定每个顶点向量的维数;参数3指定每个顶点向量的数据类型;
//参数4指定是否归一化;参数5指定各顶点向量间是否有空隙,0表示紧密排列;参数6指定顶点数组起始位置与缓冲区
//对象起始位置的偏移量,0表示无偏移。后三个参数通常取默认值。
//48=4*4*3,48之前加(void*),是因为这个参数就是一个void指针。
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void*)48);
//渲染函数,从顶点数组索引0开始,读取3个顶点,然后将他们连接成一个三角形。
glDrawArrays(GL_TRIANGLES, 0, 3);
//下面两行是清理工作,释放为了实现渲染所做的一些设置。
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glUseProgram(0);
}


渲染后的效果如下图所示。



参考链接:http://alfonse.bitbucket.org/oldtut/Basics/Tutorial%2002.html

源码链接:http://download.csdn.net/detail/caoshangpa/9473456
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: