【Unity3D】空间和变换
1 空间
1.1 左右手坐标系及其法则
1.1.1 左右手坐标系

Unity 局部空间、世界空间、裁剪空间、屏幕空间都采用左手坐标系,只有观察空间采用右手坐标系。
左右手坐标系除了坐标系朝向(旋向性)不同,还存在以下差异:
- 左手坐标系下旋转正方向的定义遵循左手法则,右手坐标系下旋转正方向的定义遵循右手法则;
- 左手坐标系下向量叉乘遵循左手法则,右手坐标系下向量叉乘遵循右手法则。
1.1.2 左右手法则
左手坐标系和右手坐标系下旋转正方形定义:

左手坐标系和右手坐标系下向量叉乘方向定义:

1.2 四维空间
在三维空间中,将向量 u 变换到向量 v,如果该变换是一个线性变换,一般可以使用一个不依赖于 u、v 的 3x3 的矩阵描述,即 A·u=v,其中 A 是描述该线性变换的变换矩阵。但是,并不是所有线性变换都能使用 3x3 矩阵描述,如:平移变换和对称变换,对于这些变换,需要将空间扩展到四维空间中,再使用 4x4 矩阵描述。
Unity 模型变换主要有平移、旋转、缩放、对称,其中旋转和缩放变换可以使用 3x3 矩阵描述,平移和对称变换必须使用 4x4 矩阵描述,为了统一描述这些变换,将三维空间扩展到四维空间中,扩展方法如下:
- 三维空间中的坐标原点 [0, 0, 0]' 映射到四维空间中的 [0, 0, 0, 1]' 点;
- 三维空间中的 x、y、z 轴正方向单位向量 [1, 0, 0]'、[0, 1, 0]'、[0, 0, 1]' 分别映射到四维空间中的 [1, 0, 0, 0]'、 [0, 1, 0, 0]'、 [0, 0, 1, 0]' 向量;
- 新添加的 w 轴 正方向单位向量为 [0, 0, 0, 1]'。
注意:三维空间中的原点 [0, 0, 0]' 并没有映射到四维空间中的原点 [0, 0, 0, 0],而是映射到 [0, 0, 0, 1]。
由以上空间映射关系可知:
- 三维空间中的任意点 [x, y, z]',对应的四维空间坐标为:[x, y, z, 1]';
- 三维空间中的任意向量 [x, y, z]',对应的四维空间坐标为:[x, y, z, 0]'。
1.3 Unity 空间
1.3.1 Unity 空间变换过程
- 从模型空间到裁剪空间的变换过程在顶点着色器中完成,顶点着色器输入模型局部坐标,输出裁剪空间中的坐标;
- 从裁剪空间到屏幕空间的变换过程由系统控制,用户不能控制该阶段变换。
1.3.2 Unity 空间变换示意图


说明:近平面和远平面间棱台称为视锥体,表示可见区域范围,视锥体以外的顶点数据将被裁剪丢弃。

说明:裁剪空间中的点满足条件:-w < x < w,-w < y < w,-w < z < w,不满足该条件的点将被踢出掉;将满足条件的点 x、y、z 坐标除以 w(齐次除法、透视除法),就会得到归一化设备空间,该空间中的点满足条件:-1 < x < 1,-1 < y < 1,-1 < z < 1。

2 变换
2.1 模型变换
2.1.1 平移变换
对于任意点 [x, y, z, 1]',将其平移 [a, b, c, 0]',可以使用以下矩阵运算描述平移变换:
对于任意向量 [x, y, z, 0]',将其平移 [a, b, c, 0]',平移后仍然是 [x, y, z, 0],如下:
2.1.2 旋转变换
绕 x 轴旋转 α 度,对应的旋转矩阵如下:
绕 y 轴旋转 α 度,对应的旋转矩阵如下:
绕 z 轴旋转 α 度,对应的旋转矩阵如下:
当旋转角度为 [α, β, γ]' 时,对应的复合旋转矩阵为:
2.1.3 缩放变换
对于缩放系数 [kx, ky, kz]',对应的缩放矩阵如下:
当 kx = ky = kz 时,该缩放变换称为统一缩放(uniform scale),否则称为非统一缩放(nonuniform scale)。
2.1.4 对称变换
对于任意点 [x, y, z, 1]',关于点 [a, b, c, 1]' 对称的点,可以使用以下矩阵运算描述对称变换:
2.2 观察变换
将世界坐标系按照 1.2 节映射方法扩展到四维空间中,假设 x、y、z、w 轴正方向的的单位向量分别为 e1、e2、e3、e4,相机在世界坐标系下的坐标为 [a, b, c, 1]',其向右、向上、向前方向的单位向量分别为:r、u、f,则向量 r、u、-f、e4 在一组基向量 e1, e2, e3, e4 下的表示如下:(f 取负是因为观察坐标系是右手坐标系,其 z 轴的正方向与相机的前方方向相反)
由于向量 r、u、-f、e4 两两垂直,并且都是单位向量,因此由这 4 个向量组成的矩阵是正交矩阵,即 A-1 = A'。由此可知,向量 e1, e2, e3, e4 在一组基向量 r、u、-f、e4 下的表示如下:
右侧的矩阵就是坐标轴的旋转变换矩阵,另外还需要进行坐标原点的平移变换,由 1.2.1 节平移变换矩阵可知,观察变换矩阵如下:
2.3 投影变换
2.3.1 透视投影
1)透视投影相机参数
Unity 提供给用户调整透视投影矩阵的参数如下:
- Near:近平面距离相机的距离;
- Far:远平面距离相机的距离;
- Field of View:视锥体在竖直方向上的张角(以下简称 FOV);
- Viewport Rect:视口起点和宽高(左下角为原点,向右宽度增加,向上高度增加)
根据 Near、Far、FOV 的值,可以计算近平面和远平面高度分别为:
近平面和远平面的宽度由相机(或屏幕)宽高比决定,假设相机(或屏幕)宽高比为 Aspect,则近平面和远平面的宽度计算如下:
2)透视投影矩阵推导
投影的目的是:将模型顶点投影到近平面上,如下,将观察坐标系下的任意顶点 [x0, y0, z0]' 投影到近平面上,投影后的坐标为 [x1, y1, z1]’(z1 = -Near)。
根据三角形相似原理,存在以下函数关系:
为了方便后续进行屏幕映射,需要将 [x1, y1]' 进行标准化,即将 x1、y1 映射到区间 [-1, 1],当前 -nearClipPlaneWidth / 2 ≤ x1 ≤ nearClipPlaneWidth / 2,-nearClipPlaneHeight / 2 ≤ y1 ≤ nearClipPlaneHeight / 2,假设归一化后的坐标为 [x2, y2],因此存在以下关系:
将 x1、y1、nearClipPlaneWidth、nearClipPlaneHeight 使用 x0、y0、z0、Aspect、FOV 替换得:
x2、y2 已标准化,但是当前 -Far ≤ z0 ≤ -Near,我们期望将 z0 也标准化,假设标准化后的变量为 z2,则 -1 ≤ z2 ≤ 1。x2 与 x0、y2 与 y0 的关系式中,都存在 (-1 / z0),我们期望 z2 与 z0 的关系式中也存在 (-1 / z0),因此,我们假设 z2 与 z0 的关系如下:
将 [-Far, 1]'、[-Near, -1]' 代入求解得:(注意:这里不能代入 [-Far, -1]'、[-Near, 1]',因为裁剪坐标系的 z 轴和 观察坐标系的 z 轴方向相反)
将 k、b 代入 z2 与 z0 的关系式中得:
整理 x2、y2、z2 与 x0、y0、z0 的关系如下:
由于 z0 是变量,在矩阵前面乘以 (-1 / z0),使得透视变换不是线性变换,因此我们将原本的透视变换拆分为以下两步:
- 对 [x0, y0, z0, 1]' 左乘透视矩阵;
- 将第一步的结果除以 (-z0)。
为保证透视变换的线性性质,我们将第二步变换移到屏幕映射中处理,并将其定义为齐次除法(或透视除法),而将第一步变换作为透视变换,其对应的矩阵如下:
说明:P(4, 3) 定义为 -1 是为了将观察坐标系中顶点的深度信息 (-z) 传递给下一步(齐次除法或透视除法)处理(用 w 存储,即 w = -z),避免深度信息丢失。经透视变换后,顶点坐标的 x、y、z 分量将约束在 [z, -z] 区间,即 [-w, w],在该区间外的顶点将被丢弃。
经过透视变换后,观察空间和裁剪空间视锥体的对比如下:
2.3.2 正交投影
1)正交投影相机参数
Unity 提供给用户调整正交投影矩阵的参数如下:
- Size:视锥体在竖直方向上的高度的一半;
- Near:近平面距离相机的距离;
- Far:远平面距离相机的距离;
- Viewport Rect:视口起点和宽高(左下角为原点,向右宽度增加,向上高度增加)
2)正交投影矩阵推导
投影的目的是:将模型顶点投影到近平面上,如下,将观察坐标系下的任意顶点 [x0, y0, z0]' 投影到近平面上,投影后的坐标为 [x0, y0, -Near]‘。
为了方便后续进行屏幕映射,需要将 [x0, y0, z0]' 进行标准化,即将 x0、y0、z0 映射到区间 [-1, 1],当前 -Aspect · Size ≤ x1 ≤ Aspect · Size,-Size ≤ y1 ≤ Size,-Far ≤ z0 ≤ -Near,假设归一化后的坐标为 [x1, y1, z1]',因此存在以下关系:
说明:正交投影变换后,w 分量的值仍然是 1,从而保证 [x1, y1, z1]' 经齐次除法(除以 w1)后,仍然是标准化坐标(即值域为 [-1, 1]),这样做的好处是:下游不用区分上游传递过来的数据是透视投影还是正交投影处理后的数据。经正交投影变换后,顶点坐标的 x、y、z 分量将约束在 [-1, 1] 区间,在该区间外的顶点将被丢弃。
经过正交投影变换后,观察空间和裁剪空间视锥体的对比如下:
2.4 齐次除法和屏幕映射
2.4.1 齐次除法
经透视投影或正交投影后,将坐标 [x, y, z, w] 中的 x、y、z 分量都除以其 w 分量的值,使得 x、y、z 都约束在 [-1, 1] 区间,该过程称为齐次除法(或透视除法),得到的坐标称为归一化的设备坐标(NDC)。
2.4.2 屏幕映射
经齐次除法后,将坐标的 x、y 值映射到屏幕像素位置,该过程称为屏幕映射。映射前 x、y 的值域为 [-1, 1],映射后 x 的值域为 [0, pixelWidth],y 的值域为 [0, pixelHeight],屏幕左下角坐标为 [0, 0],右上角坐标为 [pixelWidth, pixelHeight]。屏幕映射公式如下:
2.5 法线变换
假设模型变换为 M,模型空间中某点法线向量为 n,如果模型变换中包含非统一缩放(即 x、y、z 的缩放系数不全相等), 此时若按照 M · n 计算法线的世界坐标,就会出现变换后的法线与切面不垂直,如下图。
法线由切线计算而来,在模型空间中 A 点的切线向量为 v1,法线向量为 n1,经过模型变换(矩阵 M)后,切线向量为 v2,法线向量为 n2,假设法线向量的变换矩阵为 G,因此存在以下关系:
Unity 中线性变换主要有平移、旋转、缩放,由于向量不受平移变换影响,因此,对于法线向量而言,只受旋转和缩放影响。
- 当 M 只包含旋转变换时,M 是正交矩阵,,因此 G = M;
- 当 M 只包含统一缩放变换时,M = k·E,因此 G = 1/k·E = 1/(k^2)·M,由于法线向量只需要方向,后面会进行归一化,因此可以简写 G = M;
- 当 M 只包含旋转变换和统一缩放变换时,G = 1/(k^2)·M,由于法线向量只需要方向,后面会进行归一化,因此可以简写 G = M;
Unity 中法线变换源码如下:
UnityCG.cginc
// 局部空间->世界空间
float3 UnityObjectToWorldNormal(float3 norm) {
#ifdef UNITY_ASSUME_UNIFORM_SCALING // 统一缩放(x、y、z分量缩放系数一致)return UnityObjectToWorldDir(norm); // normalize(mul((float3x3)unity_ObjectToWorld, norm))
#elsereturn normalize(mul(norm, (float3x3)unity_WorldToObject)); // mul(IT_M, norm) => mul(norm, I_M)
#endif
}
相关文章:

【Unity3D】空间和变换
1 空间 1.1 左右手坐标系及其法则 1.1.1 左右手坐标系 左手坐标系与右手坐标系Unity 局部空间、世界空间、裁剪空间、屏幕空间都采用左手坐标系,只有观察空间采用右手坐标系。 左右手坐标系除了坐标系朝向(旋向性)不同,还存在以…...

脑洞|ChatGPT加持下,ChatOps将如何革新团队协作与运维管理?
要说近期科技圈 “顶流”,非 ChatGPT 莫属。 比起目前常见的语音助手与聊天 bot,这位机器人显得更有 “人味儿”,不仅能模拟人类的语气,跟你聊得有来有回,还能写剧本、编音乐、写代码。 说到聊天工具,就让…...
华为OD机试真题Python实现【找数字】真题+解题思路+代码(20222023)
找数字 题目 给一个二维数组nums,对于每一个元素num[i],找出距离最近的且值相等的元素,输出横纵坐标差值的绝对值之和,如果没有等值元素,则输出-1。 例如: 输入数组nums为 0 3 5 4 2 2 5 7 8 3 2 5 4 2 4对于 num[0][0] = 0,不存在相等的值。 对于 num[0][1] = 3,存…...

【Database-01】达梦数据库Docker版下载安装
1、前往达梦数据库官网下载 https://www.dameng.com/1.1、选择数据库 - 数据库产品系 1.2、选择 达梦数据库管理系统(DM8) 1.3、点击试用下载 1.4、注册达梦账户 1.5、选择DM8 Docker镜像 https://www.dameng.com/list_103.html1.6、或者使用以下网址也…...

