unity 矩阵从模型空间到屏幕空间的转换

这里参考 《unity shader 入门精要》冯乐乐的这本书
深入探索透视投影变换

0X01 变换

变换这里粗略的讲解下,为了后面的空间转换做铺垫,不是重点,如果看不 明白则看考其他文章
这里讲解的变换有三种:平移变换缩放变换旋转变换
说变换就要用到矩阵,这里用4x4矩阵来进行这三种变换

{ M 3 ∗ 3 t 3 ∗ 1 0 1 ∗ 3 1 } (1) \left\{ \begin{matrix} M_{3*3}& t _{3*1} \\ 0_{1*3} & 1 \\ \end{matrix} \right\} \tag{1} {M33013t311}(1)
M3*3用于表示旋转和缩放,t3*1用于表示平移,01*3是零矩阵

平移变换

将点(x,y,z)在空间中平移(tx,ty,tz),用矩阵表示为

[ 1 0 0 t x 0 1 0 t y 0 0 1 t z 0 0 0 1 ] × [ x y z 1 ] = [ x + t x y + t y z + t z 1 ] (1) \left[ \begin{matrix} 1 & 0&0&t_x \\ 0 & 1&0&t_y \\ 0 & 0&1&t_z \\ 0 & 0&0&1\\ \end{matrix} \right] \times{ \left[ \begin{matrix} x \\y\\z\\1\\ \end{matrix} \right] = \left[ \begin{matrix} x+t_x \\y+t_y\\z+t_z\\1\\ \end{matrix} \right] } \tag{1} 100001000010txtytz1×xyz1=x+txy+tyz+tz1(1)

缩放变换

对模型沿着x轴y轴和z轴进行缩放
[ k x 0 0 0 0 k y 0 0 0 0 k z 0 0 0 0 1 ] × [ x y z 1 ] = [ k x x k y y k z z 1 ] (2) \left[ \begin{matrix} k_x& 0&0&0 \\ 0 & k_y&0&0 \\ 0 & 0&k_z&0 \\ 0 & 0&0&1\\ \end{matrix} \right] \times{ \left[ \begin{matrix} x \\y\\z\\1\\ \end{matrix} \right] = \left[ \begin{matrix} k_xx \\k_yy\\k_zz\\1\\ \end{matrix} \right] } \tag{2} kx0000ky0000kz00001×xyz1=kxxkyykzz1(2)

旋转变换

这里着重讲下这个,吐槽下好多博客将旋转连旋转正方向都没有说 ,看的云里雾里
关于旋转的正方向,OpenGL与多数图形学书籍规定的正方向为逆时针方向(沿着坐标轴负方向向原点看)
unity 矩阵从模型空间到屏幕空间的转换_第1张图片
参考这篇文章旋转变换(一)旋转矩阵

绕着z轴旋转 θ \theta θ度的 矩阵
R x ( θ ) = [ c o s ( θ ) − s i n ( θ ) 0 0 s i n ( θ ) c o s ( θ ) 0 0 0 0 1 0 0 0 0 1 ] R_x(\theta) = \left[ \begin{matrix} cos(\theta)&-sin(\theta)&0&0 \\ sin(\theta)&cos(\theta)&0 &0\\ 0& 0&1&0 \\ 0 & 0&0&1\\ \end{matrix} \right] Rx(θ)=cos(θ)sin(θ)00sin(θ)cos(θ)0000100001

绕着y轴旋转 θ \theta θ度的 矩阵
R x ( θ ) = [ c o s ( θ ) 0 s i n ( θ ) 0 0 1 0 0 − s i n ( θ ) 0 c o s ( θ ) 0 0 0 0 1 ] R_x(\theta) = \left[ \begin{matrix} cos(\theta)&0&sin(\theta)&0\\ 0& 1&0&0 \\ -sin(\theta)&0&cos(\theta)&0 \\ 0 & 0&0&1\\ \end{matrix} \right] Rx(θ)=cos(θ)0sin(θ)00100sin(θ)0cos(θ)00001

