// // 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 tex; uniform vec2 dir; uniform vec2 cameraProj; in vec2 texCoord; out vec4 fragColor; const vec3 SKIN_SSS_RADIUS = vec3(4.8, 2.4, 1.5); const float SSS_DISTANCE_SCALE = 0.001; // Temp hash func - 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() { const int SSSS_N_SAMPLES = 15; 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.37mm kernel[2] = vec4(0.118, 0.198, 0.0, 0.97); // +0.97mm kernel[3] = vec4(0.113, 0.007, 0.007, 1.93); // +1.93mm kernel[4] = vec4(0.358, 0.004, 0.0, 3.87); // +3.87mm kernel[5] = vec4(0.078, 0.0, 0.0, 6.53); // +6.53mm (red only) kernel[6] = vec4(0.0, 0.0, 0.0, 0.0); // Unused kernel[7] = vec4(0.0, 0.0, 0.0, 0.0); // Unused kernel[8] = vec4(0.100, 0.336, 0.344, -0.37); // -0.37mm kernel[9] = vec4(0.118, 0.198, 0.0, -0.97); // -0.97mm kernel[10] = vec4(0.113, 0.007, 0.007, -1.93); // -1.93mm kernel[11] = vec4(0.358, 0.004, 0.0, -3.87); // -3.87mm kernel[12] = vec4(0.078, 0.0, 0.0, -6.53); // -6.53mm (red only) kernel[13] = vec4(0.0, 0.0, 0.0, 0.0); // Unused kernel[14] = vec4(0.0, 0.0, 0.0, 0.0); // Unused vec4 colorM = textureLod(tex, texCoord, 0.0); float depth = textureLod(gbufferD, texCoord, 0.0).r; float depthM = cameraProj.y / (depth - cameraProj.x); float distanceScale = 1.0 / max(depthM, 0.1); vec2 finalStep = sssWidth * distanceScale * dir * SSS_DISTANCE_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 = vec3(0.0); vec3 weightSum = vec3(0.0); colorBlurred += colorM.rgb * kernel[0].rgb; 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; vec4 color = textureLod(tex, offset, 0.0); const float DEPTH_THRESHOLD = 0.05; float sampleDepth = textureLod(gbufferD, offset, 0.0).r; float sampleDepthM = cameraProj.y / (sampleDepth - cameraProj.x); float depthDiff = abs(depthM - sampleDepthM); float depthWeight = exp(-depthDiff * 10.0); if (depthDiff > DEPTH_THRESHOLD) { color.rgb = mix(colorM.rgb, color.rgb, depthWeight); } colorBlurred += color.rgb * kernel[i].rgb; weightSum += kernel[i].rgb; } vec3 normalizedColor = colorBlurred / max(weightSum, vec3(0.00001)); float dither = hash13(vec3(texCoord * 1333.0, 0.0)) * 0.003 - 0.0015; normalizedColor = max(normalizedColor + vec3(dither), vec3(0.0)); return vec4(normalizedColor, colorM.a); } void main() { vec4 g0 = textureLod(gbuffer0, texCoord, 0.0); float metallic; uint matid; unpackFloatInt16(g0.a, metallic, matid); if (matid == 2u) { vec4 originalColor = textureLod(tex, texCoord, 0.0); vec4 blurredColor = SSSSBlur(); vec4 sssContribution = blurredColor - originalColor; vec4 combined = originalColor + max(vec4(0.0), sssContribution) * 0.8; fragColor = max(vec4(0.0), min(combined, vec4(10.0))); } else { fragColor = textureLod(tex, texCoord, 0.0); } }