private void CreatePortalCamera(Camera currentCamera, Camera.StereoscopicEye eye, out Camera portalCamera, ref RenderTexture portalTexture) { portalCamera = null; // Create the render texture (if needed) if (!portalTexture || m_OldReflectionTextureSize != m_TextureSize) // if it doesn't exist or the size has changed { if (portalTexture) // if it does exist { DestroyImmediate(portalTexture); // destroy it first } portalTexture = new RenderTexture(m_TextureSize, m_TextureSize, 24); // <<<< make buffer size 24?? portalTexture.name = "__MirrorReflection" + eye.ToString() + GetInstanceID(); // create the name of the object portalTexture.isPowerOfTwo = true; // https://docs.unity3d.com/Manual/Textures.html: Non power of two texture assets can be scaled up at import time using the Non Power of 2 option in the advanced texture type in the import settings. Unity will scale texture contents as requested, and in the game they will behave just like any other texture, so they can still be compressed and very fast to load. portalTexture.hideFlags = HideFlags.DontSave; // The object will not be saved to the Scene. It will not be destroyed when a new Scene is loaded. portalTexture.antiAliasing = 4; // < <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ResourceIntensive but pretty m_OldReflectionTextureSize = m_TextureSize; // save the old texture size } // Create camera with the render texture if (!m_PortalCameras.TryGetValue(currentCamera, out portalCamera)) // if it does not yet exist in the dictionary, create it. If it does, assign it. (catch both not-in-dictionary and in-dictionary-but-deleted-GO) { GameObject go = new GameObject("Mirror Reflection Camera id" + GetInstanceID() + " for " + currentCamera.GetInstanceID(), typeof(Camera), typeof(Skybox)); // create the new game object portalCamera = go.GetComponent <Camera>(); portalCamera.enabled = false; portalCamera.transform.position = transform.position; portalCamera.transform.rotation = transform.rotation; portalCamera.tag = "PortalCam"; // Tag it as a portal camera so it doesn't participate in the additional CameraRender function //portalCamera.gameObject.AddComponent<FlareLayer>(); // Adds a flare layer to make Lens Flares appear in the image?? disabled for now go.hideFlags = HideFlags.DontSave; // The object will not be saved to the Scene. It will not be destroyed when a new Scene is loaded. m_PortalCameras.Add(currentCamera, portalCamera); // add the newly created camera to the dictionary } }
// On-demand create any objects we need private void CreateMirrorObjects(Camera currentCamera, Camera.StereoscopicEye eye, out Camera reflectionCamera, ref RenderTexture reflectionTexture) { reflectionCamera = null; // Reflection render texture if (!reflectionTexture || m_OldReflectionTextureSize != m_TextureSize) { if (reflectionTexture) { DestroyImmediate(reflectionTexture); } reflectionTexture = new RenderTexture(m_TextureSize, m_TextureSize, 16); reflectionTexture.name = "__MirrorReflection" + eye.ToString() + GetInstanceID(); reflectionTexture.isPowerOfTwo = true; reflectionTexture.hideFlags = HideFlags.DontSave; m_OldReflectionTextureSize = m_TextureSize; } // Camera for reflection if (!m_ReflectionCameras.TryGetValue(currentCamera, out reflectionCamera)) // catch both not-in-dictionary and in-dictionary-but-deleted-GO { GameObject go = new GameObject("Mirror Reflection Camera id" + GetInstanceID() + " for " + currentCamera.GetInstanceID(), typeof(Camera), typeof(Skybox)); reflectionCamera = go.GetComponent <Camera>(); reflectionCamera.enabled = false; reflectionCamera.transform.position = transform.position; reflectionCamera.transform.rotation = transform.rotation; reflectionCamera.gameObject.AddComponent <FlareLayer>(); go.hideFlags = HideFlags.DontSave; m_ReflectionCameras.Add(currentCamera, reflectionCamera); } }
private void RenderCamera(Camera cam, Renderer rend, Camera.StereoscopicEye eye, ref RenderTexture reflectionTexture) { Camera reflectionCamera; CreateMirrorObjects(cam, eye, out reflectionCamera, ref reflectionTexture); // find out the reflection plane: position and normal in world space Vector3 pos = transform.position; Vector3 normal = transform.up; // Optionally disable pixel lights for reflection int oldPixelLightCount = QualitySettings.pixelLightCount; if (m_DisablePixelLights) { QualitySettings.pixelLightCount = 0; } CopyCameraProperties(cam, reflectionCamera); // Render reflection // Reflect camera around reflection plane float d = -Vector3.Dot(normal, pos) - m_ClipPlaneOffset; Vector4 reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d); Matrix4x4 reflection = Matrix4x4.zero; CalculateReflectionMatrix(ref reflection, reflectionPlane); Vector3 oldEyePos; Matrix4x4 worldToCameraMatrix; if (cam.stereoEnabled) { worldToCameraMatrix = cam.GetStereoViewMatrix(eye) * reflection; Vector3 eyeOffset; if (eye == Camera.StereoscopicEye.Left) { eyeOffset = InputTracking.GetLocalPosition(XRNode.LeftEye); } else { eyeOffset = InputTracking.GetLocalPosition(XRNode.RightEye); } eyeOffset.z = 0.0f; oldEyePos = cam.transform.position + cam.transform.TransformVector(eyeOffset); } else { worldToCameraMatrix = cam.worldToCameraMatrix * reflection; oldEyePos = cam.transform.position; } Vector3 newEyePos = reflection.MultiplyPoint(oldEyePos); reflectionCamera.transform.position = newEyePos; reflectionCamera.worldToCameraMatrix = worldToCameraMatrix; // Setup oblique projection matrix so that near plane is our reflection // plane. This way we clip everything below/above it for free. Vector4 clipPlane = CameraSpacePlane(worldToCameraMatrix, pos, normal, 1.0f); Matrix4x4 projectionMatrix; //if (cam.stereoEnabled) projectionMatrix = HMDMatrix4x4ToMatrix4x4(cam.GetStereoProjectionMatrix(Camera.StereoscopicEye.Left)); //else //if (cam.stereoEnabled) // projectionMatrix = HMDMatrix4x4ToMatrix4x4(SteamVR.instance.hmd.GetProjectionMatrix((Valve.VR.EVREye)eye, cam.nearClipPlane, cam.farClipPlane)); //else if (cam.stereoEnabled) { projectionMatrix = cam.GetStereoProjectionMatrix(eye); } else { projectionMatrix = cam.projectionMatrix; } //projectionMatrix = cam.CalculateObliqueMatrix(clipPlane); MakeProjectionMatrixOblique(ref projectionMatrix, clipPlane); reflectionCamera.projectionMatrix = projectionMatrix; reflectionCamera.cullingMask = m_ReflectLayers.value; reflectionCamera.targetTexture = reflectionTexture; GL.invertCulling = true; //Vector3 euler = cam.transform.eulerAngles; //reflectionCamera.transform.eulerAngles = new Vector3(0, euler.y, euler.z); reflectionCamera.transform.rotation = cam.transform.rotation; reflectionCamera.Render(); //reflectionCamera.transform.position = oldEyePos; GL.invertCulling = false; Material[] materials = rend.sharedMaterials; string property = "_ReflectionTex" + eye.ToString(); foreach (Material mat in materials) { if (mat.HasProperty(property)) { mat.SetTexture(property, reflectionTexture); } } // Restore pixel light count if (m_DisablePixelLights) { QualitySettings.pixelLightCount = oldPixelLightCount; } s_InsideRendering = false; }
private void RenderCamera(Camera camera, Renderer rend, Camera.StereoscopicEye eye, ref RenderTexture portalTexture, ScriptableRenderContext SRC) { // Create the camera that will render the reflection Camera portalCamera; CreatePortalCamera(camera, eye, out portalCamera, ref portalTexture); CopyCameraProperties(camera, portalCamera); // Copy the properties of the (player) camera // find out the reflection plane: position and normal in world space Vector3 pos = transform.position; //portalRenderPlane.transform.forward;// Vector3 normal = transform.TransformDirection(projectionDirection); // Alex: This is done because sometimes the object reflection direction does not align with what was the default (transform.forward), in this way, the user can specify this. //normal.Normalize(); // Alex: normalize in case someone enters a non-normalized vector. Turned off for now because it is a fun effect :P // Optionally disable pixel lights for reflection int oldPixelLightCount = QualitySettings.pixelLightCount; if (m_DisablePixelLights) { QualitySettings.pixelLightCount = 0; } // Reflect camera around reflection plane float d = -Vector3.Dot(normal, pos) - m_ClipPlaneOffset; Vector4 reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d); Matrix4x4 reflection = Matrix4x4.identity; CalculateReflectionMatrix(ref reflection, reflectionPlane); // Calculate the Eye offsets Vector3 oldEyePos; Matrix4x4 worldToCameraMatrix; if (camera.stereoEnabled) { Vector3 eyeOffset; worldToCameraMatrix = camera.GetStereoViewMatrix(eye); InputTracking.GetNodeStates(nodeStates); XRNodeState leftEyeState = findNode(nodeStates, XRNode.LeftEye); XRNodeState rightEyeState = findNode(nodeStates, XRNode.RightEye); if (eye == Camera.StereoscopicEye.Left) { leftEyeState.TryGetPosition(out eyeOffset); //eyeOffset = InputTracking.GetLocalPosition(XRNode.LeftEye); //<< Deprecated } else { rightEyeState.TryGetPosition(out eyeOffset); //eyeOffset = InputTracking.GetLocalPosition(XRNode.RightEye); //<< Deprecated } eyeOffset.z = 0.0f; oldEyePos = camera.transform.position + camera.transform.TransformVector(eyeOffset); } else { worldToCameraMatrix = camera.worldToCameraMatrix; oldEyePos = camera.transform.position; } // >>>Transform Camera<<< portalCamera.projectionMatrix = camera.projectionMatrix; // Match matrices <<< Vector3 newEyePos = reflection.MultiplyPoint(oldEyePos); portalCamera.transform.position = newEyePos; portalCamera.worldToCameraMatrix = worldToCameraMatrix * reflection; // Setup oblique projection matrix so that near plane is our reflection plane. This way we clip everything below/above it for free. Vector4 clipPlane = CameraSpacePlane(worldToCameraMatrix * reflection, pos, normal, 1.0f); Matrix4x4 projectionMatrix; if (camera.stereoEnabled) { projectionMatrix = camera.GetStereoProjectionMatrix(eye); } else { projectionMatrix = camera.projectionMatrix; } MakeProjectionMatrixOblique(ref projectionMatrix, clipPlane); portalCamera.projectionMatrix = projectionMatrix; portalCamera.cullingMask = m_LayerMask.value; // Set culling mask <<<< portalCamera.targetTexture = portalTexture; // Set the target texture <<< GL.invertCulling = true; portalCamera.transform.rotation = camera.transform.rotation; UniversalRenderPipeline.RenderSingleCamera(SRC, portalCamera); // URP Version of: portalCamera.Render(); GL.invertCulling = false; // Assign the rendertexture to the material Material[] materials = rend.sharedMaterials; // Why only get the shared materials? string property = "_ReflectionTex" + eye.ToString(); foreach (Material mat in materials) { if (mat.HasProperty(property)) { mat.SetTexture(property, portalTexture); } } // Restore pixel light count if (m_DisablePixelLights) { QualitySettings.pixelLightCount = oldPixelLightCount; } s_InsideRendering = false; }