Volumetric Light Scattering as a Custom Renderer Feature in URP

Learn how to create your own custom rendering features with Unity’s Universal Render Pipeline by adding some volumetric light scattering to a small animated scene. By Ignacio del Barrio.

4.7 (10) · 2 Reviews

Download materials
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

Adding the Radial Blur Material Instance

The following steps are similar to what you did for the occluders map. Go back to VolumetricLightScattering.cs and, in LightScatteringPass, declare a material above the constructor:

private readonly Material radialBlurMaterial;

Then, add this line inside the constructor:

radialBlurMaterial = new Material(
    Shader.Find("Hidden/RW/RadialBlur"));

Nothing new so far, you’re just creating the material instance. Make sure that the material isn’t missing by replacing this if statement:

if (!occludersMaterial)
{
    return;
}

With this one:

if (!occludersMaterial || !radialBlurMaterial )
{
    return;
}

Now you’re all set to configure the blur material.

Configuring the Radial Blur Material

The blur shader needs to know the sun’s location so it can use it as the center point. Within Execute(), replace // TODO: 2 with the following lines:

// 1
Vector3 sunDirectionWorldSpace = 
    RenderSettings.sun.transform.forward;
// 2
Vector3 cameraPositionWorldSpace = 
    camera.transform.position;
// 3
Vector3 sunPositionWorldSpace = 
    cameraPositionWorldSpace + sunDirectionWorldSpace;
// 4
Vector3 sunPositionViewportSpace = 
    camera.WorldToViewportPoint(sunPositionWorldSpace);

This might seem like some crazy voodoo magic, but it’s actualy quite simple:

  1. You get a reference to the sun from RenderSettings. You need the forward vector of the sun because directional lights don’t have a position in space.
  2. Get the camera position.
  3. This gives you a unit vector that goes from the camera towards the sun. You’ll use this for the sun’s position.
  4. The shader expects a viewport space position, but you did your calculations in world space. To fix this, you use WorldToViewportPoint() to transform the point-to-camera viewport space.

Good job, you’ve finished the hardest part.

Now, pass the data to the shader with the following code:

radialBlurMaterial.SetVector("_Center", new Vector4(
    sunPositionViewportSpace.x, sunPositionViewportSpace.y, 0, 0));
radialBlurMaterial.SetFloat("_Intensity", intensity);
radialBlurMaterial.SetFloat("_BlurWidth", blurWidth);

Keep in mind that you only really need the x and y components of sunPositionViewportSpace since it represents a pixel position on the screen.

Blurring the Occluders Map

Finally, you need to blur the occluders map.

Add the following line of code to run the shader:

Blit(cmd, occluders.Identifier(), cameraColorTargetIdent, 
    radialBlurMaterial);

The context provides Blit, a function that copies a source texture into a destination texture using a shader. It executes your shader with occluders as the source texture, then stores the output in the camera color target. Because of the blend mode you defined in the shader, it combines both the output and the color target.

To get a reference to the camera color target, add the following line at the top of LightScatteringPass:

private RenderTargetIdentifier cameraColorTargetIdent;

Add a new function below the constructor to set this variable:

public void SetCameraColorTarget(RenderTargetIdentifier cameraColorTargetIdent)
{
    this.cameraColorTargetIdent = cameraColorTargetIdent;
}

In VolumetricLightScattering, add this line at the end of AddRenderPasses():

m_ScriptablePass.SetCameraColorTarget(renderer.cameraColorTarget);

This will pass the camera color target to the render pass, which Blit() requires.

Save the script and go back to the editor. Congratulations, you completed the effect!

Go to the Scene view and you’ll see your lighting effects at work. Click Play and move around the level to see it in action:

Playing the game with volumetric light scattering

Where to Go From Here?

Download the final project by using the Download Materials button at the top or bottom of this tutorial.

In this tutorial, you learned how to write your own renderer features to extend the Universal Render Pipeline. You also learned a cool post-processing technique to visually enhance your projects.

Feel free to explore the project files and experiment with the effect. To see how everything works together, inspect the render pass with the Frame Debugger.

There’s a small surprise hidden in the scene. Look at the day and night controller attached to the directional light. Activate Auto Increment and enjoy the sunrise. :]

If you want to learn more about the Universal Render Pipeline, it’s helpful to look at the source code. Yes! It’s available to anyone for free, and it’s a great source of learning material. You can find it in Packages/Universal RP.

Also, look at the Boat Attack Demo by Andre McGrail, which uses custom renderer features for water effects and other cool things.

We hope you enjoyed this tutorial! If you have any questions or comments, feel free to join the forum discussion below.