news 2026/4/18 10:22:21

【URP】Unity[法线贴图]原理与实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【URP】Unity[法线贴图]原理与实践

解决的问题

‌性能优化‌:用低多边形模型配合法线贴图替代高模,减少计算开销

‌细节增强‌:通过RGB通道存储法线方向,模拟表面凹凸、划痕等微观结构

‌动态光照响应‌:每个像素的法线独立参与光照计算,实现更真实的明暗变化

历史发展节点

‌1998年‌:首次由Crytek在游戏《Far Cry》中大规模应用

‌2004年‌:成为DirectX 9标准特性,进入主流游戏引擎

‌2018年‌:Unity URP管线整合法线贴图标准化工作流,支持移动端优化

‌2022年‌:HLSL语法改进,分离纹理对象与采样器声明

生成与使用流程

生成方法

‌高模烘焙‌:通过ZBrush等工具将高模细节烘焙到低模法线贴图

‌程序生成‌:Substance Designer等工具从高度图转换生成

‌手动绘制‌:Photoshop使用滤镜生成基础法线纹理

详细存储原理参看了解具体如何计算和存储的。

URP实现步骤

‌纹理导入‌

类型设为Default,勾选Bump Map自动切换模式

压缩格式推荐BC5 (DXT5nm)或BC7

‌材质配置

Shader选择:URP > Lit 或 Simple Lit

法线贴图拖拽至Normal Map插槽

