EEVEE Render Passes
EEVEE supports render passes. This page does over some of the details how these render passes extracts the needed data during rendering. The original implementation was done in [T72007].
This section describes the overall process how render passes are extracted and calculated in EEVEE. The process has 3 phases.
- Allocate the buffers needed to store the data. Most of the passes use a accumulated buffer.
- During rendering per sample the pass data can be extracted from EEVEE and added to the accumulate buffers.
- After rendering or just before displaying the buffers are post processed to get the actual value of the render pass.
EEVEE_renderpasses.c is the central point for render passes.
EEVEE_renderpasses_initwill determine what render_passes are needed to be calculated. Passes like the light passes can activate other passes when they rely on them. Light passes for example will store the light multiplied by the color in one buffer, but in the end divide the result by another buffer that only contain the color part.
EEVEE_renderpasses_output_initwill allocate all the needed
DRWShadingGroupfor that active render passes. Based on the number of samples that needs to be calculated a more precise GPUTexture will be allocated. This increases the performance with few samples, but increases the quality when more samples are used.
EEVEE_renderpasses_output_accumulateIs called for every sample. At this moment every render pass can extract/render the data it needs and store it in their buffers. This function is called twice per sample. Once during just after a sample has drawn and once when samples have been merged (for effects that work on the integrated result like bloom).
EEVEE_renderpasses_postprocess: As final step when rendering, or just before displaying a render pass to the screen the buffers will be post processed to get the actual values for the render passes.
For material based passes (
EEVEE_RENDER_PASS_ENVIRONMENT) we added another mechanism. The reason is that the BSDF shader outputs the full composited color for these passes and we needed to add a mechanism to extract different components from the output.
The main idea behind the implementation is that the BSDF shader can be configured using uniforms to only output a subset of the individual components. For the active material based render passes the sample will be re-rendered, but with different uniforms (stored in
EEVEE_RenderPassData) to extract the required components. The BSDF shaders can be configured this way to output one of the next set of components:
|Render Pass||Diffuse Light||Diffuse Color||Glossy Light||Glossy Color||Emission||Subsurface Albedo||Effective Radiance|
For simplicity the refraction components are added to glossy and the transmission components are added to the diffuse.
In the future we want to enhance this mechanism to use multi-target rendering. This would bring us a step closer to use compositing directly in the viewport.
There is a screen space fragment shader that can be used for post processing. All of them are implemented in
renderpass_postprocess_frag.glsl. There are several kind of post-processings where a renderpass can select from.
PASS_POST_ACCUMULATED_COLOR: Will read a color from the accumulated buffer and divide it with the current sample number.
PASS_POST_ACCUMULATED_LIGHT: Will read a (color multiplied by light) from the accumulated buffer and divide it by the color in a color buffer.
PASS_POST_ACCUMULATED_VALUE: Will read a value from the accumulated buffer and divide it with the current sample number.
PASS_POST_DEPTH: Will read the depth from the depth buffer and de-normalize the value to a view depth.
PASS_POST_AO: Will read the accumulated AO value clamps the value.
PASS_POST_NORMAL: Normals in EEVEE are encoded to save GPU memory and to increase performance. This post process will decode the encoded normals to how Blender stores normals.
PASS_POST_TWO_LIGHT_BUFFERS: Screen space reflections are calculated separate from the glossy color/light pass. This post process will add the screen space reflection to the accumulated light+color buffer before dividing it by the color buffer. This way the screen space reflections are part of the glossy passes. This post processing is also done for the diffuse light pass when SSS is enabled.
This section gives an overview of how the different render passes are implemented.
If Screen Space Reflections are enabled
EEVEE_RENDER_PASS_GLOSSY_LIGHT will also stores the results of the Screen Space Reflections in a accumulate buffer. During Post Processing the Glossy light is calculated as
final_glossy_light = (glossy_light * glossy_color + screen_space_reflection) / glossy_color
When Screen Space Reflections are disabled the glossy light is calculated as
final_glossy_light = (glossy_light * glossy_color) / glossy_color
If SSS are enabled
EEVEE_RENDER_PASS_DIFFUSE_LIGHT will also stores the results of the SSS in a accumulate buffer. During Post Processing the Diffuse light is calculated as
final_diffuse_light = (diffuse_light * diffuse_color + sss_light * sss_albedo) / diffuse_color
When SSS are disabled the diffuse light is calculated as
final_diffuse_light = (diffuse_light * diffuse_color) / diffuse_color
SSS output is initialized after all the materials have been added to the draw cache. At this moment we can decide if SSS is needed.
Volume Transmittance/Scattering Passes
The shadow pass is implemented as a screen space shadow pass. Check
It uses the shadowing component of all light objects and divide them by the number of lights.
The bloom effect isn't sample based when needed it can be calculated from the final color buffer. When needed we execute the resolve pass, but with a different blend mode so the bloom effect is stored and not added on top of the framebuffer.
Adding New Passes
When adding new passes the next steps can help:
- Add the pass to DNA_layer_types.h -
- Add the pass to rna_scene.c -
- Add the pass to rna_space.c -
- Add the new ViewLayerEEVEE property to properties_view_layer.py
- In eevee_renderpasses.c: Add the render pass to defines where needed
- Implement an
EEVEE_*_output_initfunction and add it to the
- Implement an
EEVEE_*_output_accumulatefunction and add it to the
- Check if a special case needs to be added to
- Add the render pass to
- In eevee_render.c update
- In eevee_render.c update
eevee_render_result_*call and implement that function. This last part will perform the post processing on the GPU, download the final image and store it in the render result.