shader之半蘭伯特漫反射


看很多人實現shader都用插件shader force,那我還學shader干X!!!???

好了,廢話不多說,學習shader去。。。。


漫反射在shader里算是最基礎的知識了。入手shader,你必須可以信手拈來一個漫反射吧??什么叫信手拈來,就是不查閱資料的前提下吧

那我就信手拈來一個半蘭伯特漫反射效果吧(瞄了一眼資料,好像沒人看見)

正常的漫反射公式(Lambert)是這樣的:

C(diffuse) = C(light) * M(diffuse)max(0,n*I)

C(light):入射光的顏色和強度

M(diffuse):材質的漫反射系數

n:世界坐標系下的表面法線

I:世界坐標系下的光源方向(反射點指向光源的矢量)

max函數:,將結果截取到0,防止法線與光源方向的點乘為負值,被背后的光源照亮的錯誤效果

 

而筆者會比較喜歡一個毫無依據的半蘭伯特公式:

C(diffuse) = (C(light) * M(diffuse))(0.5(n*l)+0.5)

這個公式實現的漫反射效果看起來更真實一些(據說是讓亮度更亮一點,特別是比較暗的地方)


好了,又要放代碼了:

筆者是在頂點着色器中計算空間法線和空間燈光矢量,為的是讓效果更佳精確點

但是同樣筆者也有個疑問:我嘗試在頂點着色器里面對貼圖紋理進行采樣,但是報錯了,不知道為什么不能這樣做?

Shader "CharmingShader/Wangzhe/Diffuse"
{
    Properties
    {
        _MainTex("Main Tex",2D) = "white"{}
        _DiffuseColor("Diffuse Color",Color) = (1,1,1,1)
    }
        SubShader
        {
            Tags{"Queque" = "Opaque"}
            Pass
            {
                Tags{"LightMode" = "ForwardBase"}
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"
                #include "Lighting.cginc"

                sampler2D _MainTex;
                float4 _MainTex_ST;
                float4 _DiffuseColor;

                struct a2v
                {
                    float4 vertex : POSITION;
                    float4 texcoord : TEXCOORD0;
                    float3 normal :NORMAL;
                };
                struct v2f
                {
                    float4 pos : SV_POSITION;
                    //float3 color : COLOR;
                    float halfLambert : TEXCOORD0;
                    float2 uv : TEXCOORD1;
                };

                v2f vert(a2v v)
                {
                    v2f o;
                    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                    float3 worldNormal = normalize(UnityObjectToWorldNormal(v.normal));
                    float3 worldPos = mul(unity_ObjectToWorld, o.pos);
                    float3 worldLightDir = normalize(UnityWorldSpaceLightDir(worldPos));
                    o.halfLambert = dot(worldNormal, worldLightDir)*0.5 + 0.5;
                    
                    
                    return o;
                }

                float4 frag(v2f f) : SV_Target
                {
                    float3 col = tex2D(_MainTex, f.uv).rgb;
                    float3 diffuse = col * _DiffuseColor.rgb;
                    float3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * col;
                    float3 color = ambient + _LightColor0.rgb * diffuse * f.halfLambert;
                    return float4(color,1);
                }

                ENDCG
        }
    }
}

這里要注意一點,不管是在pass前面的tags還是pass里面的tags都是尤為重要的。

Tags標簽是一個鍵值對,它們告訴Unity的渲染引擎如何及何時去渲染這個對象

這里用到的Tags{"LightMode" = "FarwordBase"},在官方文檔是這樣解釋的:

Details

Tags are basically key-value pairs. Inside a Pass tags are used to control which role this pass has in the lighting pipeline (ambient, vertex lit, pixel lit etc.) and some other options. Note that the following tags recognized by Unity must be inside Pass section and not inside SubShader!

LightMode tag

LightMode tag defines Pass’ role in the lighting pipeline. See render pipeline for details. These tags are rarely used manually; most often shaders that need to interact with lighting are written as Surface Shaders and then all those details are taken care of.

