1. 矩阵变换到底在Unity渲染管线里扮演什么角色很多刚接触Unity渲染的朋友一看到矩阵就头大觉得这是图形学里最劝退的部分。我刚开始学的时候也一样看到UNITY_MATRIX_MVP这种宏就本能地想跳过。但后来踩的坑多了才发现矩阵变换是渲染管线的骨架你不把它吃透后面写Shader、做特效、调性能都会卡在同一个地方反复摔跤。这篇文章我想把Unity从C#脚本层设置一个物体的Transform到最终像素出现在屏幕上的整个矩阵变换链路完整地拆一遍。核心关键词就是Unity渲染、矩阵变换、MVP矩阵、空间变换、Shader矩阵。适合有一定Unity基础、写过简单Shader但对面矩阵流转还比较模糊的开发者。我会从CPU端的矩阵计算讲到GPU端的顶点着色器把每一个矩阵的来源、作用、组合方式都说清楚并且给出可以直接复现的验证方法。先说结论性的认知Unity渲染中的矩阵变换本质上就是一系列4x4矩阵的连乘把一个顶点从模型自己的局部坐标系一步步搬到屏幕坐标系。这个过程涉及模型矩阵M、视图矩阵V、投影矩阵P再加上可能存在的法线矩阵、纹理矩阵等辅助矩阵。每一步变换都对应着一个明确的几何意义理解了这个几何意义你就不会再把矩阵当成黑盒了。我在实际项目中遇到过一个问题一个角色模型的描边效果在PC上正常打包到移动端后描边偏移方向完全错了。排查了半天才发现是法线变换矩阵没有考虑非均匀缩放。这个坑让我意识到矩阵变换不只是能跑就行不同平台、不同缩放条件下矩阵的构造方式会直接影响渲染结果的正确性。所以这篇文章不只是讲理论我会把每个矩阵在Unity源码层面的构造逻辑、在Shader中的获取方式、以及实际调试时怎么验证矩阵是否正确都一并说清楚。读完你至少能做到拿到一个顶点变换结果不对的问题知道该从哪个矩阵开始查。2. 从局部空间到屏幕空间五个坐标系与四次变换的完整链路2.1 五个坐标系的定义与几何意义在Unity的渲染管线里一个顶点从被提交到最终显示会依次经过五个坐标系。理解这五个坐标系各自的定义是理解矩阵变换的前提。局部空间Local Space也叫模型空间。这是建模软件里导出模型时顶点坐标所在的坐标系原点通常在模型的中心或者脚底取决于建模时的设置。比如一个CubeUnity内置的Cube在局部空间的顶点坐标是±0.5所以它的边长是1。世界空间World Space。把模型放到场景里之后每个物体有自己的位置、旋转、缩放局部空间的顶点经过模型矩阵变换后就到了世界空间。世界空间是整个场景的统一坐标系所有物体共享同一个原点。视图空间View Space也叫观察空间或相机空间。以相机为原点相机的朝向决定了坐标轴的方向。Unity里视图空间的相机看向-Z方向这一点和OpenGL的习惯一致。视图空间是光照计算常用的空间因为很多光照模型在视图空间下计算更直观。裁剪空间Clip Space。顶点经过投影矩阵变换后进入裁剪空间这个空间的坐标是齐次坐标x, y, z, w。裁剪空间的作用是方便GPU做视锥体裁剪只有落在[-w, w]范围内的顶点才会被保留。透视投影和正交投影的区别就体现在这个矩阵上。屏幕空间Screen Space。裁剪空间经过透视除法x/w, y/w, z/w和视口变换后得到最终的屏幕像素坐标。这一步通常由GPU固定管线完成不需要我们手动计算。2.2 四次变换的矩阵组合顺序从局部空间到屏幕空间需要经过四次变换对应的矩阵组合顺序是局部空间 → [模型矩阵M] → 世界空间 → [视图矩阵V] → 视图空间 → [投影矩阵P] → 裁剪空间 → [透视除法视口变换] → 屏幕空间在Shader中最常见的写法是float4 clipPos mul(UNITY_MATRIX_MVP, float4(localPos, 1.0));这里的UNITY_MATRIX_MVP就是M、V、P三个矩阵的乘积。但要注意这个宏在Unity 5.6之后被标记为过时现在推荐分开写float4 worldPos mul(unity_ObjectToWorld, float4(localPos, 1.0)); float4 viewPos mul(UNITY_MATRIX_V, worldPos); float4 clipPos mul(UNITY_MATRIX_P, viewPos);分开写的好处是中间结果可以复用。比如你需要世界空间坐标做光照需要视图空间坐标做法线变换分开写就不用重复计算。注意矩阵乘法的顺序不能随意调换。mul(M, V)和mul(V, M)的结果完全不同。Unity使用的是列主序矩阵向量在右边所以是mul(matrix, vector)的形式。2.3 为什么是4x4矩阵而不是3x3有人会问三维空间里的变换用3x3矩阵不够吗为什么非要用4x4原因有两个。第一平移变换无法用3x3矩阵表示。3x3矩阵只能表示线性变换旋转、缩放、切变平移是非线性变换必须引入第四个维度。第二齐次坐标可以统一处理透视投影。透视投影中远处的物体看起来小这个除以w的操作在齐次坐标下可以优雅地表达为矩阵乘法。所以4x4矩阵的第4行和第4列不是凑数的它们承载了平移和投影的信息。具体来说列主序下第4列的前三个分量是平移量第4行的前三个分量是投影相关参数右下角的元素在透视投影中通常是0在正交投影中是1。3. 模型矩阵、视图矩阵、投影矩阵的构造细节与参数计算3.1 模型矩阵从TRS到矩阵的合成过程模型矩阵unity_ObjectToWorld的构造来源是物体的Transform组件。Unity在CPU端会把每个物体的Position、Rotation、Scale合成一个4x4矩阵然后传给GPU。合成的顺序是先缩放再旋转最后平移。用公式表示就是M T * R * S其中T是平移矩阵R是旋转矩阵S是缩放矩阵。这个顺序不能反因为矩阵乘法不满足交换律。你可以这样理解一个顶点先被缩放然后绕原点旋转最后平移到目标位置。如果先平移再旋转物体会绕着世界原点转而不是绕自己的中心转。旋转矩阵的构造稍微复杂一些。Unity的Rotation在内部用四元数表示转换成矩阵的公式是R [1-2(y²z²) 2(xy-wz) 2(xzwy) ] [2(xywz) 1-2(x²z²) 2(yz-wx) ] [2(xz-wy) 2(yzwx) 1-2(x²y²)]其中(x, y, z, w)是四元数的四个分量。这个公式看起来吓人但实际使用时不需要手动计算Unity会自动完成。在Shader中获取模型矩阵的方式是直接使用unity_ObjectToWorld。如果需要反向变换世界空间到局部空间使用unity_WorldToObject它是模型矩阵的逆矩阵。实操心得当物体有非均匀缩放时unity_WorldToObject不能直接用来变换法线。法线需要用专门的转置逆矩阵在Shader中通过transpose(mul(unity_WorldToObject, ...))或者Unity提供的UnityObjectToWorldNormal函数来处理。3.2 视图矩阵相机位置与朝向的矩阵表达视图矩阵UNITY_MATRIX_V的作用是把世界空间的顶点变换到以相机为原点的视图空间。它的构造依赖于相机的Position和Rotation。构造视图矩阵的标准方法是先求相机变换矩阵的逆矩阵。相机变换矩阵C T * R其中T是相机位置的平移矩阵R是相机旋转矩阵。视图矩阵V C⁻¹ R⁻¹ * T⁻¹。由于旋转矩阵是正交矩阵R⁻¹ Rᵀ。所以视图矩阵可以写成V Rᵀ * T⁻¹展开后视图矩阵的前3x3部分是相机旋转矩阵的转置第4列是相机位置经过旋转转置后的负值。在Unity中视图矩阵可以直接通过UNITY_MATRIX_V获取。如果你需要手动验证可以在C#中这样计算Matrix4x4 viewMatrix Matrix4x4.TRS( camera.transform.position, camera.transform.rotation, Vector3.one ).inverse;这个结果和Shader中的UNITY_MATRIX_V应该是一致的注意Unity的坐标系是左手系相机看向Z但视图空间看向-Z所以实际会有一些符号差异。3.3 投影矩阵透视与正交的本质区别投影矩阵UNITY_MATRIX_P决定了视锥体的形状也就是哪些物体能被看到、怎么被投影到屏幕上。Unity支持两种投影方式透视投影和正交投影。透视投影矩阵的构造需要四个参数FOV视场角、Aspect宽高比、Near近裁剪面、Far远裁剪面。矩阵形式是P [1/(Aspect*tan(FOV/2)) 0 0 0 ] [0 1/tan(FOV/2) 0 0 ] [0 0 -(FarNear)/(Far-Near) -2*Far*Near/(Far-Near)] [0 0 -1 0 ]这个矩阵的关键特征是第4行第3列是-1这就是透视除法的来源。顶点经过这个矩阵变换后w分量等于负的视图空间z值所以远处的顶点w更大除以w后坐标更小实现了近大远小的效果。正交投影矩阵的构造需要六个参数左右上下远近裁剪面。矩阵形式是P [2/(Right-Left) 0 0 0 ] [0 2/(Top-Bottom) 0 0 ] [0 0 -2/(Far-Near) -(FarNear)/(Far-Near)] [0 0 0 1 ]正交投影的第4行是(0, 0, 0, 1)没有透视除法所以物体不会因为距离而缩放。在Shader中UNITY_MATRIX_P会根据相机的投影模式自动切换。如果你需要手动构造投影矩阵可以使用GL.GetGPUProjectionMatrix来获取平台相关的投影矩阵因为不同图形API的裁剪空间z范围不同OpenGL是[-1, 1]DirectX是[0, 1]。注意在移动平台上投影矩阵的精度问题比较突出。如果Near和Far的比值过大比如Near0.01Far10000深度缓冲的精度会严重下降导致Z-Fighting。我的经验是尽量把Near设大一些比如0.1或0.3Far根据场景实际需要设置不要无脑设成10000。4. Shader中矩阵变换的实操写法与性能优化4.1 顶点着色器中的标准变换流程一个标准的顶点着色器矩阵变换流程大概长这样v2f vert(appdata v) { v2f o; // 局部空间到世界空间 float4 worldPos mul(unity_ObjectToWorld, v.vertex); // 世界空间到裁剪空间 o.pos mul(UNITY_MATRIX_VP, worldPos); // 法线变换使用逆转置矩阵 o.normal UnityObjectToWorldNormal(v.normal); // 纹理坐标变换 o.uv TRANSFORM_TEX(v.uv, _MainTex); // 世界空间位置用于光照计算 o.worldPos worldPos.xyz; return o; }这里有几个细节值得展开说。UNITY_MATRIX_VP是视图矩阵和投影矩阵的乘积Unity在CPU端预先计算好避免在Shader里做两次矩阵乘法。类似地UNITY_MATRIX_MVP是M、V、P的乘积但前面说过它已经过时了。UnityObjectToWorldNormal是Unity提供的内置函数它内部处理了非均匀缩放的情况。如果你手动写mul(unity_ObjectToWorld, v.normal)在非均匀缩放下法线方向会出错。TRANSFORM_TEX宏的作用是应用纹理的Tiling和Offset等价于v.uv * _MainTex_ST.xy _MainTex_ST.zw。4.2 矩阵乘法在Shader中的性能考量矩阵乘法在GPU上是有成本的虽然现代GPU的矩阵运算能力很强但在移动端还是要注意优化。第一个优化点是减少不必要的矩阵乘法。比如你只需要裁剪空间坐标就直接用UNITY_MATRIX_MVP或UNITY_MATRIX_VP不要分开算。虽然分开算可以复用中间结果但如果你不需要中间结果分开算就是浪费。第二个优化点是利用Shader的常量缓冲区。Unity会把常用的矩阵放在常量缓冲区里比如UnityPerDraw和UnityPerFrame。这些矩阵在多个Draw Call之间可以复用不会重复上传。你自己在Shader里定义的矩阵变量则可能每个Draw Call都要更新。第三个优化点是避免在片元着色器里做矩阵乘法。片元着色器的执行次数远多于顶点着色器矩阵乘法应该尽量放在顶点着色器里完成把结果插值后传给片元着色器。下面是一个对比表格列出了常见矩阵在Shader中的获取方式和适用场景矩阵名称Shader变量作用适用场景模型矩阵unity_ObjectToWorld局部→世界顶点变换、世界空间光照模型逆矩阵unity_WorldToObject世界→局部法线变换、局部空间效果视图矩阵UNITY_MATRIX_V世界→视图视图空间光照、反射投影矩阵UNITY_MATRIX_P视图→裁剪手动投影计算VP矩阵UNITY_MATRIX_VP世界→裁剪顶点变换推荐MVP矩阵UNITY_MATRIX_MVP局部→裁剪旧版写法已过时纹理矩阵unity_ObjectToWorldUV变换纹理动画、投影纹理4.3 法线变换矩阵的特殊处理法线变换是矩阵变换里最容易出错的地方。法线是方向向量不是位置向量所以不能用模型矩阵直接变换。原因在于如果模型有非均匀缩放比如x轴放大2倍y轴不变那么一个斜着的法线经过模型矩阵变换后方向会偏离正确的垂直方向。正确的做法是使用模型矩阵的逆转置矩阵。推导过程是这样的假设切线向量t和法线向量n垂直即dot(t, n) 0。经过模型矩阵M变换后切线变成Mt法线变成NnN是法线变换矩阵。要保持垂直关系需要dot(Mt, Nn) 0即tᵀ * Mᵀ * N * n 0。由于tᵀ * n 0所以Mᵀ * N I即N (Mᵀ)⁻¹ (M⁻¹)ᵀ。在Shader中Unity提供了UnityObjectToWorldNormal函数来处理这个变换。如果你需要手动实现可以这样写float3 normalWorld normalize(mul(v.normal, (float3x3)unity_WorldToObject));注意这里用的是unity_WorldToObject模型矩阵的逆并且是向量在左边、矩阵在右边的写法等价于用逆转置矩阵变换法线。实操心得在移动端如果模型没有非均匀缩放可以直接用unity_ObjectToWorld变换法线省去逆转置的开销。但为了代码的健壮性建议统一使用UnityObjectToWorldNormal性能差异在大多数场景下可以忽略。5. 矩阵变换调试实战从现象反推问题根源5.1 常见问题速查表矩阵变换出问题时表现往往很直观物体位置不对、朝向反了、缩放异常、渲染撕裂等。下面这张表整理了我实际项目中遇到过的典型问题、排查思路和解决方法。现象可能原因排查方法解决方案物体位置偏移模型矩阵平移分量错误打印unity_ObjectToWorld第4列检查Transform.Position物体朝向反转旋转矩阵符号错误对比Scene视图和Game视图检查四元数转矩阵逻辑非均匀缩放下法线错误未使用逆转置矩阵在Shader中输出法线可视化使用UnityObjectToWorldNormal移动端描边偏移投影矩阵平台差异对比不同平台的UNITY_MATRIX_P使用GL.GetGPUProjectionMatrix深度冲突Z-FightingNear/Far比值过大查看深度缓冲精度增大Near减小Far顶点变换后位置抖动矩阵精度不足检查是否用了half精度改用float精度正交相机下物体消失投影矩阵参数错误检查正交相机的Size调整正交相机参数纹理坐标错乱纹理矩阵未应用检查TRANSFORM_TEX正确使用_ST变量5.2 一个真实的排查案例移动端描边偏移前面提到过我在项目中遇到的描边偏移问题这里详细说一下排查过程。现象是一个角色模型的描边效果在PC编辑器里正常打包到Android后描边在屏幕上的偏移方向完全错了本来应该向外扩的描边变成了向内缩。第一步我怀疑是法线变换的问题。在Shader里把法线可视化输出发现移动端的法线方向和PC端不一致。但法线变换用的是UnityObjectToWorldNormal理论上不应该有平台差异。第二步我检查了投影矩阵。在Shader里输出UNITY_MATRIX_P的每一行对比PC和Android的结果。发现第2行第2列的值不同PC上是1/tan(FOV/2)Android上是1/tan(FOV/2) * aspect。这说明Android平台的投影矩阵在y轴方向上多乘了一个aspect。第三步我查了Unity的文档发现不同图形API对投影矩阵的处理确实不同。OpenGL风格的API包括OpenGL ES的裁剪空间y范围是[-1, 1]而DirectX风格的API是[0, 1]。Unity在GL.GetGPUProjectionMatrix中做了平台适配但如果你手动构造投影矩阵就需要自己处理这个差异。解决方案是在Shader中不要手动构造投影矩阵直接使用UNITY_MATRIX_P。如果需要在C#中传递投影矩阵使用GL.GetGPUProjectionMatrix(camera.projectionMatrix, renderIntoTexture)来获取平台相关的矩阵。这个案例给我的教训是矩阵变换的平台差异是真实存在的不要假设PC上跑通了的Shader在移动端也一定正确。特别是涉及投影矩阵和裁剪空间的操作一定要在目标平台上验证。5.3 矩阵验证的实用技巧调试矩阵变换时有几个技巧可以帮你快速定位问题。技巧一在Shader中输出矩阵的特定分量。比如你想知道模型矩阵的平移量可以这样写o.color float4(unity_ObjectToWorld._m03_m13_m23, 1.0);这样物体的颜色就反映了它的世界位置位置变了颜色就变了非常直观。技巧二用Gizmos在Scene视图中画出变换后的坐标轴。在C#中这样写void OnDrawGizmos() { Matrix4x4 oldMatrix Gizmos.matrix; Gizmos.matrix transform.localToWorldMatrix; Gizmos.color Color.red; Gizmos.DrawLine(Vector3.zero, Vector3.right); Gizmos.color Color.green; Gizmos.DrawLine(Vector3.zero, Vector3.up); Gizmos.color Color.blue; Gizmos.DrawLine(Vector3.zero, Vector3.forward); Gizmos.matrix oldMatrix; }这段代码会在Scene视图中画出物体的局部坐标轴你可以直观地看到物体的朝向和缩放。技巧三用Frame Debugger查看每个Draw Call的矩阵。Unity的Frame Debugger可以显示每个Draw Call的详细信息包括使用的Shader、材质、以及传递的矩阵常量。如果你发现某个物体的变换不对可以在Frame Debugger里找到对应的Draw Call检查矩阵的值。注意Frame Debugger显示的矩阵是列主序的和Shader中的写法一致。如果你习惯行主序的思维看的时候要注意转置。6. 进阶话题矩阵变换在特殊渲染场景中的应用6.1 阴影映射中的矩阵变换阴影映射Shadow Mapping是矩阵变换的一个典型应用场景。它的核心思路是从光源的角度渲染一遍场景把深度信息存到一张纹理里然后在正常渲染时把每个像素变换到光源空间比较深度值来判断是否在阴影中。这个过程涉及两个额外的矩阵光源视图矩阵和光源投影矩阵。在Shader中Unity提供了unity_WorldToShadow矩阵它把世界空间坐标变换到阴影纹理的UV空间。float4 shadowCoord mul(unity_WorldToShadow[0], float4(worldPos, 1.0)); float shadow tex2D(_ShadowMapTexture, shadowCoord.xy);这里unity_WorldToShadow是光源VP矩阵的逆乘以一个偏移矩阵偏移矩阵的作用是把裁剪空间的[-1, 1]范围映射到纹理的[0, 1]范围。阴影映射中常见的矩阵问题是阴影偏移Shadow Bias。由于阴影纹理的分辨率有限直接比较深度会产生自阴影Shadow Acne。解决方法是在比较深度时加一个偏移量这个偏移量需要在光源空间计算涉及投影矩阵的缩放因子。6.2 屏幕空间反射中的矩阵变换屏幕空间反射SSR需要把像素的世界空间位置反投影回屏幕空间这涉及视图矩阵和投影矩阵的逆矩阵。float4 clipPos mul(UNITY_MATRIX_VP, float4(worldPos, 1.0)); float2 screenUV clipPos.xy / clipPos.w * 0.5 0.5;这里clipPos.xy / clipPos.w是透视除法得到NDC坐标范围[-1, 1]然后乘以0.5加0.5映射到UV坐标范围[0, 1]。SSR中常见的矩阵问题是深度重建。从深度纹理重建世界空间位置时需要用到投影矩阵的逆矩阵。如果投影矩阵的Near和Far设置不当重建出的世界位置会有误差导致反射效果偏移。6.3 骨骼动画中的矩阵变换骨骼动画的每个顶点会受到多个骨骼的影响每个骨骼有一个变换矩阵。最终的顶点位置是finalPos Σ (weight_i * boneMatrix_i * bindPoseMatrix_i * localPos)其中boneMatrix_i是骨骼的当前变换矩阵bindPoseMatrix_i是骨骼的绑定姿态矩阵。这两个矩阵的乘积就是骨骼的蒙皮矩阵。在Shader中Unity把蒙皮矩阵存在unity_BoneMatrices数组里每个顶点通过boneWeights和boneIndices来索引。float4 skinnedPos 0; for (int i 0; i 4; i) { skinnedPos boneWeights[i] * mul(unity_BoneMatrices[boneIndices[i]], localPos); }骨骼动画中常见的矩阵问题是蒙皮矩阵的精度。在移动端如果骨骼数量多、模型复杂蒙皮矩阵的精度可能不够导致顶点抖动。解决方法是使用float精度存储蒙皮矩阵或者减少每顶点的骨骼影响数量。7. 矩阵变换的性能分析与优化建议7.1 CPU端矩阵计算的成本Unity在CPU端需要为每个物体计算模型矩阵为每个相机计算视图矩阵和投影矩阵。这些计算本身不贵但如果有大量物体累积起来也是一笔开销。优化的思路是减少矩阵更新的频率。如果物体的Transform没有变化Unity会缓存模型矩阵不会重复计算。所以静态物体应该标记为Static这样Unity可以批量处理减少Draw Call和矩阵上传。另一个优化点是使用GPU Instancing。当多个物体共享同一个材质和网格时GPU Instancing可以把它们的模型矩阵打包成一个数组一次性传给GPU减少Draw Call和矩阵上传的次数。7.2 GPU端矩阵乘法的成本GPU端的矩阵乘法主要发生在顶点着色器里。每个顶点都要做一次MVP变换如果有100万个顶点就是100万次4x4矩阵乘法。现代GPU的矩阵运算能力很强这个成本通常不是瓶颈。但如果你的Shader里做了额外的矩阵乘法比如在片元着色器里做矩阵运算成本就会显著上升。片元着色器的执行次数是顶点着色器的几十倍甚至上百倍矩阵乘法应该尽量放在顶点着色器里。还有一个容易被忽略的点是矩阵的精度。在移动端使用half精度可以提升性能但half精度的矩阵在变换大坐标时会有精度问题。我的经验是模型矩阵和视图矩阵用float精度投影矩阵可以用half精度因为投影矩阵的元素值通常比较小。7.3 不同平台的矩阵差异不同图形API对矩阵的处理有差异主要体现在两个方面裁剪空间的z范围和纹理坐标的y方向。裁剪空间z范围OpenGL是[-1, 1]DirectX是[0, 1]Metal是[0, 1]。Unity在GL.GetGPUProjectionMatrix中做了适配但如果你手动构造投影矩阵就需要自己处理。纹理坐标y方向OpenGL的纹理坐标原点在左下角DirectX在左上角。Unity在导入纹理时会自动处理这个差异但如果你在Shader中手动计算UV就需要注意。下面这张表总结了主流图形API的矩阵差异平台/API裁剪空间z范围纹理y方向矩阵主序OpenGL[-1, 1]下到上列主序DirectX[0, 1]上到下行主序Metal[0, 1]上到下列主序Vulkan[0, 1]上到下列主序Unity在内部统一使用列主序矩阵但在传递给不同API时会做转换。作为开发者你只需要使用Unity提供的宏和函数不需要手动处理这些差异。但如果你在写原生插件或者自定义渲染管线就需要了解这些差异。实操心得在跨平台项目中我习惯在Shader中统一使用Unity的内置矩阵和函数避免手动构造矩阵。如果确实需要手动构造会在目标平台上逐一验证。踩过几次坑之后我养成了一个习惯任何涉及矩阵变换的新Shader都要在PC、Android、iOS三个平台上各跑一遍确认结果一致再提交。8. 我个人的矩阵变换调试心得矩阵变换这个东西看再多理论不如自己动手验证一遍。我刚开始学的时候把MVP矩阵的推导过程背得滚瓜烂熟但真正写Shader时还是经常搞错。后来我养成了一个习惯每写一个涉及矩阵变换的Shader都会先用Gizmos或者颜色输出把中间结果可视化出来确认每一步变换都符合预期再继续往下写。另一个心得是不要害怕矩阵。矩阵本质上就是一种坐标映射工具你把输入坐标和输出坐标的几何意义想清楚矩阵的构造就顺理成章了。比如模型矩阵输入是局部空间的顶点输出是世界空间的顶点中间经历了缩放、旋转、平移对应的矩阵就是TRS。想清楚这个逻辑比死记矩阵元素的值有用得多。最后分享一个我常用的调试方法在Shader中把世界空间坐标直接作为颜色输出o.color float4(worldPos.xyz, 1.0)。这样你可以直观地看到每个像素的世界位置如果颜色分布不对说明矩阵变换有问题。这个方法帮我定位过很多次矩阵相关的Bug简单但有效。
阅读完成 · 觉得有帮助?