服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - 解决Unity urp级联阴影接缝问题

解决Unity urp级联阴影接缝问题

2022-11-23 11:39飞天大蟾蜍 C#

通过从unity内部函数中抽几个出来改造,强制取某个裁切球的级联阴影映射,通过案例给大家详细介绍,文中给出了完整的urp shader代码,对Unity级联阴影知识感兴趣的朋友一起看看吧

试了一下unity自带的阴影,发现有接缝问题,

上网找了一些解决方案,都非常庞大,

基本是要自己处理级联阴影

看了urp的build-in之后,想了一个比较暗黑的方式,简单处理一下,运行对比图如下,

左边是unity自带的级联阴影效果,右边是平滑后的级联阴影

解决Unity urp级联阴影接缝问题

思路比较简单,从unity内部函数中抽几个出来改造

计算当前像素点(世界坐标)位于哪个裁切球,代码如下:

解决Unity urp级联阴影接缝问题

强制取某个裁切球的级联阴影映射,代码如下:

解决Unity urp级联阴影接缝问题

本案例只处理第一个裁切球与第二个裁切球过渡效果,

其它的裁切球离摄像机比较远,处不处理影响不大,如果要处理,方法也是相同的

混合两个解析度的阴影代码如下

解决Unity urp级联阴影接缝问题

其它urp管线需要用到的pass DepthOnly ShadowCaster就不说明了,照抄就可以了,以便物体本身也能生成阴影

以下贴出完整的urp shader代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
Shader "lsc/csm_shader"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
    }
    
    SubShader
    {
        LOD 100
        Tags{"RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" "UniversalMaterialType" = "Lit" "IgnoreProjector" = "True" "ShaderModel" = "2.0"}
 
        Pass
        {
            Name "ForwardLit"
            Tags{"LightMode" = "UniversalForward"}
 
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
 
 
            #pragma exclude_renderers gles gles3 glcore
            #pragma target 4.5
 
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
            #pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS
            #pragma multi_compile_fragment _ _SHADOWS_SOFT
 
 
            #pragma vertex LitPassVertex
            #pragma fragment LitPassFragment
 
 
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
 
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };
 
            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 normal : TEXCOORD2;
                float3 world_pos : TEXCOORD3;
            };
 
            sampler2D _MainTex;
            float4 _MainTex_ST;
 
            v2f vert(appdata v)
            {
                v2f o;
                VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz);
                o.vertex = vertexInput.positionCS;;
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.normal = v.normal;
 
                o.world_pos = vertexInput.positionWS;
 
                return o;
            }
 
            //常规的计算csm纹理映射函数
            //unity把所有的级联阴影刷在一个深度纹理
            //通过切换shadow coord的方式取不同解析度的光源深度纹理
            //每个解析度区间是用裁切球的方式
            float4 anhei_TransformWorldToShadowCoord(float3 positionWS)
            {
                half cascadeIndex = ComputeCascadeIndex(positionWS);
 
                float4 shadowCoord = mul(_MainLightWorldToShadow[cascadeIndex], float4(positionWS, 1.0));
 
                return float4(shadowCoord.xyz, cascadeIndex);
            }
 
            //自定义直接指定取某个区间段级联阴影
            float4 anhei_TransformWorldToShadowCoord2(int idx, float3 positionWS)
            {
                half cascadeIndex = idx;
 
                float4 shadowCoord = mul(_MainLightWorldToShadow[cascadeIndex], float4(positionWS, 1.0));
 
                return float4(shadowCoord.xyz, cascadeIndex);
            }
 
            //常规计算当前像素点(世界坐标)处于哪个裁切球
            half anhei_ComputeCascadeIndex(float3 positionWS)
            {
                float3 fromCenter0 = positionWS - _CascadeShadowSplitSpheres0.xyz;
                float3 fromCenter1 = positionWS - _CascadeShadowSplitSpheres1.xyz;
                float3 fromCenter2 = positionWS - _CascadeShadowSplitSpheres2.xyz;
                float3 fromCenter3 = positionWS - _CascadeShadowSplitSpheres3.xyz;
                float4 distances2 = float4(dot(fromCenter0, fromCenter0), dot(fromCenter1, fromCenter1), dot(fromCenter2, fromCenter2), dot(fromCenter3, fromCenter3));
 
                half4 weights = half4(distances2 < _CascadeShadowSplitSphereRadii);
                weights.yzw = saturate(weights.yzw - weights.xyz);
 
                return 4 - dot(weights, half4(4, 3, 2, 1));
            }
 
 
        float4 frag(v2f i) : SV_Target
        {
            // sample the texture
            float4 col = tex2D(_MainTex, i.uv);
            float3 nml = normalize(i.normal.xyz);
 
            int cas_idx_1 = anhei_ComputeCascadeIndex(i.world_pos);
 
            Light light_1;
            Light light_0;
            half shadow_mix = 1.0f;
            float mix_fact = 0;
            if (cas_idx_1 == 0)//只处理第一个裁切球,其它裁切球的太远了,在画面上可能看不见
            {
                float4 shadow_coord0 = anhei_TransformWorldToShadowCoord2(0, i.world_pos);
                light_0 = GetMainLight(shadow_coord0);
 
                float4 shadow_coord1 = anhei_TransformWorldToShadowCoord2(1, i.world_pos);
                light_1 = GetMainLight(shadow_coord1);
                shadow_mix = light_1.shadowAttenuation;
 
                //离第一个裁切球心距离
                float3 fromCenter0 = i.world_pos - _CascadeShadowSplitSpheres0.xyz;
                float3 first_sphere_dis = length(fromCenter0);
                //第一个裁切球的半径
                float first_sphere_rad = sqrt(_CascadeShadowSplitSphereRadii.x);
 
                //做一个简单的插值
                mix_fact = clamp((first_sphere_dis) / (first_sphere_rad / 1.0f), 0.0f, 1.0f);
                shadow_mix = light_0.shadowAttenuation* (1 - mix_fact) + light_1.shadowAttenuation * mix_fact;
            }
            else
            {
                float4 shadow_coord1 = anhei_TransformWorldToShadowCoord2(1, i.world_pos);
                light_1 = GetMainLight(shadow_coord1);
                shadow_mix = light_1.shadowAttenuation;
            }
            col.rgb = shadow_mix * col.rgb;
 
            return col;
        }
        ENDHLSL
    }
 
        // ShadowCaster 将物体写入光源的深度纹理
        // 使用自定义的shadow caster, urp会从光源处拍摄场影
        pass
        {
            Tags{ "LightMode" = "ShadowCaster" }
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
 
            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
 
            struct v2f
            {
                float4 pos : SV_POSITION;
            };
 
            sampler2D _MainTex;
            float4 _MainTex_ST;
 
            float3 _LightDirection;
 
            v2f vert(appdata v)
            {
                v2f o;
                float3 world_pos = TransformObjectToWorld(v.vertex);
                float3 world_nml = TransformObjectToWorldNormal(v.normal);
                o.pos = TransformWorldToHClip(ApplyShadowBias(world_pos, world_nml, _LightDirection));
                return o;
            }
            float4 frag(v2f i) : SV_Target
            {
                float4 color;
                color.xyz = float3(0.0, 0.0, 0.0);
                return color;
            }
            ENDHLSL
        }
 
 
        // DepthOnly 直接使用内置hlsl代码
        Pass
        {
            Name "DepthOnly"
            Tags{"LightMode" = "DepthOnly"}
 
            ZWrite On
            ColorMask 0
            Cull[_Cull]
 
            HLSLPROGRAM
            #pragma exclude_renderers gles gles3 glcore
            #pragma target 4.5
 
            #pragma vertex DepthOnlyVertex
            #pragma fragment DepthOnlyFragment
 
            // -------------------------------------
            // Material Keywords
            #pragma shader_feature_local_fragment _ALPHATEST_ON
            #pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
 
            //--------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing
            #pragma multi_compile _ DOTS_INSTANCING_ON
 
            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl"
            ENDHLSL
        }
 
    }
}

