zoukankan      html  css  js  c++  java
  • Surface Shader

    Surface Shader:
      (1)必须放在SubShdader块,不能放在Pass内部;
      (2)#pragma sufrace surfaceFunction lightModel [optionalparams]
      (3)格式
      CG规定了声明为表面着色器的方法(就是我们这里的surf)的参数类型和名字,因此我们没有权利决定surf的输入输出参数的类型,只能按照规定写。这个规定就是第一个参数是一个Input结构,第二个参数是一个inout的SurfaceOutput结构。
      struct SurfaceOutput {
        half3 Albedo; //像素的颜色
        half3 Normal; //像素的法向值
        half3 Emission; //像素的发散颜色
        half Specular; //像素的镜面高光
        half Gloss; //像素的发光强度
        half Alpha; //像素的透明度
      };
      sampler2D就是GLSL中的2D贴图的类型,相应的,还有sampler1D,sampler3d,samplerCube等等格式。而具体地想知道像素与坐标的对应关系,以及获取这些数据,我们总不能一次一次去自己计算内存地址或者偏移,因此可以通过sampler2D来对贴图进 行操作。
      在CG程序中,我们有这样的约定,在一个贴图变量(在我们例子中是_MainTex)之前加上uv两个字母,就代表提取它的uv值(其实就是两个代表贴图上点的二维坐标 )。

      Input 这个输入结构通常拥有着色器需要的所有纹理坐标(texture coordinates)。纹理坐标(Texturecoordinates)必须被命名为“uv”后接纹理(texture)名字。(或者uv2开始,使用第二纹理坐标集)。
      可以在输入结构中根据自己的需要,可选附加这样的一些候选值:
      float3 viewDir - 视图方向( view direction)值。为了计算视差效果(Parallax effects),边缘光照(rim lighting)等,需要包含视图方向( view direction)值。
      float4 with COLOR semantic -每个顶点(per-vertex)颜色的插值。
      float4 screenPos - 屏幕空间中的位置。 为了反射效果,需要包含屏幕空间中的位置信息。比如在Dark Unity中所使用的 WetStreet着色器。
      float3 worldPos - 世界空间中的位置。
      float3 worldRefl - 世界空间中的反射向量。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。 请参考这个例子:Reflect-Diffuse 着色器。
      float3 worldNormal - 世界空间中的法线向量(normal vector)。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。
      float3 worldRefl; INTERNAL_DATA - 世界空间中的反射向量。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。为了获得基于每个顶点法线贴图( per-pixel normal map)的反射向量(reflection vector)需要使用世界反射向量(WorldReflectionVector (IN, o.Normal))。
      float3 worldNormal; INTERNAL_DATA -世界空间中的法线向量(normal vector)。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。为了获得基于每个顶点法线贴图( per-pixel normal map)的法线向量(normal vector)需要使用世界法线向量(WorldNormalVector (IN, o.Normal))。

    vertex shader modifier:Surface Shader还可以单独指定一个vertex shader,用于做一些运算,vertex函数拥有固定的输入参数 inout appdata_full 。下面的例子通过vertex shader对顶点在发现方向上做了一些偏移:

        #pragma surface surf Lambert vertex:vert
        void vert (inout appdata_full v) 
        {
             v.vertex.xyz += v.normal * _Amount;
        }

       也可在vertex shader中计算自定义变量(Input中的这些变量不能以uv开头,否则会出错),这个计算结果会逐像素的传递到surface shader, 如下: 

    复制代码
    Shader "James/Surface/CustomData"
    { 
        Properties 
        {
            _MainTex ("Base (RGB)", 2D) = "white" {}
        }
        SubShader 
        {
            Tags { "RenderType"="Opaque" }
            LOD 200
            
            CGPROGRAM
            #pragma surface surf Lambert vertex:vert
    
            sampler2D _MainTex;
    
            struct Input 
            {
                float2 uv_MainTex;
                float3 customColor; // 自定义数据
            };
            
            void vert(inout appdata_full, out Input o)
            {
                UNITY_INITIALIZE_OUTPUT(Input, o);
                o.customColor = abs(v.normal); // 在vs中计算自定义数据
            }
    
            void surf (Input IN, inout SurfaceOutput o) 
            {
                clip(frac((IN.worldPos.y + IN.worldPos.z * 0.1) * 5) - 0.5);
                o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb * IN.customColor;
                o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
            }
            ENDCG
        } 
        FallBack "Diffuse"
    }
    复制代码

    final color modifier: 编译指令为finalcolor:functionName,函数接收三个参数 Input IN, SurfaceOutput o, inout fixed4 color 
      final color会影响渲染的最终颜色,它在所有的计算的最后进行影响,比如lightmap、lightprobe等产生的颜色也会受此函数影响。
      final color可以用来实现fog,fog只影响渲染的rgb,而不影响alpha,fog的原理是让物体在最终的rgb和fog.color之间根据距离进行过度。

    复制代码
    Shader "James/Surface Shader/Final Color Fog Liner" {
      Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
      }
      SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
        
        CGPROGRAM
        #pragma surface surf Lambert finalcolor:mycolor vertex:myvert
    
        sampler2D _MainTex;
        uniform half4 unity_FogColor;
        uniform half4 unity_FogStart;
        uniform half4 unity_FogEnd;
    
        struct Input {
          float2 uv_MainTex;
          half fog;
        };
        // 顶点着色函数
        void myvert (inout appdata_full v, out Input data) {
          UNITY_INITIALIZE_OUTPUT(Input,data);
          float pos = length(mul (UNITY_MATRIX_MV, v.vertex).xyz);
          float diff = unity_FogEnd.x - unity_FogStart.x;
          float invDiff = 1.0f / diff;
          data.fog = clamp ((unity_FogEnd.x - pos) * invDiff, 0.0, 1.0);
        }
        // final color处理函数
        void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) {
          fixed3 fogColor = unity_FogColor.rgb;
          #ifdef UNITY_PASS_FORWARDADD
          fogColor = 0;
          #endif
          color.rgb = lerp (fogColor, color.rgb, IN.fog);
        }
        
        void surf (Input IN, inout SurfaceOutput o) {
          half4 c = tex2D (_MainTex, IN.uv_MainTex);
          o.Albedo = c.rgb;
          o.Alpha = c.a;
        }
        ENDCG
      } 
      FallBack "Diffuse"
    }
    复制代码

      uniform:用于指定变量的数据初始化方式。 Uniform inputs,表示一些与三维渲染有关的离散信息数据,这些数据通常由应用程序传入,并通常不会随着图元信息的变化而变化,如材质对光的反射信息、运动矩阵等。Uniform 修辞一个参数,表示该参数的值由外部应用程序初始化并传入。
      使用Uniform 修辞的变量,除了数据来源不同外,与其他变量是完全一样的。需要注意的一点是:uniform 修辞的变量的值是从外部传入的,所以在Cg 程序(顶点程序和片段程序)中通常使用uniform 参数修辞函数形参,不容许声明一个用uniform 修辞的局部变量!

    Lighting model:光照模式,在编写surface shader时,我们只需要描述各种属性即可,真正的光照计算是Lighting mode负责的。
      内置光照模式:Lambert(diffuse lighting)、BlinnPhong(specular lighting),源码在unity install path}/Data/CGIncludes/Lighting.cginc目录。
      Lighting model其实就是按照指定规范编写的一堆cg/hlsl,我们也可以定义自己的Lighting model。

      自定义光照模式:
      Surface Shader Lighting Models其实就是一些函数,这些函数我们也可以自己来写。
      下面的例子就是一个自定义的Lambert光照模式,并在此基础上实现的Toon效果:

    Shader "James/Surface Shader/Diffuse" {
        Properties {
            _MainTex ("Base (RGB)", 2D) = "white" {}
            _RampTex ("Base (RGB)", 2D) = "white" {}
        }
        SubShader {
            Tags { "RenderType"="Opaque" }
            LOD 200
            
            CGPROGRAM
            #pragma surface surf SimpleLambert
    
            sampler2D _RampTex;
            
            half4 LightingSimpleLambert(SurfaceOutput s, half3 lightDir, half atten)
            {
                half NdotL = dot(s.Normal, lightDir);
                // [-1, 1] --> [0, 1]
                half diff = NdotL *0.5 + 0.5;
                half3 ramp = tex2D(_RampTex, float2(diff, 0.5)).rgb;
                half4 c;
                c.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2);
                // c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten * 2);
                c.a = s.Alpha;
                return c;
            }
            
            sampler2D _MainTex;
    
            struct Input {
                float2 uv_MainTex;
            };
    
            void surf (Input IN, inout SurfaceOutput o) 
            {
                o.Albedo = tex2D (_MainTex, IN.uv_MainTex);
            }
            ENDCG
        } 
        FallBack "Diffuse"
    }

      下面的代码表示了一个简单的BlinnPhong的模式:

    Shader "James/Surface Shader/SimpleBlinnPhong" {
        Properties {
            _MainTex ("Base (RGB)", 2D) = "white" {}
        }
        SubShader {
            Tags { "RenderType"="Opaque" }
            LOD 200
            
            CGPROGRAM
            #pragma surface surf SimpleSpecular
    
            half4 LightingSimpleSpecular(SurfaceOutput s, half3 lightDir, half3 viewDir, half atten)
            {
                half3 h = normalize(lightDir + viewDir);
                half diff = max(0, dot(s.Normal, lightDir));
                
                float nh = max(0, dot(s.Normal, h));
                float spec = pow(nh, 48.0);
                
                half4 c;
                c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * spec) * (atten * 2);
                c.a = s.Alpha;
                return c;
            }
            
            sampler2D _MainTex;
    
            struct Input {
                float2 uv_MainTex;
            };
    
            void surf (Input IN, inout SurfaceOutput o) {
                half4 c = tex2D (_MainTex, IN.uv_MainTex);
                o.Albedo = c.rgb;
                o.Alpha = c.a;
            }
            ENDCG
        } 
        FallBack "Diffuse"
    }

      再下面是关于lightmap的光照模式代码,并在光照贴图的基础是添加了一个自定义颜色的实现:

    Shader "James/Surface Shader/SimpleLightmap" {
            Properties {
                _MainTex ("Texture", 2D) = "white" {}
                _SelfColor("SceneCooor", Color) = (0.5, 0, 0, 1)
            }
            SubShader {
                Tags { "RenderType" = "Opaque" }
                CGPROGRAM
    
                #pragma surface surf Standard
                
                half4 _SelfColor;
    
                half4 LightingStandard (SurfaceOutput s, half3 lightDir, half atten) {
                    half NdotL = dot (s.Normal, lightDir);
                    half4 c; 
                    c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten * 2);
                    c.a = s.Alpha;
                    return c;
                }
    
                // 给lightmap乘一个自定义颜色
                inline half3 ColorLight (half3 i) {
                    return _SelfColor.rgb * i;
                }
                
                inline fixed4 LightingStandard_SingleLightmap (SurfaceOutput s, fixed4 color) {
                    half3 lm = ColorLight(DecodeLightmap (color));
                    return fixed4(lm, 0);
                }
    
                inline fixed4 LightingStandard_DualLightmap (SurfaceOutput s, fixed4 totalColor, fixed4 indirectOnlyColor, half indirectFade) {
                    half3 lm = ColorLight(lerp (DecodeLightmap (indirectOnlyColor), DecodeLightmap (totalColor), indirectFade));
                    return fixed4(lm, 0);
                }
    
                inline fixed4 LightingStandard_StandardLightmap (SurfaceOutput s, fixed4 color, fixed4 scale, bool surfFuncWritesNormal) {
                    UNITY_DIRBASIS
    
                    half3 lm = ColorLight(DecodeLightmap (color));
                    half3 scalePerBasisVector = DecodeLightmap (scale);
    
                    if (surfFuncWritesNormal)
                    {
                        half3 normalInRnmBasis = saturate (mul (unity_DirBasis, s.Normal));
                        lm *= dot (normalInRnmBasis, scalePerBasisVector);
                    }
    
                    return fixed4(lm, 0);
                }
    
                struct Input {
                    float2 uv_MainTex;
                };
                
                sampler2D _MainTex;
                
                void surf (Input IN, inout SurfaceOutput o) {
                    o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
                }
                ENDCG
                }
            Fallback "Diffuse"
        }

    还有一些相关的编译选项:

      #pragma debug  查看生成的代码
      #pragma only_renderers: d3d9 只支持特定平台
      noforwardadd
      nolightmap
      exclude_path:prepass 不支持deferred模式

  • 相关阅读:
    [公告]博客园新服务器已下订单
    清除SearchNet.exe
    [新闻]微软将在2007年发布的Office产品阵容
    卸载Internet Explorer 7 Beta 2 Preview引起原来的IE无法正常使用
    博客园准备购买新服务器
    [微软活动公告]微软最有价值专家(MVP)四月份在线申请开始了
    [公告]今晚数据库迁移至新服务器
    请wz不要把别人的文章当作自己的文章发表
    SQL Server 2000 To SQL Server 2005
    [公告]博客园数据库已成功迁移至新服务器
  • 原文地址:https://www.cnblogs.com/sifenkesi/p/4678254.html
Copyright © 2011-2022 走看看