绕着x轴旋转 θ \theta θ度的 矩阵
R x ( θ ) = [ 1 0 0 0 0 c o s ( θ ) − s i n ( θ ) 0 0 s i n ( θ ) c o s ( θ ) 0 0 0 0 1 ] R_x(\theta) = \left[ \begin{matrix} 1& 0&0&0 \\ 0 & cos(\theta)&-sin(\theta)&0 \\ 0 & sin(\theta)&cos(\theta)&0 \\ 0 & 0&0&1\\ \end{matrix} \right] Rx(θ)=10000cos(θ)sin(θ)00sin(θ)cos(θ)00001

unity中旋转的顺序是首先绕Z轴进行旋转,然后绕X轴进行旋转,最后绕Y轴进行旋转。

0X02 坐标空间变换

已知子坐标空间C的三个坐标轴在父坐标空间P下的表示xc、yc、zc,以及其原点位置Oc,当给定一个子坐标空间中的点Ac=(a,b,c),求A点用父坐标空间的表示Ap
xc设为(xcx,xcy,xcz),yc设为(ycx,ycy,ycz),zc设为(zcx,zcy,zcz),Oc设为(ocx,ocy,ocz)
这里写结论不写推导过程了
A p = [ x c x y c x z c x o c x x c y y c y z c y o c y x c z y c z z c z o c z 0 0 0 1 ] × [ a b c 1 ] A_p = \left[ \begin{matrix} x_{cx}& y_{cx}& z_{cx}&o_{cx}\\ x_{cy}& y_{cy}& z_{cy}&o_{cy}\\ x_{cz}& y_{cz}& z_{cz}&o_{cz}\\ 0 & 0&0&1\\ \end{matrix} \right] \times{ \left[ \begin{matrix} a \\b\\c\\1\\ \end{matrix} \right] } Ap=xcxxcyxcz0ycxycyycz0zcxzcyzcz0ocxocyocz1×abc1
所以点从子坐标空间到父坐标空间的变换矩阵Mc->p为(|表示是按列展开的)
M c − > p = [ ∣ ∣ ∣ ∣ x c y c z c o c ∣ ∣ ∣ ∣ 0 0 0 1 ] M_{c->p} = \left[ \begin{matrix} |&|&|&|\\ x_{c}& y_{c}& z_{c}&o_{c}\\ |&|&|&|\\ 0 & 0&0&1\\ \end{matrix} \right] Mc>p=xc0yc0zc0oc1
矢量(不需要平移)从子坐标空间到父坐标空间的变换矩阵Mc->p为(|表示是按列展开的)
M c − > p = [ ∣ ∣ ∣ x c y c z c ∣ ∣ ∣ ] M_{c->p} = \left[ \begin{matrix} |&|&|\\ x_{c}& y_{c}& z_{c}\\ |&|&|\\ \end{matrix} \right] Mc>p=xcyczc
矢量(不需要平移)从父坐标空间到子坐标空间的变换矩阵Mp->c为(-表示是按行展开的)
M p − > c = [ − x c − − y c − − z c − ] M_{p->c} = \left[ \begin{matrix} -& x_{c}&-\\ -& y_{c}& -\\ -& z_{c}&-\\ \end{matrix} \right] Mp>c=xcyczc

0X03 空间变换过程

空间变换经历这几个过程:模型空间(model space)—>世界空间(world space)–>观察空间(view space)(右手坐标系)–>裁剪空间(clip space)–>屏幕空间(screen space)
除了观察空间是右手坐标系外其他的全是左手坐标系
屏幕空间 的转换需要经过NDC变换 然后视口坐标变换(只关注x,y;z不关注)再到屏幕坐标
这里用的是 透视 相机没有用正交相机
在文章最下面提供了TransformationMatrixUtil.cs脚本

模型空间到世界空间

根据世界空间对模型空间的平移、旋转、缩放来建立一个变换矩阵,变换矩阵根据上面提到的基础矩阵相乘得到
建立一个 测试脚本TestMatrix.cs 来测试变换坐标

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

public class TestMatrix : MonoBehaviour {

    // Use this for initialization
    Camera cam;

    /// 
    /// 模型空间的物体,localPosition作为模型空间的坐标
    /// 父物体的Transform作为世界空间对模型空间的变换
    /// 
    public Transform trans1;
    void Start()
    {
        cam = Camera.main;
        TestModelSpaceToWorldSpace();
    }
    

