diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2017-10-24 15:49:00 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2017-10-27 23:49:15 +0300 |
commit | 66d8f82b832b58cba3273c0a4196fae6db0e1efd (patch) | |
tree | 682a3aba8fbd23ccd72a4e4f2a02fb36ad0a0d01 /source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl | |
parent | 1c0c63ce5b3914be2d0828260e5ac777a7596d36 (diff) |
Eevee: Overhaul the volumetric system.
The system now uses several 3D textures in order to decouple every steps of the volumetric rendering.
See https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite for more details.
On the technical side, instead of using a compute shader to populate the 3D textures we use layered rendering with a geometry shader to render 1 fullscreen triangle per 3D texture slice.
Diffstat (limited to 'source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl')
-rw-r--r-- | source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl | 396 |
1 files changed, 19 insertions, 377 deletions
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl index 7f44dd53163..a3e2979a9b4 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl @@ -1,396 +1,38 @@ -#ifdef VOLUMETRICS +/* Based on Frosbite Unified Volumetric. + * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ #define NODETREE_EXEC -#define VOLUMETRIC_INTEGRATION_MAX_STEP 256 -#define VOLUMETRIC_SHADOW_MAX_STEP 128 - -uniform int light_count; -uniform vec2 volume_start_end; -uniform vec4 volume_samples_clamp; - -#define volume_start volume_start_end.x -#define volume_end volume_start_end.y +uniform ivec3 volumeTextureSize; +uniform vec3 volume_jitter; -#define volume_integration_steps volume_samples_clamp.x -#define volume_shadows_steps volume_samples_clamp.y -#define volume_sample_distribution volume_samples_clamp.z -#define volume_light_clamp volume_samples_clamp.w - -#ifdef COLOR_TRANSMITTANCE -layout(location = 0) out vec4 outScattering; -layout(location = 1) out vec4 outTransmittance; -#else -out vec4 outScatteringTransmittance; -#endif +flat in int slice; /* Warning: theses are not attributes, theses are global vars. */ vec3 worldPosition = vec3(0.0); vec3 viewPosition = vec3(0.0); vec3 viewNormal = vec3(0.0); -uniform sampler2D depthFull; - -void participating_media_properties(vec3 wpos, out vec3 extinction, out vec3 scattering, out vec3 emission, out float anisotropy) -{ -#ifndef VOLUME_HOMOGENEOUS - worldPosition = wpos; - viewPosition = (ViewMatrix * vec4(wpos, 1.0)).xyz; /* warning, Perf. */ -#endif - - Closure cl = nodetree_exec(); - - scattering = cl.scatter; - emission = cl.emission; - anisotropy = cl.anisotropy; - extinction = max(vec3(1e-4), cl.absorption + cl.scatter); -} - -vec3 participating_media_extinction(vec3 wpos) -{ -#ifndef VOLUME_HOMOGENEOUS - worldPosition = wpos; - viewPosition = (ViewMatrix * vec4(wpos, 1.0)).xyz; /* warning, Perf. */ -#endif - - Closure cl = nodetree_exec(); +layout(location = 0) out vec4 volumeScattering; +layout(location = 1) out vec4 volumeExtinction; +layout(location = 2) out vec4 volumeEmissive; +layout(location = 3) out vec4 volumePhase; - return max(vec3(1e-4), cl.absorption + cl.scatter); -} +/* Store volumetric properties into the froxel textures. */ -float phase_function_isotropic() -{ - return 1.0 / (4.0 * M_PI); -} - -float phase_function(vec3 v, vec3 l, float g) -{ -#ifndef VOLUME_ISOTROPIC /* TODO Use this flag when only isotropic closures are used */ - /* Henyey-Greenstein */ - float cos_theta = dot(v, l); - g = clamp(g, -1.0 + 1e-3, 1.0 - 1e-3); - 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 -} - -float light_volume(LightData ld, vec4 l_vector) -{ - float power; - float dist = max(1e-4, abs(l_vector.w - ld.l_radius)); - /* TODO : Area lighting ? */ - /* Removing Area Power. */ - /* TODO : put this out of the shader. */ - if (ld.l_type == AREA) { - power = 0.0962 * (ld.l_sizex * ld.l_sizey * 4.0f * M_PI); - } - else { - power = 0.0248 * (4.0 * ld.l_radius * ld.l_radius * M_PI * M_PI); - } - return min(power / (l_vector.w * l_vector.w), volume_light_clamp); -} - -vec3 irradiance_volumetric(vec3 wpos) -{ - IrradianceData ir_data = load_irradiance_cell(0, vec3(1.0)); - vec3 irradiance = ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2]; - ir_data = load_irradiance_cell(0, vec3(-1.0)); - irradiance += ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2]; - irradiance *= 0.16666666; /* 1/6 */ - return irradiance; -} - -vec3 light_volume_shadow(LightData ld, vec3 ray_wpos, vec4 l_vector, vec3 s_extinction) -{ -#ifdef VOLUME_SHADOW - -#ifdef VOLUME_HOMOGENEOUS - /* Simple extinction */ - return exp(-s_extinction * l_vector.w); -#else - /* Heterogeneous volume shadows */ - float dd = l_vector.w / volume_shadows_steps; - vec3 L = l_vector.xyz * l_vector.w; - vec3 shadow = vec3(1.0); - for (float s = 0.5; s < VOLUMETRIC_SHADOW_MAX_STEP && s < (volume_shadows_steps - 0.1); s += 1.0) { - vec3 pos = ray_wpos + L * (s / volume_shadows_steps); - vec3 s_extinction = participating_media_extinction(pos); - shadow *= exp(-s_extinction * dd); - } - return shadow; -#endif /* VOLUME_HOMOGENEOUS */ - -#else - return vec3(1.0); -#endif /* VOLUME_SHADOW */ -} - -float find_next_step(float iter, float noise) -{ - float progress = (iter + noise) / volume_integration_steps; - - float linear_split = mix(volume_start, volume_end, progress); - - if (ProjectionMatrix[3][3] == 0.0) { - float exp_split = volume_start * pow(volume_end / volume_start, progress); - return mix(linear_split, exp_split, volume_sample_distribution); - } - else { - return linear_split; - } -} - -/* 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; - -#ifdef VOLUME_HOMOGENEOUS - /* Put it out of the loop for homogeneous media. */ - vec3 s_extinction, s_scattering, s_emission; - float s_anisotropy; - participating_media_properties(vec3(0.0), s_extinction, s_scattering, s_emission, s_anisotropy); -#endif - - /* 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 dist = volume_start; - for (float i = 0.5; i < VOLUMETRIC_INTEGRATION_MAX_STEP && i < (volume_integration_steps - 0.1); ++i) { - float new_dist = max(max_z, find_next_step(rand, i)); - float step = dist - new_dist; /* Marching step */ - dist = new_dist; - - vec3 ray_wpos = ray_origin + wdir_proj * dist; - -#ifndef VOLUME_HOMOGENEOUS - vec3 s_extinction, s_scattering, s_emission; - float s_anisotropy; - participating_media_properties(ray_wpos, s_extinction, s_scattering, s_emission, s_anisotropy); -#endif - - /* Evaluate each light */ - vec3 Lscat = s_emission; - -#ifdef VOLUME_LIGHTING /* Lights */ - for (int i = 0; i < MAX_LIGHT && i < light_count; ++i) { - LightData ld = lights_data[i]; + ivec3 volume_cell = ivec3(gl_FragCoord.xy, slice); + vec3 ndc_cell = volume_to_ndc((vec3(volume_cell) + volume_jitter) / volumeTextureSize); - vec4 l_vector; - l_vector.xyz = ld.l_position - ray_wpos; - l_vector.w = length(l_vector.xyz); + viewPosition = get_view_space_from_depth(ndc_cell.xy, ndc_cell.z); + worldPosition = transform_point(ViewMatrixInverse, viewPosition); - float Vis = light_visibility(ld, ray_wpos, l_vector); - - vec3 Li = ld.l_color * light_volume(ld, l_vector) * light_volume_shadow(ld, ray_wpos, l_vector, s_extinction); - - Lscat += Li * Vis * s_scattering * phase_function(-wdir, l_vector.xyz / l_vector.w, s_anisotropy); - } -#endif - - /* Environment : Average color. */ - Lscat += irradiance_volumetric(wpos) * 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; - } - -#ifdef COLOR_TRANSMITTANCE - outScattering = vec4(scattering, 1.0); - outTransmittance = vec4(transmittance, 1.0); -#else - float mono_transmittance = dot(transmittance, vec3(1.0)) / 3.0; - - outScatteringTransmittance = vec4(scattering, mono_transmittance); -#endif -} - -#else /* STEP_UPSAMPLE */ - -out vec4 FragColor; - -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 + Closure cl = nodetree_exec(); - FragColor = integration_result / weight_sum; + volumeScattering = vec4(cl.scatter, 1.0); + volumeExtinction = vec4(max(vec3(1e-4), cl.absorption + cl.scatter), 1.0); + volumeEmissive = vec4(cl.emission, 1.0); + volumePhase = vec4(cl.anisotropy, vec3(1.0)); } -#endif |