2025-01-22 16:18:30 +01:00
|
|
|
//
|
|
|
|
|
// Copyright (C) 2012 Jorge Jimenez (jorge@iryoku.com)
|
|
|
|
|
// Copyright (C) 2012 Diego Gutierrez (diegog@unizar.es)
|
|
|
|
|
// All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
|
// modification, are permitted provided that the following conditions are met:
|
|
|
|
|
//
|
|
|
|
|
// 1. Redistributions of source code must retain the above copyright notice,
|
|
|
|
|
// this list of conditions and the following disclaimer.
|
|
|
|
|
//
|
|
|
|
|
// 2. Redistributions in binary form must reproduce the following disclaimer
|
|
|
|
|
// in the documentation and/or other materials provided with the
|
|
|
|
|
// distribution:
|
|
|
|
|
//
|
|
|
|
|
// "Uses Separable SSS. Copyright (C) 2012 by Jorge Jimenez and Diego
|
|
|
|
|
// Gutierrez."
|
|
|
|
|
//
|
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
|
|
|
|
|
// IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
|
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS
|
|
|
|
|
// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
//
|
|
|
|
|
// The views and conclusions contained in the software and documentation are
|
|
|
|
|
// those of the authors and should not be interpreted as representing official
|
|
|
|
|
// policies, either expressed or implied, of the copyright holders.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#version 450
|
|
|
|
|
|
|
|
|
|
#include "compiled.inc"
|
2026-02-24 11:44:01 -08:00
|
|
|
#include "std/gbuffer.glsl"
|
2025-01-22 16:18:30 +01:00
|
|
|
|
|
|
|
|
uniform sampler2D gbufferD;
|
|
|
|
|
uniform sampler2D gbuffer0;
|
2026-08-07 02:04:01 -07:00
|
|
|
uniform sampler2D gbuffer1;
|
2025-01-22 16:18:30 +01:00
|
|
|
uniform sampler2D tex;
|
|
|
|
|
|
|
|
|
|
uniform vec2 dir;
|
|
|
|
|
uniform vec2 cameraProj;
|
2026-08-07 02:04:01 -07:00
|
|
|
uniform mat4 projectionMatrix;
|
|
|
|
|
|
|
|
|
|
#ifdef _ExtBRDF
|
|
|
|
|
//!uniform vec4 materialParams[MAX_MATERIALS * 8];
|
|
|
|
|
#endif
|
2025-01-22 16:18:30 +01:00
|
|
|
|
|
|
|
|
in vec2 texCoord;
|
|
|
|
|
out vec4 fragColor;
|
|
|
|
|
|
2026-08-07 02:04:01 -07:00
|
|
|
// TODO: finish the SSS
|
|
|
|
|
const float SSS_SCALE = 0.05;
|
|
|
|
|
const float DEPTH_THRESHOLD = 0.05;
|
2025-01-22 16:18:30 +01:00
|
|
|
|
2025-07-22 23:20:35 +00:00
|
|
|
float hash13(vec3 p3) {
|
|
|
|
|
p3 = fract(p3 * vec3(0.1031, 0.1030, 0.0973));
|
|
|
|
|
p3 += dot(p3, p3.yzx + 33.33);
|
|
|
|
|
return fract((p3.x + p3.y) * p3.z);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-07 02:04:01 -07:00
|
|
|
vec4 SSSSBlur(vec3 sssRadius, float sssWeight) {
|
|
|
|
|
const int SSSS_N_SAMPLES = 11;
|
2025-07-22 23:20:35 +00:00
|
|
|
vec4 kernel[SSSS_N_SAMPLES];
|
2026-08-07 02:04:01 -07:00
|
|
|
|
2026-02-24 11:44:01 -08:00
|
|
|
kernel[0] = vec4(0.233, 0.455, 0.649, 0.0); // Center sample
|
2026-08-07 02:04:01 -07:00
|
|
|
kernel[1] = vec4(0.100, 0.336, 0.344, 0.37); // +0.37
|
|
|
|
|
kernel[2] = vec4(0.118, 0.198, 0.0, 0.97); // +0.97
|
|
|
|
|
kernel[3] = vec4(0.113, 0.007, 0.007, 1.93); // +1.93
|
|
|
|
|
kernel[4] = vec4(0.358, 0.004, 0.0, 3.87); // +3.87
|
|
|
|
|
kernel[5] = vec4(0.078, 0.0, 0.0, 6.53); // +6.53 (red only)
|
|
|
|
|
kernel[6] = vec4(0.100, 0.336, 0.344, -0.37); // -0.37
|
|
|
|
|
kernel[7] = vec4(0.118, 0.198, 0.0, -0.97); // -0.97
|
|
|
|
|
kernel[8] = vec4(0.113, 0.007, 0.007, -1.93); // -1.93
|
|
|
|
|
kernel[9] = vec4(0.358, 0.004, 0.0, -3.87); // -3.87
|
|
|
|
|
kernel[10] = vec4(0.078, 0.0, 0.0, -6.53); // -6.53 (red only)
|
|
|
|
|
|
|
|
|
|
vec2 texSize = vec2(textureSize(tex, 0));
|
|
|
|
|
ivec2 texelCoord = ivec2(texCoord * texSize);
|
|
|
|
|
|
|
|
|
|
vec4 colorM = texelFetch(tex, texelCoord, 0);
|
|
|
|
|
vec3 albedo = texelFetch(gbuffer1, texelCoord, 0).rgb;
|
2025-01-22 16:18:30 +01:00
|
|
|
|
2026-08-07 02:04:01 -07:00
|
|
|
vec3 irradianceM = colorM.rgb / max(albedo, vec3(0.00001));
|
|
|
|
|
|
|
|
|
|
float depth = texelFetch(gbufferD, texelCoord, 0).r;
|
2025-01-22 16:18:30 +01:00
|
|
|
float depthM = cameraProj.y / (depth - cameraProj.x);
|
|
|
|
|
|
2026-08-07 02:04:01 -07:00
|
|
|
float blurWidth = max(max(sssRadius.r, sssRadius.g), sssRadius.b);
|
|
|
|
|
float projScale = dot(dir, vec2(projectionMatrix[0][0], projectionMatrix[1][1]));
|
|
|
|
|
vec2 finalStep = blurWidth * (1.0 / depthM) * dir * projScale * SSS_SCALE;
|
2025-01-22 16:18:30 +01:00
|
|
|
|
2025-07-22 23:20:35 +00:00
|
|
|
vec3 jitterSeed = vec3(texCoord.xy * 1000.0, fract(cameraProj.x * 0.0001));
|
2026-02-24 11:44:01 -08:00
|
|
|
float jitterOffset = (hash13(jitterSeed) * 2.0 - 1.0) * 0.15;
|
2025-07-22 23:20:35 +00:00
|
|
|
finalStep *= (1.0 + jitterOffset);
|
2026-08-07 02:04:01 -07:00
|
|
|
|
|
|
|
|
vec3 colorBlurred = irradianceM * kernel[0].rgb;
|
|
|
|
|
vec3 weightSum = kernel[0].rgb;
|
|
|
|
|
|
2025-01-22 16:18:30 +01:00
|
|
|
for (int i = 1; i < SSSS_N_SAMPLES; i++) {
|
2025-07-22 23:20:35 +00:00
|
|
|
float sampleJitter = hash13(vec3(texCoord.xy * 720.0, float(i) * 37.45)) * 0.1 - 0.05;
|
|
|
|
|
vec2 offset = texCoord + (kernel[i].a + sampleJitter) * finalStep;
|
2026-08-07 02:04:01 -07:00
|
|
|
|
|
|
|
|
vec3 irradiance = irradianceM;
|
|
|
|
|
float s = 0.0;
|
|
|
|
|
if (all(greaterThanEqual(offset, vec2(0.0))) && all(lessThan(offset, vec2(1.0)))) {
|
|
|
|
|
ivec2 sampleTexel = ivec2(offset * texSize);
|
|
|
|
|
|
|
|
|
|
float sampleDepth = texelFetch(gbufferD, sampleTexel, 0).r;
|
|
|
|
|
float sampleDepthM = cameraProj.y / (sampleDepth - cameraProj.x);
|
|
|
|
|
float depthDiff = abs(depthM - sampleDepthM);
|
|
|
|
|
|
|
|
|
|
if (depthDiff < 1.0) {
|
|
|
|
|
vec4 sampleG0 = texelFetch(gbuffer0, sampleTexel, 0);
|
|
|
|
|
float sampleMetallic;
|
|
|
|
|
uint sampleMatid;
|
|
|
|
|
unpackFloatInt16(sampleG0.a, sampleMetallic, sampleMatid);
|
|
|
|
|
bool sampleIsSSS = false;
|
|
|
|
|
#ifdef _ExtBRDF
|
|
|
|
|
if (sampleMatid >= 3u && sampleMatid < uint(MAX_MATERIALS)) {
|
|
|
|
|
if (materialParams[sampleMatid * 8u + 3u].z > 0.0) sampleIsSSS = true;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (sampleIsSSS) {
|
|
|
|
|
vec3 sampleColor = texelFetch(tex, sampleTexel, 0).rgb;
|
|
|
|
|
vec3 sampleAlbedo = texelFetch(gbuffer1, sampleTexel, 0).rgb;
|
|
|
|
|
irradiance = sampleColor / max(sampleAlbedo, vec3(0.00001));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (depthDiff <= DEPTH_THRESHOLD) {
|
|
|
|
|
s = 1.0;
|
|
|
|
|
} else {
|
|
|
|
|
s = exp(-depthDiff * 10.0);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-22 23:20:35 +00:00
|
|
|
}
|
2026-08-07 02:04:01 -07:00
|
|
|
|
|
|
|
|
colorBlurred += kernel[i].rgb * mix(irradianceM, irradiance, s);
|
2025-07-22 23:20:35 +00:00
|
|
|
weightSum += kernel[i].rgb;
|
2025-01-22 16:18:30 +01:00
|
|
|
}
|
2026-08-07 02:04:01 -07:00
|
|
|
|
|
|
|
|
vec3 normalizedIrradiance = colorBlurred / max(weightSum, vec3(0.00001));
|
2025-07-22 23:20:35 +00:00
|
|
|
float dither = hash13(vec3(texCoord * 1333.0, 0.0)) * 0.003 - 0.0015;
|
2026-08-07 02:04:01 -07:00
|
|
|
normalizedIrradiance = max(normalizedIrradiance + vec3(dither), vec3(0.0));
|
|
|
|
|
vec3 blurredColor = normalizedIrradiance * albedo;
|
|
|
|
|
vec3 result = mix(colorM.rgb, blurredColor, sssWeight);
|
|
|
|
|
return vec4(result, colorM.a);
|
2025-01-22 16:18:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void main() {
|
2026-08-07 02:04:01 -07:00
|
|
|
vec2 texSize0 = vec2(textureSize(gbuffer0, 0));
|
|
|
|
|
ivec2 texelCoord0 = ivec2(texCoord * texSize0);
|
|
|
|
|
vec4 g0 = texelFetch(gbuffer0, texelCoord0, 0);
|
2026-02-24 11:44:01 -08:00
|
|
|
float metallic;
|
|
|
|
|
uint matid;
|
|
|
|
|
unpackFloatInt16(g0.a, metallic, matid);
|
2025-07-22 23:20:35 +00:00
|
|
|
|
2026-08-07 02:04:01 -07:00
|
|
|
bool applySSS = false;
|
|
|
|
|
vec3 sssRadius = vec3(1.0);
|
|
|
|
|
float sssWeight = 1.0;
|
|
|
|
|
vec4 matp0, matp1, matp2, matp3, matp4, matp5, matp6, matp7;
|
|
|
|
|
#ifdef _ExtBRDF
|
|
|
|
|
if (matid >= 3u && matid < uint(MAX_MATERIALS)) {
|
|
|
|
|
getMaterialParams(matid, matp0, matp1, matp2, matp3, matp4, matp5, matp6, matp7);
|
|
|
|
|
// matp3.z = subsurface, matp4.xyz = subsurfaceRadiusRGB, matp7.x = subsurfaceScale
|
|
|
|
|
if (matp3.z > 0.0) {
|
|
|
|
|
applySSS = true;
|
|
|
|
|
sssRadius = matp4.xyz * matp7.x;
|
|
|
|
|
sssWeight = matp3.z;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (applySSS) {
|
|
|
|
|
fragColor = SSSSBlur(sssRadius, sssWeight);
|
2025-07-22 23:20:35 +00:00
|
|
|
} else {
|
2026-08-07 02:04:01 -07:00
|
|
|
vec2 texSizeMain = vec2(textureSize(tex, 0));
|
|
|
|
|
ivec2 texelCoordMain = ivec2(texCoord * texSizeMain);
|
|
|
|
|
fragColor = texelFetch(tex, texelCoordMain, 0);
|
2025-01-22 16:18:30 +01:00
|
|
|
}
|
|
|
|
|
}
|