114 lines
3.8 KiB
HLSL
114 lines
3.8 KiB
HLSL
#ifndef FILAMENT_LIGHT_MIRROR
|
|
#define FILAMENT_LIGHT_MIRROR
|
|
// Commmon code for sampling from VRC (or similar) mirrors systems.
|
|
//
|
|
// Use a mirror as the source for the reflection. Multiple samples taken for roughness.
|
|
// This is a rough approximation for performance's sake.
|
|
// Based on https://www.shadertoy.com/view/DtBXDt (CC0)
|
|
|
|
#if defined(MIRROR_REFLECTION)
|
|
UNITY_DECLARE_TEX2D_FLOAT(_ReflectionTex0);
|
|
UNITY_DECLARE_TEX2D_FLOAT(_ReflectionTex1);
|
|
#endif
|
|
|
|
class MirrorReflectionSampler
|
|
{
|
|
// Calculate R2 for index i
|
|
float2 getR2(float2 i) {
|
|
return frac(i * float2(0.245122333753, 0.430159709002));
|
|
}
|
|
|
|
uint hilbert_idx(uint2 uv, uint offset)
|
|
{
|
|
// Hilbert curve:
|
|
uint C = 0xB4361E9Cu;// cost lookup
|
|
uint P = 0xEC7A9107u;// pattern lookup
|
|
|
|
uv += uint(offset) * uint2(2447445397u, 3242174893u);
|
|
|
|
uint c = 0u;// accumulated cost
|
|
uint p = 0u;// current pattern
|
|
|
|
const uint N = 7u;// tile size = 2^N
|
|
for(uint i = N; --i < N;)
|
|
{
|
|
uint2 m = (uv >> i) & 1u;// local uv
|
|
uint n = m.x ^ (m.y << 1u);// linearized local uv
|
|
uint o = (p << 3u) ^ (n << 1u);// offset into lookup tables
|
|
c += ((C >> o) & 3u) << (i << 1u);// accu cost (scaled by layer)
|
|
p = (P >> o) & 3u;// update pattern
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
inline half4 getMirrorReflection(float2 screenCoord) {
|
|
half4 refl = 0;
|
|
#if defined(MIRROR_REFLECTION)
|
|
refl = (unity_StereoEyeIndex == 0)
|
|
? UNITY_SAMPLE_TEX2D(_ReflectionTex0, screenCoord)
|
|
: UNITY_SAMPLE_TEX2D(_ReflectionTex1, screenCoord);
|
|
#endif // MIRROR_REFLECTION
|
|
return refl;
|
|
}
|
|
|
|
inline half4 getFilteredMirrorRadiance(const ShadingParams shading, float roughness) {
|
|
half4 refl = 0;
|
|
half weight = 0;
|
|
|
|
#if defined(MIRROR_REFLECTION)
|
|
float2 refSize;
|
|
_ReflectionTex0.GetDimensions(refSize.x, refSize.y);
|
|
|
|
// Since it's possible the user might not know the resolution will have
|
|
// a big impact on performance, force a resolution scale.
|
|
|
|
half totalPixels = refSize.x * refSize.y;
|
|
const half minArea = 256.0 * 256.0;
|
|
const half maxArea = 1024.0 * 1024.0;
|
|
half resScale = saturate((totalPixels - minArea) / (maxArea - minArea));
|
|
|
|
// If there is no mirror texture, exit.
|
|
if (totalPixels <= 0) return 0;
|
|
|
|
float3 normalOffset = shading.geometricNormal - shading.normal;
|
|
half2 normalVS = mul((float3x3)UNITY_MATRIX_V, normalOffset);
|
|
|
|
half2 screenCoord = shading.normalizedViewportCoord;
|
|
|
|
// At 256 or lower, do the full 64 samples and use a low roughness cutoff.
|
|
// At 1024 or higher, perform only one sample and no roughness.
|
|
|
|
half roughnessCutoff = lerp(0.1h, 0.01h, resScale);
|
|
uint N = clamp(uint(lerp(64.0h, 1.0h, resScale)), 1, 64); // sample count
|
|
half dmax = roughness * lerp(4.0h, 0.01h, resScale); // max blur distance
|
|
|
|
if (roughness >= roughnessCutoff) return 0;
|
|
|
|
float sigma = dmax / 2.5;
|
|
sigma *= sigma;
|
|
|
|
half idx = fmod(hilbert_idx(uint2(screenCoord * _ScreenParams.xy), 0), 100000.0h);
|
|
half sampleMask = 1.0;
|
|
|
|
[loop]
|
|
for (uint i = 0.0; i < N; i++) {
|
|
half2 uvoff = dmax * (getR2(half2(idx + i, idx + i + 1)) - 0.5);
|
|
half bw = exp(-dot(uvoff, uvoff) / sigma);
|
|
|
|
half2 sampleCoord = screenCoord + uvoff + normalVS;
|
|
|
|
sampleMask *= 1.0 - length(saturate(sampleCoord) - sampleCoord) * bw;
|
|
|
|
refl += bw * max(0, getMirrorReflection(sampleCoord));
|
|
weight += bw;
|
|
}
|
|
|
|
refl /= weight;
|
|
refl.a = 1 - saturate((roughness - MIN_ROUGHNESS) / roughnessCutoff) * sampleMask;
|
|
#endif // MIRROR_REFLECTION
|
|
return max(0, refl);
|
|
}
|
|
};
|
|
#endif // FILAMENT_LIGHT_MIRROR
|