什么是传递和多个着色器传递及其私有变量

时间:2014-01-12 20:07:12

标签: opengl unity3d glsl shader cg

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

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"
}

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

在下面的着色器中,两个遍中的代码是相同的,在第二遍中,着色器指向 _LightColor0 ,并且在第一遍中,着色器使用 _LightColor0 < / strong>。那么多个灯在哪里?两个传递都指向 _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"
}

更新:此着色器适用于多个灯光,因为我已经在下面的立方体上测试了三个灯光 有三种不同颜色的灯,结果如下: enter image description here 提前致谢

1 个答案:

答案 0 :(得分:2)

一般来说,传递更高级别的着色器/材质系统(是的,高于GLSL / Cg / HLSL)就像这样一种设置多次渲染所需的状态的方法。如果你直接与GLSL,Cg或HLSL打交道,那就没有“通行证”。

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

不要将 _LightColor0 视为引用场景中的第一个灯光。实际上,它是灯光通道处理的灯光中的第一盏灯(在这种情况下,每盏灯通过1次)。如果此着色器能够每次通过处理多个灯光,您可能会看到 _LightColor0 - _LightColorN ,并且所需的传递次数将是:1 + ceil ((NumLights-1)/(_ LightColorN + 1))。

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

<小时/>

更新

由于关于每个灯光如何与此着色器中的过程相关而存在一些混淆,并且您已更新问题以包含图表,我将使用该图解释此内容。

Lights

在此图中,有三个光源。要使用此着色器应用这三个灯需要三次通过。

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"

最终的结果是:

Final Color = Light0 + Light1 + Light2

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


关于多次绘制立方体的过程,GPU与此无关。图形引擎本身就是改变每个着色器通道的状态并重新绘制立方体的原因。由于混合函数等状态更改无法按实例更改,因此引擎实际上会绘制一次立方体,更改几个状态,然后使用此着色器再次绘制它。这个过程称为批处理,当你开始绘制一个简单的多维数据集时,它会变得更加复杂。

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

问题是,延迟着色不用于每顶点照明,并且当仅使用2或3个灯时,所涉及的整体增加的复杂性/内存要求会使其变得不切实际。