283 lines
10 KiB
Plaintext
283 lines
10 KiB
Plaintext
|
|
|
|
#if _TRIPLANAR
|
|
#define OffsetUVChannel(config, tc, offset, channel) tc.uv##channel[0].xy += offset; tc.uv##channel[1].xy += offset; tc.uv##channel[2].xy += offset
|
|
#else
|
|
#define OffsetUVChannel(config, tc, offset, channel) config.uv##channel.xy += offset
|
|
#endif
|
|
|
|
void OffsetUVs(inout Config c, inout TriplanarConfig tc, float2 offset)
|
|
{
|
|
OffsetUVChannel(c, tc, offset, 0);
|
|
OffsetUVChannel(c, tc, offset, 1);
|
|
OffsetUVChannel(c, tc, offset, 2);
|
|
OffsetUVChannel(c, tc, offset, 3);
|
|
}
|
|
|
|
|
|
half SampleHeightsPOM0(Config config, TriplanarConfig tc, MIPFORMAT mipLevel, float2 offset, half4 ptHeight)
|
|
{
|
|
OffsetUVChannel(config, tc, offset, 0);
|
|
|
|
float height = 0;
|
|
#if _TRIPLANAR
|
|
#if _USEGRADMIP
|
|
float4 d0 = mipLevel.d0;
|
|
float4 d1 = mipLevel.d1;
|
|
float4 d2 = mipLevel.d2;
|
|
#elif _USELODMIP
|
|
float d0 = mipLevel.x;
|
|
float d1 = mipLevel.y;
|
|
float d2 = mipLevel.z;
|
|
#else
|
|
MIPFORMAT d0 = mipLevel;
|
|
MIPFORMAT d1 = mipLevel;
|
|
MIPFORMAT d2 = mipLevel;
|
|
#endif
|
|
|
|
|
|
{
|
|
half a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[0], config.cluster0, d0).a;
|
|
half a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[1], config.cluster0, d1).a;
|
|
half a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[2], config.cluster0, d2).a;
|
|
half3 bf = tc.pN0;
|
|
|
|
height = a0 * bf.x + a1 * bf.y + a2 * bf.z;
|
|
}
|
|
#else
|
|
height = MICROSPLAT_SAMPLE_DIFFUSE(config.uv0, config.cluster0, mipLevel).a;
|
|
#endif
|
|
|
|
#if _PERTEXHEIGHTOFFSET || _PERTEXHEIGHTCONTRAST
|
|
|
|
#if _PERTEXHEIGHTOFFSET
|
|
height = saturate(height + ptHeight.b - 1);
|
|
#endif
|
|
#if _PERTEXHEIGHTCONTRAST
|
|
height = saturate(pow(height + 0.5, ptHeight.a) - 0.5);
|
|
#endif
|
|
#endif
|
|
return height;
|
|
}
|
|
|
|
half SampleHeightsPOM1(Config config, TriplanarConfig tc, MIPFORMAT mipLevel, float2 offset, half4 ptHeight)
|
|
{
|
|
OffsetUVChannel(config, tc, offset, 1);
|
|
|
|
float height = 0;
|
|
#if _TRIPLANAR
|
|
#if _USEGRADMIP
|
|
float4 d0 = mipLevel.d0;
|
|
float4 d1 = mipLevel.d1;
|
|
float4 d2 = mipLevel.d2;
|
|
#elif _USELODMIP
|
|
float d0 = mipLevel.x;
|
|
float d1 = mipLevel.y;
|
|
float d2 = mipLevel.z;
|
|
#else
|
|
MIPFORMAT d0 = mipLevel;
|
|
MIPFORMAT d1 = mipLevel;
|
|
MIPFORMAT d2 = mipLevel;
|
|
#endif
|
|
|
|
|
|
{
|
|
half a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[0], config.cluster1, d0).a;
|
|
half a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[1], config.cluster1, d1).a;
|
|
half a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[2], config.cluster1, d2).a;
|
|
half3 bf = tc.pN0;
|
|
|
|
height = a0 * bf.x + a1 * bf.y + a2 * bf.z;
|
|
}
|
|
#else
|
|
height = MICROSPLAT_SAMPLE_DIFFUSE(config.uv1, config.cluster1, mipLevel).a;
|
|
#endif
|
|
|
|
#if _PERTEXHEIGHTOFFSET || _PERTEXHEIGHTCONTRAST
|
|
|
|
#if _PERTEXHEIGHTOFFSET
|
|
height = saturate(height+ ptHeight.b - 1);
|
|
#endif
|
|
#if _PERTEXHEIGHTCONTRAST
|
|
height = saturate(pow(height + 0.5, ptHeight.a) - 0.5);
|
|
#endif
|
|
#endif
|
|
return height;
|
|
}
|
|
|
|
float2 POMLayer0(Config config, TriplanarConfig tc, MIPFORMAT mipLevel, float3 viewDirTan,
|
|
int numSteps, float2 texOffsetPerStep, float stepSize, half4 ptData, out float outHeight)
|
|
{
|
|
|
|
// Do a first step before the loop to init all value correctly
|
|
float2 texOffsetCurrent = float2(0.0, 0.0);
|
|
float prevHeight = SampleHeightsPOM0(config, tc, mipLevel, texOffsetCurrent, ptData);
|
|
texOffsetCurrent += texOffsetPerStep;
|
|
float currHeight = SampleHeightsPOM0(config, tc, mipLevel, texOffsetCurrent, ptData);
|
|
float rayHeight = 1.0 - stepSize; // Start at top less one sample
|
|
|
|
// Linear search
|
|
for (int stepIndex = 0; stepIndex < numSteps; ++stepIndex)
|
|
{
|
|
if (currHeight > rayHeight)
|
|
break;
|
|
|
|
prevHeight = currHeight;
|
|
rayHeight -= stepSize;
|
|
texOffsetCurrent += texOffsetPerStep;
|
|
|
|
currHeight = SampleHeightsPOM0(config, tc, mipLevel, texOffsetCurrent, ptData);
|
|
}
|
|
|
|
// secant search method
|
|
|
|
float pt0 = rayHeight + stepSize;
|
|
float pt1 = rayHeight;
|
|
float delta0 = pt0 - prevHeight;
|
|
float delta1 = pt1 - currHeight;
|
|
|
|
float delta;
|
|
float2 offset;
|
|
|
|
for (int i = 0; i < 5; ++i)
|
|
{
|
|
float intersectionHeight = (pt0 * delta1 - pt1 * delta0) / (delta1 - delta0);
|
|
offset = (1 - intersectionHeight) * texOffsetPerStep * numSteps;
|
|
|
|
currHeight = SampleHeightsPOM0(config, tc, mipLevel, offset, ptData);
|
|
|
|
delta = intersectionHeight - currHeight;
|
|
|
|
if (abs(delta) <= 0.01)
|
|
break;
|
|
|
|
// intersectionHeight < currHeight => new lower bounds
|
|
if (delta < 0.0)
|
|
{
|
|
delta1 = delta;
|
|
pt1 = intersectionHeight;
|
|
}
|
|
else
|
|
{
|
|
delta0 = delta;
|
|
pt0 = intersectionHeight;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
outHeight = currHeight;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
float2 POMLayer1(Config config, TriplanarConfig tc, MIPFORMAT mipLevel, float3 viewDirTan,
|
|
int numSteps, float2 texOffsetPerStep, float stepSize, half4 ptData, out float outHeight)
|
|
{
|
|
|
|
// Do a first step before the loop to init all value correctly
|
|
float2 texOffsetCurrent = float2(0.0, 0.0);
|
|
float prevHeight = SampleHeightsPOM1(config, tc, mipLevel, texOffsetCurrent, ptData);
|
|
texOffsetCurrent += texOffsetPerStep;
|
|
float currHeight = SampleHeightsPOM1(config, tc, mipLevel, texOffsetCurrent, ptData);
|
|
float rayHeight = 1.0 - stepSize; // Start at top less one sample
|
|
|
|
// Linear search
|
|
for (int stepIndex = 0; stepIndex < numSteps; ++stepIndex)
|
|
{
|
|
if (currHeight > rayHeight)
|
|
break;
|
|
|
|
prevHeight = currHeight;
|
|
rayHeight -= stepSize;
|
|
texOffsetCurrent += texOffsetPerStep;
|
|
|
|
currHeight = SampleHeightsPOM1(config, tc, mipLevel, texOffsetCurrent, ptData);
|
|
}
|
|
|
|
// secant search method
|
|
|
|
float pt0 = rayHeight + stepSize;
|
|
float pt1 = rayHeight;
|
|
float delta0 = pt0 - prevHeight;
|
|
float delta1 = pt1 - currHeight;
|
|
|
|
float delta;
|
|
float2 offset;
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
float intersectionHeight = (pt0 * delta1 - pt1 * delta0) / (delta1 - delta0);
|
|
offset = (1 - intersectionHeight) * texOffsetPerStep * numSteps;
|
|
|
|
currHeight = SampleHeightsPOM1(config, tc, mipLevel, offset, ptData);
|
|
|
|
delta = intersectionHeight - currHeight;
|
|
|
|
if (abs(delta) <= 0.01)
|
|
break;
|
|
|
|
// intersectionHeight < currHeight => new lower bounds
|
|
if (delta < 0.0)
|
|
{
|
|
delta1 = delta;
|
|
pt1 = intersectionHeight;
|
|
}
|
|
else
|
|
{
|
|
delta0 = delta;
|
|
pt0 = intersectionHeight;
|
|
}
|
|
}
|
|
|
|
outHeight = currHeight;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
void DoPOM(Input i, inout Config c, inout TriplanarConfig tc, MIPFORMAT mipLevel,
|
|
half4 weights, float camDist, float3 worldNormal)
|
|
{
|
|
// prefetch heights, so we don't do it every sample
|
|
#if _PERTEXHEIGHTOFFSET || _PERTEXHEIGHTCONTRAST
|
|
SAMPLE_PER_TEX(ptHeight, 10.5, c, 1);
|
|
#else
|
|
half4 ptHeight0 = half4(1,1,1,1);
|
|
half4 ptHeight1 = half4(1,1,1,1);
|
|
#endif
|
|
|
|
float3 worldView = normalize(_WorldSpaceCameraPos - i.worldPos);
|
|
float ndot = dot( worldNormal, worldView);
|
|
int numSteps = (int)lerp(4, _POMParams.w, ndot);
|
|
float3 viewDirTS = i.viewDir;
|
|
float angleFade = viewDirTS.z;
|
|
|
|
float distFade = 1 - saturate((camDist - _POMParams.y) / _POMParams.z);
|
|
float stepSize = 1.0 / (float)numSteps;
|
|
|
|
float2 parallaxMaxOffsetTS = (viewDirTS.xy / -viewDirTS.z);
|
|
|
|
float2 texOffsetPerStep = stepSize * parallaxMaxOffsetTS * _POMParams.x * distFade * angleFade;
|
|
|
|
float outHeight0 = 0;
|
|
float outHeight1 = 0;
|
|
float2 offset0 = POMLayer0(c, tc, mipLevel, viewDirTS, numSteps, texOffsetPerStep, stepSize, ptHeight0, outHeight0);
|
|
float2 offset1 = POMLayer1(c, tc, mipLevel, viewDirTS, numSteps, texOffsetPerStep, stepSize, ptHeight1, outHeight1);
|
|
|
|
|
|
#if _PERTEXPARALLAX
|
|
SAMPLE_PER_TEX(ptp, 6.5, c, 0.0);
|
|
offset0 *= ptp0.a;
|
|
offset1 *= ptp1.a;
|
|
#endif
|
|
|
|
|
|
weights.xy = TotalOne(weights.xy);
|
|
float l = HeightBlend(outHeight0, outHeight1, 1.0 - weights.x, _Contrast);
|
|
float2 offset = lerp(offset0, offset1, l);
|
|
OffsetUVs(c, tc, offset);
|
|
}
|