• 【Unity Shaders】学习笔记——SurfaceShader(十一)光照模型


    【Unity Shaders】学习笔记——SurfaceShader(十一)光照模型

    转载请注明出处:http://www.cnblogs.com/-867259206/p/5664792.html

    1. 如果你想从零开始学习Unity Shader,那么你可以看看本系列的文章入门,你只需要稍微有点编程的概念就可以。

    2. 水平有限,难免有谬误之处,望指出。


    LitSphere(Matcap)

    发光球体光照模型就是将发光球体的纹理映射在球体上,来实现光照效果。这可以创造一些效果细腻的发光球体效果,但是它不受光照影响,改变光照的方向,球体的光照效果不变。如果要在固定视角的场景里制作细腻的球体光照,这会是一个不错的选择。
    准备小球纹理贴图:


    1. 定义Properties:

        Properties {  
            _MainTint ("Diffuse Tint", Color) = (1,1,1,1)  
            _MainTex ("Base (RGB)", 2D) = "white" {}  
            _NormalMap ("Normal Map", 2D) = "bump" {}  
        }  
    
    1. 编写预编译命令:

            #pragma surface surf Unlit vertex:vert 
            #pragma target 3.0
    
    1. 在subshader里关联properties:

            float4 _MainTint;  
            sampler2D _MainTex;  
            sampler2D _NormalMap;  
    
    1. 定义光照函数Unlit:

            inline half4 LightingUnlit (SurfaceOutput s, fixed3 lightDir, fixed atten)  
            {  
                half4 c = half4(1,1,1,1);  
                c.rgb = s.Albedo;  
                c.a = s.Alpha;  
                return c;  
            } 
    

    这是一个无光照的光照函数,因为我们要用纹理上的光照效果,所以不需要计算光照。

    1. 定义Input结构体:

            struct Input {  
                float2 uv_MainTex;  
                float2 uv_NormalMap;  
                float3 tan1;  
                float3 tan2;  
            }; 
    
    1. 定义顶点函数:

            void vert (inout appdata_full v, out Input o)   
            {  
                UNITY_INITIALIZE_OUTPUT(Input,o);  
                
                TANGENT_SPACE_ROTATION;  
                o.tan1 = mul(rotation, UNITY_MATRIX_IT_MV[0].xyz);  
                o.tan2 = mul(rotation, UNITY_MATRIX_IT_MV[1].xyz);  
            } 
    

    解释:
    NITY_INITIALIZE_OUTPUT(Input,o);在HLSLSupport.cginc文件里是这样定义的:

    #if defined(UNITY_COMPILER_HLSL) || defined(SHADER_API_PSSL) || defined(SHADER_API_GLES3) || defined(SHADER_API_GLCORE)
    #define UNITY_INITIALIZE_OUTPUT(type,name) name = (type)0;
    #else
    #define UNITY_INITIALIZE_OUTPUT(type,name)
    #endif
    

    如果是HLSL编译器或某些版本的Shader API,则将变量赋值为0,否则什么都不做。这样编译器就不会报错说变量未初始化。
    TANGENT_SPACE_ROTATION;是Unity提供的一个宏,它定义了一个rotation矩阵用于从Object Space变换到Tangent Space。它在UnityCG.cginc里的定义如下:

    // Declares 3x3 matrix 'rotation', filled with tangent space basis
    #define TANGENT_SPACE_ROTATION 
        float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w; 
        float3x3 rotation = float3x3( v.tangent.xyz, binormal, v.normal ));
    

    binormal是切空间的Y轴,tangent是切空间的X轴,normal是切空间的Z轴。为什么它定义了一个切空间旋转看这篇
    UNITY_MATRIX_IT_MV是Object Space(or Model Space)变换到View Space(or Eye Space)的矩阵的逆转置矩阵。View Space以摄像机为原点,摄像机朝向为Z轴。[]是取出矩阵的一列,UNITY_MATRIX_IT_MV[0].xyz是说将向量(1,0,0)(即X轴)左乘MV矩阵的逆转置矩阵。UNITY_MATRIX_IT_MV[1].xyz则是向量(0,1,0)(即Y轴)左乘MV矩阵的逆转置矩阵。
    顺便科普一下,OpenGL是列向量,变换矩阵应该左乘向量。如果要将法线从Object Space变换到View Space是不能用MV矩阵的,原因看这篇博文。要将法线从Object Space变换到View Space要用MV矩阵的逆转置矩阵。那么,反过来,如果要将法线从View Space变换到Object Space只要将MV矩阵的逆转置矩阵右乘法线即可。这里的代码可以理解为将View Space的X轴和Y轴变换到了Object Space。变换到了Object Space以后再乘rotation变换到Tangent Space。

    1. 最后,编写表面处理函数:

            void surf (Input IN, inout SurfaceOutput o)   
            {  
                float3 normals = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap));  
                o.Normal = normals;  
      
                float2 litSphereUV;  
                litSphereUV.x = dot(IN.tan1, o.Normal);  
                litSphereUV.y = dot(IN.tan2, o.Normal);  
              
                half4 c = tex2D (_MainTex, litSphereUV*0.5+0.5);  
                o.Albedo = c.rgb * _MainTint;  
                o.Alpha = c.a;  
            }  
    

    这部分需要的解释不多。在表面处理函数里将法线和tan1、tan2点乘,相当于是把法线投影到了View Space的X轴和Y轴上。后面就是将法线当作UV来采样。乘0.5加0.5是把区间变到[0,1]。


    LitSphere有点像把我们能看见的小球的那一面当作一层皮扒下来,然后平铺在纹理上,使纹理上的球严丝合缝地投影在了小球上。效果如下:

     

    LitSphere

    LitSphere

    如果我们用纹理本身的UV坐标的话,小球图片是矩形,四周还有黑边,那么采样到小球上会是一个变形的四周有黑边的圆球图案。相当于是把画了个球的纹理贴在球身上。而用法线作UV的话,就把纹理上有球的那部分映射到了我们看得见的球的表面。
    我觉得把这理解为把球的表面投影到纹理上更好理解一点。这样要把法线从切空间变换到视空间,每个顶点都要变换,计算量太大,所以换过来,转换到切空间计算会cheap一点。
    这个光照模型也叫Matcap(Material Capture)。
    这个光照模型只能用于比较圆的物体,比如Sphere和Capsule,Cube是不能用的,因为Cube能看见的法线就只有三个,所以没办法使用这个光照模型。
    另一个版本的Matcap

     

  • 相关阅读:
    NHibernate中session.update()及session.merge()的区别
    子序列 (All in All,UVa 10340)
    古老的密码 (Ancient Cipher,NEERC 2004,LA 3213)
    例题1 勇者斗恶龙 (The Dragon of Loowater,UVa 11292)
    HDU1869 六度分离
    B. T-primes
    PoJ 1595 PrimeCuts
    poj 3518 Prime Gap
    PKU1988磁铁
    求组合数
  • 原文地址:https://www.cnblogs.com/-867259206/p/5664792.html
Copyright © 2020-2023  润新知