@@ -417,19 +417,25 @@ void main()
417417 float firstHitDistance = LUX_CLOUD_MAX_DEPTH;
418418 float lastHitDistance = LUX_CLOUD_MAX_DEPTH;
419419
420- // Empty-space skipping: when we hit a void, take larger steps until we
421- // re-enter cloud, then refine.
420+ // Empty-space skipping: stride through voids with large steps; when we
421+ // re-enter cloud while striding, rewind and resume at fine resolution so the
422+ // boundary isn't overshot. 't' is accumulated (NOT derived from the loop
423+ // index) so step lengths can vary, and density is only ever integrated on a
424+ // fine (1x) step — the integration distance therefore always equals the
425+ // sample spacing, keeping the scattering energy-conserving. 'marchSteps' sets
426+ // the fine step length (cloud sampling density); the loop index is only a
427+ // safety bound so a noisy boundary can't spin forever.
428+ const float kEmptyStepScale = 2.0 ;
422429 int emptySteps = 0 ;
430+ float t = traceStart + jitter * baseStepLength;
423431
424- for (uint i = 0u; i < 128u ; i++ )
432+ for (uint i = 0u; i < 256u ; i++ )
425433 {
426- if (i >= marchSteps || transmittance < 0.01 )
434+ if (transmittance < 0.01 || t >= traceEnd )
427435 break ;
428436
429- float stepScale = emptySteps > 2 ? 2.0 : 1.0 ;
430- float t = traceStart + (float (i) + jitter) * baseStepLength;
431- if (t >= traceEnd)
432- break ;
437+ float stepScale = emptySteps > 2 ? kEmptyStepScale : 1.0 ;
438+ float stepLength = baseStepLength * stepScale;
433439
434440 vec3 samplePosition = cameraPosition + viewDirection * t;
435441 float lod = GetCloudLODFactor(t);
@@ -438,6 +444,17 @@ void main()
438444 if (density <= LUX_CLOUD_MIN_DENSITY)
439445 {
440446 emptySteps++ ;
447+ t += stepLength;
448+ continue ;
449+ }
450+
451+ // Re-entered cloud on a coarse stride: rewind to the last empty position
452+ // and re-approach finely instead of integrating the coarse step.
453+ if (stepScale > 1.0 )
454+ {
455+ emptySteps = 0 ;
456+ t -= stepLength; // back to the last known-empty sample
457+ t += baseStepLength; // ...and step in at fine resolution
441458 continue ;
442459 }
443460 emptySteps = 0 ;
@@ -473,13 +490,16 @@ void main()
473490 vec3 ambient = ambientSky * (0.4 + 0.6 * heightFraction);
474491 vec3 luminance = (sunLuminance * powder * scatterMul + ambient) * albedo;
475492
476- // Energy-conserving scattering integration (Hillaire).
493+ // Energy-conserving scattering integration (Hillaire). Reached only on a
494+ // fine step (stepScale == 1), so stepLength == baseStepLength and the
495+ // integration distance matches the sample spacing.
477496 float sigmaT = density * extinction;
478- float stepLength = baseStepLength * stepScale;
479497 float stepTransmittance = exp (- sigmaT * stepLength);
480498 vec3 integScatter = (luminance * density - luminance * density * stepTransmittance) / max (sigmaT, 1.0e-5 );
481499 scatteredLight += transmittance * integScatter;
482500 transmittance *= stepTransmittance;
501+
502+ t += stepLength;
483503 }
484504
485505 float rawAlpha = clamp ((1.0 - transmittance) * distanceFade, 0.0 , 1.0 );
0 commit comments