GLSL编程/GLUT/截面图
本教程涵盖了丢弃片段、确定渲染的是正面还是背面,以及正面剔除。本教程假设您熟悉在RGB立方体教程中讨论的varying变量。
本教程的主要主题是剔除三角形或片段,即使它们是正在渲染的网格的一部分。主要有两个原因:我们希望透过三角形或片段(就像左侧图中屋顶的情况,它只被部分切除)观看,或者我们知道三角形无论如何都是不可见的;因此,我们可以通过不处理它来节省一些性能。OpenGL通过多种方式支持这些情况;我们将讨论其中两种。
以下着色器是一种非常简单的剔除网格部分的方法:所有在对象坐标系中具有正坐标的片段都被剔除(即在其建模的坐标系中;有关坐标系的详细信息,请参见“顶点变换”)。以下是顶点着色器
attribute vec4 v_coord;
uniform mat4 m, v, p;
varying vec4 position_in_object_coordinates;
void main()
{
mat4 mvp = p*v*m;
position_in_object_coordinates = v_coord;
gl_Position = mvp * v_coord;
}
以下是片段着色器
varying vec4 position_in_object_coordinates;
void main()
{
if (position_in_object_coordinates.y > 0.0)
{
discard; // stop processing the fragment if y coordinate is positive
}
if (gl_FrontFacing) // are we looking at a front face?
{
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); // yes: green
}
else
{
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // no: red
}
}
当您将此着色器应用于任何默认对象时,它将剔除对象的一半。这是一种生成半球或开口圆柱体的非常简单的方法。如果您启用背面剔除
glEnable(GL_CULL_FACE);
您还会注意到对象的“内部”是不可见的。我们将在下面进一步讨论背面剔除。
让我们首先关注片段着色器中的discard
指令。此指令基本上只是丢弃已处理的片段。(在早期的着色语言中,这被称为片段“kill”;我可以理解片段更喜欢术语“discard”。)根据硬件的不同,这可能是一种相当昂贵的技术,因为一旦有一个着色器包含discard
指令,渲染的性能可能会显著下降(无论实际丢弃了多少片段,仅仅指令的存在就可能导致某些重要优化的停用)。因此,应尽可能避免使用此指令,尤其是在遇到性能问题时。
还有一点需要注意:片段discard
的条件仅包含对象坐标。结果是,您可以以任何方式旋转和移动对象,并且被切除的部分将始终与对象一起旋转和移动。您可能需要检查在视图或世界空间中切割的效果:更改顶点和片段着色器,以便在片段discard
的条件中使用视图坐标或世界坐标。提示:请参阅视图空间着色教程,了解如何将顶点变换到世界空间。
您可以尝试以下想法来改进着色器:将其更改为,如果坐标大于某个阈值变量,则丢弃片段。然后引入一个uniform,允许用户控制此阈值。提示:请参阅视图空间着色教程,了解有关uniform的讨论。
如着色器所示,片段着色器中提供了一个特殊的布尔变量gl_FrontFacing
,用于指定我们是否正在查看三角形的正面。通常,正面面向网格的外部,背面面向内部。(就像表面法线向量通常指向外部一样。)但是,区分正面和背面的实际方法是三角形中顶点的顺序:如果摄像机以逆时针顺序看到三角形的顶点,则它会看到正面。如果它以顺时针顺序看到顶点,则它会看到背面。
我们的片段着色器检查变量gl_FrontFacing
,如果gl_FrontFacing
为true
(即片段是正面三角形的一部分;即它面向外部),则将绿色分配给输出片段颜色;如果gl_FrontFacing
为false
(即片段是背面三角形的一部分;即它面向内部),则分配红色。事实上,gl_FrontFacing
不仅允许您使用不同的颜色渲染表面的两个面,还可以使用完全不同的样式渲染。
请注意,基于三角形中顶点的顺序来定义正面和背面可能会导致问题,当顶点被镜像时,即使用负因子进行缩放。我们可以尝试将内部翻转到外部,方法是将一个(或三个)坐标乘以-1,例如,在顶点着色器中以这种方式分配gl_Position
gl_Position = mvp * vec4(-v_coord.x, v_coord.y, v_coord.z, 1.0);
这只是将坐标乘以-1。对于球体,您可能会认为什么也不会发生,但它实际上会将正面变成背面,反之亦然;因此,现在内部是绿色的,外部是红色的。(顺便说一句,此问题也会影响表面法线向量。)因此,在使用镜像时要小心!
我们看到可以使用glEnable(GL_CULL_FACE)
来开启三角形剔除。默认情况下,背面会被剔除,就像指定了glFrontFace(GL_CCW)
一样。您也可以使用glFrontFace(GL_CW)
指定正面剔除。可以启用背面剔除,因为物体的内部通常是不可见的;因此,背面剔除可以通过避免光栅化这些三角形来节省相当多的性能,如下所述。当然,我们能够使用着色器看到内部,因为我们丢弃了一些片段;因此,在这种情况下,我们必须禁用背面剔除。
剔除是如何工作的?三角形和顶点像往常一样被处理。但是,在将顶点通过视口变换转换到屏幕坐标后(参见“顶点变换”),图形处理器会确定三角形的顶点在屏幕上是按逆时针顺序还是按顺时针顺序出现。根据此测试,每个三角形都被视为正面三角形或背面三角形。如果它是正面三角形并且启用了正面三角形的剔除,则它将被丢弃,即停止处理它并且不进行光栅化。类似地,如果它是背面三角形并且启用了背面三角形的剔除。否则,三角形将像往常一样被处理。
恭喜你完成了另一个教程。(如果你尝试过其中一项作业:干得好!我还没有。)我们已经了解了
- 如何丢弃片段。
- 如何激活背面剔除。
- 如何以不同的颜色渲染正面和背面三角形。
如果你还想了解更多
- 关于顶点变换,例如从对象到世界坐标的模型变换或到屏幕坐标的视口变换,您应该阅读“顶点变换”。
- 关于如何定义uniform,您应该阅读关于视空间着色的教程。
返回OpenGL 编程 - 照明部分 | 返回GLSL 编程 - GLUT 部分 |