From 85e30641717ae010ad0ae4aa8b99a80c5cbc14bd Mon Sep 17 00:00:00 2001 From: milan Date: Sun, 9 Oct 2022 22:14:46 +0200 Subject: [PATCH] refactor portal rendering into portal camera --- Assets/Escape Room Engine/Portal/Portal.cs | 87 +------------- .../Escape Room Engine/Portal/Portal.prefab | 19 +++- .../Escape Room Engine/Portal/PortalCamera.cs | 106 ++++++++++++++++++ .../Portal/PortalCamera.cs.meta | 11 ++ 4 files changed, 135 insertions(+), 88 deletions(-) create mode 100644 Assets/Escape Room Engine/Portal/PortalCamera.cs create mode 100644 Assets/Escape Room Engine/Portal/PortalCamera.cs.meta diff --git a/Assets/Escape Room Engine/Portal/Portal.cs b/Assets/Escape Room Engine/Portal/Portal.cs index 6a8ddcf..641baeb 100644 --- a/Assets/Escape Room Engine/Portal/Portal.cs +++ b/Assets/Escape Room Engine/Portal/Portal.cs @@ -1,108 +1,23 @@ using System; -using System.Collections.Generic; using UnityEngine; -using UnityEngine.Rendering; -using UnityEngine.Rendering.Universal; -using UnityEngine.XR; namespace Escape_Room_Engine.Portal { public class Portal : MonoBehaviour { - private static readonly Matrix4x4 HalfRotation = Matrix4x4.Rotate(Quaternion.Euler(0, 180, 0)); - private static readonly Camera.StereoscopicEye[] Eyes = - { Camera.StereoscopicEye.Left, Camera.StereoscopicEye.Right }; - private static readonly Dictionary EyeTextureNames = new() - { - { Camera.StereoscopicEye.Left, Shader.PropertyToID("_LeftTex") }, - { Camera.StereoscopicEye.Right, Shader.PropertyToID("_RightTex") } - }; - /// /// The portal that is connected with this one. /// public Portal other; /// - /// The minimum near clip plane distance to be used when calculating the oblique clip plane. - /// - public float minNearClipPlane = 0.02f; - /// /// The camera that will draw the view for this portal. /// - [SerializeField] private Camera portalCamera; - /// - /// The quad where the rendered texture will be drawn on. - /// - [SerializeField] private MeshRenderer portalQuad; - - private PlayerCamera _playerCamera; - private Dictionary _textures = new(); + public PortalCamera portalCamera; private void Awake() { // check whether the other portal is set up if (!other || other.other != this) throw new Exception("Other portal not set up correctly."); - - // get player camera - if (Camera.main != null) _playerCamera = Camera.main.GetComponent(); - else throw new Exception("Main camera has no player camera script set up."); - } - - private void OnEnable() - { - RenderPipelineManager.beginCameraRendering += Render; - } - - private void OnDisable() - { - RenderPipelineManager.beginCameraRendering -= Render; - } - - private void Render(ScriptableRenderContext scriptableRenderContext, Camera camera) - { - // check whether the portal plane is visible from the player camera - var frustumPlanes = GeometryUtility.CalculateFrustumPlanes(_playerCamera.camera); - if (!GeometryUtility.TestPlanesAABB(frustumPlanes, other.portalQuad.bounds)) - // don't render this portal if it is not visible - return; - - var t = transform; - - foreach (var eye in Eyes) - { - // create portal texture if it doesn't exist yet - if (!_textures.ContainsKey(eye)) - { - if (XRSettings.eyeTextureWidth > 0 && XRSettings.eyeTextureHeight > 0) - { - _textures.Add(eye, - new RenderTexture(XRSettings.eyeTextureWidth, XRSettings.eyeTextureHeight, 24)); - other.portalQuad.material.SetTexture(EyeTextureNames[eye], _textures[eye]); - } - else // no texture was created so nothing should be rendered - continue; - } - - // position portal camera - var m = t.localToWorldMatrix * HalfRotation * other.transform.worldToLocalMatrix * - _playerCamera.getEyeTransform(eye).localToWorldMatrix; - portalCamera.transform.SetPositionAndRotation(m.GetPosition(), m.rotation); - portalCamera.projectionMatrix = _playerCamera.camera.GetStereoProjectionMatrix(eye); - - // set camera clip plane to portal (otherwise the wall behind the portal would be rendered) - // calculating the clip plane: https://computergraphics.stackexchange.com/a/1506 - var n = -t.forward; // clip plane normal - var portalPlane = new Plane(n, t.position); // clip plane in world space - var clipPlane = portalCamera.worldToCameraMatrix.inverse.transpose * - new Vector4(n.x, n.y, n.z, portalPlane.distance); // vector format clip plane in camera space - if (Math.Abs(portalPlane.GetDistanceToPoint(portalCamera.transform.position)) >= minNearClipPlane) - // only adjust the near clip plane if it doesn't intersect with the camera - portalCamera.projectionMatrix = portalCamera.CalculateObliqueMatrix(clipPlane); - - // render portal view - portalCamera.targetTexture = _textures[eye]; - UniversalRenderPipeline.RenderSingleCamera(scriptableRenderContext, portalCamera); - } } } } diff --git a/Assets/Escape Room Engine/Portal/Portal.prefab b/Assets/Escape Room Engine/Portal/Portal.prefab index 8cd10b4..ab5a902 100644 --- a/Assets/Escape Room Engine/Portal/Portal.prefab +++ b/Assets/Escape Room Engine/Portal/Portal.prefab @@ -48,8 +48,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: other: {fileID: 0} - portalCamera: {fileID: 7791795762741173939} - portalQuad: {fileID: 6216133567950691622} + portalCamera: {fileID: 17691322601746172} --- !u!1 &6911937082098131204 GameObject: m_ObjectHideFlags: 0 @@ -144,6 +143,7 @@ GameObject: - component: {fileID: 2398425302420252226} - component: {fileID: 7791795762741173939} - component: {fileID: 5969531196797302096} + - component: {fileID: 17691322601746172} m_Layer: 0 m_Name: Camera m_TagString: Untagged @@ -242,6 +242,21 @@ MonoBehaviour: m_RequiresDepthTexture: 0 m_RequiresColorTexture: 0 m_Version: 2 +--- !u!114 &17691322601746172 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7398326895463990628} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ad60e3973ab83f3468637a06970d7f1f, type: 3} + m_Name: + m_EditorClassIdentifier: + minNearClipPlane: 0.02 + portal: {fileID: 1249363658} + portalQuad: {fileID: 6216133567950691622} --- !u!1001 &1842852527293547269 PrefabInstance: m_ObjectHideFlags: 0 diff --git a/Assets/Escape Room Engine/Portal/PortalCamera.cs b/Assets/Escape Room Engine/Portal/PortalCamera.cs new file mode 100644 index 0000000..7a3c53a --- /dev/null +++ b/Assets/Escape Room Engine/Portal/PortalCamera.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Rendering.Universal; +using UnityEngine.XR; + +namespace Escape_Room_Engine.Portal +{ + [RequireComponent(typeof(Camera))] + public class PortalCamera : MonoBehaviour + { + private static readonly Matrix4x4 HalfRotation = Matrix4x4.Rotate(Quaternion.Euler(0, 180, 0)); + private static readonly Camera.StereoscopicEye[] Eyes = + { Camera.StereoscopicEye.Left, Camera.StereoscopicEye.Right }; + private static readonly Dictionary EyeTextureNames = new() + { + { Camera.StereoscopicEye.Left, Shader.PropertyToID("_LeftTex") }, + { Camera.StereoscopicEye.Right, Shader.PropertyToID("_RightTex") } + }; + + /// + /// The minimum near clip plane distance to be used when calculating the oblique clip plane. + /// + public float minNearClipPlane = 0.02f; + /// + /// The portal this camera renders through. + /// + [SerializeField] private Portal portal; + /// + /// The quad where the rendered texture will be drawn on. + /// + [SerializeField] private MeshRenderer portalQuad; + + private PlayerCamera _playerCamera; + private Camera _camera; + private readonly Dictionary _textures = new(); + + private void Awake() + { + // get player camera + if (Camera.main != null) _playerCamera = Camera.main.GetComponent(); + else throw new Exception("Main camera has no player camera script set up."); + + // get portal camera + _camera = GetComponent(); + } + + private void OnEnable() + { + RenderPipelineManager.beginCameraRendering += Render; + } + + private void OnDisable() + { + RenderPipelineManager.beginCameraRendering -= Render; + } + + private void Render(ScriptableRenderContext scriptableRenderContext, Camera camera) + { + // check whether the portal plane is visible from the player camera + var frustumPlanes = GeometryUtility.CalculateFrustumPlanes(_playerCamera.camera); + if (!GeometryUtility.TestPlanesAABB(frustumPlanes, portal.other.portalCamera.portalQuad.bounds)) + // don't render this portal if it is not visible + return; + + var t = portal.transform; + + foreach (var eye in Eyes) + { + // create portal texture if it doesn't exist yet + if (!_textures.ContainsKey(eye)) + { + if (XRSettings.eyeTextureWidth > 0 && XRSettings.eyeTextureHeight > 0) + { + _textures.Add(eye, + new RenderTexture(XRSettings.eyeTextureWidth, XRSettings.eyeTextureHeight, 24)); + portal.other.portalCamera.portalQuad.material.SetTexture(EyeTextureNames[eye], _textures[eye]); + } + else // no texture was created so nothing should be rendered + continue; + } + + // position portal camera + var m = t.localToWorldMatrix * HalfRotation * portal.other.transform.worldToLocalMatrix * + _playerCamera.getEyeTransform(eye).localToWorldMatrix; + transform.SetPositionAndRotation(m.GetPosition(), m.rotation); + _camera.projectionMatrix = _playerCamera.camera.GetStereoProjectionMatrix(eye); + + // set camera clip plane to portal (otherwise the wall behind the portal would be rendered) + // calculating the clip plane: https://computergraphics.stackexchange.com/a/1506 + var n = -t.forward; // clip plane normal + var portalPlane = new Plane(n, t.position); // clip plane in world space + var clipPlane = _camera.worldToCameraMatrix.inverse.transpose * + new Vector4(n.x, n.y, n.z, portalPlane.distance); // vector format clip plane in camera space + if (Math.Abs(portalPlane.GetDistanceToPoint(transform.position)) >= minNearClipPlane) + // only adjust the near clip plane if it doesn't intersect with the camera + _camera.projectionMatrix = _camera.CalculateObliqueMatrix(clipPlane); + + // render portal view + _camera.targetTexture = _textures[eye]; + UniversalRenderPipeline.RenderSingleCamera(scriptableRenderContext, _camera); + } + } + } +} diff --git a/Assets/Escape Room Engine/Portal/PortalCamera.cs.meta b/Assets/Escape Room Engine/Portal/PortalCamera.cs.meta new file mode 100644 index 0000000..22788f5 --- /dev/null +++ b/Assets/Escape Room Engine/Portal/PortalCamera.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ad60e3973ab83f3468637a06970d7f1f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: