給The Lab Renderer for Unity中地形添加陰影


  The Lab Renderer for Unity是Valve針對VR在Unity的體驗渲染器,提高VR的渲染效率,更多的大家可以查相應資料,在這,說個The Lab Renderer for Unity現階段的問題,可能是第一版,在地形並不能接受Valve渲染產生的陰影,對應地形上的樹啥的也不能產生陰影,經過相應修改后,如下是改動后的效果圖。  

  

  我們首先需要分析下Lab Renderer的基本渲染流程,主要代碼在ValveCamera中,可以看到,渲染流程還是很簡單的,相應的Lab Renderer文檔也首先點明了,前向單通道渲染。

  我們知道在以前如Ogre2.0以前的前向渲染時,如果有多個燈光,是需要多次PASS來疊加光源得到效果,嗯,Unity本身也是這樣處理的,這樣燈光越多,燈光與模型就是L*M的關系,所以大家開始采用后向渲染,把模型相應數據渲染到GBuffer中,然后與光源計算得到正確顯示,只需要L+M,雖然延遲渲染解決了多光源的問題,但是如下透明度,硬件AA,復雜材質,大量帶寬是延遲渲染比較難搞的部分。

  而在VR中,延遲渲染前沒有比較好用的空間AA算法,一般來說在VR中,采用后向抗鋸齒算法,一些UI還有字體還是還看到鋸齒,而VR眼睛分辨率比主機高的多,GBuffer你搞低了效果不好,大一點,雙攝像頭需要的顯存帶寬更是比主機多了去,所以當你導入Stream VR的包時,都會讓你選擇前向渲染,嗯,前向渲染的問題前面說了,多光源,而ValveCamera主要就是來解決如何在前向渲染里的單Pass里渲染多個光源的。

  Lab Renderer會要求你在每個實時光源下掛一個ValveRealtimeLight腳本,這個腳本主要是收集所有實時光源,然后在渲染時做二件事情,都是在OnPreCull之前,一是生成光源陰影圖(RenderShadowBuffer),二是把所有燈光的信息填入到vr_lighting.cginc中的ValveVrLighting的const buffer中。

  在以每個光源位置與方向來渲染當前的模型來生成陰影,不同類型光源會有些不同,如方向光,位置移到老后面,FOV也需要調整,肯定要保證所有模型都渲染到,而Point光源,需要渲染六個面,只有spot光源,其屬性與Camera對應,不需要啥特殊處理,渲染的RenderTarget只需要一個差不多類似深度的值就行了,所有RenderTarget全在m_shadowDepthTexture中,根據在每個ValveRealtimeLight腳本中設置的大小自動選擇一個位置,注意在場景中的任何地方不能有攝像機視野內的所有光源的ValveRealtimeLight設置的大小加起來不能超過m_shadowDepthTexture本身的大小。

  然后就是寫入所有燈光的信息到ValveVrLighting的const buffer中,在UpdateLightConstants這個方法中,其實這個過程和Ogre2.1的燈光處理很類似,大家可以看我以前寫的 Ogre2.1 燈光與陰影,當然Ogre2.1會復雜的多,采用的是Forward+,會把屏幕分成N多小格,每個小格確定受到那些光源的影響,不過思路確實有很多是一樣的。

  明白了Lab Renderer做了啥,我們才開始做最主要的部分,替換地形着色器的代碼,使之采用上面的m_shadowDepthTexture來產生陰影,並去掉原來的光照計算,采用Lab Renderer的光照算法,注意在這,我們還是想要能夠使用Unity本身的地形編輯器,所以我們並不是簡單把地形着色器有材質改成使用Custom,我們需要替換他本身的Standard地形着色器代碼,在Unity5以后,對應shader文件為Standard-FirstPass.shader,我們要做的就是,把Standard-FirstPass.shader與vr_standard.shader終合起來,地形表面的顏色采用的Standard-FirstPass.shader里的SplatmapMix方法,而陰影以及光源影響在vr_standard.shader中的ComputeLighting方法。

  需要注意的,Standard-FirstPass.shader本身做為SurfaceShader,提供的Input並不滿足我們ComputeLighting想要的參數,所以我們需要先看下Standard-FirstPass.shader生成完整的,包含頂點,片斷着色器的代碼,如下最下面的按鈕:

  

   注意,產生的文件會有很多Pass,如每種Fog對應不同的Pass,在這我們只需要一個Pass就夠了,其中Fog也讓Lab Renderer里的vr_standard.shader中的處理方法來處理。

  我們根據vr_standard.shader開始改造我們選擇的一個Pass,首先我們要確認vr_standard.shader有那些預處理定義與相應操作是我們根本不需要的,或者是在地形中默認處理方式,可以簡化大部分vr_standard.shader片斷着色器中的代碼,移除Standard-FirstPass.shader大部分片斷着色器代碼,添加vr_standard.shader片斷着色器的代碼,如前面所說,處理好Standard-FirstPass.shader里的SplatmapMix方法與vr_standard.shader中的ComputeLighting方法就成功了99%。如下是處理后的版本,還有Fog這邊沒有測試,大家自己去改,不麻煩。

  文件鏈接:vr_terrain.zip

