179 lines
4.8 KiB
HLSL
179 lines
4.8 KiB
HLSL
#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 |