Description

The Normal Blended Decal Sample demonstrates how to use the OpenGL PSI (Pixel Shader Interlock) feature to blend normals when drawing screen-space decals. This sample compares the performance of PSI with glMemoryBarrier.

Screenshot

APIs Used

  • GL_NV_fragment_shader_interlock
  • GL4.3

Shared User Interface

The Graphics samples all share a common app framework and certain user interface elements, centered around the "Tweakbar" panel on the left side of the screen which lets you interactively control certain variables in each sample.

To show and hide the Tweakbar, simply click or touch the triangular button positioned in the top-left of the view.

Technical Details

Overview

The Normal Blended Decal Sample demonstrates how to use the OpenGL PSI(Pixel Shader Interlock) feature to blend normals when drawing screen-space decals. This sample compares the performance of PSI with glMemoryBarrier.

Decal Rendering

When using deferred rendering, applying decals is quite simple. All that is required is to draw a cube that represents the decal volume onto the gbuffer. The decal application is independent of the complexity of the scene geometry.

When we render decal geometries into gbuffer, the source normal from the decal's normal texture needs to be smoothly blended with the target normal already in the gbuffer. This normal blending requires both reading current normal values from the gbuffer and writing new blended normal values back to the same gbuffer in a single pass. When data packing/unpacking routines and complex normal blending equations are needed, it is difficult to do this in the blending stage of the OpenGL pipeline.

Synchronization

We can do the blending in the shader, but GPU doesn't guarantee the order of the fragment's shader invocations. Thus, it may result in flickering or undefined artifacts when multiple decals are overlapped. This is due to the fact that shader invocations can overwrite the memory based on older value right after other invocation's write.

To avoid overwriting the same memory with multiple, interleaved shader invocations, we can call glMemoryBarrier() after each decal rendering pass to wait until all previous memory accesses are completed. To lower CPU overhead and improve CPU-GPU parallelism, we can use shader-level critical sections via a pair of beginInvocationInterlockNV() and endInvocationInterlockNV() calls surrounding the normal blending operations. The GL_NV_fragment_shader_interlock extension defines these GLSL functions. All operations within the scope defined by these two functions that touch the same pixel are guaranteed to execute sequentially.

Running the Sample

The Lock Option radio box selects which locking method is used.

  • No Lock ignores locking issues and is for displaying the artifacts like flickering.
  • MemoryBarrier uses glMemoryBarrier() calls to implement locking.
  • PixelShaderInterlock uses the GL_NV_fragment_shader_interlock extension.

The MemoryBarrier and PixelShaderInterlock modes generate the same results, but performance can be different.

The View Option radio box shows different debugging views.

  • The Normals view mode shows the resulting normal vectors in the gbuffer.
  • The Decal Boxes view mode shows the Screen Space Decal cube volumes as red boxes.

The Decal Distance adjustment changes the offset between decal volumes. You can enable Decal Boxes via the View Option radio box to see where the decal volumes are located.

The Decal Blend Weight adjustment changes the blending weight between the source normal and the target normal, increasing or decreasing the appearance of the decal.

The Models radio box selects which geometric model is used for the scene geometry. Bunny is the well-known bunny model; Box is a simple cube model.