📜  波浪的反射(1)

📅  最后修改于: 2023-12-03 15:40:44.295000             🧑  作者: Mango

波浪的反射

波浪的反射是物理学中非常基础的概念之一。当波遇到一个边界时,它的一部分会反射回来。在计算机科学中,使用这个概念可以实现各种效果,例如声波的反射,光线的反射以及图形渲染中的反射效果。

基本原理

波浪的反射可以使用两个基本公式进行计算。

reflection_formula

其中,θ1 是入射角,θ2 是反射角,n 是介质的折射率。

例如,在二维平面坐标系中,一条射线从点 A 开始,沿着方向向量 d,击中一个水平的平面。

reflection_example1

这个点的反射点是在与平面相对称的位置 B,其对称轴为平面的法向量 n。通过上述公式,可以计算出入射角为 θ1,反射角为 θ2。

应用

波浪的反射在图形渲染中受到广泛应用。当一个光源照射到一个表面时,它会被反射到观察者的方向。通过计算反射光线的角度和强度,可以实现非常逼真的渲染效果。

例如,在以下示例中,一个光源照射在一个镜面表面上。使用波浪的反射公式,可以计算出反射光线的方向和强度,从而得到这个逼真的反射效果。

reflection_example2

代码实现

下面是一个实现反射效果的示例代码,基于 Unity 引擎和 C# 语言。

public class Reflection : MonoBehaviour {
    
    public Transform reflector; // 反射器
    public LayerMask reflectLayers = -1; // 反射层
    
    private Camera cam;
    private Dictionary<Camera, Camera> m_ReflectionCameras = new Dictionary<Camera, Camera>();

    void Start() {
        cam = GetComponent<Camera>();
    }
    
    // 返回镜像矩阵
    private static Matrix4x4 CalculateReflectionMatrix(Matrix4x4 reflectionMat, 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;

        return reflectionMat;
    }

    // 渲染反射深度图
    Camera CreateReflectionCamera(Camera currentCam) {
        Camera reflectionCamera;
        if (!m_ReflectionCameras.TryGetValue(currentCam, out reflectionCamera)) {
            reflectionCamera = new GameObject("ReflectCamera" + GetInstanceID(), typeof(Camera), typeof(Skybox)).GetComponent<Camera>();
            reflectionCamera.targetTexture = new RenderTexture(Screen.width / 2, Screen.height / 2, 16);
            m_ReflectionCameras.Add(currentCam, reflectionCamera);
        }
        reflectionCamera.enabled = false;
        return reflectionCamera;
    }
    
    // 在反射器上生成反射物体
    void OnWillRenderObject() {
        Vector3 normal = reflector.transform.up;
        Vector4 reflPlane = new Vector4(normal.x, normal.y, normal.z, -Vector3.Dot(normal, reflector.position));
        Matrix4x4 reflMatrix = Matrix4x4.zero;
        reflMatrix = CalculateReflectionMatrix(reflMatrix, reflPlane);
        
        Vector3 oldpos = cam.transform.position;
        Vector3 newpos = reflMatrix.MultiplyPoint(oldpos);
        Camera reflectionCamera = CreateReflectionCamera(cam);

        reflectionCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflMatrix;

        float clipPlaneDistance = Vector3.Dot(normal, reflector.position) + 0.01f;
        Vector4 clipPlane = new Vector4(normal.x, normal.y, normal.z, -clipPlaneDistance);

        Matrix4x4 projection = cam.projectionMatrix;
        projection = CalculateObliqueMatrix(projection, clipPlane);
        reflectionCamera.projectionMatrix = projection;

        GL.invertCulling = true;
        
        reflectionCamera.targetTexture.DiscardContents();
        reflectionCamera.Render();
        
        GL.invertCulling = false;
        Material[] materials = GetComponent<Renderer>().sharedMaterials;
        foreach (Material mat in materials) {
            if (mat.HasProperty("_ReflectionTex")) {
                mat.SetTexture("_ReflectionTex", reflectionCamera.targetTexture);
            }
        }
        cam.transform.position = oldpos;
    }
}

这个代码片段实现了在反射器上生成反射物体的效果。其基本思路是,先计算出反射平面,然后将相机的位置和角度镜像到反射平面上,生成一个反射深度图,然后使用反射深度图为材质添加反射纹理。