Possible values for LightMode tag are:

  • Always: Always rendered; no lighting is applied.
  • ForwardBase: Used in Forward rendering, ambient, main directional light, vertex/SH lights and lightmaps are applied.
  • ForwardAdd: Used in Forward rendering; additive per-pixel lights are applied, one pass per light.
  • Deferred: Used in Deferred Shading; renders g-buffer.
  • ShadowCaster: Renders object depth into the shadowmap or a depth texture.
  • MotionVectors: Used to calculate per-object motion vectors.
  • PrepassBase: Used in legacy Deferred Lighting, renders normals and specular exponent.
  • PrepassFinal: Used in legacy Deferred Lighting, renders final color by combining textures, lighting and emission.
  • Vertex: Used in legacy Vertex Lit rendering when object is not lightmapped; all vertex lights are applied.
  • VertexLMRGBM: Used in legacy Vertex Lit rendering when object is lightmapped; on platforms where lightmap is RGBM encoded (PC & console).
  • VertexLM: Used in legacy Vertex Lit rendering when object is lightmapped; on platforms where lightmap is double-LDR encoded (mobile platforms).

意思是:LightMode標簽是在Pass里面,管理處在光照管道中的pass和其它選項。注意LightMode標簽必須處在Pass里面而不是SubShader里面。

由於時間的關系,我就只翻譯一下ForwardBase:ForwardBase用在Forward渲染中,渲染時可以應用到環境光、主平行光、頂點光和球諧光照以及光照貼圖。

之前一直忽視了這個標簽的作用,導致如何調效果都灰常暗。

下面放兩張對比圖吧:

普通貼圖效果------------------------------------------------->半蘭伯特漫反射效果

 

雖然半蘭伯特效果看起來更自然點,但是相對的亮度也比較暗了,反而沒有圖一看起來驚艷,而僅僅是更寫實了點。

(有一位小萌同事也提出了這個問題:喵~~臉這么黑,哪里好看了!

我當時心里嘀咕到:想要怎樣就怎樣,哪有這么好的事情哦?)

 

 


 

(后來一次機緣巧合,得到了一塊秘籍殘片)咳咳,哪有這么神話。也就是我想裝逼翻譯文章遇到很多不會的名詞,比如說SH Lights,

然后我就貼到Unity Shader群里問了問, 當時有個頭銜為話癆id為落俗的小伙伴很熱情的為我解答,並且貼出一段能讓這個漫反射更好

看的代碼。

SH Lights就是球諧光照,利用球諧光照技術可以實時重現面積光源下3D模型的全局光照效果(好吧,我也不懂,我就記住一個球)

代碼是這樣的(據說要有天空盒才會有效果)

在v2f結構體里定義sh

在頂點着色器里面計算sh,注意這里要對v2f o進行一次初始化

最后在片源着色器輸出顏色里加上對sh的處理

Ctrl+S跳轉界面,噔噔噔噔,有沒被驚艷到= =


 這里貼上完整的代碼:

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "CharmingShader/Wangzhe/Diffuse"
{
    Properties
    {
        _MainTex("Main Tex",2D) = "white"{}
        _DiffuseColor("Diffuse Color",Color) = (1,1,1,1)
    }
        SubShader
        {
            Tags{"Queque" = "Opaque"}
            Pass
            {
                Tags{"LightMode" = "ForwardBase"}
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"
                #include "Lighting.cginc"

                sampler2D _MainTex;
                float4 _MainTex_ST;
                float4 _DiffuseColor;

                struct a2v
                {
                    float4 vertex : POSITION;
                    float4 texcoord : TEXCOORD0;
                    float3 normal :NORMAL;
                };
                struct v2f
                {
                    float4 pos : SV_POSITION;
                    //float3 color : COLOR;
                    float halfLambert : TEXCOORD0;
                    float2 uv : TEXCOORD1;
                    #if UNITY_SHOULD_SAMPLE_SH
                    float3 sh : TEXCOORD2;
                    #endif
                };

                v2f vert(a2v v)
                {
                    v2f o = (v2f)0;
                    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                    float3 worldNormal = normalize(UnityObjectToWorldNormal(v.normal));
                    float3 worldPos = mul(unity_ObjectToWorld, o.pos);
                    float3 worldLightDir = normalize(UnityWorldSpaceLightDir(worldPos));
                    #ifndef LIGHTMAP_ON
                    #if UNITY_SHOULD_SAMPLE_SH
                    o.sh = ShadeSHPerVertex(worldNormal, o.sh);
                    #endif
                    #endif
                    o.halfLambert = dot(worldNormal, worldLightDir)*0.5 + 0.5;
                    
                    
                    return o;
                }

                float4 frag(v2f f) : SV_Target
                {
                    float3 col = tex2D(_MainTex, f.uv).rgb;
                    float3 diffuse = col * _DiffuseColor.rgb;
                    float3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * col;
                    float3 color = ambient + _LightColor0.rgb * diffuse * f.halfLambert + diffuse * f.sh;
                    return float4(color,1);
                }

                ENDCG
        }
    }
}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM