我在构建基于着色器的OpenGL程序中使用的工作流程时遇到了一些麻烦。虽然我从未真正使用固定功能或基于着色器的管道完成任何重大项目,但我已经开始学习和试验,而且我很清楚着色器是可行的方法。
然而,从直观的角度来看,固定功能管道对我来说更有意义。使用该方法渲染场景很简单,并且程序化,就像绘制图片一样。如果我想画一个盒子,我告诉显卡画一个盒子。如果我想要很多盒子,我会在一个循环中画出我的盒子。固定功能管道非常适合我已建立的编程倾向。
这些都似乎带着着色器走出窗外,这就是我正在碰到一块。许多基于着色器的教程展示了如何在屏幕上绘制三角形或立方体,这样可以正常工作。但是,他们似乎根本没有考虑如何在游戏中应用这些概念。如果我想绘制三个程序生成的三角形,我需要三个着色器吗?显然不是,因为那是不可行的。尽管如此,它显然不像将绘图代码粘贴在一个运行三次的循环中那么简单。
因此,我想知道在游戏开发环境中使用着色器的“最佳实践”是什么。一个简单的游戏我应该有多少着色器?如何在它们之间切换并使用它们来渲染真实场景?
我不是在寻找细节,只是一般性的理解。例如,如果我有一个渲染圆圈的着色器,我将如何重复使用该着色器在屏幕上的不同点绘制不同大小的圆圈?如果我希望每个圆圈都是不同的颜色,我如何将一些信息传递给每个圆圈的片段着色器?
答案 0 :(得分:1)
固定功能管道和可编程管道之间确实没有概念上的区别。着色器引入的唯一内容是能够对流水线的某些阶段进行编程。
在当前硬件上,您(大部分)控制顶点,基本组件,曲面细分和片段阶段。在这些阶段之间和之后发生的一些操作仍然是固定功能的,例如深度/模板测试,混合,透视分割等。
因为着色器实际上只是用于定义特定阶段的输入和输出的程序,所以您应该将片段着色器的输入视为来自前一阶段的输出。在光栅化期间内插顶点输出,这些通常是在片段着色器中有in
变量时处理的内容。
您还可以拥有程序范围的变量,称为制服。只需在每个阶段使用相同的名称,任何阶段都可以访问这些变量。它们在着色器的调用中不会发生变化,因此名称统一。
现在你应该有足够的信息来弄清楚这个圆圈的例子......你可以使用制服来缩放你的圆圈(可能是一个简单的缩放矩阵),你可以依靠每顶点颜色或一个定义颜色。
答案 1 :(得分:1)
你没有绘制圆圈的着色器(好吧,你可能有正确的技巧,但是让我们暂时忘记它,因为它具有误导性并且具有非常罕见和特殊的用途)。着色器是您编写的用于处理图形管道的某些阶段的小程序,并且比“绘制圆形”更具体。
一般来说,每次进行绘制调用时,都必须告诉openGL使用哪些着色器(调用glUseProgram
你必须至少使用一个顶点着色器和一个片段着色器。生成的管道将是像
Vertex Shader
:将要发送给openGL的每个顶点的代码。它将针对您在元素数组中发送的每个索引执行,并且它将使用相应的顶点属性作为输入数据,例如顶点位置,其法线,其uv坐标,可能是其切线(如果您正在进行法线贴图)或者你发送给它的任何东西。通常,您希望在此处进行几何计算。您还可以访问为绘制调用设置的统一变量,这些变量是每个顶点不会更改的全局变量。您可能在顶点着色器中使用的典型均匀变量是PVM矩阵。如果不使用曲面细分,则顶点着色器将写入gl_Position,即光栅化器将用于创建片段的位置。您还可以让顶点输出不同的东西(作为uv坐标,以及处理thieri几何后的法线),将它们提供给光栅化器以便稍后使用它们。
Rasterization
Fragment Shader
:将为每个片段执行的代码(对于每个像素,如果更清楚)。通常你在这里做纹理采样和光计算。您将使用来自顶点着色器和光栅化器的数据,例如法线(用于评估漫反射和镜面反射术语)和uv坐标(用于从纹理中获取正确的颜色)。纹理将是均匀的,也可能是您正在评估的灯光的参数。
深度测试,模板测试。 (您可以使用早期片段优化(http://www.opengl.org/wiki/Early_Fragment_Test)
在片段着色器之前移动混合。
我建议你看看这个很棒的程序来开发简单的着色器http://sourceforge.net/projects/quickshader/,它有很好的例子,还有一些你在每个教程中都找不到的更高级的东西。