什么是通道和多个着色器通道及其私有变量

S.A*_*hid 5 opengl shader cg glsl unity-game-engine

我知道多通道渲染是渲染场景的各个部分并将它们组合到应用混合因子的图像中,这已经在渲染图形中完成了。但是什么是着色器中的一次通道和什么是多通道。例如,下面的着色器用于使用第一盏灯的漫射照明:

Shader "Cg per-vertex diffuse lighting" {
   Properties {
      _Color ("Diffuse Material Color", Color) = (1,1,1,1) 
   }
   SubShader {
      Pass {    
         Tags { "LightMode" = "ForwardBase" } 
            // make sure that all uniforms are correctly set

         CGPROGRAM

         #pragma vertex vert  
         #pragma fragment frag 

         #include "UnityCG.cginc"

         uniform float4 _LightColor0; 
            // color of light source (from "Lighting.cginc")

         uniform float4 _Color; // define shader property for shaders

         struct vertexInput {
            float4 vertex : POSITION;
            float3 normal : NORMAL;
         };
         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 col : COLOR;
         };

         vertexOutput vert(vertexInput input) 
         {
            vertexOutput output;

            float4x4 modelMatrix = _Object2World;
            float4x4 modelMatrixInverse = _World2Object; 
               // multiplication with unity_Scale.w is unnecessary 
               // because we normalize transformed vectors

            float3 normalDirection = normalize(float3(
               mul(float4(input.normal, 0.0), modelMatrixInverse)));
            float3 lightDirection = normalize(
               float3(_WorldSpaceLightPos0));

            float3 diffuseReflection = 
               float3(_LightColor0) * float3(_Color)
               * max(0.0, dot(normalDirection, lightDirection));

            output.col = float4(diffuseReflection, 1.0);
            output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
            return output;
         }

         float4 frag(vertexOutput input) : COLOR
         {
            return input.col;
         }

         ENDCG
      }
   }
   // The definition of a fallback shader should be commented out 
   // during development:
   // Fallback "Diffuse"
}
Run Code Online (Sandbox Code Playgroud)

下面的着色器再次用于具有多个通道和多个灯光的漫射照明。

在下面的着色器中,两个通道中的代码是相同的,在第二个通道中,着色器指向_LightColor0,并且在第一个通道中,着色器使用_LightColor0。那么多个灯在哪里呢?两个通道都指向_LightColor0。我认为,两次通行证都使用了第一道光。

通行证是否真的有其私人灯光阵列?

Shader "Cg per-vertex diffuse lighting" {
   Properties {
      _Color ("Diffuse Material Color", Color) = (1,1,1,1) 
   }
   SubShader {
      Pass {    
         Tags { "LightMode" = "ForwardBase" } 
           // pass for first light source

         CGPROGRAM

         #pragma vertex vert  
         #pragma fragment frag 

         #include "UnityCG.cginc"

         uniform float4 _LightColor0; 
            // color of light source (from "Lighting.cginc")

         uniform float4 _Color; // define shader property for shaders

         struct vertexInput {
            float4 vertex : POSITION;
            float3 normal : NORMAL;
         };
         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 col : COLOR;
         };

         vertexOutput vert(vertexInput input) 
         {
            vertexOutput output;

            float4x4 modelMatrix = _Object2World;
            float4x4 modelMatrixInverse = _World2Object; 
               // multiplication with unity_Scale.w is unnecessary 
               // because we normalize transformed vectors

            float3 normalDirection = normalize(float3(
               mul(float4(input.normal, 0.0), modelMatrixInverse)));
            float3 lightDirection = normalize(
               float3(_WorldSpaceLightPos0));

            float3 diffuseReflection = 
               float3(_LightColor0) * float3(_Color)
               * max(0.0, dot(normalDirection, lightDirection));

            output.col = float4(diffuseReflection, 1.0);
            output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
            return output;
         }

         float4 frag(vertexOutput input) : COLOR
         {
            return input.col;
         }

         ENDCG
      }

      Pass {    
         Tags { "LightMode" = "ForwardAdd" } 
            // pass for additional light sources
         Blend One One // additive blending 

         CGPROGRAM

         #pragma vertex vert  
         #pragma fragment frag 

         #include "UnityCG.cginc"

         uniform float4 _LightColor0; 
            // color of light source (from "Lighting.cginc")

         uniform float4 _Color; // define shader property for shaders

         struct vertexInput {
            float4 vertex : POSITION;
            float3 normal : NORMAL;
         };
         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 col : COLOR;
         };

         vertexOutput vert(vertexInput input) 
         {
            vertexOutput output;

            float4x4 modelMatrix = _Object2World;
            float4x4 modelMatrixInverse = _World2Object; 
               // multiplication with unity_Scale.w is unnecessary 
               // because we normalize transformed vectors

            float3 normalDirection = normalize(float3(
               mul(float4(input.normal, 0.0), modelMatrixInverse)));
            float3 lightDirection = normalize(
               float3(_WorldSpaceLightPos0));

            float3 diffuseReflection = 
               float3(_LightColor0) * float3(_Color)
               * max(0.0, dot(normalDirection, lightDirection));

            output.col = float4(diffuseReflection, 1.0);
            output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
            return output;
         }

         float4 frag(vertexOutput input) : COLOR
         {
            return input.col;
         }

         ENDCG
      }
   }
   // The definition of a fallback shader should be commented out 
   // during development:
   // Fallback "Diffuse"
}
Run Code Online (Sandbox Code Playgroud)

