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
| Shader "Custom/URP/RayMarching" { Properties { // 由 RendererFeature 的 Blit 自动传入 _MainTex ("源颜色缓冲", 2D) = "white" {}
// SDF 场景参数(可在 Inspector 中调整) _SpherePos ("SDF 球心位置", Vector) = (0, 1, 5, 0) _SphereRadius ("SDF 球半径", Float) = 0.8 _BoxPos ("SDF 盒子位置", Vector) = (2, 0.5, 5, 0) _BoxSize ("SDF 盒子尺寸", Vector) = (0.5, 0.5, 0.5, 0) _SmoothK ("平滑混合系数", Range(0, 1)) = 0.3 _FogColor ("雾颜色", Color) = (0.5, 0.6, 0.8, 1) _FogDensity ("雾密度", Range(0, 0.1)) = 0.02 }
SubShader { Tags { "RenderPipeline" = "UniversalPipeline" } Cull Off ZWrite Off ZTest Always
Pass { HLSLPROGRAM #pragma vertex FullscreenVert #pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex);
CBUFFER_START(UnityPerMaterial) float4 _MainTex_TexelSize; float4 _SpherePos; float _SphereRadius; float4 _BoxPos; float4 _BoxSize; float _SmoothK; float4 _FogColor; float _FogDensity; int _MaxSteps; float _SurfaceDistance; float4 _CameraWorldPos; float4x4 _ViewProjInverse; CBUFFER_END
struct Varyings { float4 positionHCS : SV_POSITION; float2 uv : TEXCOORD0; };
Varyings FullscreenVert(uint vertexID : SV_VertexID) { // 全屏三角形顶点(无需顶点缓冲) Varyings output; output.uv = float2((vertexID << 1) & 2, vertexID & 2); output.positionHCS = float4(output.uv * 2.0 - 1.0, 0.0, 1.0); // 注意:部分平台需要翻转 UV,URP Blit Helper 已处理此问题 return output; }
// ============================================================ // SDF 基本体 // ============================================================
float sdSphere(float3 p, float3 center, float radius) { return length(p - center) - radius; }
float sdBox(float3 p, float3 center, float3 halfSize) { float3 d = abs(p - center) - halfSize; return length(max(d, 0.0)) + min(max(d.x, max(d.y, d.z)), 0.0); }
// 平滑并集(SDF 核心操作,k 控制混合带宽) float smin(float a, float b, float k) { float h = max(k - abs(a - b), 0.0); return min(a, b) - h * h * 0.25 / k; }
// 场景 SDF:组合多个基本体 float SceneSDF(float3 p) { float sphere = sdSphere(p, _SpherePos.xyz, _SphereRadius); float box = sdBox(p, _BoxPos.xyz, _BoxSize.xyz); float ground = p.y; // 无限地面
// 球和盒子平滑融合 float combined = smin(sphere, box, _SmoothK); return min(combined, ground); }
// 四面体法线估计(4 次 SDF 采样,比中心差分的 6 次更高效) float3 CalcNormal(float3 pos) { const float eps = 0.001; const float2 k = float2(1, -1); return normalize( k.xyy * SceneSDF(pos + k.xyy * eps) + k.yyx * SceneSDF(pos + k.yyx * eps) + k.yxy * SceneSDF(pos + k.yxy * eps) + k.xxx * SceneSDF(pos + k.xxx * eps) ); }
// 软阴影(步进过程中记录最小归一化距离) float CalcSoftShadow(float3 ro, float3 rd, float tMin, float tMax) { float res = 1.0, t = tMin; for (int i = 0; i < 24; i++) { float h = SceneSDF(ro + rd * t); float s = clamp(8.0 * h / t, 0.0, 1.0); res = min(res, s); t += clamp(h, 0.01, 0.2); if (res < 0.004 || t > tMax) break; } return clamp(res * res * (3.0 - 2.0 * res), 0.0, 1.0); }
// 环境光遮蔽(沿法线方向多次采样) float CalcAO(float3 pos, float3 normal) { float occ = 0.0, sca = 1.0; [unroll] for (int i = 0; i < 5; i++) { float h = 0.01 + 0.12 * float(i) / 4.0; float d = SceneSDF(pos + h * normal); occ += (h - d) * sca; sca *= 0.95; } return clamp(1.0 - 3.0 * occ, 0.0, 1.0); }
// ============================================================ // 主光线步进循环 // ============================================================ float RayMarch(float3 ro, float3 rd, out float hitDist) { float t = 0.001; hitDist = -1.0; for (int i = 0; i < _MaxSteps; i++) { float3 p = ro + t * rd; float d = SceneSDF(p);
// 自适应命中阈值:远处允许更大误差 if (abs(d) < _SurfaceDistance * (1.0 + t * 0.05)) { hitDist = t; return 1.0; // 命中 } t += d; if (t > 100.0) break; } return -1.0; // 未命中 }
half4 frag(Varyings input) : SV_Target { float2 uv = input.uv;
// 从深度缓冲读取场景深度(用于与光线步进结果融合) float sceneDepth = SampleSceneDepth(uv);
// 重建世界空间射线(通过逆投影矩阵) float4 clipPos = float4(uv * 2.0 - 1.0, sceneDepth, 1.0); float4 worldPos = mul(_ViewProjInverse, clipPos); float3 sceneWorldPos = worldPos.xyz / worldPos.w;
float3 rayOrigin = _CameraWorldPos.xyz; float3 rayDir = normalize(sceneWorldPos - rayOrigin);
// 采样原始颜色缓冲 float3 sceneColor = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv).rgb;
// 执行光线步进 float hitDist; float hit = RayMarch(rayOrigin, rayDir, hitDist);
if (hit < 0.0) { // 未命中 SDF:返回原始场景颜色(不叠加任何效果) return half4(sceneColor, 1.0); }
// 深度测试:如果 SDF 命中点比场景几何更远,则被遮挡 float3 hitPos = rayOrigin + rayDir * hitDist;
// 将命中点投影到裁剪空间,比较深度 float4 hitClip = mul(UNITY_MATRIX_VP, float4(hitPos, 1.0)); float hitDepth = hitClip.z / hitClip.w;
#if UNITY_REVERSED_Z // DirectX/Metal:深度从 1(近)到 0(远) if (hitDepth < sceneDepth) return half4(sceneColor, 1.0); #else // OpenGL:深度从 -1(近)到 1(远) if (hitDepth > sceneDepth) return half4(sceneColor, 1.0); #endif
// 计算命中点的着色 float3 normal = CalcNormal(hitPos); Light mainLight = GetMainLight();
float diffuse = saturate(dot(normal, mainLight.direction)); float shadow = CalcSoftShadow( hitPos + normal * 0.01, mainLight.direction, 0.02, 10.0 ); float ao = CalcAO(hitPos, normal);
// 材质颜色(棋盘格地面 + 蓝色几何体) float3 matColor; if (hitPos.y < 0.01) { // 棋盘格地面 float checker = fmod(floor(hitPos.x) + floor(hitPos.z), 2.0); matColor = lerp(float3(0.9, 0.9, 0.9), float3(0.3, 0.3, 0.3), checker); } else { matColor = float3(0.3, 0.5, 0.9); }
// PBR 近似着色 float3 ambient = matColor * 0.15 * ao; float3 diffuseC = matColor * diffuse * shadow * mainLight.color; float3 finalColor = ambient + diffuseC;
// 距离雾(与 Unity 场景雾融合) float fogFactor = exp(-_FogDensity * hitDist * hitDist); finalColor = lerp(_FogColor.rgb, finalColor, saturate(fogFactor));
return half4(finalColor, 1.0); } ENDHLSL } } }
|