    /// 
    /// 模型空间转世界空间的坐标
    /// 
    void TestModelSpaceToWorldSpace()
    {
        Transform _parent = trans1.parent;
        //模型空间转世界空间
        Vector3 p = TransformationMatrixUtil.MToWPosition(_parent.localScale, _parent.localEulerAngles, _parent.localPosition, trans1.localPosition);
        Debug.Log("模型空间转世界空间的坐标:" + p);
        Debug.Log("物体的世界坐标" + trans1.position);
        //模型空间转世界空间
        Matrix4x4 matrix = TransformationMatrixUtil.MToWMatrix(_parent.localScale, _parent.localEulerAngles, _parent.localPosition);
        Debug.LogFormat("matrix:\n{0}\n\nlocalToWorldMatrix:\n{1}\n\n是否相等:{2}", matrix, _parent.localToWorldMatrix, matrix == _parent.localToWorldMatrix);
    }
}

场景中配置两个物体 Root,Cube;Root的Transform代表世界空间对Cube模型空间的变换
unity 矩阵从模型空间到屏幕空间的转换_第2张图片
unity 矩阵从模型空间到屏幕空间的转换_第3张图片
测试代码的结果:说明变换后的坐标和物体本身世界坐标相同
unity 矩阵从模型空间到屏幕空间的转换_第4张图片
来看世界空间到模型空间
继续增加测试代码

    /// 
    /// 世界空间到模型空间变换对比
    /// 这里直接对比变换矩阵是否相同
    /// 
    void TestWorldSpaceToModelSpace()
    {
        //检查世界空间到模型空间
        Transform _parent = trans1.parent;
        //模型空间转世界空间
        Matrix4x4 matrix = TransformationMatrixUtil.MToWMatrix(_parent.localScale, _parent.localEulerAngles, _parent.localPosition);
        //世界空间转模型空间;就是matrix的逆矩阵
        matrix = matrix.inverse;
        Debug.LogFormat("matrix:\n{0}\n\nworldToLocalMatrix:\n{1}\n\n是否相等:{2}", matrix, _parent.worldToLocalMatrix, matrix== _parent.worldToLocalMatrix);
        

    }

在Start方法中调用后的结果:
unity 矩阵从模型空间到屏幕空间的转换_第5张图片

模型空间到观察空间

继续增加测试代码

    /// 
    /// 世界空间到观察空间变换对比
    /// 这里是透视相机,没有做正交相机的
    /// 
    void TestWorldSpaceToViewSpace()
    {
        //注意这里是透视相机,没有做正交相机变换矩阵
        cam = Camera.main;
        //世界空间坐标到观察空间坐标
        Vector3 viewPos = TransformationMatrixUtil.WToVPosition(trans1.position);
        //结果是z轴相反,因为世界空间是左手坐标系,而观察空间是右手坐标系
        Debug.LogFormat("世界空间:{0},观察空间:{1}", trans1.position, viewPos);
        //世界空间到观察空间
        Matrix4x4 matrix = TransformationMatrixUtil.WToVMatrix();
        Debug.LogFormat("matrix:\n{0}\n\n worldToCameraMatrix:\n{1}\n\n是否相等:{2}", matrix, cam.worldToCameraMatrix, matrix == cam.worldToCameraMatrix);
    }

在Start方法中调用后的结果:
unity 矩阵从模型空间到屏幕空间的转换_第6张图片

观察空间到裁剪空间

继续增加测试代码

    /// 
    /// 测试观察空间到裁剪空间的变换矩阵
    /// 
    void TestViewSpaceToClipSpace()
    {
        //观察空间到裁剪空间矩阵,注意这里是透视相机,没有做正交相机变换矩阵
        Matrix4x4 matrix = TransformationMatrixUtil.VToPMatrix();
        Debug.LogFormat("matrix:\n{0}\n\n projectionMatrix:\n{1}\n\n是否相等:{2}", matrix, cam.projectionMatrix, matrix == cam.projectionMatrix);
    }

在Start方法中调用后的结果:
unity 矩阵从模型空间到屏幕空间的转换_第7张图片

模型空间到屏幕空间

