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 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
| Shader "Custom/URP/Vegetation" { Properties { _BaseColor ("Base Color", Color) = (0.3, 0.7, 0.2, 1.0) _BaseMap ("Albedo Texture",2D) = "white" {} // Alpha Clip(树叶透明部分裁剪) _Cutoff ("Alpha Cutoff", Range(0,1)) = 0.5 // 顶点动画 _WindDirection ("Wind Direction",Vector) = (1, 0, 0.3, 0) _WindSpeed ("Wind Speed", Range(0,5)) = 1.5 _WindStrength ("Wind Strength", Range(0,0.5)) = 0.1 _WindFrequency ("Wind Frequency",Range(0.5,10)) = 2.0 // 植被特有:底部固定,顶部飘动(顶点颜色 R 通道作为弯曲权重) // 如果模型没有顶点颜色,用 UV.y 替代 [Toggle(_USE_VERTEX_COLOR_WEIGHT)] _UseVertexColor ("Use Vertex Color Weight", Float) = 0 // 双面渲染(树叶需要) [Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull Mode", Float) = 0 // 阴影偏移(防止自阴影 acne) _ShadowBias ("Shadow Depth Bias", Range(0, 0.1)) = 0.01 }
SubShader { Tags { "RenderType" = "TransparentCutout" // Alpha Clip 使用 TransparentCutout "RenderPipeline" = "UniversalPipeline" "Queue" = "AlphaTest" }
Cull [_Cull]
Pass { Name "VegetationForward" Tags { "LightMode" = "UniversalForward" }
HLSLPROGRAM #pragma vertex vert #pragma fragment frag #pragma shader_feature_local _USE_VERTEX_COLOR_WEIGHT
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE #pragma multi_compile _ _ADDITIONAL_LIGHTS #pragma multi_compile _ _SHADOWS_SOFT #pragma multi_compile_fog
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap);
CBUFFER_START(UnityPerMaterial) float4 _BaseMap_ST; float4 _BaseColor; float4 _WindDirection; float _Cutoff; float _WindSpeed; float _WindStrength; float _WindFrequency; float _ShadowBias; CBUFFER_END
struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; float4 tangentOS : TANGENT; float2 uv : TEXCOORD0; float4 color : COLOR; // 顶点颜色(R通道 = 弯曲权重) UNITY_VERTEX_INPUT_INSTANCE_ID };
struct Varyings { float4 positionHCS : SV_POSITION; float2 uv : TEXCOORD0; float3 positionWS : TEXCOORD1; float3 normalWS : TEXCOORD2; float4 shadowCoord : TEXCOORD3; float fogFactor : TEXCOORD4; UNITY_VERTEX_OUTPUT_STEREO };
// ======== 植被顶点动画 ======== float3 computeVegetationWind(float3 posWS, float bendWeight) { float3 windDir = normalize(_WindDirection.xyz); float time = _Time.y * _WindSpeed;
// 主波 + 湍流(模拟自然风的不规律性) float phase = dot(posWS.xz, windDir.xz) * _WindFrequency; float wave = sin(time + phase); float turb = sin(time * 2.3 + phase * 0.7) * 0.4 + sin(time * 4.1 + phase * 1.3) * 0.15;
float totalWave = (wave + turb) * _WindStrength * bendWeight;
// 横向弯曲(沿风向方向) float3 offset = windDir * totalWave;
// 保持顶点在地面上(约束 Y 轴位移,防止浮空) // 用球形投影近似:偏移后重新映射到固定半径的球面 // 简化版:限制 Y 轴上升 offset.y = -abs(offset.x + offset.z) * 0.1;
return offset; }
Varyings vert(Attributes IN) { Varyings OUT; UNITY_SETUP_INSTANCE_ID(IN); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
// 确定弯曲权重(优先使用顶点颜色 R 通道,否则用 UV.y) #ifdef _USE_VERTEX_COLOR_WEIGHT float bendWeight = IN.color.r; #else float bendWeight = IN.uv.y; #endif
// 对象空间 → 世界空间 float3 posWS = TransformObjectToWorld(IN.positionOS.xyz);
// 应用顶点动画 posWS += computeVegetationWind(posWS, bendWeight);
// 世界空间 → 裁剪空间 OUT.positionHCS = TransformWorldToHClip(posWS); OUT.positionWS = posWS; OUT.normalWS = TransformObjectToWorldNormal(IN.normalOS); OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
// 阴影坐标(基于动画后的世界坐标) // 重要:使用动画后的 posWS 而非原始坐标 float4 posCS = TransformWorldToHClip(posWS); OUT.shadowCoord = TransformWorldToShadowCoord(posWS); OUT.fogFactor = ComputeFogFactor(posCS.z);
return OUT; }
half4 frag(Varyings IN, bool isFrontFace : SV_IsFrontFace) : SV_Target { half4 albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv) * _BaseColor;
// Alpha Clip(透明度裁剪,树叶常用) clip(albedo.a - _Cutoff);
// 双面法线(背面翻转) float3 normalWS = normalize(IN.normalWS); normalWS = isFrontFace ? normalWS : -normalWS;
// 主光源(含阴影) Light mainLight = GetMainLight(IN.shadowCoord); float NdotL = saturate(dot(normalWS, mainLight.direction)); // Half-Lambert(植被通常有次表面散射,用 Half-Lambert 更自然) float halfLambert = NdotL * 0.5 + 0.5;
half3 diffuse = albedo.rgb * mainLight.color * halfLambert * mainLight.shadowAttenuation;
// 环境光 half3 ambient = SampleSH(normalWS) * albedo.rgb * 0.4;
// 额外光源(简化) half3 addLighting = 0; #ifdef _ADDITIONAL_LIGHTS uint count = GetAdditionalLightsCount(); for (uint i = 0; i < count; i++) { Light light = GetAdditionalLight(i, IN.positionWS); float addNdotL = saturate(dot(normalWS, light.direction)) * 0.5 + 0.5; addLighting += albedo.rgb * light.color * addNdotL * light.distanceAttenuation * 0.5; } #endif
half3 finalColor = diffuse + ambient + addLighting; finalColor = MixFog(finalColor, IN.fogFactor);
return half4(finalColor, 1.0); // Alpha Clip 后 Alpha 固定为 1 } ENDHLSL }
// ===== ShadowCaster Pass(植被阴影正确性关键)===== Pass { Name "ShadowCaster" Tags { "LightMode" = "ShadowCaster" }
ZWrite On ZTest LEqual ColorMask 0 Cull [_Cull]
HLSLPROGRAM #pragma vertex vertShadow #pragma fragment fragShadow #pragma multi_compile_instancing #pragma shader_feature_local _USE_VERTEX_COLOR_WEIGHT
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap);
CBUFFER_START(UnityPerMaterial) float4 _BaseMap_ST; float4 _BaseColor; float4 _WindDirection; float _Cutoff; float _WindSpeed; float _WindStrength; float _WindFrequency; float _ShadowBias; CBUFFER_END
// 在 CBUFFER 外声明(URP 注入的光源方向,用于阴影偏移) float3 _LightDirection; float3 _LightPosition;
struct ShadowAttributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; float2 uv : TEXCOORD0; float4 color : COLOR; UNITY_VERTEX_INPUT_INSTANCE_ID };
struct ShadowVaryings { float4 positionHCS : SV_POSITION; float2 uv : TEXCOORD0; UNITY_VERTEX_OUTPUT_STEREO };
float3 computeVegetationWind(float3 posWS, float bendWeight) { float3 windDir = normalize(_WindDirection.xyz); float time = _Time.y * _WindSpeed; float phase = dot(posWS.xz, windDir.xz) * _WindFrequency; float wave = sin(time + phase) + sin(time * 2.3 + phase * 0.7) * 0.4; float3 offset = windDir * wave * _WindStrength * bendWeight; offset.y = -abs(offset.x + offset.z) * 0.1; return offset; }
// 计算阴影裁剪空间坐标(含偏移处理) float4 getShadowPositionHClip(float3 posWS, float3 normalWS) { // ApplyShadowBias:添加阴影偏移防止 shadow acne // _LightDirection:当前渲染的光源方向(URP 自动传入) float3 adjustedPos = ApplyShadowBias(posWS, normalWS, _LightDirection); float4 posHCS = TransformWorldToHClip(adjustedPos);
// 点光源/聚光灯的额外偏移处理 #if UNITY_REVERSED_Z posHCS.z = min(posHCS.z, posHCS.w * UNITY_NEAR_CLIP_VALUE); #else posHCS.z = max(posHCS.z, posHCS.w * UNITY_NEAR_CLIP_VALUE); #endif
return posHCS; }
ShadowVaryings vertShadow(ShadowAttributes IN) { ShadowVaryings OUT; UNITY_SETUP_INSTANCE_ID(IN); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
#ifdef _USE_VERTEX_COLOR_WEIGHT float bendWeight = IN.color.r; #else float bendWeight = IN.uv.y; #endif
float3 posWS = TransformObjectToWorld(IN.positionOS.xyz); // ShadowCaster Pass 同样需要顶点动画!否则阴影形状与植被不匹配 posWS += computeVegetationWind(posWS, bendWeight);
float3 normalWS = TransformObjectToWorldNormal(IN.normalOS); OUT.positionHCS = getShadowPositionHClip(posWS, normalWS); OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap); return OUT; }
half4 fragShadow(ShadowVaryings IN) : SV_Target { // Alpha Clip(树叶需要在阴影 Pass 中也裁剪透明部分) half4 albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv) * _BaseColor; clip(albedo.a - _Cutoff); return 0; } ENDHLSL }
// DepthOnly Pass(SSAO 等深度效果需要) Pass { Name "DepthOnly" Tags { "LightMode" = "DepthOnly" } ZWrite On ColorMask R Cull [_Cull]
HLSLPROGRAM #pragma vertex vertDepth #pragma fragment fragDepth #pragma multi_compile_instancing #pragma shader_feature_local _USE_VERTEX_COLOR_WEIGHT
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap);
CBUFFER_START(UnityPerMaterial) float4 _BaseMap_ST; float4 _BaseColor; float4 _WindDirection; float _Cutoff; float _WindSpeed; float _WindStrength; float _WindFrequency; float _ShadowBias; CBUFFER_END
struct DepthAttributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; float4 color : COLOR; UNITY_VERTEX_INPUT_INSTANCE_ID };
struct DepthVaryings { float4 positionHCS : SV_POSITION; float2 uv : TEXCOORD0; UNITY_VERTEX_OUTPUT_STEREO };
float3 computeVegetationWind(float3 posWS, float bendWeight) { float3 windDir = normalize(_WindDirection.xyz); float time = _Time.y * _WindSpeed; float phase = dot(posWS.xz, windDir.xz) * _WindFrequency; float wave = sin(time + phase); return windDir * wave * _WindStrength * bendWeight; }
DepthVaryings vertDepth(DepthAttributes IN) { DepthVaryings OUT; UNITY_SETUP_INSTANCE_ID(IN); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); #ifdef _USE_VERTEX_COLOR_WEIGHT float bendWeight = IN.color.r; #else float bendWeight = IN.uv.y; #endif float3 posWS = TransformObjectToWorld(IN.positionOS.xyz); posWS += computeVegetationWind(posWS, bendWeight); OUT.positionHCS = TransformWorldToHClip(posWS); OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap); return OUT; }
half fragDepth(DepthVaryings IN) : SV_Target { half4 albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv) * _BaseColor; clip(albedo.a - _Cutoff); return 0; } ENDHLSL } } }
|