使用多个着色器


53

我目前正在研究opengl着色器,但无法弄清楚:如何将不同的着色器应用于对象,例如,使用卡通着色器渲染的茶壶,以及在同一场景中使用非常反射的表面绘制的茶壶,以及其他因扭曲而变形的对象杂讯功能,例如这部影片

http://www.youtube.com/watch?v=1ogg4ZfdBqU

另一个是在场景中应用Bloom Bloom着色器,然后再应用运动模糊着色器。当您只有一个顶点着色器和一个片段着色器时,如何实现这些效果?是否有诸如使用多个着色器程序之类的技巧?


关于Nathan和David的好答案,这就是为什么您看到术语render passshader pass的原因;合成最终的图像/帧需要多次通过。GPU处理变得如此并行且如此之快的原因之一是每帧需要多次通过。返回Quake II或Half Life软件渲染器,以提醒自己到底通过了多少次着色着色器,才增加了整个3D图形体验。
工程师

Answers:


59

简单的答案是您在每次绘图调用之间进行更改。设置一个着色器,绘制一个茶壶,设置另一个着色器,绘制另一个茶壶。

对于更复杂的东西,您需要将多个着色器仅应用于一个对象,例如模糊,发光等。基本上,所有内容都会渲染到纹理。然后,在使用另一个着色器的同时,在应用了该纹理的整个屏幕上渲染一个四边形。

例如,如果要渲染发光效果,则首先需要渲染常规的非发光场景,然后仅渲染要在纹理上发光的东西的彩色轮廓,然后切换到模糊着色器并渲染四边形,其纹理附着在不发光的场景上。

还有另一种称为“ 延迟着色”的技术,您可以在不照明的情况下渲染场景,然后将其应用到屏幕空间中。核心目标是减少每像素照明的费用。

通常,您渲染放置在屏幕上的颜色缓冲区。使用延迟着色,您可以在一次着色器遍历中渲染颜色缓冲区以及法线和深度缓冲区(您可以像使用法线和高度映射一样在纹理中存储法线向量和深度)。

这意味着,对于每个像素,您都知道颜色和法线最接近的非透明几何图形的位置(深度或距眼睛的距离)。因此,您可以将照明应用于屏幕上的每个像素,而不是应用于渲染的每个对象的每个可见像素。请记住,如果场景不是前后完美地渲染,则将在其他对象的顶部绘制一些对象。

对于阴影,实际上是从灯光的角度仅渲染深度缓冲区,然后使用该深度信息来确定光线的照射位置。这就是所谓的阴影贴图(还有另一种方法称为阴影体积,它可以绘制几何图形的轮廓并将其拉伸,但是您仍将使用着色器。)

在更现代的OpenGL(3.0+)中,您可以使用带有Renderbuffers对象的Framebuffer对象。由于渲染缓冲区可以像纹理一样对待。你可能会做这样的事情有1个着色渲染到多个不同的renderbuffers(这样你就不必渲染你的纹理那么你的法线辉光组件),但根本的做法还是一样。

另外,还希望尽可能减少着色器开关的数量,以节省开销。因此,某些引擎会将具有相同材质的所有内容组合在一起,以便可以一次绘制所有内容。


16

您只需绑定一个着色器,使用该着色器渲染所有对象,然后绑定下一个着色器,使用该着色器渲染对象,依此类推。

您可以根据需要拥有任意数量的着色器对象(将着色器加载到内存中并进行编译)。一次只能绑定(活动)一个。


5
在实现方面,在每一帧中我都使用glUseProgram(1)和glUseProgram(2)来更改着色器?这对性能有多昂贵?
ibrabeicker 2012年

4
这是不平凡的成本(尽管最近的GPU比早期的GPU便宜)。这就是为什么大多数人都按材质对对象进行排序,将所有具有相同材质的对象渲染在一起的原因。但是,您当然可以负担每帧数十至数百次(甚至更多)更改程序的费用。
内森·里德

9

在一个场景中使用多个着色器非常简单。更改着色器,为其设置值,然后渲染对象。

但是请注意,切换着色器的成本可能很高,因此应将着色器切换保持在最低限度。有几种方法可以减少这种影响,同时获得所需的所有效果。

第一种方法(通常是最理想的方法)是将所有着色器技术的功能添加到一个着色器中,并使用设置的条件以相同的着色器以不同的方式渲染每个对象。我不了解OpenGL和GLSL着色器,但是与HLSL着色器和DirectX一起可以将它们分组为“技术”,您可以设置技术而不用更改着色器。这样,您实际上可以在同一文件中具有几个不同的像素和顶点着色器。

降低性能影响的第二种方法是设置着色器,渲染使用该着色器的每个对象,然后重复。换句话说,批处理渲染。

如果您希望对同一个对象应用两种不同的效果(即应用卡通着色器然后进行一些照明),则可以采用两种不同的方式来实现。首先是编写在同一函数中应用多种效果的着色器。第二种方法是使用每个着色器渲染一次模型,并通过设置不同的混合选项来混合结果。但是,这是很多工作,并非在所有情况下都是可以实现的。因此,最好的选择是将所有效果组合到一个着色器中。


7

我发现的另一种方法是通过称为glsl子例程的东西,其中每种类型的着色器都在一个函数中定义,在OpenGL应用程序中,我们可以定义当前子例程,绘制缓冲区的顶点,更改子例程并渲染另一个缓冲区


4
这需要支持GL 4.x的硬件,它是DX11级的硬件。
Nicol Bolas 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.