Unity3D之笛卡尔坐标系转换——屏幕坐标转换世界坐标,世界坐标转换相机坐标工具

2021-01-23 11:13

阅读:555

标签:summary   his   lse   mon   投影矩阵   需要   情况   ado   etc   

因为要做AR的标记功能,所以就要用到坐标的转换,就总结了一下屏幕坐标、世界坐标、相机坐标之间的转换。
首先说明的是Unity3D遵从Direct3D标准的左手笛卡尔坐标系变换规则。
也就是说:

世界坐标系就是左手笛卡尔坐标系(x,y,z),相机也是左手笛卡尔坐标系(u,v,w),且面向自身坐标系w坐标轴正上方,屏幕以中央为原点,右和上分别为x,y的正方向。

我将工具代码直接上:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraTransformationUtilities : MonoBehaviour
{
    public static CameraTransformationUtilities Instance { get; private set; }
    protected CameraTransformationUtilities() { }

    private void Start()
    {
        Instance = this;
    }

    /// 投影矩阵
    /// 视点矩阵
    /// 列表形式的投影矩阵
    /// 列表形式的视点矩阵
    /// 要投影的点的引用
    /// 要投影的点引用列表
    /// 屏幕宽
    /// 屏幕高
    /// 列表是否以行优先进行矩阵元素的存储
    /// 水平翻转
    /// 上下翻转

    public void UnprojectScreenToWorld(
        Matrix4x4 projectionMatrix, 
        Matrix4x4 viewMatrix, 
        ref Vector3 p)
    {
        // 世界坐标->相机坐标, 相机坐标->屏幕坐标 分别需要 viewMatrix和projectionMatrix
        Matrix4x4 PV = projectionMatrix * viewMatrix;

        // 逆矩阵即为从屏幕到世界的坐标变换,需指定像素深度
        p = PV.inverse.MultiplyPoint(p);
    }

    public void UnprojectScreenToWorld(
        IReadOnlyList projection,
        IReadOnlyList view,
        ref Vector3 p,
        bool rowBase = true
    )
    {
        Matrix4x4 projectionMatrix = ConstructMatirx(projection, rowBase);
        Matrix4x4 viewMatrix = ConstructMatirx(view, rowBase);
        UnprojectScreenToWorld(projectionMatrix, viewMatrix, ref p);
    }

    public void UnprojectScreenToWorld(
        IReadOnlyList projection,
        IReadOnlyList view,
        ref Vector3 p,
        float width,
        float height,
        bool rowBase = true,
        bool flipLeftToRight = false,
        bool flipUpsideDown = false
        )
    {
        Matrix4x4 projectionMatrix = ConstructMatirx(projection, rowBase);
        Matrix4x4 viewMatrix = ConstructMatirx(view, rowBase);
        ScreenSpaceTransform(ref p, width, height, flipLeftToRight, flipUpsideDown);
        UnprojectScreenToWorld(projectionMatrix, viewMatrix, ref p);
    }

    public void UnProjectScreenToWorld(
        Matrix4x4 projectionMatrix,
        Matrix4x4 viewMatrix,
        ref List pl
        )
    {
        Matrix4x4 PV = projectionMatrix * viewMatrix;
        for (int i = 0; i  projection,
        IReadOnlyList view, 
        ref List pl, 
        float width, 
        float height, 
        bool rowBase = true,
        bool flipLeftToRight = false,
        bool flipUpsideDown = false
        )
    {
        Matrix4x4 projectionMatrix = ConstructMatirx(projection, rowBase);
        Matrix4x4 viewMatrix = ConstructMatirx(view, rowBase);
        Matrix4x4 PV = projectionMatrix * viewMatrix;
        ScreenSpaceTransform(ref pl, width, height, flipLeftToRight, flipUpsideDown);
        for(int i = 0; i 
    /// 获得相机的世界坐标,遵从 Direct3D 标准的左手笛卡尔坐标系。
    /// 
    /// 视点矩阵
    /// 
    public Vector3 GetCameraPosition(Matrix4x4 viewMatrix)
    {
        Matrix4x4 invV = viewMatrix.inverse;
        return new Vector3(invV.m30, invV.m31, invV.m32);
    }

    /// 
    /// 获得相机的世界坐标,遵从 Direct3D 标准的左手笛卡尔坐标系。
    /// 
    /// 视点矩阵元素列表
    /// 列表是否以行优先进行矩阵元素的存储
    /// 
    public Vector3 GetCameraPosition(IReadOnlyList view, bool rowBase = true)
    {
        Matrix4x4 viewMatrix = ConstructMatirx(view, rowBase);
        Matrix4x4 invV = viewMatrix.inverse;
        return new Vector3(invV.m30, invV.m31, invV.m32);
    }

    /// 
    /// 获得相机的视线方向,遵从 Direct3D 标准的左手笛卡尔坐标系。
    /// 
    /// 视点矩阵
    /// 
    public Vector3 GetCameraViewDirection(Matrix4x4 viewMatrix)
    {
        Matrix4x4 invV = viewMatrix.inverse;
        return new Vector3(invV.m20, invV.m21, invV.m22);
    }

    /// 
    /// 获得相机的视线方向,遵从 Direct3D 标准的左手笛卡尔坐标系。
    /// 
    /// 视点矩阵元素列表
    /// 列表是否以行优先进行矩阵元素的存储
    /// 
    public Vector3 GetCameraViewDirection(IReadOnlyList view, bool rowBase = true)
    {
        Matrix4x4 viewMatrix = ConstructMatirx(view, rowBase);
        Matrix4x4 invV = viewMatrix.inverse;
        return new Vector3(invV.m20, invV.m21, invV.m22);
    }

    /// 
    /// 将屏幕点坐标转换成以屏幕中心为原点,
    /// 右和上分别为x,y正方向,
    /// -1到1的坐标
    /// 
    /// 屏幕宽或高
    /// 像素坐标(指定宽则为横坐标,指定高则为纵坐标)
    /// 是否翻转(Unity通常情况下会将纵坐标翻转)
    /// 
    public void ScreenSpaceTransform(ref Vector3 p, float width, float height, bool flipLeftToRight = false, bool flipUpSideDown = false)
    {
        p.Set((p.x / width * 2 - 1) * (flipLeftToRight ? -1 : 1),
                    (p.y / height * 2 - 1) * (flipUpSideDown ? -1 : 1),
                    p.z);
    }

    public void ScreenSpaceTransform(ref List pl, float width, float height, bool flipLeftToRight = false, bool flipUpSideDown = false)
    {
        for(int i = 0; i
    /// 从列表中构造Matirx4x4矩阵
    /// 
    /// 矩阵元素列表
    /// 列表是否以行优先进行矩阵元素的存储
    /// 
    public Matrix4x4 ConstructMatirx(IReadOnlyList elements, bool rowBase = true)
    {
        if (elements.Count != 16) return Matrix4x4.identity;
        Matrix4x4 mat = new Matrix4x4();
        if (rowBase)
        {
            mat.m00 = elements[0];
            mat.m01 = elements[1];
            mat.m02 = elements[2];
            mat.m03 = elements[3];
            mat.m10 = elements[4];
            mat.m11 = elements[5];
            mat.m12 = elements[6];
            mat.m13 = elements[7];
            mat.m20 = elements[8];
            mat.m21 = elements[9];
            mat.m22 = elements[10];
            mat.m23 = elements[11];
            mat.m30 = elements[12];
            mat.m31 = elements[13];
            mat.m32 = elements[14];
            mat.m33 = elements[15];
        }
        else
        {
            mat.m00 = elements[0];
            mat.m10 = elements[1];
            mat.m20 = elements[2];
            mat.m30 = elements[3];
            mat.m01 = elements[4];
            mat.m11 = elements[5];
            mat.m21 = elements[6];
            mat.m31 = elements[7];
            mat.m02 = elements[8];
            mat.m12 = elements[9];
            mat.m22 = elements[10];
            mat.m32 = elements[11];
            mat.m03 = elements[12];
            mat.m11 = elements[13];
            mat.m23 = elements[14];
            mat.m33 = elements[15];
        }
        return mat;
    }
}

坐标的转换,用的地方特别多,尤其是屏幕划线展示在世界坐标中,我把他形成了工具,直接可以用。

Unity3D之笛卡尔坐标系转换——屏幕坐标转换世界坐标,世界坐标转换相机坐标工具

标签:summary   his   lse   mon   投影矩阵   需要   情况   ado   etc   

原文地址:https://blog.51cto.com/myselfdream/2494850


评论


亲,登录后才可以留言!