美文网首页
2021-12-27【Math】MVP无相机计算矩阵屏幕坐标转换

2021-12-27【Math】MVP无相机计算矩阵屏幕坐标转换

作者: 持刀的要迟到了 | 来源:发表于2021-12-27 18:46 被阅读0次

今天要完成一个计算,实现unity的,一个物体的世界坐标转换到相机视口坐标,然后旋转相机,再把视口坐标转换为世界坐标,把物体放到对应的世界坐标。
最终达到的效果,在game视窗下,物体的屏幕坐标位置始终没变。

第三课:矩阵 (opengl-tutorial.org)
矩阵的旋转,缩放,平移
Pnew = MtranslationMrotationMscale*Pold
先缩放,再旋转,最后平移

第一步是先计算出Mview矩阵。
就是相机相对于世界的矩阵,再取反。
How do I calculate a view matrix using Matrix4x4.LookAt? - Unity Answers

        var viewMatrix = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one).inverse;
        if (SystemInfo.usesReversedZBuffer)
        {
            viewMatrix.m20 = -viewMatrix.m20;
            viewMatrix.m21 = -viewMatrix.m21;
            viewMatrix.m22 = -viewMatrix.m22;
            viewMatrix.m23 = -viewMatrix.m23;
        }

        Debug.Log("=========================");
        Debug.Log(cam.cameraToWorldMatrix);
        Debug.Log(cam.worldToCameraMatrix);
        Debug.Log(viewMatrix);
        Debug.Log(viewMatrix.inverse);

这个步骤,就是基本的相对坐标旋转缩放转换TRS。
但是这是不够的,还需要进行屏幕坐标转换。

Debug.Log(cam.projectionMatrix);

这个投影矩阵,经过打印确定是恒定不变的。
开始实践。
再次参考文章
Calculation behind camera.WorldToScreenPoint - Unity Answers

 public Camera cam;
    public Transform go;

    private GameObject goShpere;

    private void Awake()
    {
        //goShpere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
        //goShpere.transform.localScale *= 0.3f;
        P = cam.projectionMatrix;
    }

    // Update is called once per frame
    void Update()
    {
        Debug.LogError(StringUtility.GetVec3(cam.WorldToScreenPoint(go.transform.position)));
        Debug.LogError(StringUtility.GetVec3(manualWorldToScreenPoint(go.transform.position)));

        //var vScreenPos1 = manualWorldToScreenPoint(go.transform.position);
        //var vScreenPos2 = cam.WorldToScreenPoint(go.transform.position);
        //var vScreenPos = vScreenPos1;

        //var V = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one).inverse;
        //if (SystemInfo.usesReversedZBuffer)
        //{
        //    V.m20 = -V.m20;
        //    V.m21 = -V.m21;
        //    V.m22 = -V.m22;
        //    V.m23 = -V.m23;
        //}
        ////V = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);



        ////var endPos = ManualScreenPointToWorldPoint(new Vector2(vScreenPos.x, vScreenPos.y),
        ////    vScreenPos.z, V.inverse, P.inverse);
        //var endPos = cam.ScreenToWorldPoint(vScreenPos);

        //goShpere.transform.position = endPos;
    }

    Matrix4x4 P;
    Vector3 manualWorldToScreenPoint(Vector3 wp)
    {
        var V = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one).inverse;
        if (SystemInfo.usesReversedZBuffer)
        {
            V.m20 = -V.m20;
            V.m21 = -V.m21;
            V.m22 = -V.m22;
            V.m23 = -V.m23;
        }

        // calculate view-projection matrix
        //Matrix4x4 mat = P * cam.worldToCameraMatrix;
        Matrix4x4 mat2 = P * V;

        // multiply world point by VP matrix
        Vector4 temp = mat2 * new Vector4(wp.x, wp.y, wp.z, 1f);

        //冯乐乐
        {
            // 世界坐标 * View矩阵,得到View坐标系坐标。
            var Pview = V * new Vector4(wp.x, wp.y, wp.z, 1f);

            var tanHalfFov = Mathf.Tan(cam.fieldOfView * 0.5f);
            //var nearClipPlaneHeight = 2 * cam.nearClipPlane * tanHalfFov;
            //var farClipPlaneHeight = 2 * cam.farClipPlane * tanHalfFov;
            var whAspect = cam.aspect;

            float z = -Pview.z * (cam.farClipPlane + cam.nearClipPlane) / (cam.farClipPlane - cam.nearClipPlane)
                - 2 * cam.farClipPlane * cam.nearClipPlane / (cam.farClipPlane - cam.nearClipPlane);

            Debug.LogError("z: " + z + " " + (z / -Pview.z));

            //wp.z:0.3 - 1000 -> -0.3,-1;1000,1
            //w值经过了0,与书中描述一致。
        }


        if (temp.w == 0f)
        {
            // point is exactly on camera focus point, screen point is undefined
            // unity handles this by returning 0,0,0
            return Vector3.zero;
        }
        else
        {
            // convert x and y from clip space to window coordinates
            temp.x = (temp.x / temp.w + 1f) * .5f * cam.pixelWidth;
            temp.y = (temp.y / temp.w + 1f) * .5f * cam.pixelHeight;

            //-0.3,1000 => 0.3,1000
            float z = (temp.z + 0.3f) / (1000 + 0.3f) * (1000 - 0.3f) + 0.3f;
            return new Vector3(temp.x, temp.y, z);
        }
    }

第一次,距离是有问题的,对照了冯乐乐的版本,发现原因,是z值经过了0点。
经过区间映射,将-0.31000映射到0.31000,解决问题。

