diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2017-11-14 02:49:54 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2017-11-14 02:49:54 +0300 |
commit | f8b14305668ff7b1f3ba6f886b9e1881c764b201 (patch) | |
tree | 5f773bf0b3a723e7a3b895e0f41bcaecd93f75ad /source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl | |
parent | 89e9f6ea79078f846d78b6effda2ae8a8a32de84 (diff) |
Eevee: Initial Separable Subsurface Scattering implementation.
How to use:
- Enable subsurface scattering in the render options.
- Add Subsurface BSDF to your shader.
- Check "Screen Space Subsurface Scattering" in the material panel options.
This initial implementation has a few limitations:
- only supports gaussian SSS.
- Does not support principled shader.
- The radius parameters is baked down to a number of samples and then put into an UBO. This means the radius input socket cannot be used. You need to tweak the default vector directly.
- The "texture blur" is considered as always set to 1
Diffstat (limited to 'source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl')
-rw-r--r-- | source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl new file mode 100644 index 00000000000..5cc47796ec0 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl @@ -0,0 +1,94 @@ + +/* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */ + +#define SSS_SAMPLES 25 +layout(std140) uniform sssProfile { + vec4 kernel[SSS_SAMPLES]; + vec4 radii_max_radius; +}; + +uniform sampler2D depthBuffer; +uniform sampler2D sssData; +uniform sampler2DArray utilTex; + +out vec4 FragColor; + +uniform mat4 ProjectionMatrix; +uniform vec4 viewvecs[2]; + +float get_view_z_from_depth(float depth) +{ + if (ProjectionMatrix[3][3] == 0.0) { + float d = 2.0 * depth - 1.0; + return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]); + } + else { + return viewvecs[0].z + depth * viewvecs[1].z; + } +} + +vec3 get_view_space_from_depth(vec2 uvcoords, float depth) +{ + if (ProjectionMatrix[3][3] == 0.0) { + return (viewvecs[0].xyz + vec3(uvcoords, 0.0) * viewvecs[1].xyz) * get_view_z_from_depth(depth); + } + else { + return viewvecs[0].xyz + vec3(uvcoords, depth) * viewvecs[1].xyz; + } +} + +#define LUT_SIZE 64 +#define M_PI_2 1.5707963267948966 /* pi/2 */ +#define M_2PI 6.2831853071795865 /* 2*pi */ + +void main(void) +{ + vec2 pixel_size = 1.0 / vec2(textureSize(depthBuffer, 0).xy); /* TODO precompute */ + vec2 uvs = gl_FragCoord.xy * pixel_size; + vec4 sss_data = texture(sssData, uvs).rgba; + float depth_view = get_view_z_from_depth(texture(depthBuffer, uvs).r); + + float rand = texelFetch(utilTex, ivec3(ivec2(gl_FragCoord.xy) % LUT_SIZE, 2), 0).r; +#ifdef FIRST_PASS + float angle = M_2PI * rand + M_PI_2; + vec2 dir = vec2(1.0, 0.0); +#else /* SECOND_PASS */ + float angle = M_2PI * rand; + vec2 dir = vec2(0.0, 1.0); +#endif + vec2 dir_rand = vec2(cos(angle), sin(angle)); + + /* Compute kernel bounds in 2D. */ + float homcoord = ProjectionMatrix[2][3] * depth_view + ProjectionMatrix[3][3]; + vec2 scale = vec2(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) * sss_data.aa / homcoord; + vec2 finalStep = scale * radii_max_radius.w; + finalStep *= 0.5; /* samples range -1..1 */ + + /* Center sample */ + vec3 accum = sss_data.rgb * kernel[0].rgb; + + for (int i = 1; i < SSS_SAMPLES; i++) { + /* Rotate samples that are near the kernel center. */ + vec2 sample_uv = uvs + kernel[i].a * finalStep * ((abs(kernel[i].a) > 0.3) ? dir : dir_rand); + vec3 color = texture(sssData, sample_uv).rgb; + float sample_depth = texture(depthBuffer, sample_uv).r; + sample_depth = get_view_z_from_depth(sample_depth); + + /* Depth correction factor. */ + float depth_delta = depth_view - sample_depth; + float s = clamp(1.0 - exp(-(depth_delta * depth_delta) / (2.0 * sss_data.a)), 0.0, 1.0); + + /* Out of view samples. */ + if (any(lessThan(sample_uv, vec2(0.0))) || any(greaterThan(sample_uv, vec2(1.0)))) { + s = 1.0; + } + + accum += kernel[i].rgb * mix(color, sss_data.rgb, s); + } + +#ifdef FIRST_PASS + FragColor = vec4(accum, sss_data.a); +#else /* SECOND_PASS */ + FragColor = vec4(accum, 1.0); +#endif +} |