这里直接测试从模型空间到世界空间到观察空间到屏幕空间变换

    /// 
    /// 测试模型空间到屏幕空间的变换
    /// 只验证 屏幕坐标xy,不验证zw
    /// 注意这里是透视相机,没有做正交相机变换矩阵
    /// 
    void TestModelSpaceToScreenSpace()
    {
        Transform _parent = trans1.parent;
        //模型空间转世界空间
        Vector3 worldPos = TransformationMatrixUtil.MToWPosition(_parent.localScale, _parent.localEulerAngles, _parent.localPosition, trans1.localPosition);
        //世界空间转观察空间
        Vector3 viewPos = TransformationMatrixUtil.WToVPosition(worldPos);
        //观察空间转透视裁剪空间
        Vector4 clipPos = TransformationMatrixUtil.VToPPosition(viewPos);
        //裁剪空间到屏幕空间变换
        Vector4 screenPos = TransformationMatrixUtil.PToScreenPosition(clipPos);

        Vector4 screenPos1 = cam.WorldToScreenPoint(trans1.position);
        //Vector4 viewportPos = cam.WorldToViewportPoint(trans1.position);
        Debug.LogFormat(" worldPos:{0},trans1.positon:{1}\n viewPos:{2}\n clipPos:{3}\n screenPos:{4},screenPos1:{5}",worldPos,trans1.position,viewPos,clipPos,screenPos,screenPos1);
        
    }

在Start方法中调用后的结果:(如果想要一样的结果则需要设置分辨率为1280*720,并且相机配置一样)
这里只验证屏幕坐标x和y,z值是世界坐标的z值,没有变换的,w值没有研究
unity 矩阵从模型空间到屏幕空间的转换_第8张图片

脚本TransformationMatrixUtil.cs

