Skip to content

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 #72007.

Overall process

This section describes the overall process how render passes are extracted and calculated in EEVEE. The process has 3 phases.

  1. Allocate the buffers needed to store the data. Most of the passes use a accumulated buffer.
  2. During rendering per sample the pass data can be extracted from EEVEE and added to the accumulate buffers.
  3. After rendering or just before displaying the buffers are post processed to get the actual value of the render pass.

Implementation

EEVEE_renderpasses.c is the central point for render passes.

  • EEVEE_renderpasses_init will 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_init will allocate all the needed GPUTexture, DRWPass and DRWShadingGroup for 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_accumulate Is 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.

Materials

For material based passes (EEVEE_RENDER_PASS_EMIT, EEVEE_RENDER_PASS_DIFFUSE_COLOR, EEVEE_RENDER_PASS_DIFFUSE_LIGHT, EEVEE_RENDER_PASS_GLOSSY_COLOR, EEVEE_RENDER_PASS_GLOSSY_LIGHT, 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
EEVEE_RENDER_PASS_COMBINED V V V V V radiance = dif_light*dif_col + glos_light*glos_col + emit
EEVEE_RENDER_PASS_DIFFUSE_LIGHT V V radiance = dif_light*dif_col
EEVEE_RENDER_PASS_DIFFUSE_COLOR V V radiance = dif_col + sss_albedo
EEVEE_RENDER_PASS_GLOSSY_LIGHT V V radiance = glos_light*glos_col
EEVEE_RENDER_PASS_GLOSSY_COLOR V radiance = glos_col
EEVEE_RENDER_PASS_EMIT V radiance = emit
EEVEE_RENDER_PASS_ENVIRONMENT V V V V V

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.

Post Processing

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.

Specific Passes

This section gives an overview of how the different render passes are implemented.

Glossy Light

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

Diffuse Light

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

See EEVEE_volumes_output_init and EEVEE_volumes_output_accumulate.

Shadow

The shadow pass is implemented as a screen space shadow pass. Check EEVEE_shadow_output_init and EEVEE_shadow_output_accumulate and shadow_accum_frag.glsl. It uses the shadowing component of all light objects and divide them by the number of lights.

Bloom

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 - eViewLayerEEVEEPassType
  • Add the pass to rna_scene.c - rna_def_view_layer_eevee
  • Add the pass to rna_space.c - rna_enum_view3dshading_render_pass_type_items
  • 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_init function and add it to the EEVEE_renderpasses_output_init
  • Implement an EEVEE_*_output_accumulate function and add it to the EEVEE_renderpasses_output_accumulate
  • Check if a special case needs to be added to EEVEE_renderpasses_draw
  • Add the render pass to EEVEE_renderpasses_postprocess
  • In eevee_render.c update EEVEE_render_update_passes (add a CHECK_PASS_EEVEE)
  • In eevee_render.c update EEVEE_render_draw add 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.