// // 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" #include "std/gbuffer.glsl" uniform sampler2D gbufferD; uniform sampler2D gbuffer0; uniform sampler2D gbuffer1; uniform sampler2D tex; uniform vec2 dir; uniform vec2 cameraProj; uniform mat4 projectionMatrix; #ifdef _ExtBRDF //!uniform vec4 materialParams[MAX_MATERIALS * 8]; #endif in vec2 texCoord; out vec4 fragColor; // TODO: finish the SSS const float SSS_SCALE = 0.05; const float DEPTH_THRESHOLD = 0.05; 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); } vec4 SSSSBlur(vec3 sssRadius, float sssWeight) { const int SSSS_N_SAMPLES = 11; vec4 kernel[SSSS_N_SAMPLES]; kernel[0] = vec4(0.233, 0.455, 0.649, 0.0); // Center sample 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; vec3 irradianceM = colorM.rgb / max(albedo, vec3(0.00001)); float depth = texelFetch(gbufferD, texelCoord, 0).r; float depthM = cameraProj.y / (depth - cameraProj.x); 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; vec3 jitterSeed = vec3(texCoord.xy * 1000.0, fract(cameraProj.x * 0.0001)); float jitterOffset = (hash13(jitterSeed) * 2.0 - 1.0) * 0.15; finalStep *= (1.0 + jitterOffset); vec3 colorBlurred = irradianceM * kernel[0].rgb; vec3 weightSum = kernel[0].rgb; for (int i = 1; i < SSSS_N_SAMPLES; i++) { float sampleJitter = hash13(vec3(texCoord.xy * 720.0, float(i) * 37.45)) * 0.1 - 0.05; vec2 offset = texCoord + (kernel[i].a + sampleJitter) * finalStep; 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); } } } colorBlurred += kernel[i].rgb * mix(irradianceM, irradiance, s); weightSum += kernel[i].rgb; } vec3 normalizedIrradiance = colorBlurred / max(weightSum, vec3(0.00001)); float dither = hash13(vec3(texCoord * 1333.0, 0.0)) * 0.003 - 0.0015; normalizedIrradiance = max(normalizedIrradiance + vec3(dither), vec3(0.0)); vec3 blurredColor = normalizedIrradiance * albedo; vec3 result = mix(colorM.rgb, blurredColor, sssWeight); return vec4(result, colorM.a); } void main() { vec2 texSize0 = vec2(textureSize(gbuffer0, 0)); ivec2 texelCoord0 = ivec2(texCoord * texSize0); vec4 g0 = texelFetch(gbuffer0, texelCoord0, 0); float metallic; uint matid; unpackFloatInt16(g0.a, metallic, matid); 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); } else { vec2 texSizeMain = vec2(textureSize(tex, 0)); ivec2 texelCoordMain = ivec2(texCoord * texSizeMain); fragColor = texelFetch(tex, texelCoordMain, 0); } }