//=====================================================
// - FileName:      TransformationMatrixUtil.cs
// - Created:       wangguoqing
// - UserName:      2018/09/03 17:18:56
// - Email:         [email protected]
// - Description:   
// -  (C) Copyright 2008 - 2015, codingriver,Inc.
// -  All Rights Reserved.
//======================================================
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TransformationMatrixUtil {

	
    /// 
    /// 模型空间的坐标转世界空间坐标
    /// 
    /// 世界空间对模型的缩放
    /// 世界空间对模型的旋转
    /// 世界空间对模型的平移
    /// 模型空间的坐标
    /// 
    public static Vector3 MToWPosition(Vector3 scale, Vector3 rotation, Vector3 translate, Vector3 currentPos)
    {
        Matrix4x4 convertMatrix = MToWMatrix(scale, rotation, translate);
        Vector3 pos= convertMatrix.MultiplyPoint(currentPos);
        return pos;
    }

    /// 
    /// 世界空间的坐标转模型空间坐标
    /// 
    /// 世界空间对模型的缩放
    /// 世界空间对模型的旋转
    /// 世界空间对模型的平移
    /// 世界空间的坐标
    /// 
    public static Vector3 WToMPosition(Vector3 scale, Vector3 rotation, Vector3 translate, Vector3 currentPos)
    {
        Matrix4x4 convertMatrix = MToWMatrix(scale, rotation, translate);
        Vector3 pos = convertMatrix.inverse.MultiplyPoint(currentPos);
        return pos;
    }

    /// 
    /// 模型空间到世界空间的变换矩阵
    /// 
    /// 世界空间对模型空间的缩放
    /// 世界空间对模型空间的旋转
    /// 世界空间对模型空间的平移
    /// 
    public static Matrix4x4 MToWMatrix(Vector3 scale, Vector3 rotation, Vector3 translate)
    {
        Matrix4x4 scaleMatrix = new Matrix4x4();
        scaleMatrix.SetRow(0, new Vector4(scale.x, 0, 0, 0));
        scaleMatrix.SetRow(1, new Vector4(0, scale.y, 0, 0));
        scaleMatrix.SetRow(2, new Vector4(0, 0, scale.z, 0));
        scaleMatrix.SetRow(3, new Vector4(0, 0, 0, 1));

        rotation *= Mathf.Deg2Rad;
        Matrix4x4 rotateMatrixZ = new Matrix4x4();
        rotateMatrixZ.SetRow(0, new Vector4(Mathf.Cos(rotation.z), -Mathf.Sin(rotation.z), 0, 0));
        rotateMatrixZ.SetRow(1, new Vector4(Mathf.Sin(rotation.z), Mathf.Cos(rotation.z), 0, 0));
        rotateMatrixZ.SetRow(2, new Vector4(0, 0, 1, 0));
        rotateMatrixZ.SetRow(3, new Vector4(0, 0, 0, 1));

        Matrix4x4 rotateMatrixY = new Matrix4x4();
        rotateMatrixY.SetRow(0, new Vector4(Mathf.Cos(rotation.y), 0, Mathf.Sin(rotation.y), 0));
        rotateMatrixY.SetRow(1, new Vector4(0, 1, 0, 0));
        rotateMatrixY.SetRow(2, new Vector4(-Mathf.Sin(rotation.y), 0, Mathf.Cos(rotation.y), 0));
        rotateMatrixY.SetRow(3, new Vector4(0, 0, 0, 1));

        Matrix4x4 rotateMatrixX = new Matrix4x4();
        rotateMatrixX.SetRow(0, new Vector4(1, 0, 0, 0));
        rotateMatrixX.SetRow(1, new Vector4(0, Mathf.Cos(rotation.x), -Mathf.Sin(rotation.x), 0));
        rotateMatrixX.SetRow(2, new Vector4(0, Mathf.Sin(rotation.x), Mathf.Cos(rotation.x), 0));
        rotateMatrixX.SetRow(3, new Vector4(0, 0, 0, 1));

        Matrix4x4 translateMatrix = new Matrix4x4();
        translateMatrix.SetRow(0, new Vector4(1, 0, 0, translate.x));
        translateMatrix.SetRow(1, new Vector4(0, 1, 0, translate.y));
        translateMatrix.SetRow(2, new Vector4(0, 0, 1, translate.z));
        translateMatrix.SetRow(3, new Vector4(0, 0, 0, 1));


        //这里注意顺序,矩阵不满足左右交换的,
        //unity中旋转的顺序是首先绕Z轴进行旋转,然后绕X轴进行旋转,最后绕Y轴进行旋转
        Matrix4x4 convertMatrix = translateMatrix * rotateMatrixY * rotateMatrixX * rotateMatrixZ * scaleMatrix;
        return convertMatrix;
    }


    /// 
    /// 世界空间 转到 观察空间
    /// 观察空间是右手坐标系
    /// 
    /// 
    /// 
    public static Vector3 WToVPosition(Vector3 worldPos)
    {

        Matrix4x4 mat = WToVMatrix();
        Vector3 viewPos = mat.MultiplyPoint(worldPos);
        return viewPos;
    }

    /// 
    /// 世界空间转观察空间的矩阵
    /// 观察空间是右手坐标系
    /// 
    /// 
    public static Matrix4x4 WToVMatrix()
    {
        //先获取世界空间转模型空间的矩阵(变换矩阵可逆,取逆矩阵)
        Transform camTrans = Camera.main.transform;
        Matrix4x4 matrix = MToWMatrix(camTrans.localScale, camTrans.localEulerAngles, camTrans.position);
        Matrix4x4 inverseMatrix = matrix.inverse;

        //将矩阵改成右手坐标系的矩阵,即z取反
        inverseMatrix.SetRow(2, -inverseMatrix.GetRow(2));
        return inverseMatrix;
    }

    /// 
    /// 观察空间到裁剪空间
    /// 
    /// 
    /// 
    /// 
    public static Vector4 VToPPosition(Vector3 vPos)
    {

        Matrix4x4 matrix = VToPMatrix();
        Vector4 p = matrix*new Vector4( vPos.x,vPos.y,vPos.z,1);
        return p;
    }

    /// 
    /// 观察空间到裁剪空间的变换矩阵
    /// 
    /// 
    public static Matrix4x4 VToPMatrix()
    {
        Camera cam = Camera.main;
        float near = cam.nearClipPlane;
        float far = cam.farClipPlane;
        float fov = cam.fieldOfView;
        float aspect = cam.aspect;
        Matrix4x4 matrix = VToPMatrix(fov, near, far, aspect);
        return matrix;
    }
    /// 
    /// 透视裁剪矩阵
    /// 透视投影矩阵
    /// 参考(https://blog.csdn.net/cbbbc/article/details/51296804)
    /// View to Projection
    /// 观察空间到裁剪空间
    /// 
    /// 
    /// 
    /// 
    /// 
    /// 
    public static Matrix4x4 VToPMatrix(float fov,float near,float far,float aspect)
    {
        float tan= Mathf.Tan((fov * Mathf.Deg2Rad) / 2);

        Matrix4x4 matrix = new Matrix4x4();
        matrix.SetRow(0, new Vector4(1 / (aspect * tan), 0, 0, 0));
        matrix.SetRow(1, new Vector4(0,1 / (tan), 0, 0));
        matrix.SetRow(2, new Vector4(0, 0, -(far + near) / (far - near), -2 * far * near / (far - near)));
        matrix.SetRow(3, new Vector4(0, 0, -1, 0));

        return matrix;
    }

    /// 
    /// 裁剪空间到屏幕空间的坐标变换(x,y是有效的)
    /// 
    /// 
    /// 
    public static Vector4 PToScreenPosition(Vector4 p)
    {
        Vector4 ndcPos= PToNDCPosition(p);
        Vector4 texPos = NDCToTexturePosition(ndcPos);
        Vector4 screenPos = TextureToScreenPosition(texPos);
        return screenPos;
    }


    /// 
    /// NDC是一个归一化的空间,坐标空间范围为[-1, 1]。从Projection空间到NDC空间的做法就是做了一个齐次除法!
    /// Projection to NDC
    /// 
    /// 
    /// 
    public static Vector4 PToNDCPosition(Vector4 p)
    {
        return p / p.w;
    }
    /// 
    /// NDC - Texture Space
    /// (NDC - Viewport Space 视口空间,z的值这里和视口空间不一样,z的范围是[-1,1],视口坐标的z值是实际的z值)
    /// 这个过程是将[-1, 1]映射到[0, 1]之间。
    /// NDC to Texture space
    /// 
    /// 
    /// 
    public static Vector4 NDCToTexturePosition(Vector4 ndcPoint)
    {
        return (ndcPoint + Vector4.one) / 2;
    }

    /// 
    /// Texture Space - Screen Space
    /// 这个过程就是得到顶点最终在屏幕上的坐标,其实就是利用Texture Space的坐标乘上屏幕的宽高。
    /// Texture to Screen
    /// 
    /// 
    /// 
    public static Vector4 TextureToScreenPosition(Vector4 texturePoint)
    {
        Vector4 screenPos = new Vector4(texturePoint.x * Screen.width, texturePoint.y * Screen.height, texturePoint.z, texturePoint.w);
        return screenPos;
    }

}


