【问题标题】:Understanding the shader workflow in OpenGL?了解 OpenGL 中的着色器工作流程?
【发布时间】:2013-09-02 02:24:26
【问题描述】:

我在概念化基于着色器的 OpenGL 程序中使用的工作流时遇到了一些麻烦。虽然我从未真正使用固定功能或基于着色器的管道完成任何大型项目,但我已经开始学习和试验,并且我很清楚着色器是要走的路。

但是,从直观的角度来看,固定功能管道对我来说更有意义。使用该方法渲染场景既简单又程序化——就像绘画一样。如果我想画一个盒子,我告诉显卡画一个盒子。如果我想要很多盒子,我会在一个循环中画出我的盒子。固定功能管道非常符合我既定的编程倾向。

这些似乎都带有着色器,这就是我遇到障碍的地方。许多基于着色器的教程展示了如何在屏幕上绘制三角形或立方体,这些都可以正常工作。但是,它们似乎根本没有涉及我将如何将这些概念应用到例如游戏中。如果我想绘制三个程序生成的三角形,我需要三个着色器吗?显然不是,因为那是不可行的。不过,这显然不像只是将绘图代码粘贴在运行 3 次的循环中那么简单。

因此,我想知道在游戏开发环境中使用着色器的“最佳实践”是什么。一个简单的游戏我应该有多少着色器?如何在它们之间切换并使用它们来渲染真实场景?

我不是在寻找细节,只是一般的理解。例如,如果我有一个渲染圆的着色器,我将如何重用该着色器在屏幕上的不同点绘制不同大小的圆?如果我希望每个圆圈具有不同的颜色,如何将一些信息传递给每个圆圈的片段着色器?

【问题讨论】:

  • "很多基于着色器的教程都展示了如何在屏幕上绘制三角形或立方体等,效果很好。"一页,因此不仅仅是“绘制一个对象”。有many tutorials that answer any and all of your questions
  • 我同意 NB。阅读教程,买一本书,或者找一些简单的游戏引擎,然后把它拆开。虽然这本书将是开始使用 OpenGL 的最简单方法。
  • @JakeKing:“我在前几部分中找不到我要找的东西。”那是你的问题。您不是在尝试将各种教程试图教给您的内容内化。您正试图跳过并浏览以挑选出一些数据内核。这不是任何教程的工作方式。就像您不会在中间开始阅读小说一样,您也不会通过寻找特定的东西来开始学习。只需选择一个相当长的教程,然后从头到尾阅读它。到时候,你会看到你感兴趣的。
  • 或者换一种说法,如果您了解如何使用一个对象进行渲染,那么您就足够了解如何使用 10 个对象进行渲染。如果您知道如何将参数传递给一个对象的着色器,那么您就知道它在 30 中是如何工作的。如果您了解您正在使用的系统,那么从一到多是一项微不足道的练习。
  • 最后,就我的教程而言,第一个多对象场景来自chapter 5。并不是说你应该在没有内化第 1-4 章的情况下阅读它。 chapter 7 提供了一个带有相机和许多不同对象的更复杂的场景。如果没有第 1-6 章,你不应该再碰它。

标签: opengl shader


【解决方案1】:

固定功能流水线和可编程流水线在概念上确实没有区别。着色器引入的唯一功能是对流水线的某些阶段进行编程。

在当前的硬件上,您(大部分情况下)可以控制顶点、图元组装、曲面细分和片段阶段。在这些阶段之间和之后发生的一些操作仍然是固定功能的,例如深度/模板测试、混合、透视分割等。

因为着色器实际上只不过是用于定义特定阶段的输入和输出的程序,因此您应该将片段着色器的输入视为来自先前阶段之一的输出。顶点输出在光栅化过程中被插值,当片段着色器中有in 变量时,这些通常是您要处理的。

您还可以拥有程序范围的变量,称为制服。任何阶段都可以通过在每个阶段使用相同的名称来访问这些变量。它们不会随着着色器的调用而变化,因此得名 uniform。

现在您应该有足够的信息来弄清楚这个圆圈示例...您可以使用制服来缩放您的圆圈(可能是一个简单的缩放矩阵),您可以依赖每个顶点的颜色或定义颜色。

【讨论】:

  • 我明白这一点,它有帮助,但我已经知道了很多,它仍然没有回答我的核心问题:我对如何将这些概念应用到程序上感到非常困惑生成的场景。如果我想使用大多数绘图 API(包括固定功能管道)在多个位置绘制相同的东西,我会将该代码粘贴在一个循环中并使用循环计数器来修改坐标。我不明白如何用着色器做这样的基本事情。
  • 不要误会我的意思,我并不是在抱怨着色器更糟糕,我只是断言它们在我看来不像更传统的绘图系统那样直观,并且我对着色器系统如何实现这一点感到困惑。
  • @JakeKing 您可以使用统一矩阵来执行此操作。您可能会这样做,以便每个对象都有一个绘制调用,每次向 GLSL 程序发送一个新的转换矩阵 - 您可以重用相同的程序和顶点缓冲区,只需像您一样更改矩阵的状态如果您想更改每个对象的纹理。更智能的是,您可以使用实例化和统一数组或统一缓冲区对象。可编程管道唯一改变的是你如何传达你的转换意图,它实际上更灵活地使用着色器。
  • @Alexis King 您不必将可编程着色器管道与移除固定功能管道的 API 的巨大变化混为一谈。他们曾经共存过一段时间。你甚至可以在较新的 OpenGL API 中重新实现旧的 glBegin 风格的调用,如果这让你更舒服的话,并且仍然使用着色器。
【解决方案2】:

您没有绘制圆圈的着色器(好吧,您可以使用正确的技巧,但我们现在暂时忘记它,因为它具有误导性并且具有非常罕见和特定的用途)。着色器是您编写的用于处理图形管道某些阶段的小程序,比“画一个圆”更具体。 一般来说,每次进行绘图调用时,都必须告诉 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/,它有很好的例子,还有一些你在每个教程中都找不到的更高级的东西。

【讨论】:

  • "您没有绘制圆圈的着色器。"您可以制作采用正方形并在该正方形内绘制完美圆形的片段着色器。
  • @SigTerm 当然可以,但这不是着色器的目的,理解着色器管道的全部内容也无用。使用带有“屏幕四边形”并使用 gl_FragCoord.st 的片段着色器,您可以绘制您喜欢的任何类型的 2d 几何图形,但这在 3d 图形中没有真正的应用。
  • 如果您的项目需要绘制完美的几何形状,它具有实际应用。除此之外,您可以使用基础四边形来渲染具有模拟深度的光线追踪对象,这与“圆形”着色器没有什么不同。
  • @SigTerm 你对光线追踪的对象提出了一个很好的观点。在特定情况下,几乎所有东西都有特定的应用程序,但这里问的是一般理解,并且来自固定管道,其中调用 glutSolidSphere 之类的东西很常见,我认为最好改变心态并考虑着色器作为“每个顶点”和“每个片段”代码。修改了我的答案。
  • @SigTerm:这是一种由于不学习可编程图形管道而导致的幼稚方法。由于 GPU 已经有一个顶点、图元组装和曲面细分阶段,因此解决绘制带有顶点和细分的圆的问题并在阶段之间使用逐顶点插值比计算片段中每个片段的投影位置更便宜着色器。这就是为什么画一个圆的例子实际上是一个很好的演示 OpenGL 图形管道如何工作的例子。光线追踪当然不是;我怀疑这是大流士的意思。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-29
  • 1970-01-01
相关资源
最近更新 更多