diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2017-07-03 17:38:14 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2017-07-03 23:08:33 +0300 |
commit | 65b01014b99ce7c6de67046ed7b8ae244fdb51c3 (patch) | |
tree | 3f5934f7c4ba0180851dab75486643a125e08f33 /source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl | |
parent | 9d2ee7998ad1855839fda51a5a748d31159226b3 (diff) |
Eevee: Initial implementation of Volumetrics.
Diffstat (limited to 'source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl')
-rw-r--r-- | source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl new file mode 100644 index 00000000000..8e7393a2f58 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl @@ -0,0 +1,303 @@ + +out vec4 FragColor; + +#ifdef STEP_INTEGRATE + +uniform sampler2D depthFull; + +float find_next_step(float near, float far, float noise, int iter, int iter_count) +{ + const float lambda = 0.8f; /* TODO : Parameter */ + + float progress = (float(iter) + noise) / float(iter_count); + + float linear_split = mix(near, far, progress); + + if (ProjectionMatrix[3][3] == 0.0) { + float exp_split = near * pow(far / near, progress); + return mix(linear_split, exp_split, lambda); + } + else { + return linear_split; + } +} + +void participating_media_properties(vec3 wpos, out vec3 absorption, out vec3 scattering, out float anisotropy) +{ + /* TODO Call nodetree from here. */ + absorption = vec3(0.00); + scattering = vec3(1.0) * step(-1.0, -wpos.z); + + anisotropy = -0.8; +} + +float phase_function_isotropic() +{ + return 1.0 / (4.0 * M_PI); +} + +float phase_function(vec3 v, vec3 l, float g) +{ +#if 0 + /* Henyey-Greenstein */ + float cos_theta = dot(v, l); + float sqr_g = g * g; + return (1- sqr_g) / (4.0 * M_PI * pow(1 + sqr_g - 2 * g * cos_theta, 3.0 / 2.0)); +#else + return phase_function_isotropic(); +#endif +} + +vec3 light_volume(LightData ld, vec4 l_vector, vec3 l_col) +{ + float dist = max(1e-4, abs(l_vector.w - ld.l_radius)); + return l_col * (4.0 * ld.l_radius * ld.l_radius * M_PI * M_PI) / (dist * dist); +} + +/* Based on Frosbite Unified Volumetric. + * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ +void main() +{ + vec2 uv = (gl_FragCoord.xy * 2.0) / ivec2(textureSize(depthFull, 0)); + float scene_depth = texelFetch(depthFull, ivec2(gl_FragCoord.xy) * 2, 0).r; /* use the same depth as in the upsample step */ + vec3 vpos = get_view_space_from_depth(uv, scene_depth); + vec3 wpos = (ViewMatrixInverse * vec4(vpos, 1.0)).xyz; + vec3 wdir = (ProjectionMatrix[3][3] == 0.0) ? normalize(cameraPos - wpos) : cameraForward; + + /* Note: this is NOT the distance to the camera. */ + float max_z = vpos.z; + + /* project ray to clip plane so we can integrate in even steps in clip space. */ + vec3 wdir_proj = wdir / abs(dot(cameraForward, wdir)); + float wlen = length(wdir_proj); + + /* Transmittance: How much light can get through. */ + vec3 transmittance = vec3(1.0); + + /* Scattering: Light that has been accumulated from scattered light sources. */ + vec3 scattering = vec3(0.0); + + vec3 ray_origin = (ProjectionMatrix[3][3] == 0.0) + ? cameraPos + : (ViewMatrixInverse * vec4(get_view_space_from_depth(uv, 0.5), 1.0)).xyz; + + /* Start from near clip. TODO make start distance an option. */ + float rand = texture(utilTex, vec3(gl_FragCoord.xy / LUT_SIZE, 2.0)).r; + /* Less noisy but noticeable patterns, could work better with temporal AA. */ + // float rand = (1.0 / 16.0) * float(((int(gl_FragCoord.x + gl_FragCoord.y) & 0x3) << 2) + (int(gl_FragCoord.x) & 0x3)); + float near = get_view_z_from_depth(0.0); + float far = get_view_z_from_depth(1.0); + float dist = near; + for (int i = 1; i < 64; ++i) { + float new_dist = find_next_step(near, far, rand, i, 64); + float step = dist - new_dist; /* Marching step */ + dist = new_dist; + + vec3 ray_wpos = ray_origin + wdir_proj * dist; + + /* Volume Sample */ + vec3 s_absorption, s_scattering; /* mu_a, mu_s */ + float s_anisotropy; + participating_media_properties(ray_wpos, s_absorption, s_scattering, s_anisotropy); + + vec3 s_extinction = max(vec3(1e-8), s_absorption + s_scattering); /* mu_t */ + + /* Evaluate each light */ + vec3 Lscat = vec3(0.0); + +#if 1 /* Lights */ + for (int i = 0; i < MAX_LIGHT && i < light_count; ++i) { + LightData ld = lights_data[i]; + + vec4 l_vector; + l_vector.xyz = ld.l_position - ray_wpos; + l_vector.w = length(l_vector.xyz); + +#if 1 /* Shadows & Spots */ + float Vis = light_visibility(ld, ray_wpos, l_vector); +#else + float Vis = 1.0; +#endif + vec3 Li = light_volume(ld, l_vector, ld.l_color); + + Lscat += Li * Vis * s_scattering * phase_function(-wdir, l_vector.xyz / l_vector.w, s_anisotropy); + } +#endif + + /* Environment : Average color. */ + IrradianceData ir_data = load_irradiance_cell(0, vec3(1.0)); + Lscat += (ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2]) * 0.333333 * s_scattering * phase_function_isotropic(); + + ir_data = load_irradiance_cell(0, vec3(-1.0)); + Lscat += (ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2]) * 0.333333 * s_scattering * phase_function_isotropic(); + + /* Evaluate Scattering */ + float s_len = wlen * step; + vec3 Tr = exp(-s_extinction * s_len); + + /* integrate along the current step segment */ + Lscat = (Lscat - Lscat * Tr) / s_extinction; + /* accumulate and also take into account the transmittance from previous steps */ + scattering += transmittance * Lscat; + + /* Evaluate transmittance to view independantely */ + transmittance *= Tr; + + if (dist < max_z) + break; + } + + float mono_transmittance = dot(transmittance, vec3(1.0)) / 3.0; + + FragColor = vec4(scattering, mono_transmittance); +} + +#else /* STEP_UPSAMPLE */ + +uniform sampler2D depthFull; +uniform sampler2D volumetricBuffer; + +uniform mat4 ProjectionMatrix; + +vec4 get_view_z_from_depth(vec4 depth) +{ + vec4 d = 2.0 * depth - 1.0; + return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]); +} + +void main() +{ +#if 0 /* 2 x 2 with bilinear */ + + const vec4 bilinear_weights[4] = vec4[4]( + vec4(9.0 / 16.0, 3.0 / 16.0, 3.0 / 16.0, 1.0 / 16.0 ), + vec4(3.0 / 16.0, 9.0 / 16.0, 1.0 / 16.0, 3.0 / 16.0 ), + vec4(3.0 / 16.0, 1.0 / 16.0, 9.0 / 16.0, 3.0 / 16.0 ), + vec4(1.0 / 16.0, 3.0 / 16.0, 3.0 / 16.0, 9.0 / 16.0 ) + ); + + /* Depth aware upsampling */ + vec4 depths; + ivec2 texel_co = ivec2(gl_FragCoord.xy * 0.5) * 2; + + /* TODO use textureGather on glsl 4.0 */ + depths.x = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 0)).r; + depths.y = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 0)).r; + depths.z = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 2)).r; + depths.w = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 2)).r; + + vec4 target_depth = texelFetch(depthFull, ivec2(gl_FragCoord.xy), 0).rrrr; + + depths = get_view_z_from_depth(depths); + target_depth = get_view_z_from_depth(target_depth); + + vec4 weights = 1.0 - step(0.05, abs(depths - target_depth)); + + /* Index in range [0-3] */ + int pix_id = int(dot(mod(ivec2(gl_FragCoord.xy), 2), ivec2(1, 2))); + weights *= bilinear_weights[pix_id]; + + float weight_sum = dot(weights, vec4(1.0)); + + if (weight_sum == 0.0) { + weights.x = 1.0; + weight_sum = 1.0; + } + + texel_co = ivec2(gl_FragCoord.xy * 0.5); + + vec4 integration_result; + integration_result = texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 0)) * weights.x; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 0)) * weights.y; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 1)) * weights.z; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 1)) * weights.w; + +#else /* 4 x 4 */ + + /* Depth aware upsampling */ + vec4 depths[4]; + ivec2 texel_co = ivec2(gl_FragCoord.xy * 0.5) * 2; + + /* TODO use textureGather on glsl 4.0 */ + texel_co += ivec2(-2, -2); + depths[0].x = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 0)).r; + depths[0].y = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 0)).r; + depths[0].z = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 2)).r; + depths[0].w = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 2)).r; + + texel_co += ivec2(4, 0); + depths[1].x = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 0)).r; + depths[1].y = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 0)).r; + depths[1].z = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 2)).r; + depths[1].w = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 2)).r; + + texel_co += ivec2(-4, 4); + depths[2].x = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 0)).r; + depths[2].y = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 0)).r; + depths[2].z = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 2)).r; + depths[2].w = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 2)).r; + + texel_co += ivec2(4, 0); + depths[3].x = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 0)).r; + depths[3].y = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 0)).r; + depths[3].z = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 2)).r; + depths[3].w = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 2)).r; + + vec4 target_depth = texelFetch(depthFull, ivec2(gl_FragCoord.xy), 0).rrrr; + + depths[0] = get_view_z_from_depth(depths[0]); + depths[1] = get_view_z_from_depth(depths[1]); + depths[2] = get_view_z_from_depth(depths[2]); + depths[3] = get_view_z_from_depth(depths[3]); + + target_depth = get_view_z_from_depth(target_depth); + + vec4 weights[4]; + weights[0] = 1.0 - step(0.05, abs(depths[0] - target_depth)); + weights[1] = 1.0 - step(0.05, abs(depths[1] - target_depth)); + weights[2] = 1.0 - step(0.05, abs(depths[2] - target_depth)); + weights[3] = 1.0 - step(0.05, abs(depths[3] - target_depth)); + + float weight_sum; + weight_sum = dot(weights[0], vec4(1.0)); + weight_sum += dot(weights[1], vec4(1.0)); + weight_sum += dot(weights[2], vec4(1.0)); + weight_sum += dot(weights[3], vec4(1.0)); + + if (weight_sum == 0.0) { + weights[0].x = 1.0; + weight_sum = 1.0; + } + + texel_co = ivec2(gl_FragCoord.xy * 0.5); + + vec4 integration_result; + + texel_co += ivec2(-1, -1); + integration_result = texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 0)) * weights[0].x; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 0)) * weights[0].y; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 1)) * weights[0].z; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 1)) * weights[0].w; + + texel_co += ivec2(2, 0); + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 0)) * weights[1].x; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 0)) * weights[1].y; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 1)) * weights[1].z; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 1)) * weights[1].w; + + texel_co += ivec2(-2, 2); + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 0)) * weights[2].x; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 0)) * weights[2].y; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 1)) * weights[2].z; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 1)) * weights[2].w; + + texel_co += ivec2(2, 0); + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 0)) * weights[3].x; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 0)) * weights[3].y; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 1)) * weights[3].z; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 1)) * weights[3].w; +#endif + + FragColor = integration_result / weight_sum; +} +#endif |