测试脚本TestMatrix.cs

   //=====================================================
// - FileName:      TestMatrix.cs
// - Created:       wangguoqing
// - UserName:      2018/09/03 17:18:56
// - Email:         [email protected]
// - Description:   
// -  (C) Copyright 2008 - 2015, codingriver,Inc.
// -  All Rights Reserved.
//======================================================
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// 
/// 测试空间变换
/// 这里是透视相机,没有做正交相机的
/// 
public class TestMatrix : MonoBehaviour {

    // Use this for initialization
    Camera cam;

    /// 
    /// 模型空间的物体,localPosition作为模型空间的坐标
    /// 父物体的Transform作为世界空间对模型空间的变换
    /// 
    public Transform trans1;
    void Start()
    {
        cam = Camera.main;
        TestModelSpaceToWorldSpace();
        //TestWorldSpaceToModelSpace();
        //TestWorldSpaceToViewSpace();
        //TestViewSpaceToClipSpace();
        //TestModelSpaceToScreenSpace();
    }
    

    /// 
    /// 模型空间转世界空间的坐标
    /// 
    void TestModelSpaceToWorldSpace()
    {
        Transform _parent = trans1.parent;
        //模型空间转世界空间
        Vector3 p = TransformationMatrixUtil.MToWPosition(_parent.localScale, _parent.localEulerAngles, _parent.localPosition, trans1.localPosition);
        Debug.Log("模型空间转世界空间的坐标:" + p);
        Debug.Log("物体的世界坐标" + trans1.position);
        //模型空间转世界空间
        Matrix4x4 matrix = TransformationMatrixUtil.MToWMatrix(_parent.localScale, _parent.localEulerAngles, _parent.localPosition);
        Debug.LogFormat("matrix:\n{0}\n\nlocalToWorldMatrix:\n{1}\n\n是否相等:{2}", matrix, _parent.localToWorldMatrix, matrix == _parent.localToWorldMatrix);
    }
    /// 
    /// 世界空间到模型空间变换对比
    /// 这里直接对比变换矩阵是否相同
    /// 
    void TestWorldSpaceToModelSpace()
    {
        //检查世界空间到模型空间
        Transform _parent = trans1.parent;
        //模型空间转世界空间
        Matrix4x4 matrix = TransformationMatrixUtil.MToWMatrix(_parent.localScale, _parent.localEulerAngles, _parent.localPosition);
        //世界空间转模型空间;就是matrix的逆矩阵
        matrix = matrix.inverse;
        Debug.LogFormat("matrix:\n{0}\n\nworldToLocalMatrix:\n{1}\n\n是否相等:{2}", matrix, _parent.worldToLocalMatrix, matrix== _parent.worldToLocalMatrix);
        

    }

