Search Unity

Use of 'Render to Texture' to 'UV space' VR issues

Discussion in 'Image Effects' started by MR_Fancy_Pants, Jan 12, 2018.

  1. MR_Fancy_Pants

    MR_Fancy_Pants

    Joined:
    Aug 21, 2014
    Posts:
    19
    Dear Hivemind!

    I was working on a surface shader in forward rendering in Unity 5.6 that would blend between fully opaque in the light parts and transparent in the shadow parts of a mesh. I approached this by creating a secondary camera with a replacement shader that replaces the lightblend shader with a simple standard shader to receive the shadows which are then rendered to texture. I then reproject the render to texture to uv space in the lightblend shader and apply it to the alpha slot of the lightblend shader. Which works like a charm!


    Problem is,with VR (Open VR) this does not work at all. The render to texture does not look like it captures the VR view (even though I've set the render to texture camera to both).


    I did find some info here : https://forum.unity.com/threads/supporting-single-pass-stereo-rendering-in-image-effects.419850/ and figured it has something to do with the reprojection. Unfortunately I could not figure out how to apply this to my surface shader. So any help would be much obliged!

    My replacement shader code :
    Code (JavaScript):
    1. #pragma strict
    2. @script RequireComponent(Camera)
    3.  
    4. public var replaceShader : Shader;
    5. public var renderToTexture : RenderTexture;
    6. public var setGlobalRenderTexture : String = "Please insert shader variable name.";
    7. private var prePass : RenderTexture;
    8.  
    9. function OnEnable (){
    10.     if (!renderToTexture) prePass = new RenderTexture(Screen.width, Screen.height, 1);  
    11.     else prePass = renderToTexture;
    12.  
    13.     var camera = this.GetComponent(Camera);  
    14.     camera.SetReplacementShader(replaceShader, "Replaceable");
    15.     camera.targetTexture = prePass;
    16.     Shader.SetGlobalTexture(setGlobalRenderTexture, prePass);
    17. }
    18.  
    My lightblend shader code:
    Code (CSharp):
    1. Shader "LightBlend/LightBlend - Replace Transparent"
    2. {
    3.     Properties
    4.     {
    5.         _LightTex("Light Texture", 2D) = "white" {}
    6.         _LightColor("Light Tint", Color) = (1,1,1,1)
    7.         _ShadowTex("Shadow Texture", 2D) = "white" {}
    8.         _ShadowColor("Shadow Tint", Color) = (1,1,1,1)
    9.     }
    10.  
    11.     // Calculate lighting in this pass
    12.     SubShader{
    13.  
    14.     Tags{ "RenderType" = "Alpha" "Queue" = "Transparent" "Replaceable" = "true" }
    15.     Blend Off
    16.  
    17.     CGPROGRAM
    18.     #pragma surface surf Unlit vertex:vert alpha:fade
    19.  
    20.     sampler2D _LightingGrab;
    21.     sampler2D _LightTex;
    22.     sampler2D _ShadowTex;
    23.     half4 _LightColor;
    24.     half4 _ShadowColor;
    25.  
    26.     struct Input {
    27.         float2 uv_LightTex;
    28.         float2 uv_ShadowTex;
    29.         float4 grabUV;
    30.     };
    31.  
    32.     void vert(inout appdata_full v, out Input o) {
    33.         UNITY_INITIALIZE_OUTPUT(Input, o);
    34.         float4 hpos = UnityObjectToClipPos(v.vertex);
    35.         o.grabUV = ComputeGrabScreenPos(hpos);
    36.     }
    37.  
    38.     half4 LightingUnlit(SurfaceOutput s, half3 lightDir, half atten) {
    39.         half4 c;
    40.         half NdotL = dot(s.Normal, lightDir);
    41.         c.rgb = s.Albedo * atten;
    42.         c.a = s.Alpha;
    43.         return c;
    44.     }
    45.  
    46.     void surf(Input IN, inout SurfaceOutput o) {
    47.         half grabTex = tex2Dproj(_LightingGrab, UNITY_PROJ_COORD(IN.grabUV)).r;
    48.         half4 lightTex = tex2D(_LightTex, IN.uv_LightTex)*_LightColor;
    49.         half4 shadowTex = tex2D(_ShadowTex, IN.uv_ShadowTex) * _ShadowColor;
    50.         o.Alpha = grabTex;
    51.         o.Albedo = lightTex;
    52.     }
    53.  
    54.     ENDCG
    55.     }
    56.         Fallback "Diffuse"
    57. }
    58.  
     
  2. svenneve

    svenneve

    Joined:
    May 14, 2013
    Posts:
    80
    After we met on the studio you mentioned this problem. Been tinkering with a shader, not sure it does what you where looking for completely as it wasn't clear if the shadow needs to actually make the material transparent so you can view what ever is behind it through it.

    Code (csharp):
    1.  
    2. Shader "ShadowBlend"
    3. {
    4.     Properties
    5.     {
    6.         _LightTex("Light Texture", 2D) = "white" {}
    7.         _LightColor("Light Tint", Color) = (1,1,1,1)
    8.         _ShadowTex("Shadow Texture", 2D) = "white" {}
    9.         _ShadowColor("Shadow Tint", Color) = (1,1,1,1)
    10.     }
    11.  
    12.     SubShader
    13.     {
    14.     Tags {"Queue" = "Geometry" "RenderType" = "Transparent"}
    15.     /*
    16.         Pass
    17.         {
    18.             Name "ShadowCast"
    19.             Tags {"LightMode" = "ShadowCaster"}
    20.  
    21.             CGPROGRAM
    22.             #pragma vertex vert
    23.             #pragma fragment frag
    24.             #pragma multi_compile_shadowcaster
    25.             #include "UnityCG.cginc"
    26.  
    27.             struct v2f {
    28.                 V2F_SHADOW_CASTER;
    29.             };
    30.  
    31.             v2f vert(appdata_base v)
    32.             {
    33.                     v2f o;
    34.                     TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
    35.                     return o;
    36.             }
    37.  
    38.             float4 frag(v2f i) : SV_Target
    39.             {
    40.                     SHADOW_CASTER_FRAGMENT(i)
    41.             }
    42.             ENDCG
    43.         }
    44.         */
    45.         Pass
    46.         {
    47.             //Name "ShadowReceive"
    48.             Tags {"LightMode" = "ForwardBase"}
    49.             CGPROGRAM
    50.  
    51.             #pragma vertex vert
    52.             #pragma fragment frag
    53.             #pragma multi_compile_fwdbase
    54.             #pragma fragmentoption ARB_fog_exp2
    55.             #pragma fragmentoption ARB_precision_hint_fastest
    56.             #include "UnityCG.cginc"
    57.             #include "AutoLight.cginc"
    58.  
    59.             sampler2D _LightTex;
    60.             sampler2D _ShadowTex;
    61.             half4 _LightColor;
    62.             half4 _ShadowColor;
    63.             sampler2D _BackgroundTexture;
    64.  
    65.             struct v2f
    66.             {
    67.             float4 pos : SV_POSITION;
    68.                 float2 uv1: TEXCOORD0;
    69.                 float4 screenPos: TEXCOORD1;
    70.                 float3 normal : TEXCOORD2;
    71.                 //SHADOW_COORDS(3)
    72.                 LIGHTING_COORDS(3,4)
    73.             };
    74.  
    75.             v2f vert(appdata_base v)
    76.             {
    77.                 v2f o;
    78.                 o.pos = UnityObjectToClipPos(v.vertex);
    79.                 o.uv1 = v.texcoord;
    80.                 o.normal = normalize(mul( float4( v.normal, 0.0 ), unity_WorldToObject ).xyz);
    81.                 o.screenPos = ComputeScreenPos(o.pos);
    82.                 //TRANSFER_SHADOW(o)
    83.                 TRANSFER_VERTEX_TO_FRAGMENT(o);
    84.                 return o;
    85.             }
    86.  
    87.             fixed4 frag(v2f i) : SV_Target
    88.             {
    89.                 float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);
    90.                 float atten = LIGHT_ATTENUATION(i) ;
    91.                 float3 lambert = float(max(0.0, dot(i.normal,lightDirection))) * atten;
    92.  
    93.                 float2 screenUV = i.screenPos.xy / i.screenPos.w;
    94.                 #if UNITY_SINGLE_PASS_STEREO
    95.                     // If Single-Pass Stereo mode is active, transform the
    96.                     // coordinates to get the correct output UV for the current eye.
    97.                     float4 scaleOffset = unity_StereoScaleOffset[unity_StereoEyeIndex];
    98.                     screenUV = (screenUV - scaleOffset.zw) / scaleOffset.xy;
    99.                 #endif
    100.                 half4 lightTex = tex2D(_LightTex, i.uv1) * _LightColor;
    101.                 half4 shadowTex = tex2D(_ShadowTex, screenUV) * _ShadowColor;
    102.                 float4 col = lerp(shadowTex, lightTex, saturate(lambert.x));
    103.                 return col;
    104.             }
    105.             ENDCG
    106.         }
    107.     }
    108.     FallBack "Diffuse"
    109. }
    110.  
     
  3. MR_Fancy_Pants

    MR_Fancy_Pants

    Joined:
    Aug 21, 2014
    Posts:
    19
    Ooow awesome thanks! I did need the shadow to make the material transparent, but I think this part can sort out the problems I had with going from stereo screen space to uv space. I'll try to investigate!

    Code (CSharp):
    1. float2 screenUV = i.screenPos.xy / i.screenPos.w;
    2.                 #if UNITY_SINGLE_PASS_STEREO
    3.                     // If Single-Pass Stereo mode is active, transform the
    4.                     // coordinates to get the correct output UV for the current eye.
    5.                     float4 scaleOffset = unity_StereoScaleOffset[unity_StereoEyeIndex];
    6.                     screenUV = (screenUV - scaleOffset.zw) / scaleOffset.xy;
    7.                 #endif
    And you just have to create two camera's set to left and right to render the textures from, right?

     
    Last edited: Feb 2, 2018
  4. svenneve

    svenneve

    Joined:
    May 14, 2013
    Posts:
    80
    If you're rendering single pass, you might not even have to do that, as you're not fiddling around with custom projections, you're just rendering the 'background'.
    As long as you render that to a rendertexture before the foreground of course.
     
  5. MR_Fancy_Pants

    MR_Fancy_Pants

    Joined:
    Aug 21, 2014
    Posts:
    19
    That's what I thought :S But if I try to capture a render texture from a camera set to both in single pass rendering, it only shows me 1 eye in the render texture.

    I hoped this could be fixed by using this :
    https://docs.unity3d.com/ScriptReference/XR.XRSettings-eyeTextureDesc.html

    But this also returns only 1 eye, instead of two....

    What do you mean by foreground?
     
    Last edited: Feb 2, 2018
  6. svenneve

    svenneve

    Joined:
    May 14, 2013
    Posts:
    80
    We use this inside our stereo reflection plane script, mind you, this runs on the OnWillRenderObject part of the reflection plane, still, might be a good starting point though:

    Code (csharp):
    1.  
    2. if (shadowCamera.stereoEnabled)
    3. {
    4.     if (shadowCamera.stereoTargetEye == StereoTargetEyeMask.Both || shadowCamera.stereoTargetEye == StereoTargetEyeMask.Left)
    5.     {
    6.         // Left eye
    7.     }
    8.  
    9.     if (shadowCamera.stereoTargetEye == StereoTargetEyeMask.Both || shadowCamera.stereoTargetEye == StereoTargetEyeMask.Right)
    10.     {
    11.         // Right eye
    12.     }
    13. }
    14. else
    15. {
    16.     // Non stereo
    17. }
    18.  
     
  7. MR_Fancy_Pants

    MR_Fancy_Pants

    Joined:
    Aug 21, 2014
    Posts:
    19
    Heya Sven,

    I finally got around to give it another go. I appropiated your code which seemed to be working great,with the exception that the render to texture seem to have a distortion issue. Since I was unable to get rid of it, I decided to try my luck and hook up my old Oculus CV1. And voila it works perfectly on the Oculus. So it seems this is a bug specific to the Vive and openVR ..... I wish I would have tried this earlier sigh would have saved me days.

    And I also found out my Oculus had a screen burn thanks to it laying in the deep shadows of my desk and turning itself on. GRMBLZ!!!!!!!!!!! Anyway I can go ahead and setup my scenes and worry about converting it to the Vive later on. Thanks for your help!!!!!!

    EDIT : It works alsmost perfectly on the Oculus. It seems that the left eye is perfect, but it shows the same texture on the right eye.... which is not so perfect. Guess I need more tinkering.
     
    Last edited: Feb 15, 2018