Skip to content

Instantly share code, notes, and snippets.

Last active August 14, 2022 13:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Cyanilux/be5a796cf6ddb20f20a586b94be93f2b to your computer and use it in GitHub Desktop.
Save Cyanilux/be5a796cf6ddb20f20a586b94be93f2b to your computer and use it in GitHub Desktop.
Renders Unlit Opaque objects into CameraNormalsTexture for SSAO Depth Normals mode
Render Unlit Opaque objects into CameraNormalsTexture for SSAO Depth Normals mode.
If you need Alpha clipping or vertex displacement, you'll need to use Depth mode,
or edit the generated shader code to add in the DepthNormals pass instead.
This is meant as an alternative if you don't need those.
For depthNormalsMat, I'm using a blank PBR/Lit SG with passIndex set to 4 (corresponds to it's DepthNormals pass).
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class UnlitDepthNormalsFeature : ScriptableRendererFeature {
class UnlitDepthNormalsPass : ScriptableRenderPass {
private Material depthNormalsMat;
private int passIndex;
private ProfilingSampler m_ProfilingSampler;
private FilteringSettings m_FilteringSettings;
private List<ShaderTagId> m_ShaderTagIdList = new List<ShaderTagId>();
private RenderTargetHandle depthTex;
private RenderTargetHandle normalsTex;
public UnlitDepthNormalsPass(Material depthNormalsMat, int passIndex, LayerMask layerMask) {
this.depthNormalsMat = depthNormalsMat;
this.passIndex = passIndex;
m_ProfilingSampler = new ProfilingSampler("UnlitDepthNormals");
m_FilteringSettings = new FilteringSettings(RenderQueueRange.opaque, layerMask);
m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit"));
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) {
ConfigureTarget(normalsTex.Identifier(), depthTex.Identifier());
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) {
SortingCriteria sortingCriteria = renderingData.cameraData.defaultOpaqueSortFlags;
DrawingSettings drawingSettings = CreateDrawingSettings(m_ShaderTagIdList, ref renderingData, sortingCriteria);
drawingSettings.overrideMaterial = depthNormalsMat;
drawingSettings.overrideMaterialPassIndex = passIndex;
CommandBuffer cmd = CommandBufferPool.Get();
using (new ProfilingScope(cmd, m_ProfilingSampler)) {
context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref m_FilteringSettings);
public override void OnCameraCleanup(CommandBuffer cmd) {
public Material depthNormalsMat;
public int passIndex;
public LayerMask layerMask;
UnlitDepthNormalsPass m_ScriptablePass;
public override void Create() {
m_ScriptablePass = new UnlitDepthNormalsPass(depthNormalsMat, passIndex, layerMask);
m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRenderingPrePasses;
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) {
if (depthNormalsMat == null) return;
In Unlit Shader Graph, can then use a Custom Function (String mode) to sample the SSAO texture :
DirectAO = 1;
AmbientOcclusionFactor aoFactor = GetScreenSpaceAmbientOcclusion(ScreenPos);
DirectAO = aoFactor.directAmbientOcclusion;
Unsure exactly how to combine it with the shader, but a Multiply might do.
There's also aoFactor.indirectAmbientOcclusion if you want to support that,
See the UniversalFragmentPBR method in URP's Shader Library Lighting.hlsl.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment