1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > OpenGL超级宝典(第7版)笔记10 片段着色器 清单3.10-3.12

OpenGL超级宝典(第7版)笔记10 片段着色器 清单3.10-3.12

时间:2020-08-02 11:25:53

相关推荐

OpenGL超级宝典(第7版)笔记10 片段着色器 清单3.10-3.12

OpenGL超级宝典(第7版)笔记10 片段着色器 清单3.10-3.12

文章目录

OpenGL超级宝典(第7版)笔记10 片段着色器 清单3.10-3.121 通过片段(像素)的位置来设置片段(像素)颜色2 一个顶点一个颜色3 对于细分后的颜色试验遗留的问题:解决TCS TES传递颜色数值过程中需要的是数组的情况4 总结

上一篇介绍了一下有关基元装配、裁剪、视口转换、剔除、光栅化的基础知识,前面这些过程把存在于程序中的几何图形转变为了一个个像素的形式,这一篇就要开始对这些像素填充颜色了,用到的就是片段着色器,对于每一个像素都会运行一次片段着色器,运行后该像素接受到着色器的计算结果,并将其作为像素最终显示的颜色。

1 通过片段(像素)的位置来设置片段(像素)颜色

还记得我们在之前的清单2.4吗,当时我们默认片段的颜色是固定的值。所以当在那些像素上运行片段着色器的时候,都会输出同样的颜色(即vec4(0.0, 0.8, 1.0, 1.0)),但是我们肯定不能满足与此。(毕竟都是一个颜色太单调了)

清单 2.4

#version 450 coreout vec4 color;void main(void){color = vec4(0.0, 0.8, 1.0, 1.0);}

现在我们需要每个片段输出不同的颜色,那么我们就需要一些值来决定不同的颜色,我们这里找到了gl_FragCoord这个OpenGL自带的变量。这个变量的记录的是当前片段的位置,假如是窗口中心那么是(0,0)左上角为(-1,1)右上角是(1,1),其实就是标准设备坐标系的坐标。

好的,我们现在吧它的值取四分之一,做三角函数运算,作为该片段颜色的R、B值,就有了下面的清单3.10(这里和书上的不太一样,没有计算G的值,是因为太麻烦了O(∩_∩)O)。

清单3.10 从片段位置得出颜色

#version 450 coreout vec4 color;void main(void){color=vec4(sin(gl_FragCoord.x*0.25)*0.5+0.5,cos(gl_FragCoord.y*0.25)*0.5+0.5,0.0,1.0);}

如果没有问题你将得到一个有着像格子花纹的三角形,三角形中的像素是根据它们在窗口中的位置决定的,如果你在顶点着色器中不断的改变三角形的位置(参考清单3.1),你会发现虽然三角形在不断的移动,但是三角形上的图案相对窗口是没有发生移动的!(因为像素的颜色只和像素相对窗口的位置有关)

2 一个顶点一个颜色

其实之前在笔记5中就介绍过这一部分的东西了,只不过那里是通过顶点属性传入的数据,而这里是将颜色的数据就内置在了顶点着色器中,用gl_VertexID来作为索引来调用数据:

清单3.11带一个输出的顶点着色器

#version 450 coreout vec4 vs_color;void main(void){const vec4 vertices[3] = vec4[3](vec4(0.25,-0.25,0.5,1.0),vec4(-0.25,-0.25,0.5,1.0),vec4(0.25,0.25,0.5,1.0));const vec4 color[3] = vec4[3](vec4(1.0,0.0,0.0,1.0),vec4(0.0,1.0,0.0,1.0),vec4(0.0,0.0,1.0,1.0));gl_Position = vertices[gl_VertexID];vs_color = color[gl_VertexID];}

清单3.12 从片段位置得出颜色

#version 450 corein vec4 vs_color;out vec4 color;void main(void){color=vs_color;}

别忘了之前说过顶点的属性会进行插值,所以当三角形的顶点数据传到片段着色器中时,颜色值就会产生渐变的效果。

3 对于细分后的颜色试验

首先我们要知道,如果从前面的着色器传递出了数据给后面的着色器用,那中间的着色器也要进行值的传递接力,那么如果中间过程有着曲面的细分,那么值会怎么传递呢,是否会进行插值呢,是否会产生什么意想不到的结果,我们下面来试试:

先试一试数据是否真的需要很“麻烦”的一步步接力最后给片段着色器:

首先我们的想法是通过layout直接将数据传入到片段着色器中(这是错误的示例):

顶点着色器