Shader "Nature/Terrain/Standard" {
    Properties{
        // set by terrain engine
        [HideInInspector] _Control("Control (RGBA)", 2D) = "red" {}
    [HideInInspector] _Splat3("Layer 3 (A)", 2D) = "white" {}
    [HideInInspector] _Splat2("Layer 2 (B)", 2D) = "white" {}
    [HideInInspector] _Splat1("Layer 1 (G)", 2D) = "white" {}
    [HideInInspector] _Splat0("Layer 0 (R)", 2D) = "white" {}
    [HideInInspector] _Normal3("Normal 3 (A)", 2D) = "bump" {}
    [HideInInspector] _Normal2("Normal 2 (B)", 2D) = "bump" {}
    [HideInInspector] _Normal1("Normal 1 (G)", 2D) = "bump" {}
    [HideInInspector] _Normal0("Normal 0 (R)", 2D) = "bump" {}
    [HideInInspector][Gamma] _Metallic0("Metallic 0", Range(0.0, 1.0)) = 0.0
        [HideInInspector][Gamma] _Metallic1("Metallic 1", Range(0.0, 1.0)) = 0.0
        [HideInInspector][Gamma] _Metallic2("Metallic 2", Range(0.0, 1.0)) = 0.0
        [HideInInspector][Gamma] _Metallic3("Metallic 3", Range(0.0, 1.0)) = 0.0
        [HideInInspector] _Smoothness0("Smoothness 0", Range(0.0, 1.0)) = 1.0
        [HideInInspector] _Smoothness1("Smoothness 1", Range(0.0, 1.0)) = 1.0
        [HideInInspector] _Smoothness2("Smoothness 2", Range(0.0, 1.0)) = 1.0
        [HideInInspector] _Smoothness3("Smoothness 3", Range(0.0, 1.0)) = 1.0

        // used in fallback on old cards & base map
        [HideInInspector] _MainTex("BaseMap (RGB)", 2D) = "white" {}
    [HideInInspector] _Color("Main Color", Color) = (1,1,1,1)
    }

        SubShader{
                Tags{
                "Queue" = "Geometry-100"
                "RenderType" = "Opaque"
                "PerformanceChecks" = "False"
                }

            Pass
            {
                Name "FORWARD"
                Tags{ "LightMode" = "ForwardBase" "PassFlags" = "OnlyDirectional" } // NOTE: "OnlyDirectional" prevents Unity from baking dynamic lights into SH terms at runtime

                CGPROGRAM
        #pragma target 5.0
        #pragma only_renderers d3d11
        #pragma exclude_renderers gles
        #pragma vertex MainVs
        #pragma fragment MainPs
        #pragma shader_feature S_RECEIVE_SHADOWS
        #pragma multi_compile _ D_VALVE_SHADOWING_POINT_LIGHTS
        #pragma shader_feature S_OVERRIDE_LIGHTMAP
        #pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON
        #pragma multi_compile DIRLIGHTMAP_OFF DIRLIGHTMAP_COMBINED DIRLIGHTMAP_SEPARATE
        #pragma multi_compile DYNAMICLIGHTMAP_OFF DYNAMICLIGHTMAP_ON
        // Includes -------------------------------------------------------------------------------------------------------------------------------------------------
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"
#include "UnityStandardUtils.cginc"
#include "UnityStandardInput.cginc"

#define S_RECEIVE_SHADOWS 1
#define D_VALVE_SHADOWING_POINT_LIGHTS 1
#include "Lighting.cginc"

#pragma multi_compile __ _TERRAIN_NORMAL_MAP
#define TERRAIN_STANDARD_SHADER
#define TERRAIN_SURFACE_OUTPUT SurfaceOutputStandard
#include "TerrainSplatmapCommon.cginc"
#include "vr_utils.cginc"
#include "vr_lighting.cginc"
#include "vr_matrix_palette_skinning.cginc"
#include "vr_fog.cginc"

#define LIGHTMAP_ON 1
#define DYNAMICLIGHTMAP_ON 1
#define DYNAMICLIGHTMAP_OFF 0
#define DIRLIGHTMAP_COMBINED 1
#define S_OVERRIDE_LIGHTMAP 0

    half _Metallic0;
    half _Metallic1;
    half _Metallic2;
    half _Metallic3;

    half _Smoothness0;
    half _Smoothness1;
    half _Smoothness2;
    half _Smoothness3;

    // Structs --------------------------------------------------------------------------------------------------------------------------------------------------
    struct VS_INPUT
{
    float4 vPositionOs : POSITION;
    float3 vNormalOs : NORMAL;
    float2 vTexCoord0 : TEXCOORD0;
    float2 vTexCoord1 : TEXCOORD1;
#if ( DYNAMICLIGHTMAP_ON || UNITY_PASS_META )
        float2 vTexCoord2 : TEXCOORD2;
#endif
    };

    struct PS_INPUT
    {
        float4 vPositionPs : SV_Position;
        float3 vPositionWs : TEXCOORD0;
        float3 vNormalWs : TEXCOORD1;
        float2 vTextureCoords : TEXCOORD2;
        float4 vLightmapUV : TEXCOORD3;
        float2 vFogCoords : TEXCOORD4;
        float4 pack0 : TEXCOORD5; // _Splat0 _Splat1
        float4 pack1 : TEXCOORD6; // _Splat2 _Splat3
        float2 custompack0 : TEXCOORD7; // tc_Control
    };

    float g_flValveGlobalVertexScale = 1.0; // Used to "hide" all valve materials for debugging

    // World-aligned texture
    float3 g_vWorldAlignedTextureSize = float3(1.0, 1.0, 1.0);
    float3 g_vWorldAlignedNormalTangentU = float3(-1.0, 0.0, 0.0);
    float3 g_vWorldAlignedNormalTangentV = float3(0.0, 0.0, 1.0);
    float3 g_vWorldAlignedTexturePosition = float3(0.0, 0.0, 0.0);

    float4 _Splat0_ST;
    float4 _Splat1_ST;
    float4 _Splat2_ST;
    float4 _Splat3_ST;
    // MainVs ---------------------------------------------------------------------------------------------------------------------------------------------------
    PS_INPUT MainVs(appdata_full i)
    {
        PS_INPUT o = (PS_INPUT)0;

        UNITY_SETUP_INSTANCE_ID(i);
        UNITY_TRANSFER_INSTANCE_ID(v, o);
        Input customInputData;
        SplatmapVert(i, customInputData);
        o.custompack0.xy = customInputData.tc_Control;

        float3 vPositionWs = mul(unity_ObjectToWorld, i.vertex).xyz;
        o.vPositionWs.xyz = vPositionWs.xyz;
        o.vPositionPs.xyzw = mul(UNITY_MATRIX_MVP, i.vertex.xyzw);

        // Normal
        float3 vNormalWs = UnityObjectToWorldNormal(i.normal);
        o.vNormalWs.xyz = vNormalWs.xyz;
#if ( LIGHTMAP_ON )
        o.vLightmapUV.xy = i.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
#endif

#if ( DYNAMICLIGHTMAP_ON )
        o.vLightmapUV.zw = i.texcoord2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
#endif

        o.vFogCoords.xy = CalculateFogCoords(vPositionWs.xyz);

        o.pack0.xy = TRANSFORM_TEX(i.texcoord, _Splat0);
        o.pack0.zw = TRANSFORM_TEX(i.texcoord, _Splat1);
        o.pack1.xy = TRANSFORM_TEX(i.texcoord, _Splat2);
        o.pack1.zw = TRANSFORM_TEX(i.texcoord, _Splat3);
        return o;
    }

    // MainPs ---------------------------------------------------------------------------------------------------------------------------------------------------
    struct PS_OUTPUT
    {
        float4 vColor : SV_Target0;
    };

    PS_OUTPUT MainPs(PS_INPUT i)
    {
        PS_OUTPUT po = (PS_OUTPUT)0;

        Input surfIN;
        UNITY_INITIALIZE_OUTPUT(Input, surfIN);
        surfIN.uv_Splat0.x = 1.0;
        surfIN.uv_Splat1.x = 1.0;
        surfIN.uv_Splat2.x = 1.0;
        surfIN.uv_Splat3.x = 1.0;
        surfIN.tc_Control.x = 1.0;
        surfIN.uv_Splat0 = i.pack0.xy;
        surfIN.uv_Splat1 = i.pack0.zw;
        surfIN.uv_Splat2 = i.pack1.xy;
        surfIN.uv_Splat3 = i.pack1.zw;
        surfIN.tc_Control = i.custompack0.xy;

        half4 splat_control;
        half weight;
        fixed4 mixedDiffuse;
        half4 defaultSmoothness = half4(_Smoothness0, _Smoothness1, _Smoothness2, _Smoothness3);
        SplatmapMix(surfIN, defaultSmoothness, splat_control, weight, mixedDiffuse, i.vNormalWs);
        //mixedDiffuse.rgb;weight;mixedDiffuse.a;dot(splat_control, half4(_Metallic0, _Metallic1, _Metallic2, _Metallic3));

        float3 vAlbedo = mixedDiffuse.rgb;

        float3 vTangentUWs = float3(1.0, 0.0, 0.0);
        float3 vTangentVWs = float3(0.0, 1.0, 0.0);
        float3 vGeometricNormalWs = float3(0.0, 0.0, 1.0);
        i.vNormalWs.xyz = normalize(i.vNormalWs.xyz);
        vGeometricNormalWs.xyz = i.vNormalWs.xyz;
        float3 vNormalWs = vGeometricNormalWs.xyz;
        float3 vNormalTs = float3(0.0, 0.0, 1.0);

        // Roughness //
        float2 vRoughness = float2(0.6, 0.6);
        // Reflectance and gloss
        float3 vReflectance = float3(0.0, 0.0, 0.0);
        float flGloss = 0.0;
        vRoughness.xy = (1.0 - flGloss).xx;

        LightingTerms_t lightingTerms;
        lightingTerms.vDiffuse.rgb = float3(1.0, 1.0, 1.0);
        lightingTerms.vSpecular.rgb = float3(0.0, 0.0, 0.0);
        lightingTerms.vIndirectDiffuse.rgb = float3(0.0, 0.0, 0.0);
        lightingTerms.vIndirectSpecular.rgb = float3(0.0, 0.0, 0.0);
        lightingTerms.vTransmissiveSunlight.rgb = float3(0.0, 0.0, 0.0);

        float flFresnelExponent = 5.0;
        float flMetalness = 0.2f;

        float4 vLightmapUV = float4(0.0, 0.0, 0.0, 0.0);
#if (LIGHTMAP_ON || DYNAMICLIGHTMAP_ON )
        vLightmapUV.xy = i.vLightmapUV.xy;
    #if ( DYNAMICLIGHTMAP_ON )
        vLightmapUV.zw = i.vLightmapUV.zw;
    #endif
        // Compute lighting
    lightingTerms = ComputeLighting(i.vPositionWs.xyz, vNormalWs.xyz, vTangentUWs.xyz, vTangentVWs.xyz, vRoughness.xy, vReflectance.rgb, flFresnelExponent, vLightmapUV.xyzw);

#if ( S_OCCLUSION )        
    float flOcclusion = tex2D(_OcclusionMap, i.vTextureCoords.xy).g;
    lightingTerms.vDiffuse.rgb *= LerpOneTo(flOcclusion, _OcclusionStrength * _OcclusionStrengthDirectDiffuse);
    lightingTerms.vSpecular.rgb *= LerpOneTo(flOcclusion, _OcclusionStrength * _OcclusionStrengthDirectSpecular);
    lightingTerms.vIndirectDiffuse.rgb *= LerpOneTo(flOcclusion, _OcclusionStrength * _OcclusionStrengthIndirectDiffuse);
    lightingTerms.vIndirectSpecular.rgb *= LerpOneTo(flOcclusion, _OcclusionStrength * _OcclusionStrengthIndirectSpecular);
#endif
#endif

    // Diffuse
        po.vColor.rgb = (lightingTerms.vDiffuse.rgb + lightingTerms.vIndirectDiffuse.rgb) * vAlbedo.rgb;

        po.vColor.rgb += lightingTerms.vIndirectSpecular.rgb; // Indirect specular applies its own fresnel in the forward lighting header file

        //po.vColor.rgb = lightingTerms.vDiffuse.rgb;
        po.vColor.a = mixedDiffuse.a;

        // Emission - Unity just adds the emissive term at the end instead of adding it to the diffuse lighting term. Artists may want both options.
        //float3 vEmission = Emission(i.vTextureCoords.xy);
        //po.vColor.rgb += vEmission.rgb;
        //o.vColor.rgb = ApplyFog(o.vColor.rgb, i.vFogCoords.xy, _FogMultiplier);
        // Dither to fix banding artifacts
        //po.vColor.rgb = ScreenSpaceDither(i.vPositionPs.xy);

        return po;
    }
    ENDCG
    }

    }
        Fallback "Nature/Terrain/Diffuse"
}
Nature/Terrain/Standard

  地形中的樹開始很奇怪,為啥沒有投射陰影,在RenderShadowBuffer時調用m_shadowCamera.RenderWithShader時,在Frame Debug中發現並沒有把樹加進來,后面把渲染樹的shader找到看了下,發現RenderType都不是Opaque,那么一是改變相應樹的shader,使RenderType為Opaque,二是直接在RenderWithShader第二個參數填string.Empty就行,這樣會有一個問題,會把啥透明的都渲染進來。

  其中vr_standard.shader與Standard-FirstPass.shader針對地形的混合,只是能看到陰影,如果想用,肯定還是要針對性的修改才行。


免責聲明!

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



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