Replace shaders + Single pass stereo + scene mesh using screen coords

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 :slight_smile:

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?

1 Like

You should use ComputeNonStereoScreenPos() to access textures that aren’t in stereo.

Is it possible to perform shader replacement with single pass stereo?

I’m trying to setup shader replacement to render high-precision normal’s into a render texture. And later using a command buffer to render “masked objects” into another render texture.

After using CustomCamera.CopyFrom(SourceCamera), polling the CustomCamera.stereoEnabled property returns false, and it’s not settable, so there appears no way to enable it on the custom camera. Currently it appears as if the left eye is simply stretched over the entire texture.

The next idea was to split the render texture in two and render each eye separately into there perspective half of the texture, not ideal, but it would at least work. But setting the stereoTargetEye appears to have no effect, so I’m left with a render containing 2 copies of the left eyes perspective, instead of one the left and the right combined, as I’d hoped.

So what is the correct way to perform single pass stereo shader replacement? I believe you guys use a similar method internally to render your depthtexturemodes?

The same issue appears to be occurring with command buffers (left eye perspective stretched over entire sceen), so what is the correct way to render single pass stereo objects using command buffers?

Rendering the texture as a non-stereo render-texture and accessing it doesn’t make a lot of sense when each eye has a different view and projection matrix and essentially sees different normal’s / masking.

Any help would be appreciated, cheers
Dan

We’re working on fixes for the command buffer and stereoTargetEye issues. They should be fixed in 5.6 and 2017.1 within the next couple of weeks.

1 Like

I am still observing the broken behavior in 5.6. Did it not make it in?

Is there any Unity version > 5.6 that doesn’t have unity_StereoEyeIndex always being 0?

@scottb

Hi all,
I’m trying to use posto processing with Single-Pass Stereo Rendering on GearVR and DayDream.
Now using Windows 10 + Unity 2017.2. My mobile device is a Samsung S6.
My post effect consists of two steps:
RenderWithShader renders the scene with my very-simple shader that draws everything is visible with White color
Blit operations to perform more complex operations.
Now I’m trying to make work the RenderWithShader operation. First of all: is it allowed to use RenderWithShader with single pass stereo rendering, or is there any issue related to its usage? Here it’s the script code I use:

RenderTexture tempRT1 = new RenderTexture( width, height, depth, format );
[...]
MyCamera.targetTexture = tempRT1;
MyCamera.RenderWithShader( drawSimpleShader, "" );
Graphics.Blit( tempRT1, destination );

I added to my shader all the additional instruction described in https://docs.unity3d.com/Manual/Android-SinglePassStereoRendering.html . Here it is the code (anything wrong?):

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "ShaderTest/DrawSimple SPSR"
{
    Properties {
        _MainTex ("Base (RGB)", 2D) = "" {}
    }
    CGINCLUDE
   
    #include "UnityCG.cginc"
    // FOR SINGLE PASS STEREO RENDERING
    //sampler2D _MainTex;
    UNITY_DECLARE_SCREENSPACE_TEXTURE(_MainTex);
    half4 _MainTex_ST;
    struct appdata
    {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
    };
    struct v2f {
        float2 uv : TEXCOORD0;
        float4 vertex : SV_POSITION;
        UNITY_VERTEX_OUTPUT_STEREO
    };
   
   
   
    v2f vert( appdata v )
    {
        v2f o;
        // FOR SINGLE PASS STEREO RENDERING
        UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
        o.vertex = UnityObjectToClipPos(v.vertex);
        o.uv = TRANSFORM_TEX(v.uv, _MainTex); // v.texcoord.xy;
        return o;
    }
   
    fixed4 frag(v2f i) : SV_Target
    {
        //        return 1-tex2D(_MainTex, i.uv);
        // FOR SINGLE PASS STEREO RENDERING
        UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
        half4 col = tex2D(_MainTex, i.uv);
        half val = col.r + col.g + col.b;
        return (val > 0) ? 1 : 0;
    }
    ENDCG
    Subshader
    {
        Pass
        {
              ZTest Always Cull Off ZWrite Off
              CGPROGRAM
              #pragma vertex vert
              #pragma fragment frag
              ENDCG
        }
    }
}

When I try to play it in my GearVR, I can see only a dark screen with a lighter blinking screen in the upper right corner of my FOV. I expect to see a full-white moving cube, but it is not visible at all.
Any project/graphics/camera/anything-else setting missing or wrong?
Please help.
Thanks