using System; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; namespace Escape_Room_Engine.Portal { public class Portal : MonoBehaviour { private static readonly Matrix4x4 HalfRotation = Matrix4x4.Rotate(Quaternion.Euler(0, 180, 0)); /// /// The portal that is connected with this one. /// public Portal other; /// /// The main camera to take the position for the portal camera from. /// [SerializeField] private Camera playerCamera; /// /// 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 RenderTexture _texture; 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."); // check whether the player camera is set up if (!playerCamera) throw new Exception("No camera initialised."); // create render texture _texture = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.ARGB32); } private void Start() { // assign render texture portalCamera.targetTexture = _texture; other.portalQuad.material.mainTexture = _texture; } private void OnEnable() { RenderPipelineManager.beginCameraRendering += Render; } private void OnDisable() { RenderPipelineManager.beginCameraRendering -= Render; } private void Render(ScriptableRenderContext scriptableRenderContext, Camera camera) { var t = transform; // position portal camera var portalCameraMatrix = t.localToWorldMatrix * HalfRotation * other.transform.worldToLocalMatrix * playerCamera.transform.localToWorldMatrix; portalCamera.transform.SetPositionAndRotation(portalCameraMatrix.GetPosition(), portalCameraMatrix.rotation); // 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 portalCamera.projectionMatrix = playerCamera.CalculateObliqueMatrix(clipPlane); // render portal view UniversalRenderPipeline.RenderSingleCamera(scriptableRenderContext, portalCamera); } } }