#version 450 corelayout (location=0) in vec4 offset;void main(void){const vec4 vertices[3]=vec4[3](vec4(0.25,-0.25,0.5,1.0),vec4(-0.25,-0.25,0.5,1.0),vec4(0.25,0.25,0.5,1.0));gl_Position = vertices[gl_VertexID];//gl_Position = vertices[gl_VertexID]+offset;//这里先关闭offset对三角形位置的影响,省得它在屏幕上乱动}

片段着色器

#version 450 corelayout (location=1) in vec4 ccolor;out vec4 color;void main(void){color = ccolor;}

当然现实是骨感的,这么做并不行,会报错

这里显示错误,OpenGL不知道从ccolor这个变量是一个前面流程的输出,也就是说它在前的着色器输出中(顶点着色器中)没看到ccolor的输出,所以OpenGL不知道这个数据怎么获取。所以说数据并不能直接给片段着色器,还需要从顶点着色器一步步传过来(即之前做过的流程,示意图如下)

好了,我们要在管线中添加细分曲面控制着色器、细分曲面评估着色器、几何着色器了(一下简称TCS、TES、GS,顶点着色器简称VS,片段着色器简称FS)。我们看看颜色数据在这几个之间应该怎样传递。

示意图:

我们现在试试:

顶点着色器:

#version 450 corelayout (location=0) in vec4 offset;layout (location=1) in vec4 in_color;out vec4 vs_color;void main(void){const vec4 vertices[3]=vec4[3](vec4(0.25,-0.25,0.5,1.0),vec4(-0.25,-0.25,0.5,1.0),vec4(0.25,0.25,0.5,1.0));gl_Position = vertices[gl_VertexID];vs_color = in_color;}

输出vs_color,传递给细分曲面控制着色器

细分曲面控制着色器:

#version 450 corelayout (vertices=3) out;in vec4 vs_color;out vec4 tc_color;void main(void){if(gl_InvocationID==0){gl_TessLevelInner[0]=3.0;gl_TessLevelOuter[0]=5.0;gl_TessLevelOuter[1]=5.0;gl_TessLevelOuter[2]=5.0;}gl_out[gl_InvocationID].gl_Position=gl_in[gl_InvocationID].gl_Position;tc_color=vs_color;}

继续传递从vs_color到tc_color

细分曲面评估着色器:

#version 450 corelayout (triangles,equal_spacing,cw)in;in vec4 tc_color;out vec4 geometry_color;void main(void){gl_Position=(gl_TessCoord.x*gl_in[0].gl_Position+gl_TessCoord.y*gl_in[1].gl_Position+gl_TessCoord.z*gl_in[2].gl_Position);geometry_color=tc_color;}

继续传递从tc_color到geometry_color

片段着色器:

#version 450 corein vec4 geometry_color;out vec4 color;void main(void){color = vec4(1.0,1.0,1.0,1.0);}

最后连接后运行(这里没带几何着色器),但还是出错了,这是错误:

它说OpenGL中细分曲面控制着色器的输入输出、细分曲面评估着色器的输入需要是数组,似乎并不是单一的变量就行。。。。想想也是,两个着色器都是对一个基元中的数据进行运算的(因为gl_in和gl_out中都包含多个顶点的数据),所以传入的数据都要是数组。但这里我们就暂时不知道怎么解决了。

遗留的问题:解决TCS TES传递颜色数值过程中需要的是数组的情况

当然我们不能气馁,我们想把每个小细分三角形都有一个单独的顶点颜色,我们找到了暂时的解决办法,就是通过几何着色器中生成(因为EmitVertex();会产生新的顶点,所以在设置好gl_Position之后再传个颜色数据出去,这样每个小三角形就有单独的顶点颜色了),示意图:

着色器依次是VS TCS TES GS FS:

#version 450 corelayout (location=0) in vec4 offset;void main(void){const vec4 vertices[3]=vec4[3](vec4(0.25,-0.25,0.5,1.0),vec4(-0.25,-0.25,0.5,1.0),vec4(0.25,0.25,0.5,1.0));gl_Position = vertices[gl_VertexID];}#version 450 corelayout (vertices=3) out;void main(void){if(gl_InvocationID==0){gl_TessLevelInner[0]=3.0;gl_TessLevelOuter[0]=5.0;gl_TessLevelOuter[1]=5.0;gl_TessLevelOuter[2]=5.0;}gl_out[gl_InvocationID].gl_Position=gl_in[gl_InvocationID].gl_Position;}#version 450 corelayout (triangles,equal_spacing,cw)in;void main(void){gl_Position=(gl_TessCoord.x*gl_in[0].gl_Position+gl_TessCoord.y*gl_in[1].gl_Position+gl_TessCoord.z*gl_in[2].gl_Position);}#version 450 corelayout (triangles) in;layout (triangle_strip,max_vertices=3) out;out vec4 geometry_color;void main(void){gl_Position = gl_in[0].gl_Position;geometry_color = vec4(1.0, 0.0, 0.0, 1.0);EmitVertex();gl_Position = gl_in[1].gl_Position;geometry_color = vec4(0.0, 1.0, 0.0, 1.0);EmitVertex();gl_Position = gl_in[2].gl_Position;geometry_color = vec4(0.0, 0.0, 1.0, 1.0);EmitVertex();//因为只有三个顶点所以这里就不用for了,颜色顺序分别是红、绿色、蓝色}#version 450 corein vec4 geometry_color;out vec4 color;void main(void){color = geometry_color;}

运行结果(上为结果,下为细分情况图作为比较):

每个小三角形的红色顶点就是该三角形第一个从几何着色器中输出的顶点。

虽然这里的颜色是几何着色器的生成,但我们以后一定可以用自己传入的数据来设置(再研究研究吧~)

你还可以根据清单3.10的条纹,来对片段着色器进行一个修改:

#version 450 corein vec4 geometry_color;out vec4 color;void main(void){color=vec4(sin(gl_FragCoord.x*0.25)*0.5+0.5,cos(gl_FragCoord.y*0.25)*0.5+0.5,0.0,1.0)*2.0*geometry_color;}

结果也是很有意思,这是叠加了条纹和小三角形的顶点颜色得到的结果(可能看上去有些乱)。

4 总结

这一篇我们介绍了片段着色器的工作,(别忘了片段着色器是对每一个像素都会进行运行,其传出的值就是该像素的颜色值)还通过我们自己的探究了解了着色器之间的值是怎样传递的,当然我们还遇到了数组的问题未解决,我们最后还是通过几何着色器暂时实现了我们的想法。下面一篇我们说一说帧缓存运算和计算着色器,这应该是管线的最后一部分,这一部分我们并不会十分深入的剖析,而是让你了解一下他们的功能和大致的流程。

我们下篇见~~

关于书籍的问题

如果你手中没有该书,我还是建议你购买一本,毕竟书本毕竟更加严谨专业,我这里难免遗漏一些细节,主要是提供实例,并做一个消化,将很混乱的流程为大家理清,但这笔记一定是通俗的,是对新手友好的(当然有时候你需要在某些方面自己努努力,比如后面出现的基本线性代数的内容,还有C语言或是c++的基础知识,虽然我可能也不太懂O(∩_∩)O,慢慢来吧)。

别被吓住

刚开始的时候很容易被OpenGL的巨长的函数和超级复杂的流程吓到,其实并没有那么可怕,只要对这样或那样的流程熟悉之后,一切都变得相当简单(当然如果你能提出一个更好的流程那就更好了,当我们把很多基础的工作做完,我们会不断的提出新问题新点子,用新的技术来实现它,最终完成OpenGL的学习)

虽然我也不知道后面将是怎样的道路,但至少努力学习是没错的。

我看过的相关内容

以下并不是全看完了,大部分看了15%就看不下去了,实在是没看懂。(本人没什么计算机编程基础,算是野生程序员吧,很多内容都不能标准表述,望见谅)

如果你对opengl的工作有了一定的了解,我一开始也是从这里开始的,但是仍然有很多的不懂的,最后至今为止,我杂糅了很多的网站内容包括LearnOpenGL、极客学院、哔哩哔哩的闫令琪计算机图形学、哔哩哔哩的傅老师的OpenGL课程、OpenGL编程指南"也称为红宝书"、OpenGL超级宝典"也称为蓝宝书"、当然还有很多的csdn文章O(∩_∩)O这就不介绍了,等用到是时候我在放链接吧O(∩_∩)O

这里面图形学比较易懂也很基础推荐可以作为开始(如果你是学OpenGL需要马上用,应该可以跳过,但是其中的内容很是很重要,这会让后面涉及变换透视的章节更加易懂,推荐大家看看),之后是蓝宝书或是极客学院翻译的教程比较推荐,这两个还是比较适合你我这样的新手的。

这里不推荐看的是红宝书,这本书我看了有点类似于字典那样的工具书,不太适合新手上手学,而且讲的也并不是很通俗易懂(可能是我的书版本比较老吧…)

加油

当然如果你对我有信心,我也会持续更新(虽然前路漫漫),跟大家一同进步(虽然很可能没人看(╥╯^╰╥),无所谓了,当然如有错误还请大家指正∠(°ゝ°),哪里不懂我会尽力解决,哪里说的不好也可以指出我会及时修改~)

我们下篇见~~

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。