以上就是解决Unity urp级联阴影接缝问题的详细内容,更多关于Unity级联阴影的资料请关注服务器之家其它相关文章!

原文链接:https://blog.csdn.net/lsccsl/article/details/118161524

延伸 · 阅读

精彩推荐
  • C#C#学习笔记——基本语法

    C#学习笔记——基本语法

    本文给大家详细介绍了C#的基本语法知识以及一些基础知识的汇总,非常的简单基础,有需要的小伙伴可以参考下...

    C#教程网4112021-12-22
  • C#Unity实现新手引导镂空效果

    Unity实现新手引导镂空效果

    这篇文章主要为大家详细介绍了Unity实现新手引导的镂空效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    LLLLL__11532022-08-27
  • C#C# 设计模式系列教程-组合模式

    C# 设计模式系列教程-组合模式

    组合模式可以使客户端调用简单,它可以一致使用组合结构或是其中单个对象,简化了客户端代码。...

    Wang Juqiang4862021-11-23
  • C#C#如何控制IIS动态添加删除网站详解

    C#如何控制IIS动态添加删除网站详解

    这篇文章主要给大家介绍了关于C#如何控制IIS动态添加删除网站的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C#具有一定的参考学习...

    小y3962022-08-09
  • C#C# 实例化接口对象的方法

    C# 实例化接口对象的方法

    下面小编就为大家带来一篇C# 实例化接口对象的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    C#教程网11642021-12-27
  • C#C#如何获取计算机信息

    C#如何获取计算机信息

    这篇文章主要为大家详细介绍了C#获取计算机信息的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    gmadc7702022-09-22
  • C#C# 中AutoMapper的使用方法

    C# 中AutoMapper的使用方法

    这篇文章主要介绍了C# 中AutoMapper的使用方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...

    一线码农4522022-10-25
  • C#C#中委托的基本用法总结

    C#中委托的基本用法总结

    以下是对C#中委托的基本用法进行了详细的总结分析,需要的朋友可以过来参考下。希望对大家有所帮助...

    C#教程网10322021-01-04