#ifndef STRIPES_INCLUDED #define STRIPES_INCLUDED /* STRIPES - MEDIUM VERSION Goal: Reduce the GPU cost of the procedural stripe pattern. Changes from the original version: - Removed the dynamic loop over the full stripe count. Original behavior: evaluate every stripe from 0 to Count. New behavior: evaluate only 6 nearby stripe candidates. - Clamped Count to 32 to prevent expensive high-count cases. - Kept Van der Corput distribution to preserve stable pseudo-random variation. - Kept the two distortion paths to preserve part of the organic look. - Visual result is an approximation of the original, but much cheaper. */ float VanDerCorput(uint index) { return (float)reversebits(index) * 2.3283064365386963e-10; } int WrapIndex(int x, int count) { int r = x % count; return r < 0 ? r + count : r; } float EvalStripeCandidate( uint index, uint stripeCount, float wrappedXA, float wrappedXB, float stripDistanceY, float density, float thicknessInput ) { float vdc = VanDerCorput(index); /* Same progressive reveal logic as the original shader. Original: saturate(density * stripeCount - vdc * stripeCount) Equivalent simplified form: saturate((density - vdc) * stripeCount) */ float localProgress = saturate((density - vdc) * stripeCount); if (localProgress <= 0.0001) return 0.0; /* Keep the original two distortion paths. Each stripe chooses one path based on its pseudo-random value. */ float wrappedX = vdc >= 0.5 ? wrappedXB : wrappedXA; /* Keep the original thickness variation. This prevents the stripes from looking too uniform. */ float thickness = vdc < 0.3 ? 1.0 : (vdc < 0.6 ? 1.8 : 2.6); thickness *= saturate(thicknessInput); float stripeCenterX = ((float)index + 0.5) / (float)stripeCount; float stripDistanceX = abs(wrappedX - stripeCenterX); stripDistanceX = min(stripDistanceX, 1.0 - stripDistanceX); float stepX = (1.0 / (float)stripeCount) * localProgress * thickness * 0.5; float stepY = localProgress * 0.5 - 0.01; /* Soft edges are kept from the original shader. If more performance is needed, these smoothstep calls can be replaced with step calls, but the result will look harsher. */ float widthMask = 1.0 - smoothstep(stepX - 0.01, stepX, stripDistanceX); float heightMask = 1.0 - smoothstep(stepY - 0.05, stepY, stripDistanceY); return widthMask * heightMask; } void Stripes_float( float2 UV, float Density, float Count, float Thickness, float Offset, float DistortionAmplitude, float DistortionFrequency, out float Stripes ) { float density = saturate(Density); float offset = frac(Offset); /* Intentional clamp. Above 32 stripes, the cost increases while the visual gain is usually limited, especially on distant objects or in a strongly stylized render. */ uint stripeCount = max((uint)round(Count), 1u); stripeCount = min(stripeCount, 32u); float distortionAmplitude = DistortionAmplitude * 0.1; float2 uv = UV; uv.x = frac(uv.x + offset); /* The two distortion paths are computed once and reused for all nearby candidates. */ float wrappedXA = frac( uv.x + cos((uv.y + offset) * sin(uv.x) * DistortionFrequency + offset * 7.8) * distortionAmplitude ); float wrappedXB = frac( uv.x + sin((uv.y + offset) * cos(uv.x) * DistortionFrequency + offset * 5.3) * distortionAmplitude ); float stripDistanceY = abs(saturate(uv.y) - 0.5); int sc = (int)stripeCount; /* Find the nearest stripe indices for both distortion paths. Original shader tested every stripe. Medium version tests only base - 1, base, base + 1 for A and B. */ int baseA = (int)floor(wrappedXA * sc); int baseB = (int)floor(wrappedXB * sc); float stripeMax = 0.0; [unroll] for (int k = -1; k <= 1; k++) { uint idxA = (uint)WrapIndex(baseA + k, sc); uint idxB = (uint)WrapIndex(baseB + k, sc); stripeMax = max( stripeMax, EvalStripeCandidate( idxA, stripeCount, wrappedXA, wrappedXB, stripDistanceY, density, Thickness ) ); stripeMax = max( stripeMax, EvalStripeCandidate( idxB, stripeCount, wrappedXA, wrappedXB, stripDistanceY, density, Thickness ) ); } Stripes = stripeMax; } #endif