更新:这个着色器适用于多个灯光,因为我已经在下面的立方体上使用三种不同颜色的三个灯光进行了测试,结果如下: 在此输入图像描述 提前致谢

And*_*man 4

一般来说,像这样的高级着色器/材质系统(是的,高于 GLSL/Cg/HLSL)中的通道是设置多通道渲染所需状态的一种方法。如果您直接使用 GLSL、Cg 或 HLSL,则不存在“通过”之类的东西。

在这种情况下,您有两种不同类型的通道,因为一种通道建立基本照明贡献,并且每个连续通道都会添加到其中。换句话说,它们具有不同的混合功能。第一个通道替换帧缓冲区中的任何内容(glBlendFunc (GL_ONE, GL_ZERO)如果您熟悉 OpenGL,则有效),第二个通道将计算出的光添加到所有先前的通道中 ( glBlendFunc (GL_ONE, GL_ONE))。

不要将_LightColor0视为引用场景中的第一个灯光。实际上,它是由照明通道处理的一组灯光中的第一个灯光(在本例中,每个灯光 1 个通道)。如果此着色器能够每次处理多个灯光,您可能会看到_LightColor0 - _LightColorN,并且所需的通道数将类似于:1 + ceil ((NumLights-1)/(_LightColorN+1))。

这个丑陋的着色器需要对每个顶点进行照明,并且每个灯光需要 1 次通过。即使对于前向渲染,这也是非常低效的。我可以理解,如果使用阴影贴图,则需要多次通过,但这大约是您能得到的最简单的照明着色器,并且每个灯光仍然需要 1 次通过。即使是古老的固定功能硬件也可以每次通过 8 个灯。


更新:

由于对于每个灯光如何与此着色器中的通道相关存在一些困惑,并且您已更新您的问题以包含图表,因此我将使用图表来解释这一点。

       灯

在此图中,存在三个光源。要使用此着色器应用这三种灯光需要三遍。

Pass 0: <Yellow Light>
  Blend Function: Framebuffer = (1 * Light) + (0 * Framebuffer)
  Pass Type:      "ForwardBase"

Pass 1: <Red Light>
  Blend Function: Framebuffer = (1 * Light) + (1 * Framebuffer)
  Pass Type:      "ForwardAdd"

Pass 2: <Green Light>
  Blend Function: Framebuffer = (1 * Light) + (1 * Framebuffer)
  Pass Type:      "ForwardAdd"
Run Code Online (Sandbox Code Playgroud)

最终的结果是这样的:

Final Color = Light0 + Light1 + Light2
Run Code Online (Sandbox Code Playgroud)

如果您有更多灯光,它们都会使用ForwardAdd着色器中的通道。顺便说一句,由于混合后颜色值被限制为0.0 - 1.0,并且每个灯光都会将其强度添加到所有先前的灯光中,因此在灯光变为纯白色之前不需要太多灯光。使用附加照明时必须非常小心,除非使用 HDR(高动态范围)来解决此问题。


关于多次绘制立方体的过程,GPU与此无关。图形引擎本身会更改每个着色器通道的状态并重新绘制立方体。由于像混合函数这样的状态变化不能针对每个实例进行更改,因此引擎实际上已经绘制了一次立方体,更改了一些状态,然后使用此着色器再次绘制它。这个过程称为批处理,当您开始绘制的不仅仅是一个简单的立方体时,它会变得更加复杂。

减少绘制立方体的次数对于实现高性能非常重要。当使用许多灯光时,引擎通常会从前向着色切换到延迟着色。这会将立方体一次绘制到多个纹理中,并且每个纹理存储计算照明所需的一个或多个属性,例如位置、光泽度、反照率、法线、材质 ID 等。当需要应用灯光时,而不是绘制立方体一遍又一遍地,从片段/计算着色器中预先计算的纹理(G-Buffer)中查找属性。

问题是,延迟着色不用于逐顶点光照,并且当仅使用 2 或 3 个光源时,所涉及的总体增加的复杂性/内存要求可能使其不切实际。