成功代码:

using UnityEngine;

public class MatrixTest : MonoBehaviour
{
    public Camera cam;
    public Transform go;
    private GameObject goShpere;

    private void Awake()
    {
        goShpere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
        goShpere.transform.localScale *= 1.2f;

        InitWP2WPMatrix();
    }

    // Update is called once per frame
    void Update()
    {
        var vScreenPos = ManualWorldToScreenPoint(go.transform.position, transform);
        var endPos = ManualScreenToWorldPoint(vScreenPos, transform);

        goShpere.transform.position = endPos;
    }


    public void InitWP2WPMatrix()
    {
        P = cam.projectionMatrix;
        camPixelWidth = cam.pixelWidth;
        camPixelHeight = cam.pixelHeight;
    }

    static Matrix4x4 P;
    static int camPixelWidth;
    static int camPixelHeight;
    public static Vector4 ManualWorldToScreenPoint(Vector4 wp, Transform fakeCamera)
    {
        var V = Matrix4x4.TRS(fakeCamera.position, fakeCamera.rotation, Vector3.one).inverse;
        if (SystemInfo.usesReversedZBuffer)
        {
            V.m20 = -V.m20;
            V.m21 = -V.m21;
            V.m22 = -V.m22;
            V.m23 = -V.m23;
        }

        // calculate view-projection matrix
        //Matrix4x4 mat = P * cam.worldToCameraMatrix;
        Matrix4x4 mat2 = P * V;

        // multiply world point by VP matrix
        Vector4 temp = mat2 * new Vector4(wp.x, wp.y, wp.z, 1f);

        if (temp.w == 0f)
        {
            // point is exactly on camera focus point, screen point is undefined
            // unity handles this by returning 0,0,0
            return Vector3.zero;
        }
        else
        {
            // convert x and y from clip space to window coordinates
            temp.x = (temp.x / temp.w + 1f) * .5f * camPixelWidth;
            temp.y = (temp.y / temp.w + 1f) * .5f * camPixelHeight;

            //-0.3,1000 => 0.3,1000
            float z = (temp.z + 0.3f) / (1000 + 0.3f) * (1000 - 0.3f) + 0.3f;
            return new Vector4(temp.x, temp.y, z, temp.w);
        }
    }

    public static Vector3 ManualScreenToWorldPoint(Vector4 sp, Transform fakeCamera)
    {
        float z = (sp.z - 0.3f) / (1000 - 0.3f) * (1000 + 0.3f) - 0.3f;
        float x = (sp.x / camPixelWidth * 2 - 1) * sp.w;
        float y = (sp.y / camPixelHeight * 2 - 1) * sp.w;
        Vector4 temp = new Vector4(x, y, z, sp.w);

        var V = Matrix4x4.TRS(fakeCamera.position, fakeCamera.rotation, Vector3.one).inverse;
        if (SystemInfo.usesReversedZBuffer)
        {
            V.m20 = -V.m20;
            V.m21 = -V.m21;
            V.m22 = -V.m22;
            V.m23 = -V.m23;
        }

        // calculate view-projection matrix
        Matrix4x4 mat2 = V.inverse * P.inverse;

        // multiply world point by VP matrix
        Vector4 worldPoint = mat2 * temp;

        return worldPoint;
    }

}

相关文章

  • 2021-12-27【Math】MVP无相机计算矩阵屏幕坐标转换

    今天要完成一个计算,实现unity的,一个物体的世界坐标转换到相机视口坐标,然后旋转相机,再把视口坐标转换为世界坐...

  • 相机矩阵

    视图矩阵 是 相机世界矩阵的逆矩阵;位于相机坐标系里的点,乘以相机世界矩阵,即转到了世界坐标系里;位于世界坐标系里...

  • 旋转矩阵 look at

    视图矩阵 是 相机世界矩阵的逆矩阵;位于相机坐标系里的点,乘以相机世界矩阵,即转到了世界坐标系里;位于世界坐标系里...

  • ARCamera的transform

    介绍: 转换矩阵,用于定义相机在世界坐标系中的旋转和平移。 transform 是一个 4x4 矩阵。关于矩阵中信...

  • OpenGL的常见矩阵和变化

    前言 OpenGL的坐标转换中需要对顶点坐标进行计算,这种对多个顶点的计算我们需要借助到矩阵,OpenGL所有的变...

  • OpenCV:相机坐标转图像坐标

    其中,XYZ为相机坐标系下坐标.uv为图像坐标 根据公式将相机坐标系下坐标转换成图像坐标 这里图像是无畸变的. 已...

  • 读懂TSDF(Truncated Signed Distance

    1、获取体素在全局坐标系下的坐标(x,y,z),根据ICP配准得到的变换矩阵,将体素的坐标从全局坐标系转换到相机坐...

  • OpenGLES7-天空盒子

    一. MVP矩阵 ●本质上是一个变换矩阵。 ●将一个世界坐标系的点转换裁剪空间的点 ●3D物体从建模到最终显示到屏...

  • 顶视图-世界坐标系-相机坐标系-Scara坐标系转换

    顶视图-世界坐标系-相机坐标系-Scara坐标系转换 1. 定义 Scara相机坐标系 标准相机坐标系 顶视图坐标...

  • 相机模型

    相机坐标系 -> 像素坐标系 (内参矩阵 ) 即:加上x轴y轴偏置 世界坐标系 -> 相机坐标系 世界坐标系 ->...

网友评论

      本文标题:2021-12-27【Math】MVP无相机计算矩阵屏幕坐标转换

      本文链接:https://www.haomeiwen.com/subject/sfqyqrtx.html