kingBook

导航

Unity URP 平面反射

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):
image

posted on 2023-03-01 23:48  kingBook  阅读(174)  评论(0编辑  收藏  举报