调整Normal Scale参数控制凹凸强度(0.5-1.5为常用范围‌

‌Shader核心原理‌

‌切线空间转换‌:通过TBN矩阵将法线从切线空间转到世界空间

‌光照计算‌:转换后的法线与光源方向点积决定漫反射强度

完整示例代码

以下URP Shader实现法线贴图与基础光照:

‌顶点着色器‌:计算世界空间法线和切线

‌片段着色器‌:采样法线贴图并通过TBN矩阵转换

‌光照模型‌:采用Lambert漫反射计算

NormalMapShader.shader

Shader "Custom/NormalMapShader" {

Properties {

_MainTex("Albedo", 2D) = "white" {}

_NormalMap("Normal Map", 2D) = "bump" {}

_NormalScale("Normal Scale", Range(0,2)) = 1

}

SubShader {

Tags { "RenderPipeline"="UniversalPipeline" }

HLSLINCLUDE

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

ENDHLSL

Pass {

HLSLPROGRAM

#pragma vertex vert

#pragma fragment frag

struct Attributes {

float4 positionOS : POSITION;

float2 uv : TEXCOORD0;

float3 normalOS : NORMAL;

float4 tangentOS : TANGENT;

};

struct Varyings {

float4 positionCS : SV_POSITION;

float2 uv : TEXCOORD0;

float3 normalWS : TEXCOORD1;

float4 tangentWS : TEXCOORD2;

};

sampler2D _MainTex;

sampler2D _NormalMap;

float _NormalScale;

Varyings vert(Attributes IN) {

Varyings OUT;

VertexPositionInputs posInput = GetVertexPositionInputs(IN.positionOS.xyz);

OUT.positionCS = posInput.positionCS;

OUT.uv = IN.uv;

VertexNormalInputs normInput = GetVertexNormalInputs(IN.normalOS, IN.tangentOS);

OUT.normalWS = normInput.normalWS;

OUT.tangentWS = float4(normInput.tangentWS, IN.tangentOS.w);

return OUT;

}

half4 frag(Varyings IN) : SV_Target {

float4 normalSample = tex2D(_NormalMap, IN.uv);

float3 tangentNormal = UnpackNormalScale(normalSample, _NormalScale);

float3 normalWS = IN.normalWS;

float3 tangentWS = IN.tangentWS.xyz;

float3 bitangentWS = cross(normalWS, tangentWS) * IN.tangentWS.w;

float3x3 TBN = float3x3(tangentWS, bitangentWS, normalWS);

float3 finalNormal = mul(tangentNormal, TBN);

Light mainLight = GetMainLight();

float NdotL = saturate(dot(finalNormal, mainLight.direction));

half3 albedo = tex2D(_MainTex, IN.uv).rgb;

half3 diffuse = albedo * NdotL * mainLight.color;

return half4(diffuse, 1);

}

ENDHLSL

}

}

}

数据结构定义

Attributes结构体:声明顶点输入数据

positionOS:模型空间顶点位置

uv:纹理坐标

normalOS:模型空间法线

tangentOS:模型空间切线(含手性信息)

Varyings结构体:定义顶点到片段的传递数据

positionCS:裁剪空间位置

normalWS:世界空间法线(通过URP内置函数转换)

tangentWS:世界空间切线(保留手性分量)

顶点着色器实现

核心流程:

调用GetVertexPositionInputs转换模型空间到裁剪空间

通过GetVertexNormalInputs计算世界空间法线和切线

保持原始UV坐标传递

片段着色器实现

法线贴图处理:

float4 normalSample = tex2D(_NormalMap, IN.uv); float3 tangentNormal = UnpackNormalScale(normalSample, _NormalScale);

使用UnpackNormalScale函数解压法线贴图(范围从[0,1]映射到[-1,1])并应用强度参数。

TBN矩阵构建:

float3x3 TBN = float3x3(tangentWS, bitangentWS, normalWS); float3 finalNormal = mul(tangentNormal, TBN);

通过切向量、副法线和法线构建正交基,将切线空间法线转换到世界空间。

光照计算:

Light mainLight = GetMainLight(); float NdotL = saturate(dot(finalNormal, mainLight.direction)); half3 diffuse = albedo * NdotL * mainLight.color;

采用Lambert漫反射模型,计算法线与光源方向的点积作为光照强度因子。

关键函数说明

GetVertexPositionInputs:URP内置函数,处理顶点位置变换

UnpackNormalScale:URP提供的法线贴图解压函数

GetMainLight:获取场景主光源信息(需配合URP的Lightweight Render Pipeline使用)

小结

坐标空间转换:完整实现模型空间→世界空间→切线空间的转换链

光照模型:基于物理的简单漫反射计算

性能优化:使用half类型减少内存占用,适合移动端

扩展性:通过_NormalScale参数可动态调整法线贴图强度

实际项目应用

‌角色模型‌:增强皮肤皱纹或服装褶皱细节

‌环境场景‌:表现砖墙缝隙或金属表面划痕

‌性能权衡‌:移动端建议使用Simple Lit简化版着色器

关键注意事项:

确保模型具有正确的UV和切线数据

避免sRGB模式导入法线贴图

多光源场景需在Shader中添加额外光照循环

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

从0到1:我用LLM搭建医学知识库,附详细教程,建议永久收藏

作为一名医学生,作者分享了他使用阿里开源的qwen2.5:7b模型,在本地构建医学知识库的实践经历。他详细描述了从下载模型、处理教材到调试系统的全过程,并强调了AI技术已将创造门槛大幅降低。作者认为,在这个时代,通过动…

作者头像 李华
网站建设 2026/4/18 6:30:25

【年度消费观察】2025,年轻人没有抛弃白酒

文 | 螳螂观察作者 | 风车关于多渠道飞天茅台价格跌破1499元/瓶这事,外界大多将原因归于行业周期调整的大环境因素,或者是禁酒令,或者是消费降级。通俗地讲,当下的产业状态以及酒企集体下行的趋势,支撑不起原有的价格了…

作者头像 李华
网站建设 2026/4/18 8:39:07

【课程设计/毕业设计】基于springboot的校园一卡通管理系统的设计与实现学生身份认证、餐饮消费、缴费管理【附源码、数据库、万字文档】

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

作者头像 李华
网站建设 2026/4/3 18:43:41

精通C++類型系統的工程師:為何他們平均薪資高85%及完整學習路線圖

精通C類型系統的工程師:為何他們平均薪資高85%及完整學習路線圖引言:類型系統的經濟價值在當今軟件工程領域,一個令人矚目的數據值得關注:精通C類型系統的工程師平均薪資比其他工程師高出85%。這個差距不是偶然的,而是…

作者头像 李华