在 GLSL http://en.wikibooks.org/wiki/GLSL_Programming/GLUT/Multiple_Lights中有一篇关于多个光源的精彩文章
但是在着色器代码中描述的light0和light1参数,如果必须绘制火炬枪射击怎么办,例如每个火炬都有它有自己的位置、颜色和必须照亮周围的环境。我们如何管理其他对象着色器来处理未知(屏幕上的最大耀斑有限制)位置、耀斑颜色?例如,屏幕上最多有 8 个照明弹,我必须通过 8*2 制服,即使它们此时不存在?
或者想象你制作关卡编辑器,用户可以放置灯,其他对象如何“知道”新光源并渲染然后添加了新灯?
我认为必须有聪明的解决方案,但我找不到。
1 回答
照明方程通常依赖于加色。所以输出是光一的颜色加上光二的颜色加上光三的颜色,以此类推。
OpenGL 提供的帧缓冲区内混合模式之一是加法混合。因此,您绘制的任何新内容的颜色输出都将添加到缓冲区中已有的内容中。
因此,最天真的解决方案是编写你的着色器来做一个光。如果您有多个灯光,请多次绘制场景,每次使用不同的指定线。这是多通道渲染的一个例子。
更好的解决方案包括编写着色器来一次做两个、四个、八个或任何灯光,例如,做 15 个灯光作为 8 灯绘制然后是 4 灯绘制然后是 2 灯绘制然后是 1 灯绘制,并且当您通过时,仅包括每个灯光范围内的几何图形。这往往意味着找到智能的方法来按位置对灯光进行分组。
编辑:稍微考虑一下,我应该补充一点,在deferred shading中还有另一个选项,尽管由于输出缓冲区的选项有限,目前它在大多数 GL ES 设备上并不完全有用。
假设理论上您可以只渲染一次几何图形并存储每个像素所需的任何内容。因此,您不仅会输出颜色,还会输出 3d 空间中的位置、法线、漫反射颜色、镜面反射颜色和镜面反射指数。然后这些都将位于每个像素的缓冲区中。
然后,您可以通过 (i) 计算出投影到屏幕上时可以占用的最大空间(因此,一个与像素直接相关的 2d 矩形)来渲染每个灯光;(ii) 将光渲染为该大小的单个四边形,每个像素从您刚刚设置的缓冲区中读取相关值并输出适当点亮的颜色。
然后,您只需在场景中执行所有实际几何图形一次,并且每个额外的灯光最多将花费一个全屏四边形。
实际上,您不能真正做到这一点,因为您倾向于能够在 ES 中使用的输出缓冲区提供的存储空间太少。但是您通常可以做的是渲染到带有附加深度缓冲区的 32 位颜色缓冲区。因此,您可以将深度存储在深度缓冲区中,然后根据该值加上相机在光照着色器中的 [uniform] 位置计算出世界 (x, y, z)。您可以将 8 位版本的法线 x 和 y 存储在颜色缓冲区中,以便花费 16 位并在颜色缓冲区中计算 z,因为您知道法线始终是单位长度。然后,随机选择一个具体的例子,也许你可以在剩余空间中存储一个 16 位版本的漫反射颜色,可能在 YCrCb 中,为 Y 提供额外的存储空间。
主要缺点是硬件抗锯齿与透明度和深度缓冲区不太相同。但是,如果您达到了大幅节省照明的地步,那么通过渲染大版本的场景然后在最后一次通过时将其缩小来进行手动抗锯齿仍然是有意义的。