forked from LeenkxTeam/LNXSDK
251 lines
8.3 KiB
GLSL
251 lines
8.3 KiB
GLSL
// TODO: Integrate with Blender UI
|
|
// TODO: Option to disable cone tracing
|
|
|
|
#version 450
|
|
|
|
#include "compiled.inc"
|
|
#include "std/math.glsl"
|
|
#include "std/gbuffer.glsl"
|
|
|
|
// Environment map
|
|
uniform float envmapStrength;
|
|
#ifdef _Irr
|
|
uniform vec4 shirr[7];
|
|
uniform float ambientIntensity;
|
|
#include "std/shirr.glsl"
|
|
#endif
|
|
#include "std/environment_sample.glsl"
|
|
|
|
uniform sampler2D tex; // Environment map
|
|
//uniform sampler2D depthtex; // Full Depth buffer
|
|
uniform sampler2D gbufferD; // Cheap Depth buffer
|
|
uniform sampler2D gbuffer0; // Normal, roughness
|
|
uniform sampler2D gbuffer1; // Base color, spec
|
|
uniform mat4 P; // Projection matrix
|
|
uniform mat3 V3; // View matrix
|
|
uniform vec2 cameraProj; // Camera projection params
|
|
uniform vec2 invScreenSize; // (1.0/width, 1.0/height)
|
|
|
|
const float ssrPrecision = 0.0; // 0.0 - 100.0 (user slider control)
|
|
//const float rayThickness = 0.1; // TODO: Adds some thickness to prevent gaps
|
|
uniform int ssrConetraceMode = 2; // 0 = no weighting, 1 = light, 2 = strong
|
|
const int ssrConetraceTaps = 18; // Number of taps (higher = more precision)
|
|
|
|
in vec3 viewRay;
|
|
in vec2 texCoord;
|
|
out vec4 fragColor;
|
|
|
|
vec3 hitCoord;
|
|
float depth;
|
|
|
|
const int baseBinarySearchSteps = 10; // Parameterize?
|
|
const int baseMaxSteps = int(ceil(1.0 / ssrRayStep) * ssrSearchDist);
|
|
|
|
int dynamicBinarySearchSteps() {
|
|
return int(mix(7.0, 20.0, clamp(ssrPrecision / 100.0, 0.0, 1.0)));
|
|
}
|
|
|
|
int dynamicMaxSteps() {
|
|
return int(mix(float(baseMaxSteps), 300.0, clamp(ssrPrecision / 100.0, 0.0, 1.0)));
|
|
}
|
|
|
|
vec2 getProjectedCoord(const vec3 hit) {
|
|
vec4 clip = P * vec4(hit, 1.0);
|
|
vec2 uv = clip.xy / clip.w;
|
|
uv = uv * 0.5 + 0.5;
|
|
#ifdef _InvY
|
|
uv.y = 1.0 - uv.y;
|
|
#endif
|
|
uv = clamp(uv, 0.0, 1.0);
|
|
uv += invScreenSize * 0.5; // half-pixel offset
|
|
return uv;
|
|
}
|
|
|
|
float getDeltaDepth(const vec3 hit) {
|
|
vec2 screenUV = getProjectedCoord(hit);
|
|
float raw = textureLod(gbufferD, screenUV, 0.0).r;
|
|
float sampledDepth = raw * 2.0 - 1.0;
|
|
float linearSampledDepth = linearize(sampledDepth, cameraProj);
|
|
|
|
return linearSampledDepth - hit.z;
|
|
}
|
|
|
|
vec4 binarySearch(vec3 dir) {
|
|
float ddepth;
|
|
for (int i = 0; i < dynamicBinarySearchSteps(); i++) {
|
|
dir *= 0.5;
|
|
hitCoord -= dir;
|
|
ddepth = getDeltaDepth(hitCoord);
|
|
if (ddepth < 0.0) hitCoord += dir;
|
|
}
|
|
|
|
vec2 projectedCoord = getProjectedCoord(hitCoord);
|
|
float pixelSize = length(fwidth(projectedCoord)) * 0.5;
|
|
float epsilon = max(pixelSize * 10.0, 0.01);
|
|
|
|
if (abs(ddepth) > epsilon) return vec4(0.0);
|
|
|
|
hitCoord.xy = clamp(projectedCoord, 0.0, 1.0);
|
|
|
|
return vec4(projectedCoord, 0.0, 1.0);
|
|
}
|
|
|
|
vec4 rayCast(vec3 dir, float roughness) {
|
|
vec3 stepDir = normalize(dir);
|
|
|
|
// Apply small jitter if high precision
|
|
if (ssrPrecision > 80.0 && roughness > 0.05) {
|
|
stepDir = normalize(stepDir + vec3(rand2(texCoord), 0.0) * 0.01);
|
|
}
|
|
|
|
float distance = length(hitCoord - viewRay);
|
|
float stepDivisor = mix(100.0, 300.0, clamp(ssrPrecision / 100.0, 0.0, 1.0));
|
|
float stepSize = max(0.01, distance / stepDivisor);
|
|
float maxStepSize = 0.1;
|
|
|
|
for (int i = 0; i < dynamicMaxSteps(); i++) {
|
|
hitCoord += stepDir * stepSize;
|
|
|
|
vec2 projCoord = getProjectedCoord(hitCoord);
|
|
float sampledDepth = textureLod(gbufferD, projCoord, 0.0).r;
|
|
float depthTolerance = fwidth(sampledDepth) * 2.0; // Dynamic tolerance
|
|
|
|
if (getDeltaDepth(hitCoord) > depthTolerance) {
|
|
return binarySearch(stepDir);
|
|
}
|
|
|
|
stepSize = min(stepSize * 1.1, maxStepSize);
|
|
}
|
|
|
|
return vec4(0.0);
|
|
}
|
|
|
|
vec3 coneTraceApprox(vec3 reflDir, float roughness) {
|
|
vec3 result = vec3(0.0);
|
|
float totalWeight = 0.0;
|
|
|
|
float randAngle = hash12(texCoord) * 6.2831853;
|
|
mat2 rot = mat2(cos(randAngle), -sin(randAngle), sin(randAngle), cos(randAngle));
|
|
|
|
float coneSpread = roughness * 0.5; // widen based on roughness
|
|
|
|
for (int i = 0; i < ssrConetraceTaps; ++i) {
|
|
float angle = float(i) / float(ssrConetraceTaps) * 6.2831853;
|
|
vec2 offset = rot * vec2(cos(angle), sin(angle)) * coneSpread;
|
|
vec3 sampleDir = normalize(reflDir + vec3(offset, 0.0));
|
|
|
|
float weight = 1.0;
|
|
if (ssrConetraceMode == 1) {
|
|
weight = pow(max(dot(reflDir, sampleDir), 0.0), 2.0); // Light cosine lobe
|
|
}
|
|
else if (ssrConetraceMode == 2) {
|
|
weight = pow(max(dot(reflDir, sampleDir), 0.0), 8.0); // Strong GGX lobe
|
|
}
|
|
|
|
// Approximate sampling by using tex at reflection direction
|
|
vec2 envUV = envMapEquirect(sampleDir);
|
|
vec3 sampleColor = textureLod(tex, envUV, 0.0).rgb;
|
|
|
|
result += sampleColor * weight;
|
|
totalWeight += weight;
|
|
}
|
|
|
|
return result / max(totalWeight, 0.0001);
|
|
}
|
|
|
|
vec3 tangentSpaceGGX(vec3 N, float roughness) {
|
|
float a = roughness * roughness;
|
|
float phi = rand(texCoord) * 6.2831853;
|
|
float cosTheta = sqrt((1.0 - rand(texCoord)) / (1.0 + (a * a - 1.0) * rand(texCoord)));
|
|
float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
|
|
|
|
vec3 T = normalize(cross(N, vec3(0.0, 1.0, 0.0)));
|
|
if (length(T) < 0.01) T = normalize(cross(N, vec3(1.0, 0.0, 0.0)));
|
|
vec3 B = cross(N, T);
|
|
|
|
return normalize(T * cos(phi) * sinTheta + B * sin(phi) * sinTheta + N * cosTheta);
|
|
}
|
|
|
|
void main() {
|
|
|
|
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0);
|
|
float roughness = unpackFloat(g0.b).y;
|
|
if (roughness == 1.0) { fragColor.rgb = vec3(0.0); return; }
|
|
|
|
float spec = fract(textureLod(gbuffer1, texCoord, 0.0).a);
|
|
if (spec == 0.0) { fragColor.rgb = vec3(0.0); return; }
|
|
|
|
// sample raw depth, bail if empty
|
|
float dRaw = textureLod(gbufferD, texCoord, 0.0).r;
|
|
if (dRaw == 0.0) { fragColor.rgb = vec3(0.0); return; }
|
|
// convert to NDC z before reconstructing
|
|
float d = dRaw * 2.0 - 1.0;
|
|
|
|
vec3 n = decode_oct(g0.rg);
|
|
vec3 viewNormal = V3 * n;
|
|
viewNormal = normalize(mix(viewNormal, normalize(-viewRay), 0.05)); // slightly bias the normal toward the view direction at glancing angles
|
|
vec3 viewPos = getPosView(viewRay, d, cameraProj);
|
|
vec3 viewDir = normalize(-viewPos);
|
|
vec3 idealReflection = reflect(normalize(viewPos), viewNormal);
|
|
hitCoord = viewPos;
|
|
|
|
// Apply GGX importance sampling in tangent space
|
|
vec3 jitteredDir = tangentSpaceGGX(viewNormal, roughness);
|
|
|
|
// Blend based on roughness (0 = perfect mirror, 1 = fully scattered)
|
|
vec3 dir = normalize(mix(idealReflection, jitteredDir, roughness));
|
|
|
|
vec4 coords = rayCast(dir, roughness);
|
|
|
|
vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy);
|
|
float screenEdgeFactor = clamp(1.0 - (deltaCoords.x + deltaCoords.y), 0.0, 1.0);
|
|
|
|
float reflectivity = 1.0 - roughness;
|
|
|
|
#ifdef _CPostprocess
|
|
// Postprocess mode intensity calculation...
|
|
#else
|
|
float intensity = 0.0;
|
|
|
|
if (coords.w > 0.0) {
|
|
// Ray hit in screen-space
|
|
intensity = pow(reflectivity, ssrFalloffExp) * screenEdgeFactor *
|
|
clamp(-idealReflection.z, 0.0, 1.0) *
|
|
clamp((ssrSearchDist - length(viewPos - hitCoord)) * (1.0 / ssrSearchDist), 0.0, 1.0);
|
|
}
|
|
else if (roughness < 0.7) {
|
|
// Ray miss, roughness low enough: do cone trace approximation
|
|
vec3 coneColor = coneTraceApprox(idealReflection, roughness);
|
|
vec3 envCol = sampleSpecularEnvironment(idealReflection, roughness);
|
|
|
|
coneColor = clamp(coneColor, 0.0, 1.0);
|
|
envCol = clamp(envCol, 0.0, 1.0);
|
|
|
|
vec3 finalColor = mix(envCol, mix(envCol, coneColor, intensity), coords.w);
|
|
fragColor.rgb = finalColor * 0.5; // match previous intensity scaling
|
|
return;
|
|
}
|
|
else {
|
|
// Ray miss, roughness too high: fallback to environment map
|
|
vec3 fallbackEnvColor = sampleSpecularEnvironment(reflect(viewDir, n), roughness);
|
|
fragColor.rgb = fallbackEnvColor;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
vec3 reflCol = textureLod(tex, coords.xy, 0.0).rgb; // SSR reflection
|
|
vec3 envCol = textureLod(tex, texCoord, 0.0).rgb; // Background environment
|
|
|
|
intensity = clamp(intensity, 0.0, 1.0);
|
|
reflCol = clamp(reflCol, 0.0, 1.0);
|
|
envCol = clamp(envCol, 0.0, 1.0);
|
|
|
|
// Roughness-based fade (smoothstep)
|
|
float roughFade = smoothstep(0.2, 0.7, roughness);
|
|
float ssrVisibility = coords.w * (1.0 - roughFade);
|
|
|
|
// Blend SSR
|
|
vec3 finalColor = mix(envCol, reflCol, ssrVisibility);
|
|
|
|
fragColor.rgb = finalColor;
|
|
} |