Hello everyone,
I’ve encountered a persistent rendering issue with a custom ScriptableRendererFeature in a VR build for the Oculus Quest 2, and after extensive debugging, I believe it may be an engine bug. I’m hoping an expert can confirm this or point out what I might be missing.
I have a multi-pass ScriptableRendererFeature that first calculates a screen-space shadow map, then blurs it, and finally composites it. The pass that calculates the shadow map works correctly. However, the very next pass that is supposed to read the output of the first pass receives an empty/default texture instead. This only happens in a standalone VR build using Single-Pass Instanced rendering on the Quest 2; it works correctly in the Editor.
The Goal: I am creating a self-contained “Sketch” effect. The render pipeline is as follows:
- Calculate VR Shadows Pass: A custom procedural pass that reads the depth buffer and calculates a screen-space shadow map, outputting to a temporary
TextureHandle(_CalculatedShadowMap). - Blur Passes: Two passes that take
_CalculatedShadowMapas input, blur it, and write the final result back to_CalculatedShadowMap. - Composite Pass: A final pass that reads the blurred shadow map and the scene color to apply the sketch effect.
The Problem: As confirmed by on-device debugging with RenderDoc, the pipeline is breaking after the first step.
- The
Calculate VR Shadowspass executes successfully and produces a correct, valid shadow map. - The
Horizontal Shadow Blurpass, which is the very next step, receives a default empty texture as its input instead of the shadow map from the previous pass. - This causes the entire effect chain to fail.
What I’ve Tried (Troubleshooting Steps): We have exhaustively ruled out all common causes for this type of issue:
- Shader Stripping: All custom shaders are included in the “Always Included Shaders” list.
- Renderer Configuration: The URP Renderer asset has “Opaque Texture,” “Depth Texture,” and “Normals Texture” enabled.
- Graphics API: The issue persists with both Vulkan and OpenGLES3.
- VR Render Mode: The issue occurs in
Single-Pass Instanced. Switching toMulti-Passintroduces different visual artifacts (color loss). - Shader VR Compatibility: All custom shaders have been rewritten to be fully VR-aware, using the correct
TEXTURE2D_Xmacros and stereo setup functions (UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX,UnityStereoTransformScreenSpaceTex, etc.). - C# Implementation: We have switched from using the
Blitter.BlitTexturehelper to using manual, explicitDrawProceduralcalls for all passes to remove any hidden variables. The Render Graph dependencies are declared correctly usingbuilder.UseTexture(sourceHandle, AccessFlags.Read).
As you can see from the below screenshot from unity frame debugger it is working correctly in unity editor
Environment:
- Unity Version: 6000.0.41f1
- URP Version: The version included with Unity 6000.0.41f1
- Target Platform: Android (Oculus Quest 2)
- Graphics API: Vulkan
- XR Plugin: OpenXR
- VR Render Mode: Single-Pass Instanced (Multi-view)
Has anyone encountered a similar issue where the Render Graph fails to pass a texture dependency between two custom render passes in a Single-Pass Instanced VR build? Is this a known bug or limitation in this version of Unity 6, and is there a known workaround other than abandoning the multi-pass approach for the blur?(Attaching C# and Shader files)
Thank you for your time and expertise.
namespace VRDefender.Rendering
{
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
#if UNITY_6000_0_OR_NEWER
using UnityEngine.Rendering.RenderGraphModule;
using System.Reflection;
using System.Text;
using UnityEngine.Rendering.RenderGraphModule.Util;
using UnityEngine.XR;
#endif
public class Sketch : ScriptableRendererFeature
{
SketchRenderPass sketchPass;
private FieldInfo _screenSpaceShadowmapTextureField;
private const string FIELD_NAME = "m_ScreenSpaceShadowmapTexture";
public override void Create()
{
sketchPass = new SketchRenderPass();
name = "Sketch";
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
if (renderingData.cameraData.cameraType == CameraType.Preview
|| renderingData.cameraData.cameraType == CameraType.Reflection
|| UniversalRenderer.IsOffscreenDepthTexture(ref renderingData.cameraData))
return;
sketchPass.ConfigureInput(ScriptableRenderPassInput.Depth | ScriptableRenderPassInput.Normal | ScriptableRenderPassInput.Color);
var settings = VolumeManager.instance.stack.GetComponent<SketchSettings>();
if (settings != null && settings.IsActive())
{
renderer.EnqueuePass(sketchPass);
}
}
protected override void Dispose(bool disposing)
{
sketchPass.Dispose();
base.Dispose(disposing);
}
class SketchRenderPass : ScriptableRenderPass
{
private Material m_sketchmaterial;
private Material m_grabmaterial;
private SketchSettings m_Settings;
private RTHandle tempTexHandle;
private RTHandle sourceShadowmap;
private RTHandle shadowmapHandle1;
private RTHandle shadowmapHandle2;
private Material m_shadowMaterial;
// Get a static Property ID for the global texture we need to read
private static readonly int s_ScreenSpaceShadowmapTextureID = Shader.PropertyToID("_ScreenSpaceShadowmapTexture");
// --- FIX: Get a property ID for our new, reliable texture name ---
private static readonly int s_MainTexID = Shader.PropertyToID("_MainTex");
public SketchRenderPass()
{
profilingSampler = new ProfilingSampler("SketchEffect");
renderPassEvent = RenderPassEvent.BeforeRenderingTransparents;
#if UNITY_6000_0_OR_NEWER
requiresIntermediateTexture = true;
#endif
}
// Method to receive the shadowmap handle from the feature
public void SetSource(RTHandle source)
{
this.sourceShadowmap = source;
}
private void CreateMaterial()
{
if (m_sketchmaterial == null)
{
var shader = Shader.Find("VRDefender/Sketch");
if (shader != null) m_sketchmaterial = new Material(shader);
}
// if (m_grabmaterial == null)
// {
// var shader = Shader.Find("Hidden/VRDefender/GrabShadows");
// if (shader != null) m_grabmaterial = new Material(shader);
// }
if (m_shadowMaterial == null)
{
// Load our new, VR-compatible screen space shadow shader
var shader = Shader.Find("Hidden/VR_ScreenSpaceShadows");
if (shader != null) m_shadowMaterial = new Material(shader);
}
}
public void Dispose()
{
tempTexHandle?.Release();
shadowmapHandle1?.Release();
shadowmapHandle2?.Release();
CoreUtils.Destroy(m_sketchmaterial);
CoreUtils.Destroy(m_grabmaterial);
CoreUtils.Destroy(m_shadowMaterial);
}
#if UNITY_6000_0_OR_NEWER
// private class CopyPassData
// {
// public TextureHandle inputTexture;
// }
// private class MainPassData
// {
// public Material material;
// public TextureHandle inputTexture;
// }
private class BlurPassData { public Material material; public TextureHandle source; }
private class MainPassData { public Material material; public TextureHandle sourceColor; public TextureHandle sourceShadow; }
private class GrabPassData { public Material material; }
private class CopyPassData { public TextureHandle source; public Material material; }
private class PassData { public Material material; public TextureHandle source; }
private class ShadowAndBlurPassData
{
public Material sketchMaterial;
public Material shadowMaterial;
public SketchSettings settings;
}
private void UpdateMaterialProperties()
{
if (m_sketchmaterial == null || m_Settings == null) return;
m_sketchmaterial.SetTexture("_SketchTexture", m_Settings.sketchTexture.value);
m_sketchmaterial.SetColor("_SketchColor", m_Settings.sketchColor.value);
m_sketchmaterial.SetVector("_SketchTiling", m_Settings.sketchTiling.value);
m_sketchmaterial.SetVector("_SketchThresholds", m_Settings.sketchThresholds.value);
m_sketchmaterial.SetFloat("_DepthSensitivity", m_Settings.extendDepthSensitivity.value);
m_sketchmaterial.SetFloat("_CrossHatching", m_Settings.crossHatching.value ? 1.0f : 0.0f);
m_sketchmaterial.SetInt("_KernelSize", m_Settings.blurAmount.value);
m_sketchmaterial.SetFloat("_Spread", m_Settings.blurAmount.value / 6.0f);
m_sketchmaterial.SetInt("_BlurStepSize", m_Settings.blurStepSize.value);
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if(m_sketchmaterial == null)
{
CreateMaterial();
}
m_Settings = VolumeManager.instance.stack.GetComponent<SketchSettings>();
if (m_sketchmaterial == null || m_shadowMaterial == null || m_Settings == null || !m_Settings.IsActive()) return;
UpdateMaterialProperties();
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
var shadowDescriptor = cameraData.cameraTargetDescriptor;
shadowDescriptor = XRSettings.eyeTextureDesc;
shadowDescriptor.colorFormat = RenderTextureFormat.ARGB32;
shadowDescriptor.depthBufferBits = (int)DepthBits.None;
if (cameraData.xrRendering)
{
shadowDescriptor.dimension = TextureDimension.Tex2DArray;
shadowDescriptor.volumeDepth = 2; // 2 slices for 2 eyes
}
// --- FIX: Calculate and set the texel size for the blur passes ---
Vector4 texelSize = new Vector4(1.0f / shadowDescriptor.width, 1.0f / shadowDescriptor.height, shadowDescriptor.width, shadowDescriptor.height);
m_sketchmaterial.SetVector("_CustomTexelSize", texelSize);
// --- END FIX ---
var calculatedShadowMap = UniversalRenderer.CreateRenderGraphTexture(renderGraph, shadowDescriptor, "_CalculatedShadowMap", false);
// --- 1. Calculate the shadow map using our custom VR shader ---
using (var builder = renderGraph.AddRasterRenderPass<PassData>("Calculate VR Shadows", out var passData))
{
passData.material = m_shadowMaterial;
builder.UseTexture(resourceData.cameraDepthTexture, AccessFlags.Read);
builder.SetRenderAttachment(calculatedShadowMap, 0);
builder.SetRenderFunc((PassData data, RasterGraphContext context) => Blitter.BlitTexture(context.cmd, (Texture)null, Vector2.one, data.material, 0));
// builder.SetRenderFunc((PassData data, RasterGraphContext context) => {
// context.cmd.DrawProcedural(Matrix4x4.identity, data.material, 0, MeshTopology.Triangles, 3);
// });
}
// --- Step 1: The "Grab" Pass ---
// Create a texture handle that our grab pass will write into.
// TextureHandle grabbedShadowMap = UniversalRenderer.CreateRenderGraphTexture(renderGraph, shadowDescriptor, "_ShadowMap_Copy", false);
// using (var builder = renderGraph.AddRasterRenderPass<GrabPassData>("Grab Shadow Map", out var passData))
// {
// passData.material = m_grabmaterial;
// // FIX: Explicitly declare that this pass reads the global screen space shadow map.
// // This forces the Render Graph to wait until the shadow map is generated before running this pass.
// builder.UseGlobalTexture(s_ScreenSpaceShadowmapTextureID, AccessFlags.Read);
// builder.SetRenderAttachment(grabbedShadowMap, 0);
// // This pass will read the global _ScreenSpaceShadowmapTexture and output it to our 'grabbedShadowMap' handle.
// builder.SetRenderFunc((GrabPassData data, RasterGraphContext context) => Blitter.BlitTexture(context.cmd, (Texture)null, Vector2.one, data.material, 0));
// }
// --- From here, we use our 'grabbedShadowMap' handle ---
var colorCopyDescriptor = cameraData.cameraTargetDescriptor;
// FIX: The camera descriptor includes depth format. We must remove it for a color texture.
colorCopyDescriptor.depthBufferBits = (int)DepthBits.None;
if (cameraData.xr.enabled)
{
colorCopyDescriptor.dimension = TextureDimension.Tex2DArray;
colorCopyDescriptor.volumeDepth = 2;
}
TextureHandle blurredShadowMap2 = UniversalRenderer.CreateRenderGraphTexture(renderGraph, shadowDescriptor, "_blurredShadowMap2", false);
TextureHandle copiedColor = UniversalRenderer.CreateRenderGraphTexture(renderGraph, colorCopyDescriptor, "_SketchColorCopy", false);
// --- Step 2: Blur the shadow map ---
if (m_Settings.blurAmount.value > m_Settings.blurStepSize.value * 2)
{
// Horizontal Blur (reads from grabbed, writes to blur2)
using (var builder = renderGraph.AddRasterRenderPass<BlurPassData>("Horizontal Shadow Blur", out var passData))
{
passData.material = m_sketchmaterial;
passData.source = calculatedShadowMap;
builder.UseTexture(passData.source, AccessFlags.Read);
// FIX: Declare that this pass needs to read the depth texture.
builder.UseTexture(resourceData.cameraDepthTexture, AccessFlags.Read);
builder.SetRenderAttachment(blurredShadowMap2, 0);
builder.UseAllGlobalTextures(true);
builder.AllowPassCulling(false);
// FIX: Allow this pass to modify global shader properties.
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc((BlurPassData data, RasterGraphContext context) => Blitter.BlitTexture(context.cmd, data.source, Vector2.one, data.material, 2));
// builder.SetRenderFunc((BlurPassData data, RasterGraphContext context) =>
// {
// data.material.SetTexture(s_MainTexID, data.source);
// context.cmd.DrawProcedural(Matrix4x4.identity, data.material, 2, MeshTopology.Triangles, 3);
// });
// builder.SetRenderFunc((BlurPassData data, RasterGraphContext context) => {
// context.cmd.SetGlobalTexture("_BlitTexture", data.source);
// context.cmd.DrawProcedural(Matrix4x4.identity, data.material, 1, MeshTopology.Triangles, 3);
// });
}
// Vertical Blur (reads from blur2, writes back to grabbed)
using (var builder = renderGraph.AddRasterRenderPass<BlurPassData>("Vertical Shadow Blur", out var passData))
{
passData.material = m_sketchmaterial;
passData.source = blurredShadowMap2;
builder.UseTexture(passData.source, AccessFlags.Read);
builder.SetRenderAttachment(calculatedShadowMap, 0);
builder.UseAllGlobalTextures(true);
builder.AllowPassCulling(false);
builder.SetRenderFunc((BlurPassData data, RasterGraphContext context) => Blitter.BlitTexture(context.cmd, data.source, Vector2.one, data.material, 2));
// builder.AllowGlobalStateModification(true);
// builder.SetRenderFunc((BlurPassData data, RasterGraphContext context) => {
// data.material.SetTexture(s_MainTexID, data.source);
// context.cmd.DrawProcedural(Matrix4x4.identity, data.material, 3, MeshTopology.Triangles, 3);
// });
}
}
// --- Step 3: Composite the final image ---
// --- 3. Copy the Camera Color (manual pass to avoid API errors) ---
using (var builder = renderGraph.AddRasterRenderPass<CopyPassData>("Copy Color", out var passData))
{
passData.source = resourceData.activeColorTexture;
passData.material = m_sketchmaterial;
builder.UseTexture(passData.source, AccessFlags.Read); // Corrected enum
builder.UseTexture(resourceData.cameraDepthTexture, AccessFlags.Read);
builder.SetRenderAttachment(copiedColor, 0);
builder.UseAllGlobalTextures(true);
builder.AllowPassCulling(false);
builder.SetRenderFunc((CopyPassData data, RasterGraphContext context) => Blitter.BlitTexture(context.cmd, data.source, new Vector4(1, 1, 0, 0), 0.0f, false));
// builder.AllowGlobalStateModification(true);
// builder.SetRenderFunc((CopyPassData data, RasterGraphContext context) => {
// data.material.SetTexture(s_MainTexID, data.source);
// context.cmd.DrawProcedural(Matrix4x4.identity, data.material, 0, MeshTopology.Triangles, 3);
// });
}
//renderGraph.AddBlitPass(new RenderGraphUtils.BlitMaterialParameters(resourceData.activeColorTexture, copiedColor));
using (var builder = renderGraph.AddRasterRenderPass<MainPassData>("Sketch Main Pass", out var passData))
{
passData.material = m_sketchmaterial;
passData.sourceColor = copiedColor;
passData.sourceShadow = calculatedShadowMap; // This is now our final, blurred shadow map
builder.UseTexture(passData.sourceColor, AccessFlags.Read);
builder.UseTexture(passData.sourceShadow, AccessFlags.Read);
// FIX: The final pass also needs depth and normals for triplanar mapping.
builder.UseTexture(resourceData.cameraDepthTexture, AccessFlags.Read);
builder.UseTexture(resourceData.cameraNormalsTexture, AccessFlags.Read);
builder.SetRenderAttachment(resourceData.activeColorTexture, 0);
builder.UseAllGlobalTextures(true);
builder.AllowPassCulling(false);
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc((MainPassData data, RasterGraphContext context) =>
{
data.material.SetTexture("_BlitTexture", data.sourceColor);
data.material.SetTexture("_ShadowmapTexture", data.sourceShadow);
Blitter.BlitTexture(context.cmd, data.sourceColor, Vector2.one, data.material, 0);
// context.cmd.DrawProcedural(Matrix4x4.identity, data.material, 1, MeshTopology.Triangles, 3);
});
}
}
#endif
}
}
}
// VRDefender/Sketch.shader
Shader "VRDefender/Sketch"
{
SubShader
{
Tags
{
"RenderType" = "Opaque"
"RenderPipeline" = "UniversalPipeline"
}
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
#define E 2.71828f
// This struct and helper function correctly prepare UVs for stereo rendering.
struct StereoVaryings
{
float2 uv;
uint eyeIndex;
};
StereoVaryings GetStereoVaryings(Varyings input)
{
StereoVaryings output;
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
output.uv = UnityStereoTransformScreenSpaceTex(input.texcoord);
output.eyeIndex = unity_StereoEyeIndex;
return output;
}
// Declare variables used by multiple passes here
float4 _CustomTexelSize;
uint _KernelSize;
float _Spread;
uint _BlurStepSize;
float gaussian(int x)
{
float sigmaSqu = _Spread * _Spread;
return (1 / sqrt(TWO_PI * sigmaSqu)) * pow(E, -(x * x) / (2 * sigmaSqu));
}
ENDHLSL
Pass
{
Name "Sketch Main"
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareNormalsTexture.hlsl"
TEXTURE2D(_SketchTexture);
TEXTURE2D_X(_ShadowmapTexture);
float4 _SketchColor;
float2 _SketchThresholds;
float2 _SketchTiling;
float _CrossHatching;
// FIX: Restored the full function body for triplanar sampling.
float4 triplanarSample(Texture2D tex, SamplerState texSampler, float2x2 rotation, float3 uv, float3 normals, float blend)
{
float2 uvX = mul(rotation, uv.zy * _SketchTiling);
float2 uvY = mul(rotation, uv.xz * _SketchTiling);
float2 uvZ = mul(rotation, uv.xy * _SketchTiling);
if (normals.x < 0) { uvX.x = -uvX.x; }
if (normals.y < 0) { uvY.x = -uvY.x; }
if (normals.z >= 0){ uvZ.x = -uvZ.x; }
float4 colX = SAMPLE_TEXTURE2D(tex, texSampler, uvX);
float4 colY = SAMPLE_TEXTURE2D(tex, texSampler, uvY);
float4 colZ = SAMPLE_TEXTURE2D(tex, texSampler, uvZ);
float3 blending = pow(abs(normals), blend);
blending /= dot(blending, 1.0f);
return (colX * blending.x + colY * blending.y + colZ * blending.z);
}
float4 frag (Varyings i) : SV_Target
{
StereoVaryings stereo = GetStereoVaryings(i);
float3 uv = float3(stereo.uv, stereo.eyeIndex);
float4 col = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv);
float depth = SampleSceneDepth(stereo.uv);
float3 worldPos = ComputeWorldSpacePosition(stereo.uv, depth, UNITY_MATRIX_I_VP);
float3 worldNormal = SampleSceneNormals(stereo.uv);
float shadows = 1.0f - SAMPLE_TEXTURE2D_X(_ShadowmapTexture, sampler_LinearClamp, uv).r;
float sketchVisibility = smoothstep(_SketchThresholds.x, _SketchThresholds.y, shadows);
float2x2 rotationMatrix = float2x2(1, 0, 0, 1);
float4 sketchTexture = saturate(triplanarSample(_SketchTexture, sampler_LinearRepeat, rotationMatrix, worldPos, worldNormal, 10.0f));
if(_CrossHatching > 0.5f)
{
rotationMatrix = float2x2(0.707, -0.707, 0.707, 0.707);
float4 sketchTexture2 = saturate(triplanarSample(_SketchTexture, sampler_LinearRepeat, rotationMatrix, worldPos, worldNormal, 10.0f));
sketchTexture.rgb = saturate(sketchTexture.rgb + sketchTexture2.rgb);
sketchTexture.a = max(sketchTexture.a, sketchTexture2.a);
}
sketchTexture *= _SketchColor;
return lerp(col, sketchTexture, sketchVisibility * sketchTexture.a);
}
ENDHLSL
}
Pass
{
Name "Horizontal Blur"
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment frag_horizontal
float4 frag_horizontal (Varyings i) : SV_Target
{
StereoVaryings stereo = GetStereoVaryings(i);
float3 col = 0.0f;
float kernelSum = 0.0f;
int upper = ((_KernelSize - 1) / 2);
int lower = -upper;
for (int x = lower; x <= upper; x += _BlurStepSize)
{
float2 offset_uv = stereo.uv + float2(_CustomTexelSize.x * x, 0.0f);
float gauss = gaussian(x);
kernelSum += gauss;
col += gauss * SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, float3(offset_uv, stereo.eyeIndex)).r;
}
if (kernelSum > 0.0f)
col /= kernelSum;
return float4(col, 1.0);
}
ENDHLSL
}
Pass
{
Name "Vertical Blur"
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment frag_vertical
float4 frag_vertical (Varyings i) : SV_Target
{
StereoVaryings stereo = GetStereoVaryings(i);
float3 col = 0.0f;
float kernelSum = 0.0f;
int upper = ((_KernelSize - 1) / 2);
int lower = -upper;
for (int y = lower; y <= upper; y += _BlurStepSize)
{
float2 offset_uv = stereo.uv + float2(0.0f, _CustomTexelSize.y * y);
float gauss = gaussian(y);
kernelSum += gauss;
col += gauss * SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, float3(offset_uv, stereo.eyeIndex)).r;
}
if (kernelSum > 0.0f)
col /= kernelSum;
return float4(col, 1.0);
}
ENDHLSL
}
}
}
// Shaders/Hidden/VR_ScreenSpaceShadows.shader
Shader "Hidden/VR_ScreenSpaceShadows"
{
SubShader
{
Tags{ "RenderPipeline" = "UniversalPipeline" "IgnoreProjector" = "True"}
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/EntityLighting.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/ImageBasedLighting.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
// --- FIX: A custom, VR-aware vertex shader for manual DrawProcedural calls ---
Varyings VertFullscreen(uint vertexID : SV_VertexID, uint instanceID : SV_InstanceID)
{
Varyings output;
// Required setup for Single-Pass Instanced rendering
UNITY_SETUP_INSTANCE_ID(instanceID);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
// Generates a fullscreen triangle
output.texcoord = float2((vertexID << 1) & 2, vertexID & 2);
output.positionCS = float4(output.texcoord * 2.0 - 1.0, 0.0, 1.0);
// Flip the triangle upside down on platforms where UVs start at the top
#if UNITY_UV_STARTS_AT_TOP
output.positionCS.y = -output.positionCS.y;
#endif
return output;
}
// --- END FIX ---
half4 Fragment(Varyings input) : SV_Target
{
// This macro is essential for setting up which eye (0 or 1) is currently being rendered.
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
// --- VR COMPATIBILITY FIX ---
// This function transforms the standard texture coordinates into the correct coordinates
// for the current eye's slice in the stereo texture array. This is the key fix.
float2 uv = UnityStereoTransformScreenSpaceTex(input.texcoord.xy);
// --- END FIX ---
#if UNITY_REVERSED_Z
float deviceDepth = SAMPLE_TEXTURE2D_X(_CameraDepthTexture, sampler_PointClamp, uv).r;
#else
float deviceDepth = SAMPLE_TEXTURE2D_X(_CameraDepthTexture, sampler_PointClamp, uv).r;
deviceDepth = deviceDepth * 2.0 - 1.0;
#endif
// Fetch shadow coordinates for cascade using the corrected UVs.
float3 wpos = ComputeWorldSpacePosition(uv, deviceDepth, unity_MatrixInvVP);
float4 coords = TransformWorldToShadowCoord(wpos);
// Screenspace shadowmap is only used for directional lights which use orthogonal projection.
half realtimeShadow = MainLightRealtimeShadow(coords);
return realtimeShadow;
}
ENDHLSL
Pass
{
Name "VR_ScreenSpaceShadows"
ZTest Always
ZWrite Off
Cull Off
HLSLPROGRAM
#pragma multi_compile _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH
// FIX: Tell the shader to use our new custom vertex shader
#pragma vertex VertFullscreen
#pragma vertex Vert
#pragma fragment Fragment
ENDHLSL
}
}
}


