https://www.bilibili.com/video/BV1TA4y1D7s4/?spm_id_from=333.999.0.0&vd_source=5672eeb8a9721da01495e313f0dff6c2
https://zhuanlan.zhihu.com/p/493766119
PlannarReflectionTest.cs 挂载在平面对象上
// https://www.bilibili.com/video/BV1TA4y1D7s4/?spm_id_from=333.999.0.0&vd_source=5672eeb8a9721da01495e313f0dff6c2
// https://zhuanlan.zhihu.com/p/493766119
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class PlannarReflectionTest : MonoBehaviour {
public LayerMask reflectMask = -1;
[SerializeField] private MeshRenderer m_targetPlaneRenderer;
[SerializeField] private int m_targetTextureWidth = 1024;
[SerializeField] private string m_reflectTexName = "_MirrorReflectTex";
[SerializeField] private float m_scale = 1.0f;
[HideInInspector] private float m_clipPlaneOffset = 0.01f;
private Camera m_cameraMain;
private Transform m_cameraMainTransform;
private Camera m_cameraReflection;
private Transform m_cameraReflectionTransform;
private RenderTexture m_targetTexture;
private int m_reflectTexInt;
private Vector3 m_planeNormal;
private Vector3 m_planePosition;
private Transform m_normalTransform;
private Vector4 m_reflectionPlane;
private void Awake() {
m_cameraMain = Camera.main;
m_cameraMainTransform = m_cameraMain.transform;
var go = new GameObject("ReflectionCamera");
m_cameraReflection = go.AddComponent<Camera>();
m_cameraReflection.aspect = m_cameraMain.aspect;
m_cameraReflection.fieldOfView = m_cameraMain.fieldOfView;
m_cameraReflection.enabled = false;
m_cameraReflection.depth = -10;
//go.hideFlags = HideFlags.HideAndDontSave;
var cameraData = go.AddComponent<UniversalAdditionalCameraData>();
cameraData.requiresColorOption = CameraOverrideOption.Off;
cameraData.requiresDepthOption = CameraOverrideOption.Off;
cameraData.SetRenderer(0);
m_cameraReflectionTransform = m_cameraReflection.transform;
int newWidth = (int)(m_targetTextureWidth * m_scale);
int newHeight = (int)(newWidth * ((float)Screen.height / Screen.width));
m_targetTexture = new RenderTexture(newWidth, newHeight, 24);
//m_targetTexture.antiAliasing = 4;
m_targetTexture.format = RenderTextureFormat.ARGB32;
m_cameraReflection.targetTexture = m_targetTexture;
m_cameraReflection.cullingMask = reflectMask.value;
m_normalTransform = new GameObject("Normal").transform;
var planeTransform = m_targetPlaneRenderer.transform;
m_normalTransform.SetPositionAndRotation(planeTransform.position, planeTransform.rotation);
m_normalTransform.SetParent(planeTransform);
m_planePosition = m_normalTransform.position;
m_planeNormal = m_normalTransform.up;
m_cameraReflection.transform.SetParent(m_normalTransform);
m_reflectTexInt = Shader.PropertyToID(m_reflectTexName);
Shader.SetGlobalTexture(m_reflectTexName, m_targetTexture);
}
private void OnEnable() {
RenderPipelineManager.beginCameraRendering += OnBeginCameraRendering;
}
private void OnDisable() {
RenderPipelineManager.beginCameraRendering -= OnBeginCameraRendering;
}
private static void CalculateReflectionMatrix(ref Matrix4x4 reflectionMat, ref Vector4 plane) {
reflectionMat.m00 = (1F - 2F * plane[0] * plane[0]);
reflectionMat.m01 = (-2F * plane[0] * plane[1]);
reflectionMat.m02 = (-2F * plane[0] * plane[2]);
reflectionMat.m03 = (-2F * plane[3] * plane[0]);
reflectionMat.m10 = (-2F * plane[1] * plane[0]);
reflectionMat.m11 = (1F - 2F * plane[1] * plane[1]);
reflectionMat.m12 = (-2F * plane[1] * plane[2]);
reflectionMat.m13 = (-2F * plane[3] * plane[1]);
reflectionMat.m20 = (-2F * plane[2] * plane[0]);
reflectionMat.m21 = (-2F * plane[2] * plane[1]);
reflectionMat.m22 = (1F - 2F * plane[2] * plane[2]);
reflectionMat.m23 = (-2F * plane[3] * plane[2]);
reflectionMat.m30 = 0F;
reflectionMat.m31 = 0F;
reflectionMat.m32 = 0F;
reflectionMat.m33 = 1F;
}
private void OnBeginCameraRendering(ScriptableRenderContext context, Camera camera) {
if (camera.cameraType == CameraType.Reflection || camera.cameraType == CameraType.Preview) return;
// 判断相机是不是在法线下方,如果在下方就不做渲染了
Vector3 localPos = m_normalTransform.worldToLocalMatrix.MultiplyPoint3x4(m_cameraMainTransform.position);
if (localPos.y < 0) return;
// 调整位置
// 首先计算反射矩阵
// 法线
Vector3 normal = m_normalTransform.up;
// 平面上一个点的位置
Vector3 pos = m_normalTransform.position;
// 获取反射面
float d = -Vector3.Dot(normal, pos) - m_clipPlaneOffset;
m_reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d);
var reflection = Matrix4x4.identity;
reflection *= Matrix4x4.Scale(new Vector3(1, -1, 1));
// 计算反射矩阵
CalculateReflectionMatrix(ref reflection, ref m_reflectionPlane);
// 直接计算世界到相机矩阵
Matrix4x4 worldToCameraMatrix = m_cameraMain.worldToCameraMatrix * reflection;
m_cameraReflection.worldToCameraMatrix = worldToCameraMatrix;
// m_cameraReflectionTransform.eulerAngles = m_cameraMainTransform.eulerAngles;
// Vector3 localEuler = m_cameraReflectionTransform.localEulerAngles;
// localEuler.x *= -1;
// localEuler.z *= -1;
// localEuler.y *= -1;
// m_cameraReflectionTransform.localEulerAngles = localEuler;
// m_cameraReflectionTransform.localPosition = localPos;
// 计算相机空间下的斜切平面
Vector3 offsetPos = pos + normal * m_clipPlaneOffset;
Vector3 cpos = worldToCameraMatrix.MultiplyPoint3x4(offsetPos);
Vector3 cnormal = worldToCameraMatrix.MultiplyVector(normal).normalized;
// 通过斜切面算投影矩阵
Vector4 clipPlane = new Vector4(cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos, cnormal));
m_cameraReflection.projectionMatrix = m_cameraMain.CalculateObliqueMatrix(clipPlane);
//m_cameraReflection.projectionMatrix = CalculateObliqueMatrix(m_cameraMain, clipPlane);
GL.invertCulling = true;
UniversalRenderPipeline.RenderSingleCamera(context, m_cameraReflection);
GL.invertCulling = false;
Shader.SetGlobalTexture(m_reflectTexName, m_targetTexture);
}
// private Matrix4x4 CalculateObliqueMatrix(Camera cam, Vector4 clipPlane) {
// Matrix4x4 projectionMatrix= cam.projectionMatrix;
// Vector4 qClip = new Vector4(Mathf.Sign(clipPlane.x), Mathf.Sign(clipPlane.y), 1f, 1f);
// Vector4 qView = projectionMatrix.inverse.MultiplyPoint(qClip);
//
// Vector4 scaledPlane = clipPlane * 2.0f / Vector4.Dot(clipPlane, qView);
//
// Vector4 m3 = scaledPlane - projectionMatrix.GetRow(3);
//
// Matrix4x4 newM = projectionMatrix;
// newM.SetRow(2, m3);
//
// return newM;
// }
}
平面对象使用的材质的ShaderGraph(Unity 2019.4.28f1c1):