Shader "L04_URP/Grass_With_Interactor"{ Properties{ _TopColor("Top Color", Color) = (1,1,1,1) _BottomColor("Bottom Color", Color) = (1,1,1,1) _TranslucentGain("Translucent Gain", Range(0,1)) = 0.5 _BendRotationRandom("Bend Rotation Random", Range(0, 1)) = 0.2 _BladeWidth("Blade Width", Float) = 0.05 _BladeWidthRandom("Blade Width Random", Float) = 0.02 _BladeHeight("Blade Height", Float) = 0.5 _BladeHeightRandom("Blade Height Random", Float) = 0.3 [KeywordEnum(INTEGER, FRAC_EVEN, FRAC_ODD, POW2)] _PARTITIONING("Partition algoritm", Int) = 0 [KeywordEnum(FIXED, SCREEN_BASE, WORLD_BASE)] _DYNAMIC("Dynamic Partition Mode", Int) = 0 _TessellationFactor_FIXED("Tessellation FIXED", Range(1, 8)) = 1 _TessellationFactor_SCREEN_BASE("Tessellation SCREEN_BASE", Range(0.01, 8)) = 0.05 _TessellationFactor_WORLD_BASE("Tessellation WORLD_BASE", Range(0.01, 8)) = 0.08 _WindDistortionMap("Wind Distortion Map", 2D) = "white" {} _WindFrequency("Wind Frequency", Vector) = (0.05, 0.05, 0, 0) _WindStrength("Wind Strength", Float) = 1 _BladeForward("Blade Forward Amount", Float) = 0.38 _BladeCurve("Blade Curvature Amount", Range(1, 4)) = 2 _Radius("Interactor Radius", Float) = 2.4 _Strength("Interactor Strength", Float) = 1.7 [KeywordEnum(TRUE, FALSE)] _CUTTESS("Cut Tess", Int) = 0 } SubShader{ Tags{ "RenderType" = "Opaque" "Queue" = "Geometry" "RenderPipeline" = "UniversalPipeline" } LOD 100 Cull Off HLSLINCLUDE #pragma require geometry #pragma require tessellation tessHW #pragma vertex vert #pragma fragment frag #pragma hull hull #pragma domain domain #pragma geometry geo #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl" #pragma multi_compile _ _MAIN_LIGHT_SHADOWS #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS #pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS #pragma multi_compile _ _SHADOWS_SOFT #pragma shader_feature_local _PARTITIONING_INTEGER _PARTITIONING_FRAC_EVEN _PARTITIONING_FRAC_ODD _PARTITIONING_POW2 #pragma shader_feature_local _CUTTESS_TRUE _CUTTESS_FALSE #pragma shader_feature_local _DYNAMIC_FIXED _DYNAMIC_SCREEN_BASE _DYNAMIC_WORLD_BASE #define UNITY_PI 3.14159265359f #define UNITY_TWO_PI 6.28318530718f #define BLADE_SEGMENTS 3 CBUFFER_START(UnityPerMaterial) float4 _BaseColor; float4 _BaseMap_ST; float _BendRotationRandom; float _BladeHeight; float _BladeHeightRandom; float _BladeWidth; float _BladeWidthRandom; sampler2D _WindDistortionMap; float4 _WindDistortionMap_ST; float2 _WindFrequency; float _WindStrength; float _BladeForward; float _BladeCurve; float4 _TopColor; float4 _BottomColor; float _TessellationFactor_FIXED; float _TessellationFactor_SCREEN_BASE; float _TessellationFactor_WORLD_BASE; float _TranslucentGain; uniform float3 _PositionMoving; // float3 _PositionMoving; // 物体的位置 float _Radius; // 物体的交互半径 float _Strength; // 交互强度 CBUFFER_END TEXTURE2D (_BaseMap);SAMPLER(sampler_BaseMap); struct Attributes { float4 posOS : POSITION; float3 normalOS : NORMAL; float4 tangentOS : TANGENT; }; struct TessellationControlPoint { float4 posOS : INTERNALTESSPOS; float3 normalOS : NORMAL; float4 tangentOS : TANGENT; float4 positionCS : TEXCOORD0; }; struct Interpolators { float4 posOS : POSITION; float3 normalOS : NORMAL; float4 tangentOS : TANGENT; }; struct TessellationFactors { float edge[3] : SV_TessFactor; float inside : SV_InsideTessFactor; }; struct geometryOutput{ float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float4 shadowCoord : TEXCOORD1; float3 norWS : TEXCOORD2; // float3 normal : NORMAL; }; /* Tools Function */ // float rand(float3 co){ // return frac(sin(dot(co.xyz, float3(12.9898, 78.233, 53.539))) * 43758.5453); // } float rand(float3 p){ float3 p3 = frac(float3(p.xyz) * .1031); p3 += dot(p3, p3.yzx + 33.33); return frac((p3.x + p3.y) * p3.z); } float3x3 AngleAxis3x3(float angle, float3 axis){ float c, s; sincos(angle, s, c); float t = 1 - c; float x = axis.x; float y = axis.y; float z = axis.z; return float3x3( t * x * x + c, t * x * y - s * z, t * x * z + s * y, t * x * y + s * z, t * y * y + c, t * y * z - s * x, t * x * z - s * y, t * y * z + s * x, t * z * z + c ); } bool IsOutOfBounds(float3 p, float3 lower, float3 higher) { //给定矩形判断 return p.x < lower.x || p.x > higher.x || p.y < lower.y || p.y > higher.y || p.z < lower.z || p.z > higher.z; } bool IsPointOutOfFrustum(float4 positionCS) { //视锥体判断 float3 culling = positionCS.xyz; float w = positionCS.w; float3 lowerBounds = float3(-w, -w, -w * _ProjectionParams.z); float3 higherBounds = float3(w, w, w); return IsOutOfBounds(culling, lowerBounds, higherBounds); } bool ShouldClipPatch(float4 p0PositionCS, float4 p1PositionCS, float4 p2PositionCS) { bool allOutside = IsPointOutOfFrustum(p0PositionCS) && IsPointOutOfFrustum(p1PositionCS) && IsPointOutOfFrustum(p2PositionCS); return allOutside; } // 生成草的具體座標, 草的寬度, 草的高度, 草的朝向, uv傳遞, 變換矩陣 geometryOutput GenerateGrassVertex(float3 vertexPosition, float width, float height, float forward, float2 uv, float3x3 transformMatrix) { float3 tangentPoint = float3(width, forward, height); float3 localPosition = vertexPosition + mul(transformMatrix, tangentPoint); float3 tangentNormal = normalize(float3(0, -1, forward)); float3 localNormal = mul(transformMatrix, tangentNormal); float3 posWS = TransformObjectToWorld(localPosition.xyz); float3 norWS = TransformObjectToWorldNormal(localNormal); geometryOutput o; o.pos = TransformObjectToHClip(localPosition); o.uv = uv; o.norWS = norWS; o.shadowCoord = TransformWorldToShadowCoord(posWS); #if UNITY_PASS_SHADOWCASTER // o.pos = UnityApplyLinearShadowBias(o.pos); o.shadowCoord = TransformWorldToShadowCoord(ApplyShadowBias(posWS, norWS, 0)); #endif return o; } float EdgeTessellationFactor_ScreenBase(float scale, float4 p0PositionCS, float4 p1PositionCS) { float factor = distance(p0PositionCS.xyz / p0PositionCS.w, p1PositionCS.xyz / p1PositionCS.w); return max(1, factor / scale); } float EdgeTessellationFactor_WorldBase(float scale, float3 p0PositionWS, float3 p1PositionWS) { float length = distance(p0PositionWS, p1PositionWS); float distanceToCamera = distance(_WorldSpaceCameraPos, (p0PositionWS + p1PositionWS) * 0.5); // 使用平方根函数调整距离的影响,使中距离的镶嵌因子变化更平滑 float adjustedDistance = sqrt(distanceToCamera); float factor = length / (scale * adjustedDistance); return max(1, factor); } /* Vertex Shader */ TessellationControlPoint vert(Attributes v){ TessellationControlPoint o; o.posOS = v.posOS; o.normalOS = v.normalOS; o.tangentOS = v.tangentOS; o.positionCS = TransformObjectToHClip(v.posOS.xyz); return o; } /* Tesselation Shader */ TessellationFactors patchConstantFunction (InputPatch patch){ TessellationFactors f; #if defined(_DYNAMIC_FIXED) f.edge[0] = _TessellationFactor_FIXED; f.edge[1] = _TessellationFactor_FIXED; f.edge[2] = _TessellationFactor_FIXED; f.inside = _TessellationFactor_FIXED; #elif defined(_DYNAMIC_SCREEN_BASE) f.edge[0] = EdgeTessellationFactor_ScreenBase(_TessellationFactor_SCREEN_BASE, patch[1].positionCS, patch[2].positionCS); f.edge[1] = EdgeTessellationFactor_ScreenBase(_TessellationFactor_SCREEN_BASE, patch[2].positionCS, patch[0].positionCS); f.edge[2] = EdgeTessellationFactor_ScreenBase(_TessellationFactor_SCREEN_BASE, patch[0].positionCS, patch[1].positionCS); f.inside = (f.edge[0] + f.edge[1] + f.edge[2]) / 3.0; #elif defined(_DYNAMIC_WORLD_BASE) f.edge[0] = EdgeTessellationFactor_WorldBase(_TessellationFactor_WORLD_BASE, patch[1].posOS, patch[2].posOS); f.edge[1] = EdgeTessellationFactor_WorldBase(_TessellationFactor_WORLD_BASE, patch[2].posOS, patch[0].posOS); f.edge[2] = EdgeTessellationFactor_WorldBase(_TessellationFactor_WORLD_BASE, patch[0].posOS, patch[1].posOS); f.inside = (f.edge[0] + f.edge[1] + f.edge[2]) / 3.0; #else f.edge[0] = _TessellationFactor_FIXED; f.edge[1] = _TessellationFactor_FIXED; f.edge[2] = _TessellationFactor_FIXED; f.inside = _TessellationFactor_FIXED; #endif #if defined(_CUTTESS_TRUE) if(ShouldClipPatch(patch[0].positionCS, patch[1].positionCS, patch[2].positionCS)) f.edge[0] = f.edge[1] = f.edge[2] = f.inside = 0; #endif return f; } [patchconstantfunc("patchConstantFunction")] [domain("tri")][outputcontrolpoints(3)] [outputtopology("triangle_cw")] #if defined(_PARTITIONING_INTEGER) [partitioning("integer")] #elif defined(_PARTITIONING_FRAC_EVEN) [partitioning("fractional_even")] #elif defined(_PARTITIONING_FRAC_ODD) [partitioning("fractional_odd")] #elif defined(_PARTITIONING_POW2) [partitioning("pow2")] #else [partitioning("integer")] #endif TessellationControlPoint hull (InputPatch patch, uint id : SV_OutputControlPointID){ return patch[id]; } [domain("tri")] Interpolators domain(TessellationFactors factors, OutputPatch patch, float3 barycentricCoordinates : SV_DomainLocation) { Interpolators v; #define MY_DOMAIN_PROGRAM_INTERPOLATE(fieldName) v.fieldName = \ patch[0].fieldName * barycentricCoordinates.x + \ patch[1].fieldName * barycentricCoordinates.y + \ patch[2].fieldName * barycentricCoordinates.z; MY_DOMAIN_PROGRAM_INTERPOLATE(posOS) MY_DOMAIN_PROGRAM_INTERPOLATE(normalOS) MY_DOMAIN_PROGRAM_INTERPOLATE(tangentOS) return v; } [maxvertexcount(BLADE_SEGMENTS * 2 + 1)] void geo(triangle Interpolators IN[3], inout TriangleStream triStream){ float3 vNormal = IN[0].normalOS; float4 vTangent = IN[0].tangentOS; float3 vBinormal = cross(vNormal, vTangent.xyz) * vTangent.w; float3x3 tangentToLocal = float3x3( vTangent.x, vBinormal.x, vNormal.x, vTangent.y, vBinormal.y, vNormal.y, vTangent.z, vBinormal.z, vNormal.z ); float3 pos = IN[0].posOS.xyz; float3 worldPos = TransformObjectToWorld(IN[0].posOS.xyz); float3x3 facingRotationMatrix = AngleAxis3x3(rand(pos) * UNITY_TWO_PI, float3(0, 0, 1)); float3x3 bendRotationMatrix = AngleAxis3x3(rand(pos.zzx) * _BendRotationRandom * UNITY_PI * 0.2, float3(-1, 0, 0)); float2 uv = pos.xz * _WindDistortionMap_ST.xy + _WindDistortionMap_ST.zw + _WindFrequency * _Time.y; float2 windSample = (tex2Dlod(_WindDistortionMap, float4(uv, 0, 0)).xy * 2 - 1) * _WindStrength; float3 wind = normalize(float3(windSample.x, windSample.y, 0)); float3x3 windRotation = AngleAxis3x3(UNITY_PI * windSample.x * 0.2, wind); float3x3 transformationMatrix = mul(mul(mul(tangentToLocal, windRotation), facingRotationMatrix), bendRotationMatrix); float height = (rand(pos.zyx) * 2 - 1) * _BladeHeightRandom + _BladeHeight; float width = (rand(pos.xzy) * 2 - 1) * _BladeWidthRandom + _BladeWidth; float forward = rand(pos.yyz) * _BladeForward; float3x3 transformationMatrixFacing = mul(tangentToLocal, facingRotationMatrix); // 计算交互效果 float dis = distance(_PositionMoving, worldPos); float radiusEffect = 1 - saturate(dis / _Radius); float3 sphereDisp = pos - _PositionMoving; sphereDisp *= radiusEffect * _Strength; sphereDisp = clamp(sphereDisp, -0.8, 0.8); for (int i = 0; i < BLADE_SEGMENTS; i++){ float t = i / (float)BLADE_SEGMENTS; float segmentHeight = height * t; float segmentWidth = width * (1 - t); float segmentForward = pow(t, _BladeCurve) * forward; float3x3 transformMatrix = i == 0 ? transformationMatrixFacing : transformationMatrix; // 应用交互效果 float3 newPos = i == 0 ? pos : pos + (sphereDisp * t); triStream.Append(GenerateGrassVertex(newPos, segmentWidth, segmentHeight, segmentForward, float2(0, t), transformMatrix)); triStream.Append(GenerateGrassVertex(newPos, -segmentWidth, segmentHeight, segmentForward, float2(1, t), transformMatrix)); } // 最后的草片段 float3 newPosTop = pos + sphereDisp; triStream.Append(GenerateGrassVertex(newPosTop, 0, height, forward, float2(0.5, 1), transformationMatrix)); triStream.RestartStrip(); } ENDHLSL Pass{ Name "GrassPass" Tags{ "LightMode" = "UniversalForward" } HLSLPROGRAM float4 frag (geometryOutput IN, float facing : VFACE) : SV_Target { Light mainLight = GetMainLight(IN.shadowCoord); float3 normal = facing > 0 ? IN.norWS : -IN.norWS; float shadow = lerp(0.0, 1.0, mainLight.shadowAttenuation + 0.25f); float NdotL = saturate(saturate(dot(normal, mainLight.direction)) + _TranslucentGain) * shadow; float3 ambient = SampleSH(normal); float4 lightIntensity = float4(ambient + NdotL * _MainLightColor.rgb, 1.0); float4 col = lerp(_BottomColor, _TopColor * lightIntensity, IN.uv.y); return col; } ENDHLSL } Pass{ Name "ShadowCaster" Tags{ "RenderType" = "Opaque" "LightMode" = "ShadowCaster" } ZWrite On ZTest LEqual HLSLPROGRAM half4 frag(geometryOutput input) : SV_TARGET{ return 1; } ENDHLSL } } }