用于Blinn-Phong光照模型的半角向量可视化工具

发布时间 2023-05-23 00:30:56作者: yanghui01

效果图

 半角向量和法线重叠时,为最亮, 此时夹角为0, dot(normal, halfDir)=1

 

using UnityEngine;

[RequireComponent(typeof(MeshFilter))]
public class ShowHalfDirTool : MonoBehaviour
{

#if UNITY_EDITOR
    private static int s_InstIcr;
    private int m_InstId;

    public Transform m_Light; //光源
    public Transform m_Viewer; //观察点
    public float m_HalfDirLength = 2;
    public bool m_ShowLines = false;

    public int m_VertIndex; //442,490,497,494
    public Color m_LightDirColor = Color.red;
    public Color m_ViewDirColor = Color.green;
    public Color m_HalfDirColor = Color.blue;
    public Color m_NormalColor = Color.yellow;
    public float m_HalfDirNormalAngle;
    public float m_HalfDirNormalPow;

    private Mesh m_Mesh;
    private MeshFilter m_MF;
    private Vector3[] m_Verts;
    private Vector3[] m_Normals;

    public ShowHalfDirTool()
    {
        m_InstId = s_InstIcr++;
    }

    private void Awake()
    {
        if (null == m_Light.GetComponent<Light>())
            Debug.LogError("m_Light no Light Component");

        if (null == m_Viewer.GetComponent<Camera>())
            Debug.LogError("m_Viewer no Camera Component");

    }

    private void Update()
    {
        if (!m_ShowLines || null == m_Light || null == m_Viewer)
            return;

        if (0 == m_InstId && Input.GetKeyDown(KeyCode.Alpha1))
        {
            //与Viewr在同一个y-z平面的
            Debug.Log($"----- y-z");
            for (int i = 0; i < m_Mesh.vertexCount; i++)
            {
                var wPos = m_MF.transform.TransformPoint(m_Verts[i]);
                var viewDir = m_Viewer.position - wPos;
                if (Mathf.Abs(viewDir.x) <= 0.001f)
                    Debug.Log($"{i}");
            }
            Debug.Log($"-----");

            Debug.Log($"----- x-z");
            for (int i = 0; i < m_Mesh.vertexCount; i++)
            {
                var wPos = m_MF.transform.TransformPoint(m_Verts[i]);
                var viewDir = m_Viewer.position - wPos;
                if (Mathf.Abs(viewDir.y) <= 0.001f)
                    Debug.Log($"{i}");
            }
            Debug.Log($"-----");
        }
    }

    private void CheckMFInit()
    {
        if (null == m_MF)
        {
            m_MF = GetComponent<MeshFilter>();
            m_Mesh = m_MF.sharedMesh;
            m_Verts = m_Mesh.vertices;
            m_Normals = m_Mesh.normals;
        }
    }

    private void OnDrawGizmos()
    {
        if (!m_ShowLines || null == m_Light || null == m_Viewer)
            return;
        CheckMFInit();

        //Gizmos.matrix = m_MF.transform.localToWorldMatrix;
        //Gizmos.matrix = m_MF.transform.worldToLocalMatrix;
        var colorBak = Gizmos.color;
        int len = m_Mesh.vertexCount;

        for (int i = 0; i < len; i++)
        {
            if (m_VertIndex == i)
            {
                DrawLines(i);
            }
        }
        Gizmos.color = colorBak;
    }

    void DrawLines(int i)
    {
        var wPos = m_MF.transform.TransformPoint(m_Verts[i]);
        var normalDir = m_Normals[i];

        var lightDir = -m_Light.forward;
        lightDir.Normalize();
        var lightDirLinePt2 = wPos + lightDir;
        Gizmos.color = m_LightDirColor;
        Gizmos.DrawLine(wPos, lightDirLinePt2); //光线方向

        var viewDir = m_Viewer.position - wPos;
        viewDir.Normalize();
        var viewDirLinePt2 = wPos + viewDir;
        Gizmos.color = m_ViewDirColor;
        Gizmos.DrawLine(wPos, viewDirLinePt2); //视角方向

        Gizmos.color = Color.gray;
        Gizmos.DrawLine(viewDirLinePt2, viewDirLinePt2 + lightDir); //光线方向平行线
        Gizmos.DrawLine(lightDirLinePt2, lightDirLinePt2 + viewDir); //视角方向平行线

        var halfDir = lightDir + viewDir;
        halfDir.Normalize();
        Gizmos.color = m_HalfDirColor;
        Gizmos.DrawLine(wPos, wPos + halfDir * m_HalfDirLength); //半角方向

        Gizmos.color = m_NormalColor;
        Gizmos.DrawLine(wPos, wPos + normalDir); //法线

        m_HalfDirNormalAngle = Vector3.Angle(normalDir, halfDir);
        m_HalfDirNormalPow = Mathf.Pow(Mathf.Max(0, Vector3.Dot(normalDir, halfDir)), 10);
    }

#endif

}

 

关于视角对齐菜单

选中相机, 菜单 -> GameObject -> Align View to Selected, 把Scene View的视角正向设置成和所选物体forward相同

Align With View, 把所选物体的forward设置成和Scene View的视角正向相同

 

参考

Unity中显示法线 - 知乎 (zhihu.com)