Allegro如何打开格点显示效果操作指导
Allegro如何打开格点显示效果操作指导 Allegro可以设置格点显示效果,以格点来判定走线等等是否都处于格点上,如下图 如何打开格点显示效果,具体操作如下 点击Setup点击Grids...

电子技术——反馈放大器的分析方法总结
电子技术——反馈放大器的分析方法总结 第一种也是最简单的估算方法,直接拿出反馈网络,计算 β\betaβ 则假设在 AβA\betaAβ 无限大的情况下有 Af≃1/βA_f \simeq 1/\betaAf≃1/β 。开环法。比第一种方法更能精确的估计 AAA 和 β\betaβ 的值。系…...

微服务系统启动,环境从0开始的搭建过程
1. JDK的下载安装(傻瓜式) 安装过程傻瓜式,直接一步到位。我安装的版本为:jdk-17_windows-x64_bin 2. 集成开发工具的下载安装:IDEA(傻瓜式) ideaIU-2021.2.1 网上资源很多,自己找…...

手工测试1年经验面试,张口要13K,我真是服了····
由于朋友临时有事, 所以今天我代替朋友进行一次面试,他需要应聘一个测试工程师, 我以很认真负责的态度完成这个过程, 大概近30分钟。 主要是技术面试, 在近30分钟内, 我与被面试者是以交流学习的方式进行的…...

【保姆级】手把手捋动态代理流程(JDK+Cglib超详细源码分析)
简介动态代理,通俗点说就是:无需声明式的创建java代理类,而是在运行过程中生成"虚拟"的代理类,被ClassLoader加载。 从而避免了静态代理那样需要声明大量的代理类。上面的简介中提到了两个关键的名词:“静态…...

