A game about forced loneliness, made by TACStudios
at master 456 lines 16 kB view raw
1#ifndef UNITY_VOLUME_RENDERING_INCLUDED 2#define UNITY_VOLUME_RENDERING_INCLUDED 3 4#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonLighting.hlsl" 5 6// Reminder: 7// OpticalDepth(x, y) = Integral{x, y}{Extinction(t) dt} 8// Transmittance(x, y) = Exp(-OpticalDepth(x, y)) 9// Transmittance(x, z) = Transmittance(x, y) * Transmittance(y, z) 10// Integral{a, b}{Transmittance(0, t) dt} = Transmittance(0, a) * Integral{a, b}{Transmittance(0, t - a) dt} 11 12float TransmittanceFromOpticalDepth(float opticalDepth) 13{ 14 return exp(-opticalDepth); 15} 16 17float3 TransmittanceFromOpticalDepth(float3 opticalDepth) 18{ 19 return exp(-opticalDepth); 20} 21 22float OpacityFromOpticalDepth(float opticalDepth) 23{ 24 return 1 - TransmittanceFromOpticalDepth(opticalDepth); 25} 26 27float3 OpacityFromOpticalDepth(float3 opticalDepth) 28{ 29 return 1 - TransmittanceFromOpticalDepth(opticalDepth); 30} 31 32float OpticalDepthFromOpacity(float opacity) 33{ 34 return -log(1 - opacity); 35} 36 37float3 OpticalDepthFromOpacity(float3 opacity) 38{ 39 return -log(1 - opacity); 40} 41 42// 43// ---------------------------------- Deep Pixel Compositing --------------------------------------- 44// 45 46// TODO: it would be good to improve the perf and numerical stability 47// of approximations below by finding a polynomial approximation. 48 49// input = {radiance, opacity} 50// Note that opacity must be less than 1 (not fully opaque). 51real4 LinearizeRGBA(real4 value) 52{ 53 // See "Deep Compositing Using Lie Algebras". 54 // log(A) = {OpticalDepthFromOpacity(A.a) / A.a * A.rgb, -OpticalDepthFromOpacity(A.a)}. 55 // We drop redundant negations. 56 real a = value.a; 57 real d = -log(1 - a); 58 real r = (a >= REAL_EPS) ? (d * rcp(a)) : 1; // Prevent numerical explosion 59 return real4(r * value.rgb, d); 60} 61 62// input = {radiance, optical_depth} 63// Note that opacity must be less than 1 (not fully opaque). 64real4 LinearizeRGBD(real4 value) 65{ 66 // See "Deep Compositing Using Lie Algebras". 67 // log(A) = {A.a / OpacityFromOpticalDepth(A.a) * A.rgb, -A.a}. 68 // We drop redundant negations. 69 real d = value.a; 70 real a = 1 - exp(-d); 71 real r = (a >= REAL_EPS) ? (d * rcp(a)) : 1; // Prevent numerical explosion 72 return real4(r * value.rgb, d); 73} 74 75// output = {radiance, opacity} 76// Note that opacity must be less than 1 (not fully opaque). 77real4 DelinearizeRGBA(real4 value) 78{ 79 // See "Deep Compositing Using Lie Algebras". 80 // exp(B) = {OpacityFromOpticalDepth(-B.a) / -B.a * B.rgb, OpacityFromOpticalDepth(-B.a)}. 81 // We drop redundant negations. 82 real d = value.a; 83 real a = 1 - exp(-d); 84 real i = (a >= REAL_EPS) ? (a * rcp(d)) : 1; // Prevent numerical explosion 85 return real4(i * value.rgb, a); 86} 87 88// input = {radiance, optical_depth} 89// Note that opacity must be less than 1 (not fully opaque). 90real4 DelinearizeRGBD(real4 value) 91{ 92 // See "Deep Compositing Using Lie Algebras". 93 // exp(B) = {OpacityFromOpticalDepth(-B.a) / -B.a * B.rgb, -B.a}. 94 // We drop redundant negations. 95 real d = value.a; 96 real a = 1 - exp(-d); 97 real i = (a >= REAL_EPS) ? (a * rcp(d)) : 1; // Prevent numerical explosion 98 return real4(i * value.rgb, d); 99} 100 101// 102// ----------------------------- Homogeneous Participating Media ----------------------------------- 103// 104 105real OpticalDepthHomogeneousMedium(real extinction, real intervalLength) 106{ 107 return extinction * intervalLength; 108} 109 110real TransmittanceHomogeneousMedium(real extinction, real intervalLength) 111{ 112 return TransmittanceFromOpticalDepth(OpticalDepthHomogeneousMedium(extinction, intervalLength)); 113} 114 115// Integral{a, b}{TransmittanceHomogeneousMedium(k, t - a) dt}. 116real TransmittanceIntegralHomogeneousMedium(real extinction, real intervalLength) 117{ 118 // Note: when multiplied by the extinction coefficient, it becomes 119 // Albedo * (1 - TransmittanceFromOpticalDepth(d)) = Albedo * Opacity(d). 120 return rcp(extinction) - rcp(extinction) * exp(-extinction * intervalLength); 121} 122 123// 124// ----------------------------------- Height Fog -------------------------------------------------- 125// 126 127// Can be used to scale base extinction and scattering coefficients. 128float ComputeHeightFogMultiplier(real height, real baseHeight, real2 heightExponents) 129{ 130 real h = max(height - baseHeight, 0); 131 float rcpH = heightExponents.x; 132 133 return exp(-h * rcpH); 134} 135 136// Optical depth between two endpoints. 137float OpticalDepthHeightFog(real baseExtinction, real baseHeight, real2 heightExponents, 138 real cosZenith, real startHeight, real intervalLength) 139{ 140 // Height fog is composed of two slices of optical depth: 141 // - homogeneous fog below 'baseHeight': d = k * t 142 // - exponential fog above 'baseHeight': d = Integrate[k * e^(-(h + z * x) / H) dx, {x, 0, t}] 143 144 float H = heightExponents.y; 145 float rcpH = heightExponents.x; 146 real Z = cosZenith; 147 real absZ = max(abs(cosZenith), 0.001f); 148 real rcpAbsZ = rcp(absZ); 149 150 real endHeight = startHeight + intervalLength * Z; 151 real minHeight = min(startHeight, endHeight); 152 real h = max(minHeight - baseHeight, 0); 153 154 real homFogDist = clamp((baseHeight - minHeight) * rcpAbsZ, 0, intervalLength); 155 real expFogDist = intervalLength - homFogDist; 156 float expFogMult = exp(-h * rcpH) * (1 - exp(-expFogDist * absZ * rcpH)) * (rcpAbsZ * H); 157 158 return baseExtinction * (homFogDist + expFogMult); 159} 160 161// This version of the function assumes the interval of infinite length. 162float OpticalDepthHeightFog(real baseExtinction, real baseHeight, real2 heightExponents, 163 real cosZenith, real startHeight) 164{ 165 float H = heightExponents.y; 166 float rcpH = heightExponents.x; 167 real Z = cosZenith; 168 real absZ = max(abs(cosZenith), REAL_EPS); 169 real rcpAbsZ = rcp(absZ); 170 171 real minHeight = (Z >= 0) ? startHeight : -rcp(REAL_EPS); 172 real h = max(minHeight - baseHeight, 0); 173 174 real homFogDist = max((baseHeight - minHeight) * rcpAbsZ, 0); 175 float expFogMult = exp(-h * rcpH) * (rcpAbsZ * H); 176 177 return baseExtinction * (homFogDist + expFogMult); 178} 179 180float TransmittanceHeightFog(real baseExtinction, real baseHeight, real2 heightExponents, 181 real cosZenith, real startHeight, real intervalLength) 182{ 183 float od = OpticalDepthHeightFog(baseExtinction, baseHeight, heightExponents, 184 cosZenith, startHeight, intervalLength); 185 return TransmittanceFromOpticalDepth(od); 186} 187 188float TransmittanceHeightFog(real baseExtinction, real baseHeight, real2 heightExponents, 189 real cosZenith, real startHeight) 190{ 191 float od = OpticalDepthHeightFog(baseExtinction, baseHeight, heightExponents, 192 cosZenith, startHeight); 193 return TransmittanceFromOpticalDepth(od); 194} 195 196// 197// ----------------------------------- Phase Functions --------------------------------------------- 198// 199 200real IsotropicPhaseFunction() 201{ 202 return INV_FOUR_PI; 203} 204 205real RayleighPhaseFunction(real cosTheta) 206{ 207 real k = 3 / (16 * PI); 208 return k * (1 + cosTheta * cosTheta); 209} 210 211real HenyeyGreensteinPhasePartConstant(real anisotropy) 212{ 213 real g = anisotropy; 214 215 return INV_FOUR_PI * (1 - g * g); 216} 217 218real HenyeyGreensteinPhasePartVarying(real anisotropy, real cosTheta) 219{ 220 real g = anisotropy; 221 real x = 1 + g * g - 2 * g * cosTheta; 222 real f = rsqrt(max(x, REAL_EPS)); // x^(-1/2) 223 224 return f * f * f; // x^(-3/2) 225} 226 227real HenyeyGreensteinPhaseFunction(real anisotropy, real cosTheta) 228{ 229 return HenyeyGreensteinPhasePartConstant(anisotropy) * 230 HenyeyGreensteinPhasePartVarying(anisotropy, cosTheta); 231} 232 233// "Physically Based Rendering, 15.2.3 Sampling Phase Functions" 234bool SampleHenyeyGreenstein(real3 incomingDir, 235 real anisotropy, 236 real3 inputSample, 237 out real3 outgoingDir, 238 out real pdf) 239{ 240 real g = anisotropy; 241 242 // Compute costheta 243 real cosTheta; 244 if (abs(g) < 0.001) 245 { 246 cosTheta = 1.0 - 2.0 * inputSample.x; 247 } 248 else 249 { 250 real sqrTerm = (1.0 - g * g) / (1.0 - g + 2.0 * g * inputSample.x); 251 cosTheta = (1.0 + g * g - sqrTerm * sqrTerm) / (2 * g); 252 } 253 254 // Compute direction 255 real sinTheta = sqrt(max(0.0, 1.0 - cosTheta * cosTheta)); 256 real phi = 2.0 * PI * inputSample.y; 257 258 real3 wc = normalize(incomingDir); 259 real3x3 coordsys = GetLocalFrame(wc); 260 261 real sinPhi, cosPhi; 262 sincos(phi, sinPhi, cosPhi); 263 outgoingDir = sinTheta * cosPhi * coordsys[0] + 264 sinTheta * sinPhi * coordsys[1] + 265 cosTheta * coordsys[2]; 266 pdf = HenyeyGreensteinPhaseFunction(g, cosTheta); 267 268 return any(pdf); 269} 270 271real CornetteShanksPhasePartConstant(real anisotropy) 272{ 273 real g = anisotropy; 274 275 return (3 / (8 * PI)) * (1 - g * g) / (2 + g * g); 276} 277 278// Similar to the RayleighPhaseFunction. 279real CornetteShanksPhasePartSymmetrical(real cosTheta) 280{ 281 real h = 1 + cosTheta * cosTheta; 282 return h; 283} 284 285real CornetteShanksPhasePartAsymmetrical(real anisotropy, real cosTheta) 286{ 287 real g = anisotropy; 288 real x = 1 + g * g - 2 * g * cosTheta; 289 real f = rsqrt(max(x, REAL_EPS)); // x^(-1/2) 290 return f * f * f; // x^(-3/2) 291} 292 293real CornetteShanksPhasePartVarying(real anisotropy, real cosTheta) 294{ 295 return CornetteShanksPhasePartSymmetrical(cosTheta) * 296 CornetteShanksPhasePartAsymmetrical(anisotropy, cosTheta); // h * x^(-3/2) 297} 298 299// A better approximation of the Mie phase function. 300// Ref: Henyey-Greenstein and Mie phase functions in Monte Carlo radiative transfer computations 301real CornetteShanksPhaseFunction(real anisotropy, real cosTheta) 302{ 303 return CornetteShanksPhasePartConstant(anisotropy) * 304 CornetteShanksPhasePartVarying(anisotropy, cosTheta); 305} 306 307// 308// --------------------------------- Importance Sampling ------------------------------------------- 309// 310 311// Samples the interval of homogeneous participating medium using the closed-form tracking approach 312// (proportionally to the transmittance). 313// Returns the offset from the start of the interval and the weight = (transmittance / pdf). 314// Ref: Monte Carlo Methods for Volumetric Light Transport Simulation, p. 5. 315void ImportanceSampleHomogeneousMedium(real rndVal, real extinction, real intervalLength, 316 out real offset, out real weight) 317{ 318 // pdf = extinction * exp(extinction * (intervalLength - t)) / (exp(intervalLength * extinction) - 1) 319 // pdf = extinction * exp(-extinction * t) / (1 - exp(-extinction * intervalLength)) 320 // weight = TransmittanceFromOpticalDepth(t) / pdf 321 // weight = exp(-extinction * t) / pdf 322 // weight = (1 - exp(-extinction * intervalLength)) / extinction 323 // weight = OpacityFromOpticalDepth(extinction * intervalLength) / extinction 324 325 real x = 1 - exp(-extinction * intervalLength); 326 real c = rcp(extinction); 327 328 // TODO: return 'rcpPdf' to support imperfect importance sampling... 329 weight = x * c; 330 offset = -log(1 - rndVal * x) * c; 331} 332 333void ImportanceSampleExponentialMedium(real rndVal, real extinction, real falloff, 334 out real offset, out real rcpPdf) 335{ 336 337 // Extinction[t] = Extinction[0] * exp(-falloff * t). 338 real c = extinction; 339 real a = falloff; 340 341 // TODO: optimize... 342 offset = -log(1 - a / c * log(rndVal)) / a; 343 rcpPdf = rcp(c * exp(-a * offset) * exp(-c / a * (1 - exp(-a * offset)))); 344} 345 346// Implements equiangular light sampling. 347// Returns the distance from the origin of the ray, the squared distance from the light, 348// and the reciprocal of the PDF. 349// Ref: Importance Sampling of Area Lights in Participating Medium. 350void ImportanceSamplePunctualLight(real rndVal, real3 lightPosition, real lightSqRadius, 351 real3 rayOrigin, real3 rayDirection, 352 real tMin, real tMax, 353 out real t, out real sqDist, out real rcpPdf) 354{ 355 real3 originToLight = lightPosition - rayOrigin; 356 real originToLightProjDist = dot(originToLight, rayDirection); 357 real originToLightSqDist = dot(originToLight, originToLight); 358 real rayToLightSqDist = originToLightSqDist - originToLightProjDist * originToLightProjDist; 359 360 // Virtually offset the light to modify the PDF distribution. 361 real sqD = max(rayToLightSqDist + lightSqRadius, REAL_EPS); 362 real rcpD = rsqrt(sqD); 363 real d = sqD * rcpD; 364 real a = tMin - originToLightProjDist; 365 real b = tMax - originToLightProjDist; 366 real x = a * rcpD; 367 real y = b * rcpD; 368 369#if 0 370 real theta0 = FastATan(x); 371 real theta1 = FastATan(y); 372 real gamma = theta1 - theta0; 373 real tanTheta = tan(theta0 + rndVal * gamma); 374#else 375 // Same but faster: 376 // atan(y) - atan(x) = atan((y - x) / (1 + x * y)) 377 // tan(atan(x) + z) = (x * cos(z) + sin(z)) / (cos(z) - x * sin(z)) 378 // Both the tangent and the angle cannot be negative. 379 real tanGamma = abs((y - x) * rcp(max(0, 1 + x * y))); 380 real gamma = FastATanPos(tanGamma); 381 real z = rndVal * gamma; 382 real numer = x * cos(z) + sin(z); 383 real denom = cos(z) - x * sin(z); 384 real tanTheta = numer * rcp(denom); 385#endif 386 387 real tRelative = d * tanTheta; 388 389 sqDist = sqD + tRelative * tRelative; 390 rcpPdf = gamma * rcpD * sqDist; 391 t = originToLightProjDist + tRelative; 392 393 // Remove the virtual light offset to obtain the real geometric distance. 394 sqDist = max(sqDist - lightSqRadius, REAL_EPS); 395} 396 397// Returns the cosine. 398// Weight = Phase / Pdf = 1. 399real ImportanceSampleRayleighPhase(real rndVal) 400{ 401 // real a = sqrt(16 * (rndVal - 1) * rndVal + 5); 402 // real b = -4 * rndVal + a + 2; 403 // real c = PositivePow(b, 0.33333333); 404 // return rcp(c) - c; 405 406 // Approximate... 407 return lerp(cos(PI * rndVal + PI), 2 * rndVal - 1, 0.5); 408} 409 410// 411// ------------------------------------ Miscellaneous ---------------------------------------------- 412// 413 414// Absorption coefficient from Disney: http://blog.selfshadow.com/publications/s2015-shading-course/burley/s2015_pbs_disney_bsdf_notes.pdf 415real3 TransmittanceColorAtDistanceToAbsorption(real3 transmittanceColor, real atDistance) 416{ 417 return -log(transmittanceColor + REAL_EPS) / max(atDistance, REAL_EPS); 418} 419 420float ApplyExponentialFadeFactor(float fade, bool exponential, bool multiplyBlendMode) 421{ 422 if (exponential) 423 { 424 if (multiplyBlendMode) 425 fade = 1 - PositivePow(abs(fade - 1), 2.2); 426 else 427 fade = PositivePow(fade, 2.2); 428 } 429 return fade; 430} 431 432float ComputeVolumeFadeFactor(float3 coordNDC, float dist, 433 float3 rcpPosFaceFade, float3 rcpNegFaceFade, bool invertFade, 434 float rcpDistFadeLen, float endTimesRcpDistFadeLen, 435 bool exponentialFalloff, bool multiplyBlendMode) 436{ 437 float3 posF = Remap10(coordNDC, rcpPosFaceFade, rcpPosFaceFade); 438 float3 negF = Remap01(coordNDC, rcpNegFaceFade, 0); 439 float dstF = Remap10(dist, rcpDistFadeLen, endTimesRcpDistFadeLen); 440 float fade = posF.x * posF.y * posF.z * negF.x * negF.y * negF.z; 441 442 // We only apply exponential falloff on the Blend Distance and not Distance Fade 443 fade = ApplyExponentialFadeFactor(fade, exponentialFalloff, multiplyBlendMode); 444 445 fade = dstF * (invertFade ? (1 - fade) : fade); 446 447 return fade; 448} 449 450float ExtinctionFromMeanFreePath(float meanFreePath) 451{ 452 // Keep in sync with kMinFogDistance 453 return rcp(max(0.05, meanFreePath)); 454} 455 456#endif // UNITY_VOLUME_RENDERING_INCLUDED