VR portal rendering
This commit is contained in:
@@ -1,22 +1,31 @@
|
||||
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<Camera.StereoscopicEye, int> EyeTextureNames = new()
|
||||
{
|
||||
{ Camera.StereoscopicEye.Left, Shader.PropertyToID("_LeftTex") },
|
||||
{ Camera.StereoscopicEye.Right, Shader.PropertyToID("_RightTex") }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The portal that is connected with this one.
|
||||
/// </summary>
|
||||
public Portal other;
|
||||
/// <summary>
|
||||
/// The main camera to take the position for the portal camera from.
|
||||
/// The minimum near clip plane distance to be used when calculating the oblique clip plane.
|
||||
/// </summary>
|
||||
[SerializeField] private Camera playerCamera;
|
||||
public float minNearClipPlane = 0.02f;
|
||||
/// <summary>
|
||||
/// The camera that will draw the view for this portal.
|
||||
/// </summary>
|
||||
@@ -26,24 +35,17 @@ namespace Escape_Room_Engine.Portal
|
||||
/// </summary>
|
||||
[SerializeField] private MeshRenderer portalQuad;
|
||||
|
||||
private RenderTexture _texture;
|
||||
private PlayerCamera _playerCamera;
|
||||
private Dictionary<Camera.StereoscopicEye, RenderTexture> _textures = new();
|
||||
|
||||
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.");
|
||||
if (!other || other.other != this) throw new Exception("Other portal not set up correctly.");
|
||||
|
||||
// create render texture
|
||||
if (!_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;
|
||||
// get player camera
|
||||
if (Camera.main != null) _playerCamera = Camera.main.GetComponent<PlayerCamera>();
|
||||
else throw new Exception("Main camera has no player camera script set up.");
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
@@ -59,22 +61,42 @@ namespace Escape_Room_Engine.Portal
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user