Appium自动化测试 Inspector定位Webview/H5页面元素
目录操作步骤Python操作该混合App代码Appium在操作混合App或Android App的H5页面时, 常常需要定位H5页面中的元素, 传统方式是 FQ 使用Chrome://inspect来定位元素, 环境准备相当繁琐, 不仅需要想办法FQ, 而且还需要Android设备安装Google框架以及手机版Chrome浏览器以及相应的…...
数组求和方法总结,学点干货
1.循环 (新手用) 1.1 普通for 循环 简单质朴 const arr [1, 2, 3, 4, 5];let sum 0;for (let i 0; i < arr.length; i) {sum arr[i];}1.2 for in 循环 与普通for循环大同小异 const arr [1, 2, 3, 4, 5];let sum 0;for (let i in arr) {sum …...

斗地主洗牌发牌-课后程序(JAVA基础案例教程-黑马程序员编著-第六章-课后作业)
【案例6-4】 斗地主洗牌发牌 【案例介绍】 1.任务描述 扑克牌游戏“斗地主”,相信许多人都会玩,本案例要求编写一个斗地主的洗牌发牌程序,要求按照斗地主的规则完成洗牌发牌的过程。一副扑克总共有54张牌,牌面由花色和数字组成…...
基于antd封装的二次业务筛选组件-table-filter
文档地址:https://flowerofsummer.github.io/components/ 业务筛选组件 支持各种类型的高级搜索组件 基础用法 组件响应式布局,默认显示两行,可以通过 maxLineCount 配置最多显示行数每行个数: 如果含有 time-range࿰…...

逆向-还原代码之max 再画堆栈图 (Interl 64)
// source code #include <stdio.h> void max(int * a, int * b) { if (*a < *b) *a *b; } int main() { int a 5, b 6; max(&a, &b); printf("a, b max %d\n", a); return 0; } // 再画堆栈图 下周一(2.27…...

GitHub标星30K+的Java面试八股文长啥样?
2023年的互联网行业竞争越来越严峻,面试也是越来越难,一直以来我都想整理一套完美的面试宝典,奈何难抽出时间,这套1000道的Java面试手册我整理了整整1个月,上传到Git上目前star数达到了30K 一、32 道 MySQL 面试题 1&…...

CVE-2022-39197 POC(CobaltStrike XSS <=4.7)漏洞复现
漏洞说明 根据9.20日CobaltStrike官方发布的最新4.7.1版本的更新日志中介绍,<4.7的teamserver版本存在XSS漏洞,从而可以造成RCE远程代码执行 一位名为“Beichendream”的独立研究人员联系我们,告知我们他们在团队服务器中发现的一个 XSS …...

我们来说说蹿红的AIGC到底是什么?ChatGPT又是什么?
近期,人工智能(AI)领域动作频频,OPENAI公司Chat GPT的出现,标志着人工智能的研究与应用已经进入了一个崭新的发展阶段,国内腾讯、阿里巴巴、百度、易网、国外微软、谷歌、苹果、IBM、Amazon,等互…...

新手如何从零开始搭建配置Windows云服务器?
新手如何从零开始搭建配置Windows云服务器?本文是搭建 Windows 云服务器入门教程,主要介绍如何从零开始,以最简单的方式搭建和配置你的Windows 云服务器。如果您之前没有搭建云服务器的经验,建议您按照本文介绍的方式来购买和配置…...

百趣代谢组学-抑郁症居然“男女有别”,脑膜淋巴管起关键作用!
文章标题:A functional role of meningeal lymphatics in sex difference of stress susceptibility in mice 发表期刊:Nature Communications 影响因子:17.694 发表时间:2022年8月 作者单位:中山大学中山医学院 …...

C语言实现用堆解决 TOP-K 问题
目录 TopK函数实现 如何测试 完整源码 生活中我们经常能见到TopK问题,例如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。 所以,TopK问题即求出一组数据中前K个最大或最小的元素,一般情况下,数据量都…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...

五子棋测试用例
一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏,有着深厚的文化底蕴。通过将五子棋制作成网页游戏,可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家,都可以通过网页五子棋感受到东方棋类…...

Spring AOP代理对象生成原理
代理对象生成的关键类是【AnnotationAwareAspectJAutoProxyCreator】,这个类继承了【BeanPostProcessor】是一个后置处理器 在bean对象生命周期中初始化时执行【org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization】方法时…...

C# winform教程(二)----checkbox
一、作用 提供一个用户选择或者不选的状态,这是一个可以多选的控件。 二、属性 其实功能大差不差,除了特殊的几个外,与button基本相同,所有说几个独有的 checkbox属性 名称内容含义appearance控件外观可以变成按钮形状checkali…...

【免费数据】2005-2019年我国272个地级市的旅游竞争力多指标数据(33个指标)
旅游业是一个城市的重要产业构成。旅游竞争力是一个城市竞争力的重要构成部分。一个城市的旅游竞争力反映了其在旅游市场竞争中的比较优势。 今日我们分享的是2005-2019年我国272个地级市的旅游竞争力多指标数据!该数据集源自2025年4月发表于《地理学报》的论文成果…...