news 2026/6/12 10:21:58

Unity URP 切线空间详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Unity URP 切线空间详解

深入理解 TBN 矩阵、法线贴图与着色器实现

一、什么是切线空间(Tangent Space)

在 3D 图形渲染中,切线空间(Tangent Space)是一个相对于模型表面的局部坐标系。 与世界空间(World Space)或物体空间(Object Space)不同,切线空间的原点位于每个顶点的表面上, 其三个基向量会随着表面的弯曲而"贴合"在模型上。

切线空间的核心价值在于:让法线贴图(Normal Map)可以通用化。一张在平面上的法线贴图,可以直接应用到任意弯曲的表面上,而无需为每个模型单独制作法线贴图。

💡 核心概念:切线空间是一个"跟随表面"的坐标系。每个顶点都有自己的切线空间, 法线贴图中存储的 RGB 值,实际上是在这个局部空间中的 (x, y, z) 方向偏移量。

二、TBN 矩阵的构成

TBN 矩阵由三个相互垂直的向量组成,将向量在切线空间与世界/物体空间之间进行转换:

向量英文方向作用
TTangent(切线)沿表面的水平方向(UV 的 U 方向)定义表面的"左右"方向
BBitangent / Binormal(副切线)沿表面的垂直方向(UV 的 V 方向)定义表面的"上下"方向
NNormal(法线)垂直于表面向外定义表面的"凹凸"方向

2.1 法线(Normal)

法线向量N垂直于表面,通常由模型顶点数据直接提供(mesh.normals)。 在 Unity 中,每个顶点都携带一个法线向量,用于光照计算的基础方向。

2.2 切线(Tangent)

切线向量T沿着表面的 UV 坐标的 U 方向。 它告诉着色器:在模型表面上,"向右"是哪个方向。 Unity 的Mesh.RecalculateTangents()可以自动计算切线。

2.3 副切线(Bitangent)

副切线B由法线和切线的叉积得到:B = N × T。 它沿着 UV 的 V 方向,与 T 和 N 共同构成正交坐标系。

数学关系:
T · N = 0(切线与法线垂直)
B · N = 0(副切线与法线垂直)
B · T = 0(副切线与切线垂直)
|T| = |B| = |N| = 1(单位向量)

三、URP 中的切线空间实现

在 Unity 的 URP(Universal Render Pipeline)中,切线空间的计算和使用已经高度封装。 核心 Shader 变量和函数位于以下文件中:

  • Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl
  • Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl

3.1 顶点着色器中的 TBN 计算

在 URP 的VertexShader中,通常这样计算 TBN 矩阵:

// 在顶点着色器中 v2f vert(appdata v) { v2f o; // 基本的 MVP 变换 o.positionCS = TransformObjectToHClip(v.vertex.xyz); // 法线(世界空间) o.normalWS = TransformObjectToWorldNormal(v.normal); // 切线(世界空间) o.tangentWS = TransformObjectToWorldDir(v.tangent.xyz); // 副切线(世界空间) // w 分量存储手性信息(用于镜像 UV) o.bitangentWS = cross(o.normalWS, o.tangentWS) * v.tangent.w; return o; }

⚠️ 注意:v.tangent.w存储了副切线的方向符号(+1 或 -1), 用于处理 UV 镜像导致的坐标系手性问题。忽略它会导致法线贴图在镜像模型上出现错误。

3.2 片元着色器中的法线贴图解码

在片元着色器中,将法线贴图的采样结果从切线空间转换到世界空间:

// 在片元着色器中 half4 frag(v2f i) : SV_Target { // 采样法线贴图 half4 normalMap = SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, i.uv); // 解码法线贴图(URP 工具函数) // 法线贴图中的值范围是 [0, 1],需要映射到 [-1, 1] half3 normalTS = UnpackNormal(normalMap); // 构建 TBN 矩阵(世界空间) half3 N = normalize(i.normalWS); half3 T = normalize(i.tangentWS); half3 B = normalize(i.bitangentWS); // 将法线从切线空间转换到世界空间 half3 normalWS = normalize( T * normalTS.x + B * normalTS.y + N * normalTS.z ); // 使用世界空间法线进行光照计算 // ... }

3.3 URP 内置的 TBN 辅助函数

URP 提供了更简洁的实现方式,使用内置宏:

// 方法一:使用 URP 内置的 TransformTangentToWorld half3 normalWS = TransformTangentToWorld( normalTS, half3x3(i.tangentWS, i.bitangentWS, i.normalWS) ); // 方法二:使用法线贴图助手(推荐) // 在 Properties 中声明 _NormalMap 和 _BumpScale half3 normalWS = NormalizeNormalPerPixel( TransformTangentToWorld( normalTS, half3x3(i.tangentWS, i.bitangentWS, i.normalWS) ) );

四、完整的 URP Shader 代码示例

以下是一个完整的 URP Shader 示例,展示如何正确使用切线空间进行法线贴图计算:

