-
Notifications
You must be signed in to change notification settings - Fork 9
Creating Custom Effects
Version 4 introduced a shared underwater rendering framework that allows custom underwater effects to be added without modifying the renderer feature.
Effects are automatically discovered, sorted, and executed through the framework.
A custom underwater effect consists of:
- A Volume Component
- An Underwater Effect
- (Optional) One or more shaders
The typical workflow looks like:
Volume
↓
UnderwaterEffect<TVolume>
↓
RenderGraph
↓
Shader
Create a volume component that stores all user-facing settings.
using System;
using UnityEngine.Rendering;
[Serializable]
[VolumeComponentMenu(
"Underwater Effects/Example")]
public sealed class ExampleVolume
: VolumeComponent
{
public ClampedFloatParameter intensity =
new ClampedFloatParameter(
1f,
0f,
10f);
public BoolParameter debug =
new BoolParameter(
false);
}This makes the effect configurable through Unity's Volume system.
Create a class that inherits from:
UnderwaterEffect<TVolume>Example:
using UnityEngine.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
[UnderwaterEffect(
After =
UnderwaterEffectOrder.Fog)]
public sealed class ExampleEffect
: UnderwaterEffect<ExampleVolume>
{
public override void RecordRenderGraph(
RenderGraph renderGraph,
ContextContainer frameData)
{
if (!IsActive())
return;
// RenderGraph work here.
}
}The base class automatically provides access to the active volume.
float intensity =
Volume.intensity.value;No manual volume lookups are required.
The framework automatically sorts effects by execution order.
Built-in stages are:
0 WaterLine
100 TransparentDepthTexture
200 Caustics
300 Fog
400 Refraction
500 SunShafts
600 BlurredWaterLine
Insert effects relative to built-in stages.
Run before fog:
[UnderwaterEffect(
Before =
UnderwaterEffectOrder.Fog)]Run after fog:
[UnderwaterEffect(
After =
UnderwaterEffectOrder.Fog)]Run at a custom position:
[UnderwaterEffect(350)]Example:
Caustics
↓
Fog
↓
MyEffect
↓
Refraction
Avoid directly using built-in stage values unless you intentionally want to share the same execution order as a built-in effect.
Example:
[UnderwaterEffect(
UnderwaterEffectOrder.Fog)]This places the effect at the exact same execution stage as the built-in fog effect.
Execution order between effects sharing the same order value should not be relied upon.
For custom effects, prefer:
Beforeor:
Afterinstead.
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.RenderGraphModule.Util;
using UnityEngine.Rendering.Universal;
[UnderwaterEffect(
After =
UnderwaterEffectOrder.Fog)]
public sealed class ExampleEffect
: UnderwaterEffect<ExampleVolume>
{
private readonly Material material;
public ExampleEffect()
{
material =
CoreUtils.CreateEngineMaterial(
Shader.Find(
"Hidden/Example"));
}
public override void RecordRenderGraph(
RenderGraph renderGraph,
ContextContainer frameData)
{
if (!IsActive())
return;
UniversalResourceData resourceData =
frameData.Get<UniversalResourceData>();
if (resourceData.isActiveTargetBackBuffer)
return;
TextureHandle cameraColor =
resourceData.activeColorTexture;
TextureDesc descriptor =
cameraColor.GetDescriptor(
renderGraph);
descriptor.name =
"Example Effect";
TextureHandle destination =
renderGraph.CreateTexture(
descriptor);
material.SetFloat(
"_Intensity",
Volume.intensity.value);
renderGraph.AddBlitPass(
new RenderGraphUtils
.BlitMaterialParameters(
cameraColor,
destination,
material,
0),
"Example Effect");
renderGraph.AddCopyPass(
destination,
cameraColor,
"Example Effect Copy");
}
public override void Dispose()
{
CoreUtils.Destroy(
material);
}
}Several built-in effects generate reusable resources.
Custom effects can consume these resources the same way built-in effects do.
Currently available:
_WaterLineMask
_TransparentDepthTexture
Separates underwater pixels from pixels above the water surface.
float horizonMask =
SAMPLE_TEXTURE2D(
_WaterLineMask,
sampler_WaterLineMask,
input.uv).r;
float underwaterMask =
saturate(
1.0 -
horizonMask);Provides transparent object participation data.
Stored channels:
R = Transparent Depth
G = Thickness
B = Opacity
A = Reserved
float4 transparentData =
SAMPLE_TEXTURE2D(
_TransparentDepthTexture,
sampler_TransparentDepthTexture,
input.uv);
float depth = transparentData.r;
float thickness = transparentData.g;
float opacity = transparentData.b;Objects may opt into the transparent depth system using:
TransparentDepthSettingsAvailable settings:
Thickness
Opacity
This allows effects such as:
- Glass
- Observation Domes
- Force Fields
- Shields
- Portals
- Energy Barriers
to participate in underwater fog and other custom underwater effects.
The framework automatically:
- Discovers effects
- Sorts effects
- Executes effects
- Provides Volume integration
- Supports RenderGraph
- Exposes shared underwater resources
Most custom effects only need:
- A Volume Component
- An Underwater Effect
- A Shader
Everything else is handled by the framework.