UnityTips 之 矩阵反射
2023-03-20 本文已影响0人
暴走TA
简介: 需要一个 URP 的镜面反射脚本,从网上抄了一个,做了下调整,有些坑点改了一下
来源 https://zhuanlan.zhihu.com/p/559002264
unity版本:20222.1.7fc1
[ExecuteAlways]
public class Reflect: MonoBehaviour
{
public Vector2Int TexSize = new Vector2Int(960, 540);
Camera _reflectionCamera;
RenderTexture _reflectionRT;
private void OnEnable()
{
//生成相机并设置相关rt
var CameraObj = new GameObject("myCamera");
_reflectionCamera = CameraObj.AddComponent<Camera>();
_reflectionCamera.name = "__ReflectCamera__";
_reflectionCamera.enabled = false;
if (_reflectionRT == null)
{
_reflectionRT = RenderTexture.GetTemporary(TexSize.x, TexSize.y, 0);
_reflectionRT.name = "__reflectRT__";
_reflectionRT.depth = 16;
_reflectionRT.useMipMap = true;
_reflectionRT.filterMode = FilterMode.Trilinear;
}
_reflectionCamera.targetTexture = _reflectionRT;
Shader.SetGlobalTexture("_ReflectionTex", _reflectionRT);
//添加相机渲染事件
RenderPipelineManager.beginCameraRendering += UpdateCamera;
}
private void OnDisable()
{
//取消相机渲染事件
RenderPipelineManager.beginCameraRendering -= UpdateCamera;
//删除相机物体
if (_reflectionCamera != null)
{
DestroyImmediate(_reflectionCamera.gameObject);
}
//释放RT 记得置空
if (_reflectionRT != null)
{
RenderTexture.ReleaseTemporary(_reflectionRT);
_reflectionRT =null;
}
}
private void UpdateCamera(ScriptableRenderContext src, Camera camera)
{
if (_reflectionCamera == null)
return;
var isSceneCam = false;//考虑场景相机的渲染
#if UNITY_EDITOR
isSceneCam = camera == SceneView.lastActiveSceneView.camera;
#endif
if (camera == Camera.main||isSceneCam)//之渲染主相机和场景相机
{
GL.invertCulling = true; //反转矩阵会反转面剔除~~
CameraSetup(camera);
UniversalRenderPipeline.RenderSingleCamera(src, _reflectionCamera);
GL.invertCulling = false;//渲染完设置回来
}
}
//设置相机信息
private void CameraSetup(Camera viewCamera)
{
//设置相机基础信息
_reflectionCamera.aspect = viewCamera.aspect;//设定一样的长宽比
_reflectionCamera.fieldOfView = viewCamera.fieldOfView;//设置一样的fov 其他需要的设置也可以一样设置过来
var reflectM = CaculateReflectMatrix();//计算反射矩阵
//根据参照相机设置其镜像矩阵给反射相机,用来做镜像渲染
_reflectionCamera.worldToCameraMatrix = viewCamera.worldToCameraMatrix * reflectM;
//计算反射平面和裁剪矩阵, 用来裁剪对称方向的模型
var normal = transform.up;
var d = -Vector3.Dot(normal, transform.position);
var plane = new Vector4(normal.x, normal.y, normal.z, d);
var viewSpacePlane = _reflectionCamera.worldToCameraMatrix.inverse.transpose * plane;
var clipMatrix = _reflectionCamera.CalculateObliqueMatrix(viewSpacePlane);
_reflectionCamera.projectionMatrix = clipMatrix;
}
#if UNITY_EDITOR
//设置相机的镜像位置,对渲染没啥实际影响,因为反射 相机的矩阵已经被设置过了
private void Update()
{
Vector3 viewerPos = viewCamera.transform.position;
float reflectHeight = transform.position.y;
reflectionCamera.transform.position = new Vector3(viewerPos.x, viewerPos.y - 2 * (viewerPos.y - reflectHeight), viewerPos.z);
reflectionCamera.transform.eulerAngles = new Vector3(-viewCamera.transform.eulerAngles.x, viewCamera.transform.eulerAngles.y, viewCamera.transform.eulerAngles.z);
}
#endif
//本人不会推导这玩意,这个就是计算反射矩阵用的,网上铺天盖地的都是,直接复制
Matrix4x4 CaculateReflectMatrix()
{
var normal = transform.up;
var d = -Vector3.Dot(normal, transform.position);
var reflectM = new Matrix4x4();
reflectM.m00 = 1 - 2 * normal.x * normal.x;
reflectM.m01 = -2 * normal.x * normal.y;
reflectM.m02 = -2 * normal.x * normal.z;
reflectM.m03 = -2 * d * normal.x;
reflectM.m10 = -2 * normal.x * normal.y;
reflectM.m11 = 1 - 2 * normal.y * normal.y;
reflectM.m12 = -2 * normal.y * normal.z;
reflectM.m13 = -2 * d * normal.y;
reflectM.m20 = -2 * normal.x * normal.z;
reflectM.m21 = -2 * normal.y * normal.z;
reflectM.m22 = 1 - 2 * normal.z * normal.z;
reflectM.m23 = -2 * d * normal.z;
reflectM.m30 = 0;
reflectM.m31 = 0;
reflectM.m32 = 0;
reflectM.m33 = 1;
return reflectM;
}
}