using System.Collections; using System.Collections.Generic; using UnityEngine; public class WorkingPortal : MonoBehaviour { [Header ("Main Settings")] public WorkingPortal linkedWorkingPortal; public MeshRenderer screen; public int recursionLimit = 5; public bool visibleFromCamera = true; [Header ("Advanced Settings")] public float nearClipOffset = 0.05f; public float nearClipLimit = 0.2f; public GameObject myGameObject; // Private variables public RenderTexture viewTexture; public Camera WorkingPortalCam; Camera playerCam; Material firstRecursionMat; MeshFilter screenMeshFilter; void Awake () { myGameObject = gameObject; WorkingPortalCam.enabled = false; screenMeshFilter = screen.GetComponent (); screen.material.SetInt ("displayMask", 1); } // Manually render the camera attached to this WorkingPortal // Called after PreWorkingPortalRender, and before PostWorkingPortalRender public void Render (Camera newplayerCam) { playerCam = newplayerCam; // Skip rendering the view from this WorkingPortal if player is not looking at the linked WorkingPortal if (!visibleFromCamera) { return; } CreateViewTexture (); var localToWorldMatrix = playerCam.transform.localToWorldMatrix; var renderPositions = new Vector3[recursionLimit]; var renderRotations = new Quaternion[recursionLimit]; int startIndex = 0; WorkingPortalCam.projectionMatrix = playerCam.projectionMatrix; for (int i = 0; i < recursionLimit; i++) { if (i > 0) { break; } localToWorldMatrix = transform.localToWorldMatrix * linkedWorkingPortal.transform.worldToLocalMatrix * localToWorldMatrix; int renderOrderIndex = recursionLimit - i - 1; renderPositions[renderOrderIndex] = localToWorldMatrix.GetColumn (3); renderRotations[renderOrderIndex] = localToWorldMatrix.rotation; WorkingPortalCam.transform.SetPositionAndRotation (renderPositions[renderOrderIndex], renderRotations[renderOrderIndex]); startIndex = renderOrderIndex; } // Hide screen so that camera can see through WorkingPortal screen.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.ShadowsOnly; linkedWorkingPortal.screen.material.SetInt ("displayMask", 0); for (int i = startIndex; i < recursionLimit; i++) { WorkingPortalCam.transform.SetPositionAndRotation (renderPositions[i], renderRotations[i]); Debug.Log(string.Format("rendering at: ", renderPositions[i].x, renderPositions[i].y, renderPositions[i].z)); SetNearClipPlane (); WorkingPortalCam.Render (); if (i == startIndex) { linkedWorkingPortal.screen.material.SetInt ("displayMask", 1); } } // Unhide objects hidden at start of render 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 WorkingPortal camera to the view texture WorkingPortalCam.targetTexture = viewTexture; // Display the view texture on the screen of the linked WorkingPortal linkedWorkingPortal.screen.material.SetTexture ("_MainTex", viewTexture); } } // Use custom projection matrix to align WorkingPortal camera's near clip plane with the surface of the WorkingPortal // Note that this affects precision of the depth buffer, which can cause issues with effects like screenspace AO void SetNearClipPlane () { // Learning resource: // http://www.terathon.com/lengyel/Lengyel-Oblique.pdf 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 WorkingPortal as it seems this can cause some visual artifacts if (Mathf.Abs (camSpaceDst) > nearClipLimit) { Vector4 clipPlaneCameraSpace = new Vector4 (camSpaceNormal.x, camSpaceNormal.y, camSpaceNormal.z, camSpaceDst); // Update projection based on new clip plane // Calculate matrix with player cam so that player camera settings (fov, etc) are used WorkingPortalCam.projectionMatrix = playerCam.CalculateObliqueMatrix (clipPlaneCameraSpace); } else { WorkingPortalCam.projectionMatrix = playerCam.projectionMatrix; } } /* ** Some helper/convenience stuff: */ int SideOfWorkingPortal (Vector3 pos) { return System.Math.Sign (Vector3.Dot (pos - transform.position, transform.forward)); } bool SameSideOfWorkingPortal (Vector3 posA, Vector3 posB) { return SideOfWorkingPortal (posA) == SideOfWorkingPortal (posB); } Vector3 WorkingPortalCamPos { get { return WorkingPortalCam.transform.position; } } void OnValidate () { if (linkedWorkingPortal != null) { linkedWorkingPortal.linkedWorkingPortal = this; } } }