using System.Collections; using System.Collections.Generic; using UnityEngine; // This class manages the rendering effects of portals. // !!! Caution: Modifying this code without proper understanding may lead to unintended consequences. !!! public class WorkingPortal : MonoBehaviour { // The portal that this portal is linked to; the one whose view is seen when looking through this portal. public WorkingPortal linkedWorkingPortal; // The mesh renderer that renders the portal. public MeshRenderer screen; // Determines if the portal is visible from the camera. public bool visibleFromCamera = true; public GameObject myGameObject; // The render texture that this portal's camera renders to, providing the view when looking through the linked portal. public RenderTexture viewTexture; // The camera used for rendering this portal. public Camera WorkingPortalCam; // Offset values for adjusting the near clipping plane. float nearClipOffset = 0.05f; float nearClipLimit = 0.2f; // Reference to the player's camera. Camera playerCam; // Mesh filter of the portal's screen. MeshFilter screenMeshFilter; void Awake () { // Set reference to this GameObject. myGameObject = gameObject; // Disable camera rendering by default; rendering is triggered manually via the Render() function. WorkingPortalCam.enabled = false; // Retrieve the mesh filter component of the portal screen. screenMeshFilter = screen.GetComponent (); // Set the display mask to render solid color instead of the camera view. screen.material.SetInt ("displayMask", 1); } // Renders the view through this portal. public void Render (Camera newplayerCam) { if (!visibleFromCamera) { return; } playerCam = newplayerCam; CreateViewTexture (); // This section aligns the portal camera with the player's view and renders only the portion visible through the portal. var localToWorldMatrix = playerCam.transform.localToWorldMatrix; var renderPosition = new Vector3(); var renderRotation = new Quaternion(); WorkingPortalCam.projectionMatrix = playerCam.projectionMatrix; localToWorldMatrix = transform.localToWorldMatrix * linkedWorkingPortal.transform.worldToLocalMatrix * localToWorldMatrix; renderPosition = localToWorldMatrix.GetColumn (3); renderRotation = localToWorldMatrix.rotation; // Hide the portal screen to allow camera to see through it. screen.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.ShadowsOnly; linkedWorkingPortal.screen.material.SetInt ("displayMask", 1); WorkingPortalCam.transform.SetPositionAndRotation (renderPosition, renderRotation); SetNearClipPlane (); WorkingPortalCam.Render (); // Revert hiding objects that were hidden at the start of rendering. screen.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On; } void CreateViewTexture () { if (viewTexture == null || viewTexture.width != Screen.width || viewTexture.height != Screen.height) { if (viewTexture != null) { viewTexture.Release (); } viewTexture = new RenderTexture (Screen.width, Screen.height, 0); // Render the view from the portal camera to the view texture. WorkingPortalCam.targetTexture = viewTexture; // Display the view texture on the screen of the linked portal. linkedWorkingPortal.screen.material.SetTexture ("_MainTex", viewTexture); } } // Adjusts the near clipping plane to align with the surface of the portal. void SetNearClipPlane () { Transform clipPlane = transform; int dot = System.Math.Sign (Vector3.Dot (clipPlane.forward, transform.position - WorkingPortalCam.transform.position)); Vector3 camSpacePos = WorkingPortalCam.worldToCameraMatrix.MultiplyPoint (clipPlane.position); Vector3 camSpaceNormal = WorkingPortalCam.worldToCameraMatrix.MultiplyVector (clipPlane.forward) * dot; float camSpaceDst = -Vector3.Dot (camSpacePos, camSpaceNormal) + nearClipOffset; // Don't use oblique clip plane if very close to the portal as it may cause visual artifacts. if (Mathf.Abs (camSpaceDst) > nearClipLimit) { Vector4 clipPlaneCameraSpace = new Vector4 (camSpaceNormal.x, camSpaceNormal.y, camSpaceNormal.z, camSpaceDst); // Update projection based on the new clip plane. // Calculate matrix with player cam to use its settings (e.g., fov). WorkingPortalCam.projectionMatrix = playerCam.CalculateObliqueMatrix (clipPlaneCameraSpace); } else { // Use default projection matrix if very close to the portal. WorkingPortalCam.projectionMatrix = playerCam.projectionMatrix; } } }