Shader "Custom/URPTangentSpaceExample" { Properties { _BaseMap ("Albedo", 2D) = "white" {} _NormalMap ("Normal Map", 2D) = "bump" {} _BumpScale ("Normal Scale", Range(0, 2)) = 1.0 _BaseColor ("Color", Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" } Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float4 tangent : TANGENT; float2 uv : TEXCOORD0; }; struct v2f { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; float3 normalWS : TEXCOORD1; float3 tangentWS : TEXCOORD2; float3 bitangentWS : TEXCOORD3; float3 positionWS : TEXCOORD4; }; TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap); TEXTURE2D(_NormalMap); SAMPLER(sampler_NormalMap); CBUFFER_START(UnityPerMaterial) float4 _BaseMap_ST; float4 _BaseColor; float _BumpScale; CBUFFER_END v2f vert(appdata v) { v2f o; o.positionCS = TransformObjectToHClip(v.vertex.xyz); o.positionWS = TransformObjectToWorld(v.vertex.xyz); o.uv = TRANSFORM_TEX(v.uv, _BaseMap); // 计算世界空间 TBN o.normalWS = TransformObjectToWorldNormal(v.normal); o.tangentWS = TransformObjectToWorldDir(v.tangent.xyz); o.bitangentWS = cross(o.normalWS, o.tangentWS) * v.tangent.w; return o; } half4 frag(v2f i) : SV_Target { // 采样法线贴图 half4 normalSample = SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, i.uv); half3 normalTS = UnpackNormal(normalSample); normalTS.xy *= _BumpScale; // 构建 TBN 矩阵并转换到世界空间 half3 N = normalize(i.normalWS); half3 T = normalize(i.tangentWS); half3 B = normalize(i.bitangentWS); half3 normalWS = normalize(mul(normalTS, half3x3(T, B, N))); // 采样 Albedo half4 albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, i.uv) * _BaseColor; // 光照计算 Light mainLight = GetMainLight(); half NdotL = max(0, dot(normalWS, mainLight.direction)); half3 lighting = mainLight.color * NdotL; // 最终颜色 half3 color = albedo.rgb * lighting + albedo.rgb * 0.1; // 加上环境光 return half4(color, albedo.a); } ENDHLSL } } }

五、常见问题与注意事项

❌ 常见错误
  • 忘记对法线进行normalize
  • 忽略tangent.w手性符号
  • 在片元着色器中对未归一化的 TBN 向量进行变换
  • 法线贴图未设置为"Normal Map"类型
  • 在 URP 中使用了内置管线的_WorldSpaceLightPos0
✅ 最佳实践
  • 始终在片元着色器中重新归一化法线
  • 使用UnpackNormal()解码法线贴图
  • 使用 URP 的GetMainLight()获取光源
  • 使用TransformTangentToWorld()辅助函数
  • 在 Shader 面板中正确设置法线贴图纹理类型

5.1 为什么需要重新归一化?

在光栅化过程中,顶点着色器输出的向量在三角面片上进行插值后,长度不再为 1。 因此,在片元着色器中必须重新对法线进行归一化:

// 正确做法 half3 normalWS = normalize(TransformTangentToWorld(normalTS, half3x3(T, B, N)));

5.2 切线空间的左右手坐标系

Unity 使用左手坐标系。 切线空间通常也是左手系(T 向右,B 向上,N 向外)。 但当模型的 UV 出现镜像时,tangent.w会变为 -1, 此时副切线的方向需要翻转以保持正交性。

六、总结

切线空间是 Unity URP 着色器开发中的核心概念,理解它的构成对于正确实现法线贴图至关重要:

要点说明
TBN 矩阵由切线、副切线、法线组成的正交矩阵,用于空间转换
法线贴图存储在切线空间中存储表面凹凸信息,实现贴图复用
URP 实现使用TransformObjectToWorldDirUnpackNormalTransformTangentToWorld
注意事项归一化、手性符号、纹理类型设置

🎯 核心要点:切线空间让法线贴图"跟随表面"。 TBN 矩阵是实现这一点的数学工具,将切线空间的法线方向转换到世界空间, 从而实现正确的光照计算。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/12 10:21:53

GHelper突破性技术架构:重新定义华硕笔记本硬件控制体验

GHelper突破性技术架构:重新定义华硕笔记本硬件控制体验 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook, …

作者头像 李华
网站建设 2026/6/12 10:18:00

2026年高口碑网站建设公司推荐:技术+案例+经验全解析(精选10家)

在数字经济深度渗透商业运营的2026年,企业官方网站已从静态的信息展示载体进化为集品牌战略表达、用户交互体验、业务增长转化于一体的核心数字资产。据《中国企业数字化发展白皮书》数据显示,92%的B2B采购决策者将企业官网作为供应商评估的首要触点。面…

作者头像 李华
网站建设 2026/6/12 10:09:56

TI C2000 DSP浮点性能实战:用TMS320F28377D的FPU库加速你的向量与复数运算

TMS320F28377D浮点加速实战:从理论到实测的性能跃迁在电机控制、音频处理和通信算法等实时性要求严苛的领域,工程师们常常需要面对一个核心挑战:如何在有限的计算资源内完成复杂的浮点运算。德州仪器(TI)的C2000系列DSP凭借其浮点运算单元(FP…

作者头像 李华