    /// 
    /// 世界空间到观察空间变换对比
    /// 这里是透视相机,没有做正交相机的
    /// 
    void TestWorldSpaceToViewSpace()
    {
        //注意这里是透视相机,没有做正交相机变换矩阵
        cam = Camera.main;
        //世界空间坐标到观察空间坐标
        Vector3 viewPos = TransformationMatrixUtil.WToVPosition(trans1.position);
        //结果是z轴相反,因为世界空间是左手坐标系,而观察空间是右手坐标系
        Debug.LogFormat("世界空间:{0},观察空间:{1}", trans1.position, viewPos);
        //世界空间到观察空间
        Matrix4x4 matrix = TransformationMatrixUtil.WToVMatrix();
        Debug.LogFormat("matrix:\n{0}\n\n worldToCameraMatrix:\n{1}\n\n是否相等:{2}", matrix, cam.worldToCameraMatrix, matrix == cam.worldToCameraMatrix);
    }

    /// 
    /// 测试观察空间到裁剪空间的变换矩阵
    /// 注意这里是透视相机,没有做正交相机变换矩阵
    /// 
    void TestViewSpaceToClipSpace()
    {
        //观察空间到裁剪空间矩阵,注意这里是透视相机,没有做正交相机变换矩阵
        Matrix4x4 matrix = TransformationMatrixUtil.VToPMatrix();
        Debug.LogFormat("matrix:\n{0}\n\n projectionMatrix:\n{1}\n\n是否相等:{2}", matrix, cam.projectionMatrix, matrix == cam.projectionMatrix);
    }

    /// 
    /// 测试模型空间到屏幕空间的变换
    /// 只验证 屏幕坐标xy,不验证zw
    /// 注意这里是透视相机,没有做正交相机变换矩阵
    /// 
    void TestModelSpaceToScreenSpace()
    {
        Transform _parent = trans1.parent;
        //模型空间转世界空间
        Vector3 worldPos = TransformationMatrixUtil.MToWPosition(_parent.localScale, _parent.localEulerAngles, _parent.localPosition, trans1.localPosition);
        //世界空间转观察空间
        Vector3 viewPos = TransformationMatrixUtil.WToVPosition(worldPos);
        //观察空间转透视裁剪空间
        Vector4 clipPos = TransformationMatrixUtil.VToPPosition(viewPos);
        //裁剪空间到屏幕空间变换
        Vector4 screenPos = TransformationMatrixUtil.PToScreenPosition(clipPos);

        Vector4 screenPos1 = cam.WorldToScreenPoint(trans1.position);
        //Vector4 viewportPos = cam.WorldToViewportPoint(trans1.position);
        Debug.LogFormat(" worldPos:{0},trans1.positon:{1}\n viewPos:{2}\n clipPos:{3}\n screenPos:{4},screenPos1:{5}",worldPos,trans1.position,viewPos,clipPos,screenPos,screenPos1);
        
    }
}

github项目

这篇文章整理太麻烦了,前面关于使用矩阵做变换的原理的整理很粗糙,只是为了下面的使用,这篇文章不做过多介绍,之前研究过了,没有记录现在反过头来整理需要仔细理理

你可能感兴趣的:(unity,计算机图形学,unity变换,坐标变换,unity,变换矩阵)