I have to do some offscreen render textures. These render textures are then used by scene objects later. Everything was fun until I looked to the HMD screen ![]()
For simplicity, I have built a refractive shader with shaderforge to test my generated RTs. Then, I made some modifications to sample _GrabTexture with correct uvs in SPSR.
This is the result (its looking correct in vr and editor):
Shader "Shader Forge/refract stereo single pass test" {
Properties {
_Color ("Color", Color) = (0.07843138,0.3921569,0.7843137,1)
_node_9734 ("node_9734", 2D) = "bump" {}
_node_8661 ("node_8661", Range(0, 1)) = 0
[HideInInspector]_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
}
SubShader {
Tags {
"IgnoreProjector"="True"
"Queue"="Transparent"
"RenderType"="Transparent"
}
GrabPass{ }
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#define UNITY_PASS_FORWARDBASE
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase
#pragma only_renderers d3d9 d3d11 glcore gles gles3 metal
#pragma target 3.0
uniform sampler2D _GrabTexture;
uniform float4 _Color;
uniform sampler2D _node_9734; uniform float4 _node_9734_ST;
uniform float _node_8661;
struct VertexInput {
float4 vertex : POSITION;
float2 texcoord0 : TEXCOORD0;
};
struct VertexOutput {
float4 pos : SV_POSITION;
float2 uv0 : TEXCOORD0;
float4 uvgrab : TEXCOORD1;
};
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
o.uv0 = v.texcoord0;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex );
o.uvgrab = ComputeScreenPos(o.pos);
return o;
}
float4 frag(VertexOutput i) : COLOR {
#if UNITY_UV_STARTS_AT_TOP
float grabSign = -_ProjectionParams.x;
#else
float grabSign = _ProjectionParams.x;
#endif
float3 _node_9734_var = UnpackNormal(tex2D(_node_9734,TRANSFORM_TEX(i.uv0, _node_9734)));
float2 dist =(_node_9734_var.rgb.rg*_node_8661);
i.uvgrab.xy += dist;
float4 sceneColor = tex2Dproj(_GrabTexture, UNITY_PROJ_COORD(i.uvgrab));
////// Lighting:
////// Emissive:
float3 emissive = _Color.rgb;
float3 finalColor = emissive;
return fixed4(lerp(sceneColor.rgb, finalColor,_Color.a),1);
}
ENDCG
}
}
FallBack "Diffuse"
// CustomEditor "ShaderForgeMaterialInspector"
}
Next step was to substitute _GrabTexture with my RT, which was generated using a copy of the main camera and performing RenderWithShader. But nope, it is not that simple. Seems like I have to so something else in the RT camera. But what? I don’t know.
The internal method to generate _GrabTexture does some secret magic that I need to replicate, I think.
Let’s compare my custom depth with the generated _CameraDepthTexture:
There is some black magic in _CameraDepthTexture generation process to pack both eyes in the same RT. I bet each half is rendered separately with a different Camera.rect.
Ideas?

