Search Unity

  1. New Unity Live Help updates. Check them out here!

    Dismiss Notice

Mirror Reflections in VR

Discussion in 'AR/VR (XR) Discussion' started by BLMSTN, Jul 18, 2016.

  1. BLMSTN

    BLMSTN

    Joined:
    Jul 18, 2016
    Posts:
    1
    Hej there,

    I'm currently working on a little VR project that needs to have mirror reflections. I'm using this script: http://wiki.unity3d.com/index.php/MirrorReflection4 for the reflections.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. // This is in fact just the Water script from Pro Standard Assets,
    5. // just with refraction stuff removed.
    6.  
    7. [ExecuteInEditMode] // Make mirror live-update even when not in play mode
    8. public class MirrorReflection : MonoBehaviour {
    9.     public bool m_DisablePixelLights = true;
    10.     public int m_TextureSize = 256;
    11.     public float m_ClipPlaneOffset = 0.07f;
    12.  
    13.     public LayerMask m_ReflectLayers = -1;
    14.  
    15.     private Hashtable m_ReflectionCameras = new Hashtable(); // Camera -> Camera table
    16.  
    17.     private RenderTexture m_ReflectionTexture = null;
    18.     private int m_OldReflectionTextureSize = 0;
    19.  
    20.     private static bool s_InsideRendering = false;
    21.  
    22.     // This is called when it's known that the object will be rendered by some
    23.     // camera. We render reflections and do other updates here.
    24.     // Because the script executes in edit mode, reflections for the scene view
    25.     // camera will just work!
    26.     public void OnWillRenderObject() {
    27.         var rend = GetComponent<Renderer>();
    28.         if (!enabled || !rend || !rend.sharedMaterial || !rend.enabled)
    29.             return;
    30.  
    31.         Camera cam = Camera.current;
    32.         if (!cam)
    33.             return;
    34.  
    35.         // Safeguard from recursive reflections.      
    36.         if (s_InsideRendering)
    37.             return;
    38.         s_InsideRendering = true;
    39.  
    40.         Camera reflectionCamera;
    41.         CreateMirrorObjects(cam, out reflectionCamera);
    42.  
    43.         // find out the reflection plane: position and normal in world space
    44.         Vector3 pos = transform.position;
    45.         Vector3 normal = transform.up;
    46.  
    47.         // Optionally disable pixel lights for reflection
    48.         int oldPixelLightCount = QualitySettings.pixelLightCount;
    49.         if (m_DisablePixelLights)
    50.             QualitySettings.pixelLightCount = 0;
    51.  
    52.         UpdateCameraModes(cam, reflectionCamera);
    53.  
    54.         // Render reflection
    55.         // Reflect camera around reflection plane
    56.         float d = -Vector3.Dot(normal, pos) - m_ClipPlaneOffset;
    57.         Vector4 reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d);
    58.  
    59.         Matrix4x4 reflection = Matrix4x4.zero;
    60.         CalculateReflectionMatrix(ref reflection, reflectionPlane);
    61.         Vector3 oldpos = cam.transform.position;
    62.         Vector3 newpos = reflection.MultiplyPoint(oldpos);
    63.         reflectionCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection;
    64.  
    65.         // Setup oblique projection matrix so that near plane is our reflection
    66.         // plane. This way we clip everything below/above it for free.
    67.         Vector4 clipPlane = CameraSpacePlane(reflectionCamera, pos, normal, 1.0f);
    68.         //Matrix4x4 projection = cam.projectionMatrix;
    69.         Matrix4x4 projection = cam.CalculateObliqueMatrix(clipPlane);
    70.         reflectionCamera.projectionMatrix = projection;
    71.  
    72.         reflectionCamera.cullingMask = ~(1 << 4) & m_ReflectLayers.value; // never render water layer
    73.         reflectionCamera.targetTexture = m_ReflectionTexture;
    74.         GL.invertCulling = true;
    75.         reflectionCamera.transform.position = newpos;
    76.         Vector3 euler = cam.transform.eulerAngles;
    77.         reflectionCamera.transform.eulerAngles = new Vector3(0, euler.y, euler.z);
    78.         reflectionCamera.Render();
    79.         reflectionCamera.transform.position = oldpos;
    80.         GL.invertCulling = false;
    81.         Material[] materials = rend.sharedMaterials;
    82.         foreach (Material mat in materials) {
    83.             if (mat.HasProperty("_ReflectionTex"))
    84.                 mat.SetTexture("_ReflectionTex", m_ReflectionTexture);
    85.         }
    86.  
    87.         // Restore pixel light count
    88.         if (m_DisablePixelLights)
    89.             QualitySettings.pixelLightCount = oldPixelLightCount;
    90.  
    91.         s_InsideRendering = false;
    92.     }
    93.  
    94.  
    95.     // Cleanup all the objects we possibly have created
    96.     void OnDisable() {
    97.         if (m_ReflectionTexture) {
    98.             DestroyImmediate(m_ReflectionTexture);
    99.             m_ReflectionTexture = null;
    100.         }
    101.         foreach (DictionaryEntry kvp in m_ReflectionCameras)
    102.             DestroyImmediate(((Camera)kvp.Value).gameObject);
    103.         m_ReflectionCameras.Clear();
    104.     }
    105.  
    106.  
    107.     private void UpdateCameraModes(Camera src, Camera dest) {
    108.         if (dest == null)
    109.             return;
    110.         // set camera to clear the same way as current camera
    111.         dest.clearFlags = src.clearFlags;
    112.         dest.backgroundColor = src.backgroundColor;
    113.         if (src.clearFlags == CameraClearFlags.Skybox) {
    114.             Skybox sky = src.GetComponent(typeof(Skybox)) as Skybox;
    115.             Skybox mysky = dest.GetComponent(typeof(Skybox)) as Skybox;
    116.             if (!sky || !sky.material) {
    117.                 mysky.enabled = false;
    118.             } else {
    119.                 mysky.enabled = true;
    120.                 mysky.material = sky.material;
    121.             }
    122.         }
    123.         // update other values to match current camera.
    124.         // even if we are supplying custom camera&projection matrices,
    125.         // some of values are used elsewhere (e.g. skybox uses far plane)
    126.         dest.farClipPlane = src.farClipPlane;
    127.         dest.nearClipPlane = src.nearClipPlane;
    128.         dest.orthographic = src.orthographic;
    129.         dest.fieldOfView = src.fieldOfView;
    130.         dest.aspect = src.aspect;
    131.         dest.orthographicSize = src.orthographicSize;
    132.     }
    133.  
    134.     // On-demand create any objects we need
    135.     private void CreateMirrorObjects(Camera currentCamera, out Camera reflectionCamera) {
    136.         reflectionCamera = null;
    137.  
    138.         // Reflection render texture
    139.         if (!m_ReflectionTexture || m_OldReflectionTextureSize != m_TextureSize) {
    140.             if (m_ReflectionTexture)
    141.                 DestroyImmediate(m_ReflectionTexture);
    142.             m_ReflectionTexture = new RenderTexture(m_TextureSize, m_TextureSize, 16);
    143.             m_ReflectionTexture.name = "__MirrorReflection" + GetInstanceID();
    144.             m_ReflectionTexture.isPowerOfTwo = true;
    145.             m_ReflectionTexture.hideFlags = HideFlags.DontSave;
    146.             m_OldReflectionTextureSize = m_TextureSize;
    147.         }
    148.  
    149.         // Camera for reflection
    150.         reflectionCamera = m_ReflectionCameras[currentCamera] as Camera;
    151.         if (!reflectionCamera) // catch both not-in-dictionary and in-dictionary-but-deleted-GO
    152.         {
    153.             GameObject go = new GameObject("Mirror Refl Camera id" + GetInstanceID() + " for " + currentCamera.GetInstanceID(), typeof(Camera), typeof(Skybox));
    154.             reflectionCamera = go.GetComponent<Camera>();
    155.             reflectionCamera.enabled = false;
    156.             reflectionCamera.transform.position = transform.position;
    157.             reflectionCamera.transform.rotation = transform.rotation;
    158.             reflectionCamera.gameObject.AddComponent<FlareLayer>();
    159.             go.hideFlags = HideFlags.HideAndDontSave;
    160.             m_ReflectionCameras[currentCamera] = reflectionCamera;
    161.         }
    162.     }
    163.  
    164.     // Extended sign: returns -1, 0 or 1 based on sign of a
    165.     private static float sgn(float a) {
    166.         if (a > 0.0f) return 1.0f;
    167.         if (a < 0.0f) return -1.0f;
    168.         return 0.0f;
    169.     }
    170.  
    171.     // Given position/normal of the plane, calculates plane in camera space.
    172.     private Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal, float sideSign) {
    173.         Vector3 offsetPos = pos + normal * m_ClipPlaneOffset;
    174.         Matrix4x4 m = cam.worldToCameraMatrix;
    175.         Vector3 cpos = m.MultiplyPoint(offsetPos);
    176.         Vector3 cnormal = m.MultiplyVector(normal).normalized * sideSign;
    177.         return new Vector4(cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos, cnormal));
    178.     }
    179.  
    180.     // Calculates reflection matrix around the given plane
    181.     private static void CalculateReflectionMatrix(ref Matrix4x4 reflectionMat, Vector4 plane) {
    182.         reflectionMat.m00 = (1F - 2F * plane[0] * plane[0]);
    183.         reflectionMat.m01 = (-2F * plane[0] * plane[1]);
    184.         reflectionMat.m02 = (-2F * plane[0] * plane[2]);
    185.         reflectionMat.m03 = (-2F * plane[3] * plane[0]);
    186.  
    187.         reflectionMat.m10 = (-2F * plane[1] * plane[0]);
    188.         reflectionMat.m11 = (1F - 2F * plane[1] * plane[1]);
    189.         reflectionMat.m12 = (-2F * plane[1] * plane[2]);
    190.         reflectionMat.m13 = (-2F * plane[3] * plane[1]);
    191.  
    192.         reflectionMat.m20 = (-2F * plane[2] * plane[0]);
    193.         reflectionMat.m21 = (-2F * plane[2] * plane[1]);
    194.         reflectionMat.m22 = (1F - 2F * plane[2] * plane[2]);
    195.         reflectionMat.m23 = (-2F * plane[3] * plane[2]);
    196.  
    197.         reflectionMat.m30 = 0F;
    198.         reflectionMat.m31 = 0F;
    199.         reflectionMat.m32 = 0F;
    200.         reflectionMat.m33 = 1F;
    201.     }
    202. }
    Everything is working fine until i use my HTC Vive. The Reflections in the mirror seem to drift apart depending on my SteamVR's CameraPosition. The further I move away from the mirror the more the reflections are drifting apart and vise versa. I googled for a while and read something about the vergence of the eyes but i don't know how to implement such things in the script.

    -Any help is appreciated
     
    Pillo likes this.
  2. dariony

    dariony

    Joined:
    May 16, 2015
    Posts:
    12
    for stereo mirror reflection see https://www.assetstore.unity3d.com/en/#!/content/71255

     
    jk15, osmoberlin and fffMalzbier like this.
  3. aeroson

    aeroson

    Joined:
    Jul 20, 2013
    Posts:
    22
    Since this was the first post about VR mirror I found when Googling this issue.
    Here is modified http://wiki.unity3d.com/index.php/MirrorReflection4 that works acceptably in VR, it uses ideas from: https://github.com/AADProductions/VR-Mirrors/blob/master/Assets/MirrorEffect/MirrorCamera.cs

    ShaderLab - Mirror.shader
    Code (CSharp):
    1. // original source from: http://wiki.unity3d.com/index.php/MirrorReflection4
    2. Shader "FX/MirrorReflection"
    3. {
    4.     Properties
    5.     {
    6.         _MainTex ("_MainTex", 2D) = "white" {}
    7.         _ReflectionTexLeft ("_ReflectionTexLeft", 2D) = "white" {}
    8.         _ReflectionTexRight ("_ReflectionTexRight", 2D) = "white" {}
    9.     }
    10.     SubShader
    11.     {
    12.         Tags { "RenderType"="Opaque" }
    13.         LOD 100
    14.         Pass {
    15.             CGPROGRAM
    16.             #pragma vertex vert
    17.             #pragma fragment frag
    18.             #include "UnityCG.cginc"
    19.             struct v2f
    20.             {
    21.                 float2 uv : TEXCOORD0;
    22.                 float4 refl : TEXCOORD1;
    23.                 float4 pos : SV_POSITION;
    24.             };
    25.             float4 _MainTex_ST;
    26.             v2f vert(float4 pos : POSITION, float2 uv : TEXCOORD0)
    27.             {
    28.                 v2f o;
    29.                 o.pos = UnityObjectToClipPos (pos);
    30.                 o.uv = TRANSFORM_TEX(uv, _MainTex);
    31.                 o.refl = ComputeScreenPos (o.pos);
    32.                 return o;
    33.             }
    34.             sampler2D _MainTex;
    35.             sampler2D _ReflectionTexLeft;
    36.             sampler2D _ReflectionTexRight;
    37.             fixed4 frag(v2f i) : SV_Target
    38.             {
    39.                 fixed4 tex = tex2D(_MainTex, i.uv);
    40.                 fixed4 refl;
    41.                 if (unity_StereoEyeIndex == 0) refl = tex2Dproj(_ReflectionTexLeft, UNITY_PROJ_COORD(i.refl));
    42.                 else refl = tex2Dproj(_ReflectionTexRight, UNITY_PROJ_COORD(i.refl));
    43.                 return tex * refl;
    44.             }
    45.             ENDCG
    46.         }
    47.     }
    48. }
    C# - MirrorReflection.cs
    Code (CSharp):
    1. // original source from: http://wiki.unity3d.com/index.php/MirrorReflection4
    2.  
    3. using UnityEngine;
    4. using System.Collections;
    5. using System.Collections.Generic;
    6.  
    7. // This is in fact just the Water script from Pro Standard Assets,
    8. // just with refraction stuff removed.
    9.  
    10. [ExecuteInEditMode] // Make mirror live-update even when not in play mode
    11. public class MirrorReflection : MonoBehaviour
    12. {
    13.     public bool m_DisablePixelLights = true;
    14.     public int m_TextureSize = 256;
    15.     public float m_ClipPlaneOffset = 0.07f;
    16.     public int m_framesNeededToUpdate = 0;
    17.  
    18.     public LayerMask m_ReflectLayers = -1;
    19.  
    20.     private Dictionary<Camera, Camera> m_ReflectionCameras = new Dictionary<Camera, Camera>();
    21.  
    22.     private RenderTexture m_ReflectionTextureLeft = null;
    23.     private RenderTexture m_ReflectionTextureRight = null;
    24.     private int m_OldReflectionTextureSize = 0;
    25.  
    26.     private int m_frameCounter = 0;
    27.  
    28.     private static bool s_InsideRendering = false;
    29.  
    30.     // This is called when it's known that the object will be rendered by some
    31.     // camera. We render reflections and do other updates here.
    32.     // Because the script executes in edit mode, reflections for the scene view
    33.     // camera will just work!
    34.     public void OnWillRenderObject()
    35.     {
    36.         if (m_frameCounter > 0)
    37.         {
    38.             m_frameCounter--;
    39.             return;
    40.         }
    41.  
    42.         var rend = GetComponent<Renderer>();
    43.         if (!enabled || !rend || !rend.sharedMaterial || !rend.enabled)
    44.             return;
    45.  
    46.         Camera cam = Camera.current;
    47.         if (!cam)
    48.             return;
    49.  
    50.         // Safeguard from recursive reflections.    
    51.         if (s_InsideRendering)
    52.             return;
    53.         s_InsideRendering = true;
    54.  
    55.         m_frameCounter = m_framesNeededToUpdate;
    56.  
    57.         RenderCamera(cam, rend, Camera.StereoscopicEye.Left, ref m_ReflectionTextureLeft);
    58.         if (cam.stereoEnabled)
    59.             RenderCamera(cam, rend, Camera.StereoscopicEye.Right, ref m_ReflectionTextureRight);
    60.     }
    61.  
    62.     private void RenderCamera(Camera cam, Renderer rend, Camera.StereoscopicEye eye, ref RenderTexture reflectionTexture)
    63.     {
    64.         Camera reflectionCamera;
    65.         CreateMirrorObjects(cam, eye, out reflectionCamera, ref reflectionTexture);
    66.  
    67.         // find out the reflection plane: position and normal in world space
    68.         Vector3 pos = transform.position;
    69.         Vector3 normal = transform.up;
    70.  
    71.         // Optionally disable pixel lights for reflection
    72.         int oldPixelLightCount = QualitySettings.pixelLightCount;
    73.         if (m_DisablePixelLights)
    74.             QualitySettings.pixelLightCount = 0;
    75.  
    76.         CopyCameraProperties(cam, reflectionCamera);
    77.  
    78.         // Render reflection
    79.         // Reflect camera around reflection plane
    80.         float d = -Vector3.Dot(normal, pos) - m_ClipPlaneOffset;
    81.         Vector4 reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d);
    82.  
    83.         Matrix4x4 reflection = Matrix4x4.zero;
    84.         CalculateReflectionMatrix(ref reflection, reflectionPlane);
    85.  
    86.         Vector3 oldEyePos;
    87.         Matrix4x4 worldToCameraMatrix;
    88.         if (cam.stereoEnabled)
    89.         {
    90.             worldToCameraMatrix = cam.GetStereoViewMatrix(eye) * reflection;
    91.  
    92.             var eyeOffset = SteamVR.instance.eyes[(int)eye].pos;
    93.             eyeOffset.z = 0.0f;
    94.             oldEyePos = cam.transform.position + cam.transform.TransformVector(eyeOffset);
    95.         }
    96.         else
    97.         {
    98.             worldToCameraMatrix = cam.worldToCameraMatrix * reflection;
    99.             oldEyePos = cam.transform.position;
    100.         }
    101.  
    102.         Vector3 newEyePos = reflection.MultiplyPoint(oldEyePos);
    103.         reflectionCamera.transform.position = newEyePos;
    104.  
    105.         reflectionCamera.worldToCameraMatrix = worldToCameraMatrix;
    106.  
    107.         // Setup oblique projection matrix so that near plane is our reflection
    108.         // plane. This way we clip everything below/above it for free.
    109.         Vector4 clipPlane = CameraSpacePlane(worldToCameraMatrix, pos, normal, 1.0f);
    110.  
    111.         Matrix4x4 projectionMatrix;
    112.         if (cam.stereoEnabled) projectionMatrix = HMDMatrix4x4ToMatrix4x4(SteamVR.instance.hmd.GetProjectionMatrix((Valve.VR.EVREye)eye, cam.nearClipPlane, cam.farClipPlane));
    113.         else projectionMatrix = cam.projectionMatrix;
    114.      
    115.         //projectionMatrix = cam.CalculateObliqueMatrix(clipPlane);
    116.         MakeProjectionMatrixOblique(ref projectionMatrix, clipPlane);
    117.  
    118.         reflectionCamera.projectionMatrix = projectionMatrix;
    119.         reflectionCamera.cullingMask = m_ReflectLayers.value;
    120.         reflectionCamera.targetTexture = reflectionTexture;
    121.         GL.invertCulling = true;
    122.         //Vector3 euler = cam.transform.eulerAngles;
    123.         //reflectionCamera.transform.eulerAngles = new Vector3(0, euler.y, euler.z);
    124.         reflectionCamera.transform.rotation = cam.transform.rotation;
    125.         reflectionCamera.Render();
    126.         //reflectionCamera.transform.position = oldEyePos;
    127.         GL.invertCulling = false;
    128.         Material[] materials = rend.sharedMaterials;
    129.         string property = "_ReflectionTex" + eye.ToString();
    130.         foreach (Material mat in materials)
    131.         {
    132.             if (mat.HasProperty(property))
    133.                 mat.SetTexture(property, reflectionTexture);
    134.         }
    135.  
    136.         // Restore pixel light count
    137.         if (m_DisablePixelLights)
    138.             QualitySettings.pixelLightCount = oldPixelLightCount;
    139.  
    140.         s_InsideRendering = false;
    141.     }
    142.  
    143.  
    144.     // Cleanup all the objects we possibly have created
    145.     void OnDisable()
    146.     {
    147.         if (m_ReflectionTextureLeft)
    148.         {
    149.             DestroyImmediate(m_ReflectionTextureLeft);
    150.             m_ReflectionTextureLeft = null;
    151.         }
    152.         if (m_ReflectionTextureRight)
    153.         {
    154.             DestroyImmediate(m_ReflectionTextureRight);
    155.             m_ReflectionTextureRight = null;
    156.         }
    157.         foreach (var kvp in m_ReflectionCameras)
    158.             DestroyImmediate(((Camera)kvp.Value).gameObject);
    159.         m_ReflectionCameras.Clear();
    160.     }
    161.  
    162.  
    163.     private void CopyCameraProperties(Camera src, Camera dest)
    164.     {
    165.         if (dest == null)
    166.             return;
    167.         // set camera to clear the same way as current camera
    168.         dest.clearFlags = src.clearFlags;
    169.         dest.backgroundColor = src.backgroundColor;
    170.         if (src.clearFlags == CameraClearFlags.Skybox)
    171.         {
    172.             Skybox sky = src.GetComponent(typeof(Skybox)) as Skybox;
    173.             Skybox mysky = dest.GetComponent(typeof(Skybox)) as Skybox;
    174.             if (!sky || !sky.material)
    175.             {
    176.                 mysky.enabled = false;
    177.             }
    178.             else
    179.             {
    180.                 mysky.enabled = true;
    181.                 mysky.material = sky.material;
    182.             }
    183.         }
    184.         // update other values to match current camera.
    185.         // even if we are supplying custom camera&projection matrices,
    186.         // some of values are used elsewhere (e.g. skybox uses far plane)
    187.         dest.farClipPlane = src.farClipPlane;
    188.         dest.nearClipPlane = src.nearClipPlane;
    189.         dest.orthographic = src.orthographic;
    190.         dest.fieldOfView = src.fieldOfView;
    191.         dest.aspect = src.aspect;
    192.         dest.orthographicSize = src.orthographicSize;
    193.     }
    194.  
    195.     // On-demand create any objects we need
    196.     private void CreateMirrorObjects(Camera currentCamera, Camera.StereoscopicEye eye, out Camera reflectionCamera, ref RenderTexture reflectionTexture)
    197.     {
    198.         reflectionCamera = null;
    199.  
    200.  
    201.         // Reflection render texture
    202.         if (!reflectionTexture || m_OldReflectionTextureSize != m_TextureSize)
    203.         {
    204.             if (reflectionTexture)
    205.                 DestroyImmediate(reflectionTexture);
    206.             reflectionTexture = new RenderTexture(m_TextureSize, m_TextureSize, 16);
    207.             reflectionTexture.name = "__MirrorReflection" + eye.ToString() + GetInstanceID();
    208.             reflectionTexture.isPowerOfTwo = true;
    209.             reflectionTexture.hideFlags = HideFlags.DontSave;
    210.             m_OldReflectionTextureSize = m_TextureSize;
    211.         }
    212.  
    213.         // Camera for reflection
    214.         if (!m_ReflectionCameras.TryGetValue(currentCamera, out reflectionCamera)) // catch both not-in-dictionary and in-dictionary-but-deleted-GO
    215.         {
    216.             GameObject go = new GameObject("Mirror Reflection Camera id" + GetInstanceID() + " for " + currentCamera.GetInstanceID(), typeof(Camera), typeof(Skybox));
    217.             reflectionCamera = go.GetComponent<Camera>();
    218.             reflectionCamera.enabled = false;
    219.             reflectionCamera.transform.position = transform.position;
    220.             reflectionCamera.transform.rotation = transform.rotation;
    221.             reflectionCamera.gameObject.AddComponent<FlareLayer>();
    222.             go.hideFlags = HideFlags.DontSave;
    223.             m_ReflectionCameras.Add(currentCamera, reflectionCamera);
    224.         }
    225.     }
    226.  
    227.     // Extended sign: returns -1, 0 or 1 based on sign of a
    228.     private static float sgn(float a)
    229.     {
    230.         if (a > 0.0f) return 1.0f;
    231.         if (a < 0.0f) return -1.0f;
    232.         return 0.0f;
    233.     }
    234.  
    235.     // Given position/normal of the plane, calculates plane in camera space.
    236.     private Vector4 CameraSpacePlane(Matrix4x4 worldToCameraMatrix, Vector3 pos, Vector3 normal, float sideSign)
    237.     {
    238.         Vector3 offsetPos = pos + normal * m_ClipPlaneOffset;
    239.         Vector3 cpos = worldToCameraMatrix.MultiplyPoint(offsetPos);
    240.         Vector3 cnormal = worldToCameraMatrix.MultiplyVector(normal).normalized * sideSign;
    241.         return new Vector4(cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos, cnormal));
    242.     }
    243.  
    244.     // Calculates reflection matrix around the given plane
    245.     private static void CalculateReflectionMatrix(ref Matrix4x4 reflectionMat, Vector4 plane)
    246.     {
    247.         reflectionMat.m00 = (1F - 2F * plane[0] * plane[0]);
    248.         reflectionMat.m01 = (-2F * plane[0] * plane[1]);
    249.         reflectionMat.m02 = (-2F * plane[0] * plane[2]);
    250.         reflectionMat.m03 = (-2F * plane[3] * plane[0]);
    251.  
    252.         reflectionMat.m10 = (-2F * plane[1] * plane[0]);
    253.         reflectionMat.m11 = (1F - 2F * plane[1] * plane[1]);
    254.         reflectionMat.m12 = (-2F * plane[1] * plane[2]);
    255.         reflectionMat.m13 = (-2F * plane[3] * plane[1]);
    256.  
    257.         reflectionMat.m20 = (-2F * plane[2] * plane[0]);
    258.         reflectionMat.m21 = (-2F * plane[2] * plane[1]);
    259.         reflectionMat.m22 = (1F - 2F * plane[2] * plane[2]);
    260.         reflectionMat.m23 = (-2F * plane[3] * plane[2]);
    261.  
    262.         reflectionMat.m30 = 0F;
    263.         reflectionMat.m31 = 0F;
    264.         reflectionMat.m32 = 0F;
    265.         reflectionMat.m33 = 1F;
    266.     }
    267.  
    268.     // taken from http://www.terathon.com/code/oblique.html
    269.     private static void MakeProjectionMatrixOblique(ref Matrix4x4 matrix, Vector4 clipPlane)
    270.     {
    271.         Vector4 q;
    272.  
    273.         // Calculate the clip-space corner point opposite the clipping plane
    274.         // as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and
    275.         // transform it into camera space by multiplying it
    276.         // by the inverse of the projection matrix
    277.  
    278.         q.x = (sgn(clipPlane.x) + matrix[8]) / matrix[0];
    279.         q.y = (sgn(clipPlane.y) + matrix[9]) / matrix[5];
    280.         q.z = -1.0F;
    281.         q.w = (1.0F + matrix[10]) / matrix[14];
    282.  
    283.         // Calculate the scaled plane vector
    284.         Vector4 c = clipPlane * (2.0F / Vector3.Dot(clipPlane, q));
    285.  
    286.         // Replace the third row of the projection matrix
    287.         matrix[2] = c.x;
    288.         matrix[6] = c.y;
    289.         matrix[10] = c.z + 1.0F;
    290.         matrix[14] = c.w;
    291.     }
    292.  
    293.     protected Matrix4x4 HMDMatrix4x4ToMatrix4x4(Valve.VR.HmdMatrix44_t input)
    294.     {
    295.         var m = Matrix4x4.identity;
    296.  
    297.         m[0, 0] = input.m0;
    298.         m[0, 1] = input.m1;
    299.         m[0, 2] = input.m2;
    300.         m[0, 3] = input.m3;
    301.  
    302.         m[1, 0] = input.m4;
    303.         m[1, 1] = input.m5;
    304.         m[1, 2] = input.m6;
    305.         m[1, 3] = input.m7;
    306.  
    307.         m[2, 0] = input.m8;
    308.         m[2, 1] = input.m9;
    309.         m[2, 2] = input.m10;
    310.         m[2, 3] = input.m11;
    311.  
    312.         m[3, 0] = input.m12;
    313.         m[3, 1] = input.m13;
    314.         m[3, 2] = input.m14;
    315.         m[3, 3] = input.m15;
    316.  
    317.         return m;
    318.     }
    319.  
    320. }
     
  4. YouDesign

    YouDesign

    Joined:
    Jun 21, 2017
    Posts:
    55
    Version for Oculus Rift:

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using UnityEngine.XR;
    6.  
    7.  
    8. // This is in fact just the Water script from Pro Standard Assets,
    9. // just with refraction stuff removed.
    10.  
    11. [ExecuteInEditMode] // Make mirror live-update even when not in play mode
    12. public class MirrorReflection : MonoBehaviour
    13. {
    14.     public bool m_DisablePixelLights = true;
    15.     public int m_TextureSize = 256;
    16.     public float m_ClipPlaneOffset = 0.07f;
    17.     public int m_framesNeededToUpdate = 0;
    18.  
    19.     public LayerMask m_ReflectLayers = -1;
    20.  
    21.     private Dictionary<Camera, Camera> m_ReflectionCameras = new Dictionary<Camera, Camera>();
    22.  
    23.     private RenderTexture m_ReflectionTextureLeft = null;
    24.     private RenderTexture m_ReflectionTextureRight = null;
    25.     private int m_OldReflectionTextureSize = 0;
    26.  
    27.     private int m_frameCounter = 0;
    28.  
    29.     private static bool s_InsideRendering = false;
    30.  
    31.     // This is called when it's known that the object will be rendered by some
    32.     // camera. We render reflections and do other updates here.
    33.     // Because the script executes in edit mode, reflections for the scene view
    34.     // camera will just work!
    35.     public void OnWillRenderObject()
    36.     {
    37.         if (m_frameCounter > 0)
    38.         {
    39.             m_frameCounter--;
    40.             return;
    41.         }
    42.  
    43.         var rend = GetComponent<Renderer>();
    44.         if (!enabled || !rend || !rend.sharedMaterial || !rend.enabled)
    45.             return;
    46.  
    47.         Camera cam = Camera.current;
    48.         if (!cam)
    49.             return;
    50.  
    51.         // Safeguard from recursive reflections.  
    52.         if (s_InsideRendering)
    53.             return;
    54.         s_InsideRendering = true;
    55.  
    56.         m_frameCounter = m_framesNeededToUpdate;
    57.  
    58.         RenderCamera(cam, rend, Camera.StereoscopicEye.Left, ref m_ReflectionTextureLeft);
    59.         if (cam.stereoEnabled)
    60.             RenderCamera(cam, rend, Camera.StereoscopicEye.Right, ref m_ReflectionTextureRight);
    61.     }
    62.  
    63.     private void RenderCamera(Camera cam, Renderer rend, Camera.StereoscopicEye eye, ref RenderTexture reflectionTexture)
    64.     {
    65.         Camera reflectionCamera;
    66.         CreateMirrorObjects(cam, eye, out reflectionCamera, ref reflectionTexture);
    67.  
    68.         // find out the reflection plane: position and normal in world space
    69.         Vector3 pos = transform.position;
    70.         Vector3 normal = transform.up;
    71.  
    72.         // Optionally disable pixel lights for reflection
    73.         int oldPixelLightCount = QualitySettings.pixelLightCount;
    74.         if (m_DisablePixelLights)
    75.             QualitySettings.pixelLightCount = 0;
    76.  
    77.         CopyCameraProperties(cam, reflectionCamera);
    78.  
    79.         // Render reflection
    80.         // Reflect camera around reflection plane
    81.         float d = -Vector3.Dot(normal, pos) - m_ClipPlaneOffset;
    82.         Vector4 reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d);
    83.  
    84.         Matrix4x4 reflection = Matrix4x4.zero;
    85.         CalculateReflectionMatrix(ref reflection, reflectionPlane);
    86.  
    87.         Vector3 oldEyePos;
    88.         Matrix4x4 worldToCameraMatrix;
    89.         if (cam.stereoEnabled)
    90.         {
    91.             worldToCameraMatrix = cam.GetStereoViewMatrix(eye) * reflection;
    92.             Vector3 eyeOffset;
    93.             if (eye == Camera.StereoscopicEye.Left)
    94.                 eyeOffset = InputTracking.GetLocalPosition(XRNode.LeftEye);
    95.             else
    96.                 eyeOffset = InputTracking.GetLocalPosition(XRNode.RightEye);
    97.             eyeOffset.z = 0.0f;
    98.             oldEyePos = cam.transform.position + cam.transform.TransformVector(eyeOffset);
    99.         }
    100.         else
    101.         {
    102.             worldToCameraMatrix = cam.worldToCameraMatrix * reflection;
    103.             oldEyePos = cam.transform.position;
    104.         }
    105.  
    106.         Vector3 newEyePos = reflection.MultiplyPoint(oldEyePos);
    107.         reflectionCamera.transform.position = newEyePos;
    108.  
    109.         reflectionCamera.worldToCameraMatrix = worldToCameraMatrix;
    110.  
    111.         // Setup oblique projection matrix so that near plane is our reflection
    112.         // plane. This way we clip everything below/above it for free.
    113.         Vector4 clipPlane = CameraSpacePlane(worldToCameraMatrix, pos, normal, 1.0f);
    114.  
    115.         Matrix4x4 projectionMatrix;
    116.  
    117.  
    118.  
    119.         //if (cam.stereoEnabled) projectionMatrix = HMDMatrix4x4ToMatrix4x4(cam.GetStereoProjectionMatrix(Camera.StereoscopicEye.Left));
    120.         //else
    121.         //if (cam.stereoEnabled)
    122.         //    projectionMatrix = HMDMatrix4x4ToMatrix4x4(SteamVR.instance.hmd.GetProjectionMatrix((Valve.VR.EVREye)eye, cam.nearClipPlane, cam.farClipPlane));
    123.         //else
    124.         if (cam.stereoEnabled)
    125.             projectionMatrix = cam.GetStereoProjectionMatrix(eye);
    126.         else
    127.             projectionMatrix = cam.projectionMatrix;
    128.         //projectionMatrix = cam.CalculateObliqueMatrix(clipPlane);
    129.         MakeProjectionMatrixOblique(ref projectionMatrix, clipPlane);
    130.  
    131.         reflectionCamera.projectionMatrix = projectionMatrix;
    132.         reflectionCamera.cullingMask = m_ReflectLayers.value;
    133.         reflectionCamera.targetTexture = reflectionTexture;
    134.         GL.invertCulling = true;
    135.         //Vector3 euler = cam.transform.eulerAngles;
    136.         //reflectionCamera.transform.eulerAngles = new Vector3(0, euler.y, euler.z);
    137.         reflectionCamera.transform.rotation = cam.transform.rotation;
    138.         reflectionCamera.Render();
    139.         //reflectionCamera.transform.position = oldEyePos;
    140.         GL.invertCulling = false;
    141.         Material[] materials = rend.sharedMaterials;
    142.         string property = "_ReflectionTex" + eye.ToString();
    143.         foreach (Material mat in materials)
    144.         {
    145.             if (mat.HasProperty(property))
    146.                 mat.SetTexture(property, reflectionTexture);
    147.         }
    148.  
    149.         // Restore pixel light count
    150.         if (m_DisablePixelLights)
    151.             QualitySettings.pixelLightCount = oldPixelLightCount;
    152.  
    153.         s_InsideRendering = false;
    154.     }
    155.  
    156.  
    157.     // Cleanup all the objects we possibly have created
    158.     void OnDisable()
    159.     {
    160.         if (m_ReflectionTextureLeft)
    161.         {
    162.             DestroyImmediate(m_ReflectionTextureLeft);
    163.             m_ReflectionTextureLeft = null;
    164.         }
    165.         if (m_ReflectionTextureRight)
    166.         {
    167.             DestroyImmediate(m_ReflectionTextureRight);
    168.             m_ReflectionTextureRight = null;
    169.         }
    170.         foreach (var kvp in m_ReflectionCameras)
    171.             DestroyImmediate(((Camera)kvp.Value).gameObject);
    172.         m_ReflectionCameras.Clear();
    173.     }
    174.  
    175.  
    176.     private void CopyCameraProperties(Camera src, Camera dest)
    177.     {
    178.         if (dest == null)
    179.             return;
    180.         // set camera to clear the same way as current camera
    181.         dest.clearFlags = src.clearFlags;
    182.         dest.backgroundColor = src.backgroundColor;
    183.         if (src.clearFlags == CameraClearFlags.Skybox)
    184.         {
    185.             Skybox sky = src.GetComponent(typeof(Skybox)) as Skybox;
    186.             Skybox mysky = dest.GetComponent(typeof(Skybox)) as Skybox;
    187.             if (!sky || !sky.material)
    188.             {
    189.                 mysky.enabled = false;
    190.             }
    191.             else
    192.             {
    193.                 mysky.enabled = true;
    194.                 mysky.material = sky.material;
    195.             }
    196.         }
    197.         // update other values to match current camera.
    198.         // even if we are supplying custom camera&projection matrices,
    199.         // some of values are used elsewhere (e.g. skybox uses far plane)
    200.         dest.farClipPlane = src.farClipPlane;
    201.         dest.nearClipPlane = src.nearClipPlane;
    202.         dest.orthographic = src.orthographic;
    203.         dest.fieldOfView = src.fieldOfView;
    204.         dest.aspect = src.aspect;
    205.         dest.orthographicSize = src.orthographicSize;
    206.     }
    207.  
    208.     // On-demand create any objects we need
    209.     private void CreateMirrorObjects(Camera currentCamera, Camera.StereoscopicEye eye, out Camera reflectionCamera, ref RenderTexture reflectionTexture)
    210.     {
    211.         reflectionCamera = null;
    212.  
    213.  
    214.         // Reflection render texture
    215.         if (!reflectionTexture || m_OldReflectionTextureSize != m_TextureSize)
    216.         {
    217.             if (reflectionTexture)
    218.                 DestroyImmediate(reflectionTexture);
    219.             reflectionTexture = new RenderTexture(m_TextureSize, m_TextureSize, 16);
    220.             reflectionTexture.name = "__MirrorReflection" + eye.ToString() + GetInstanceID();
    221.             reflectionTexture.isPowerOfTwo = true;
    222.             reflectionTexture.hideFlags = HideFlags.DontSave;
    223.             m_OldReflectionTextureSize = m_TextureSize;
    224.         }
    225.  
    226.         // Camera for reflection
    227.         if (!m_ReflectionCameras.TryGetValue(currentCamera, out reflectionCamera)) // catch both not-in-dictionary and in-dictionary-but-deleted-GO
    228.         {
    229.             GameObject go = new GameObject("Mirror Reflection Camera id" + GetInstanceID() + " for " + currentCamera.GetInstanceID(), typeof(Camera), typeof(Skybox));
    230.             reflectionCamera = go.GetComponent<Camera>();
    231.             reflectionCamera.enabled = false;
    232.             reflectionCamera.transform.position = transform.position;
    233.             reflectionCamera.transform.rotation = transform.rotation;
    234.             reflectionCamera.gameObject.AddComponent<FlareLayer>();
    235.             go.hideFlags = HideFlags.DontSave;
    236.             m_ReflectionCameras.Add(currentCamera, reflectionCamera);
    237.         }
    238.     }
    239.  
    240.     // Extended sign: returns -1, 0 or 1 based on sign of a
    241.     private static float sgn(float a)
    242.     {
    243.         if (a > 0.0f) return 1.0f;
    244.         if (a < 0.0f) return -1.0f;
    245.         return 0.0f;
    246.     }
    247.  
    248.     // Given position/normal of the plane, calculates plane in camera space.
    249.     private Vector4 CameraSpacePlane(Matrix4x4 worldToCameraMatrix, Vector3 pos, Vector3 normal, float sideSign)
    250.     {
    251.         Vector3 offsetPos = pos + normal * m_ClipPlaneOffset;
    252.         Vector3 cpos = worldToCameraMatrix.MultiplyPoint(offsetPos);
    253.         Vector3 cnormal = worldToCameraMatrix.MultiplyVector(normal).normalized * sideSign;
    254.         return new Vector4(cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos, cnormal));
    255.     }
    256.  
    257.     // Calculates reflection matrix around the given plane
    258.     private static void CalculateReflectionMatrix(ref Matrix4x4 reflectionMat, Vector4 plane)
    259.     {
    260.         reflectionMat.m00 = (1F - 2F * plane[0] * plane[0]);
    261.         reflectionMat.m01 = (-2F * plane[0] * plane[1]);
    262.         reflectionMat.m02 = (-2F * plane[0] * plane[2]);
    263.         reflectionMat.m03 = (-2F * plane[3] * plane[0]);
    264.  
    265.         reflectionMat.m10 = (-2F * plane[1] * plane[0]);
    266.         reflectionMat.m11 = (1F - 2F * plane[1] * plane[1]);
    267.         reflectionMat.m12 = (-2F * plane[1] * plane[2]);
    268.         reflectionMat.m13 = (-2F * plane[3] * plane[1]);
    269.  
    270.         reflectionMat.m20 = (-2F * plane[2] * plane[0]);
    271.         reflectionMat.m21 = (-2F * plane[2] * plane[1]);
    272.         reflectionMat.m22 = (1F - 2F * plane[2] * plane[2]);
    273.         reflectionMat.m23 = (-2F * plane[3] * plane[2]);
    274.  
    275.         reflectionMat.m30 = 0F;
    276.         reflectionMat.m31 = 0F;
    277.         reflectionMat.m32 = 0F;
    278.         reflectionMat.m33 = 1F;
    279.     }
    280.  
    281.     // taken from http://www.terathon.com/code/oblique.html
    282.     private static void MakeProjectionMatrixOblique(ref Matrix4x4 matrix, Vector4 clipPlane)
    283.     {
    284.         Vector4 q;
    285.  
    286.         // Calculate the clip-space corner point opposite the clipping plane
    287.         // as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and
    288.         // transform it into camera space by multiplying it
    289.         // by the inverse of the projection matrix
    290.  
    291.         q.x = (sgn(clipPlane.x) + matrix[8]) / matrix[0];
    292.         q.y = (sgn(clipPlane.y) + matrix[9]) / matrix[5];
    293.         q.z = -1.0F;
    294.         q.w = (1.0F + matrix[10]) / matrix[14];
    295.  
    296.         // Calculate the scaled plane vector
    297.         Vector4 c = clipPlane * (2.0F / Vector3.Dot(clipPlane, q));
    298.  
    299.         // Replace the third row of the projection matrix
    300.         matrix[2] = c.x;
    301.         matrix[6] = c.y;
    302.         matrix[10] = c.z + 1.0F;
    303.         matrix[14] = c.w;
    304.     }
    305.  
    306. }
    307.  
     
    Last edited: Oct 12, 2018
    Shizola, P_Jong, krougeau and 3 others like this.
  5. raiwin

    raiwin

    Joined:
    Jun 24, 2016
    Posts:
    7
    I got this error:

    Assets/Scripts/MirrorReflection.cs(305,49): error CS0246: The type or namespace name `Valve' could not be found. Are you missing an assembly reference?

    Any clue?
     
    YouDesign likes this.
  6. YouDesign

    YouDesign

    Joined:
    Jun 21, 2017
    Posts:
    55
    raiwin likes this.
  7. raiwin

    raiwin

    Joined:
    Jun 24, 2016
    Posts:
    7
  8. swanickj

    swanickj

    Joined:
    May 15, 2019
    Posts:
    23
    Has anyone been able to get this reflection script working in Single-Pass?
     
    ROBYER1 and P_Jong like this.
  9. Needle0

    Needle0

    Joined:
    Jul 20, 2013
    Posts:
    10
    I have another question: this solution seems to work by rendering reflections twice, once for each eye, which may be computationally expensive. In the use case I am working with, most of the reflected objects are very far away, making the stereo parallax between L/R negligible. In this case the second render would be redundant and making a mono reflection sufficient in theory. However, when I try to modify the script/shader so that the same rendertexture is applied to both eyes, the reflections appear incorrect when seen from inside the Oculus Rift (the two images appear offset wrong, especially when tilting your head to the right/left). This seems to specifically occur in Unity 2017.4.11 and later (The release notes for this version mention asymmetric FOV support for Rift; perhaps this is related). Is there a way to make mono reflections appear OK?
     
    Last edited: Jun 9, 2019
  10. swanickj

    swanickj

    Joined:
    May 15, 2019
    Posts:
    23
    The shader offsets the UVs for the reflection based on the current Stereo Eye Index, because of the assumption that the images are different. You could try removing the offset altogether, or changing it to always use the offset of whatever eye you chose to use twice.
     
  11. Needle0

    Needle0

    Joined:
    Jul 20, 2013
    Posts:
    10
    I later found out that the asymmetric FOV rendering feature is indeed the cause, as it is specifically mentioned in Oculus documentation that some shaders will break: https://developer.oculus.com/blog/tech-note-asymmetric-field-of-view-faq/ Unfortunately, unlike some other shader-breaking optimizations like single pass stereo, I cannot find a way to disable asymmetric FOV.

    Pardon my not being very bright with shader code, but is it correct to assume the "UVs for the reflection" you mention is
    i.refl
    in the Mirror.shader code? Or is it some other value? I experimented around modifying the
    i.refl.xy
    values, but they never seem to appear right.
     
  12. swanickj

    swanickj

    Joined:
    May 15, 2019
    Posts:
    23
    oh, my mistake! I've been using my own variant of this shader for a while now and I didn't realize that I had changed my versikon of the shader a bunch. The version of this shader that i've been using renders a single double-wide texture for the reflection and adjusts the UVs for single-pass stereo. The only issue is for some reason it's unable to reflect the skybox correctly, so I've been using a gigantic sphere for my skybox instead. Asymmetric FOV should only be needed for multi-pass, right?
     
  13. Needle0

    Needle0

    Joined:
    Jul 20, 2013
    Posts:
    10
    Ideally, everyone should benefit if it works both in single and multi-pass, but for the time being, the project I am working on is in multi-pass since more stuff begins to break if I switch it to single-pass.
     
  14. ROBYER1

    ROBYER1

    Joined:
    Oct 9, 2015
    Posts:
    749
    Is there an example of the working Single-pass version?
     
  15. swanickj

    swanickj

    Joined:
    May 15, 2019
    Posts:
    23
    I'm having trouble locating the project files from when i did single-pass stereo mirrors, but I'm going to try to do it again in the next few days! If I do, I'll share the code here.
     
    AlessandroElifani likes this.
  16. ROBYER1

    ROBYER1

    Joined:
    Oct 9, 2015
    Posts:
    749
    You are an absolute hero, please do share your findings!
     
  17. LiminalTeam

    LiminalTeam

    Joined:
    Sep 17, 2019
    Posts:
    5
    Did anyone have a single pass version of mirrors working?
     
  18. swanickj

    swanickj

    Joined:
    May 15, 2019
    Posts:
    23
    I do, somewhere! check back here again after the weekend, i'm hoping to re-do it by then.
     
    ROBYER1 likes this.
  19. Jichaels

    Jichaels

    Joined:
    Dec 27, 2018
    Posts:
    227
    I don't really undestand what you are trying to achieve, but can't you simply use a Camera with appropriate culling to render to a RenderTexture ? At least that's what I'm doing and it's working fine
     
  20. swanickj

    swanickj

    Joined:
    May 15, 2019
    Posts:
    23
    update: I've been extremely busy! I'll do the thing... at some point. Sorry fam ;-;

    The normal method of planar reflections - with an oblique near clip plane on a second camera - works just fine for Multi-pass VR and non-VR. Multi-pass is the easiest and least performant of the stereo rendering modes; it renders the whole scene for left eye, and then again for the right eye, so each eye renders like a normal scene.

    In single-pass stereo, however, the left and right eye are done at the same time, using a double wide render texture containing the image for both eyes. This is nearly twice as fast - or multi-pass is twice as slow - making single-pass stereo the only real option for performance-constrained VR games, especially those on the Oculus Quest, or any PC VR that aims for a high graphical fidelity and realtime lighting.
    The problem is, the unusual projection required for that double-wide texture needs to be built into custom shaders and image effects manually. It's not too bad for most shaders, but for ray marching, reflection, projection, and pretty much anything that moves pixels around in screen-space, it's difficult or impossible to implement. Lots of effects don't work without being made specifically for single-pass stereo rendering, and planar reflections are one of them, due both the positioning of the reflection camera and the later projection of the reflected render texture.
     
    Tom-Goethals, jeromeWork and ROBYER1 like this.
  21. jeromeWork

    jeromeWork

    Joined:
    Sep 1, 2015
    Posts:
    342
    Thanks @swanickj That's a great explanation of why a second camera to a render texture just won't cut it for VR.

    Eagerly awaiting that single-pass stereo mirror magic :)
     
    swanickj likes this.
  22. KORTEN123

    KORTEN123

    Joined:
    Feb 19, 2019
    Posts:
    1
    Did you do something similar to this tutorial?

    That's essentially what I did but am having trouble getting the camera FOV and near clipping planes right. Keep seeing the backside of the wall the mirror is attached to and the reflection seems to have an improper perspective when I get too close.
     
  23. Nikud

    Nikud

    Joined:
    Dec 10, 2019
    Posts:
    1
    I have the same problem. I used the code from the very beginning of this tutorial and my mirror reflects the wall it is attached to. Has anyone a solution for this by now? Or would you recommend another vr mirror apporach?
     
  24. CheeseOmelette

    CheeseOmelette

    Joined:
    Jan 22, 2019
    Posts:
    10
    Hi, just to add to this... I was able to get the above code and shader to (somewhat) work in VR on the Oculus Rift, but it only works when the mirror is at a 90 degree angle. If I rotate it, the images on the left and right eye are completely different and make it impossible to see.

    Is there any suggestion for how to fix it? I am not familiar with rendering at all... I'm using multi-pass since others have had success with that.
     
    Last edited: Feb 6, 2020
  25. Leovc1

    Leovc1

    Joined:
    Mar 19, 2019
    Posts:
    1
    I'm having the same problem, any solutions?
     
  26. avandenberg

    avandenberg

    Joined:
    Nov 22, 2018
    Posts:
    22
    Same, still looking for a solution... :/
     
  27. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    610
    As @swanickj said ...


    ... 1. if you're doing multipass it should be easy (google it: there are a large number of people who have written tutorials on this specifically (ie non-VR) and there are many free implementations floating around the web, plus many cheap/paid solutions on the Asset Store). NB: some of those I've seen are just ... broken. So try a few!

    ... 2. If you're trying to do single-pass, better get good at writing hardcore shaders, my friend :). Because no-one else's shader is going to support this (unless they specifically design for it, and charge $$$ for it - it's a horrible chunk of code to write and maintain).

    (I'm currently doing option 2 for myself in my own water/lighting shader. I understand the math, but debugging it gives me headaches :))
     
  28. avandenberg

    avandenberg

    Joined:
    Nov 22, 2018
    Posts:
    22
    thank you so much for the reply :) I feel really stupid now. I misinterpreted aeroson's post. Applied those scripts and it works now! HELL YEAH <3

    The issue mentioned by @CheeseOmelette still persists though. I'll be diving a little deeper into the code this week, If I happen to find out why it happens I'll post it here.

    Edit: This is a screenshot of the issue: https://imgur.com/a/BGOfPFQ
     
    a436t4ataf likes this.
  29. avandenberg

    avandenberg

    Joined:
    Nov 22, 2018
    Posts:
    22
    Figured out the problem: upload_2020-3-4_16-54-26.png
    You need to make sure the plane on which the mirror effect is being rendered is in a seperate layer. Then you want to exclude this layer from rendering in the MirrorReflection Script. This fixes the issue entirely.
     
    a436t4ataf and codestage like this.
  30. avandenberg

    avandenberg

    Joined:
    Nov 22, 2018
    Posts:
    22
    Does anyone know how to port this to URP?

    Error message:
    Recursive rendering is not supported in SRP (are you calling Camera.Render from within a render pipeline?).
    UnityEngine.Camera:Render()

    When I do so anyway the scene view trips out hard. On the HMD (HTC Vive) Nothing at all is shown.

    Pfff just when I thought I had the solution >.<
    I need it in URP unfortunately :/
     
    Last edited: Mar 4, 2020
  31. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    610
    Honestly, any time I see someone using URP, I walk away. It doesn't work, it's not stable, it's not production-ready yet, it's not documented, and the shaders are horribly horribly messed up.

    (there's a lot of active discussion and complaints on this at the moment elsehwere on the forums. Personally: I'm about to launch a major graphical asset explicitly with NO SUPPORT For URP/HDRP because Unity has messed up the shader side so badly)

    You can make it work, but you will have to potentially rewrite your code for every single point release of Unity (that's how much they are breaking / changing it right now).

    /rant.

    TL;DR: friends don't let friends do non-trivial graphics in URP ... yet. Wait for Unity to fix it :).
     
    codestage likes this.
  32. avandenberg

    avandenberg

    Joined:
    Nov 22, 2018
    Posts:
    22
    I actually feel URP is pretty close to done and it performs quite well with a lot of new and useful features, it does take some time to get used to though but I think it's def worth it as its going to be the new default in the near future anyway.

    Anyway, I got it to work. The key was to change the Camera.Render() to UniversalRenderPipeline.RenderSingleCamera(SRC, mirrorCamera);
    where SRC is the ScriptableRenderContext passed from the RenderPipeline.beginCameraRendering function.
     
    a436t4ataf and codestage like this.
  33. Tom-Goethals

    Tom-Goethals

    Joined:
    Mar 6, 2015
    Posts:
    87
    VRChat has it working..
    Found their shader on Github, made it work on quest. (non-commercial project)
    Perfect stereo mirror rendering.. heavy hit on performance though.
    Single Pass Double Wide Forward Rendered.
    Bit hesitant to share the link as i'm not sure the VRChat code is supposed to be open on the internet.
    Might just be from the open VRChat SDK.. not sure.
     
  34. swanickj

    swanickj

    Joined:
    May 15, 2019
    Posts:
    23
    I agree. What i love about URP though is that the non-builtin pipelines are fully visible to me, and i can figure out or modify how anything works when i need to. URP makes some compromises for a few things in order to achieve greater performance than built-in can produce, and those make things tricky sometimes, but i haven't found anything I couldn't hack, yet. ... except double-grab-pass shaders, like a gaussian blur filter plane. I managed a really nice gaussian blur with some fancy blue noise scattering in one pass, though, so i have forgiven URP for this one
     
unityunity