法线贴图呈现蓝紫色调(尤其以蓝色为主)是由其‌存储原理、切线空间坐标系设计及颜色编码规则共同决定的‌。

【从UnityURP开始探索游戏渲染】专栏-直达

核心原因:法线向量的存储规则‌

‌法线向量的物理范围‌

法线是单位向量,每个分量(X, Y, Z)的取值范围为 ‌[-1, 1],分别代表切线空间中的方向:

  • ‌X(红色通道):左右偏移(左为负,右为正)
  • ‌Y(绿色通道):上下偏移(下为负,上为正)
  • ‌Z(蓝色通道):垂直表面的方向(指向外部为正)‌。

‌颜色空间的映射限制‌

图像颜色值范围是 ‌[0, 1](对应0~255),因此需要进行转换:

RGB=(Normalxyz+1)/2

  • 默认法线方向‌:当表面完全垂直(无倾斜)时,法线向量为 ‌(0, 0, 1)。
  • 转换结果‌:
    • R=20+1=0.5 (128)
    • G=20+1=0.5 (128)
    • B=21+1=1 (255)
    • 最终颜色为 ‌(128, 128, 255),即 ‌蓝紫色‌(蓝色占主导)‌。

‌现实模型的主导方向

  • 大多数模型表面(如墙面、地面)以‌垂直方向为主‌(Z≈1),因此蓝色通道值接近255,而XY通道接近128(中性灰),整体呈现蓝色基调‌。

‌颜色变化的场景解释‌

颜色表现 对应的法线方向 表面形态
‌深蓝色 (0,0,1) 完全垂直向外 平坦表面(如地板)
‌蓝紫色 (0.5,0.5,1) 轻微倾斜 缓坡、弧形表面
‌青色/绿色 (低R,高G,中B) 明显上/下倾斜(Y≠0) 边缘、陡坡
‌红色/粉色 (高R,中G,中B)‌ 明显左/右倾斜(X≠0) 侧壁、凹凸边缘

‌示例‌:墙面法线贴图中,砖缝凹陷处因法线指向侧方(X/Y增大),可能呈现红绿色调,但整体仍以蓝紫色为基底‌。


️ ‌技术实现验证‌

‌生成与解码逻辑‌

  • 生成法线贴图‌:通过公式 color = (normal + 1) / 2 将高模法线烘焙为贴图‌。

  • Shader解码‌:在着色器中逆向计算还原法线向量:此步骤是光照计算的基础‌。

    glsl
    vec3 normal = texture(normalMap, uv).rgb * 2.0 - 1.0; // [0,1] → [-1,1]

‌切线空间的意义‌

法线贴图通常在‌ 切线空间(Tangent Space)中定义:

  • 以顶点法线为Z轴,切线为X轴,副切线为Y轴构建坐标系。
  • 优势‌:无论模型如何旋转,法线方向始终相对于表面本地坐标,确保凹凸效果稳定‌。

常见误区澄清‌

  • 误区1‌:蓝色是人为设定的美术风格。‌真相‌:蓝色是数学映射的必然结果,由垂直方向(0,0,1)的编码规则决定‌。
  • 误区2‌:法线贴图的颜色代表凹凸高度。‌真相‌:它存储的是‌方向‌而非高度,凹凸感通过光照模拟实现‌。

实际应用案例‌

  • Unity 工作流‌:将法线贴图拖入材质球的 ‌Normal Map‌ 插槽,通过 UnpackNormal() 函数解码(内置管线见 UnityCG.cginc,URP管线UnpackNormalScale()见Packing.hlsl)‌。
  • 效果增强‌:调整 ‌Normal Scale‌ 参数控制凹凸强度(值>1增强凸起,<1弱化)‌。

‌URP中的法线贴图

法线贴图设置流程

  • 导入法线贴图

    • 纹理类型设为"Default/Normal map"
    • 压缩格式推荐BC5(DXT5nm)或BC7
    • 勾选"sRGB"选项确保正确色彩空间转换
  • 创建URP材质
    • 使用Shader路径:Universal Render Pipeline/Lit
    • 将法线贴图拖拽到Normal Map插槽
    • 调整Normal Scale参数(建议0.5-1.5)

完整Shader代码实现

  • NormalMapURP.shader

    Shader "Custom/URPNormalMap"
    {
    Properties
    {
    _BaseMap("Albedo", 2D) = "white" {}
    _BaseColor("Color", Color) = (1,1,1,1)
    _NormalMap("Normal Map", 2D) = "bump" {}
    _NormalScale("Normal Scale", Range(0,2)) = 1
    _Metallic("Metallic", Range(0,1)) = 0
    _Smoothness("Smoothness", Range(0,1)) = 0.5
    } SubShader
    {
    Tags {
    "RenderType"="Opaque"
    "RenderPipeline"="UniversalPipeline"
    } HLSLINCLUDE
    #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
    #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" TEXTURE2D(_BaseMap);
    SAMPLER(sampler_BaseMap);
    TEXTURE2D(_NormalMap);
    SAMPLER(sampler_NormalMap); CBUFFER_START(UnityPerMaterial)
    float4 _BaseMap_ST;
    half4 _BaseColor;
    half _Metallic;
    half _Smoothness;
    half _NormalScale;
    CBUFFER_END struct Attributes
    {
    float4 positionOS : POSITION;
    float3 normalOS : NORMAL;
    float4 tangentOS : TANGENT;
    float2 uv : TEXCOORD0;
    }; struct Varyings
    {
    float4 positionCS : SV_POSITION;
    float2 uv : TEXCOORD0;
    float3 normalWS : TEXCOORD1;
    float4 tangentWS : TEXCOORD2;
    float3 positionWS : TEXCOORD3;
    };
    ENDHLSL Pass
    {
    Name "ForwardLit"
    Tags { "LightMode"="UniversalForward" } HLSLPROGRAM
    #pragma vertex vert
    #pragma fragment frag Varyings vert(Attributes input)
    {
    Varyings output;
    VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
    VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS); output.positionCS = vertexInput.positionCS;
    output.positionWS = vertexInput.positionWS;
    output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
    output.normalWS = normalInput.normalWS;
    output.tangentWS = float4(normalInput.tangentWS, input.tangentOS.w);
    return output;
    } half4 frag(Varyings input) : SV_Target
    {
    // 采样基础贴图
    half4 baseColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv) * _BaseColor; // 采样和解压法线贴图
    half4 normalSample = SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, input.uv);
    half3 normalTS = UnpackNormalScale(normalSample, _NormalScale); // 构建TBN矩阵
    half3 bitangentWS = cross(input.normalWS, input.tangentWS.xyz) * input.tangentWS.w;
    half3x3 TBN = half3x3(input.tangentWS.xyz, bitangentWS, input.normalWS);
    half3 normalWS = TransformTangentToWorld(normalTS, TBN); // 光照计算
    Light mainLight = GetMainLight();
    half3 lightDir = normalize(mainLight.direction);
    half NdotL = saturate(dot(normalWS, lightDir));
    half3 diffuse = baseColor.rgb * NdotL * mainLight.color; // 高光计算
    half3 viewDir = normalize(_WorldSpaceCameraPos - input.positionWS);
    half3 halfVec = normalize(lightDir + viewDir);
    half NdotH = saturate(dot(normalWS, halfVec));
    half specular = pow(NdotH, _Smoothness * 256) * _Metallic; half3 finalColor = diffuse + specular * mainLight.color;
    return half4(finalColor, baseColor.a);
    }
    ENDHLSL
    }
    }
    }

关键实现说明

  • 法线解压‌:使用UnpackNormalScale函数处理法线贴图数据,支持强度调节
  • TBN矩阵‌:通过切线、副切线和法线构建转换矩阵,将切线空间法线转到世界空间
  • 光照模型‌:采用Blinn-Phong模型计算漫反射和高光
  • URP适配‌:使用URP特有的GetVertexPositionInputs等函数替代传统Shader写法

常见问题解决方案

  • 法线效果异常‌:检查切线空间计算是否正确,确保模型导入时勾选"Calculate Tangents"
  • 性能优化‌:移动端可考虑在切线空间计算光照减少矩阵运算
  • 多光源支持‌:需添加AdditionalLights Pass处理额外光源

总结

法线贴图的蓝色基调本质是‌垂直方向向量(0,0,1)经归一化映射后的颜色表达‌,这种方法平衡了存储效率与光照计算需求,是3D渲染中模拟表面细节的核心技术‌,直观的颜色样式只是数据可视化的一种直观显示。


【从UnityURP开始探索游戏渲染】专栏-直达

(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,)

【URP】法线贴图为什么主要是蓝色的?的更多相关文章

  1. 【Unity Shader】六、使用法线贴图(Normal Map)的Shader

    学习资料: http://www.sikiedu.com/course/37/task/456/show# http://www.sikiedu.com/course/37/task/458/show ...

  2. Esfog_UnityShader教程_NormalMap法线贴图

    咳咳,好久没有更新了,一来是这段时间很忙很忙,再来就是自己有些懒了,这个要不得啊,赶紧补上.在前面我们已经介绍过了漫反射和镜面反射,这两个是基本的光照类型,仅仅依靠它们就想制作出精美的效果是远远不够的 ...

  3. 翻译:非常详细易懂的法线贴图(Normal Mapping)

    翻译:非常详细易懂的法线贴图(Normal Mapping) 本文翻译自: Shaders » Lesson 6: Normal Mapping 作者: Matt DesLauriers 译者: Fr ...

  4. shader复杂与深入:Normal Map(法线贴图)1

    转自:http://www.zwqxin.com/archives/shaderglsl/review-normal-map-bump-map.htmlNormal Map法线贴图,想必每个学习计算机 ...

  5. WebGL学习之法线贴图

    实际效果请看demo:纹理贴图 为了增加额外细节,提升真实感,我们使用了漫反射贴图和高光贴图,它们都是向三角形进行附加纹理.但是从光的视角来看是表面法线向量使表面被视为平坦光滑的表面.以光照算法的视角 ...

  6. Unity 着色器训练营(2) - MVP转换和法线贴图

    https://mp.weixin.qq.com/s/Qf4qT15s9bWjbVGh7H32lw 我们刚刚公布了Unity 2018.1中,Unity将会内置可视化编程工具Shader Graph, ...

  7. NormalMap 法线贴图

    法线贴图+纹理贴图(细节明显) 纹理贴图 法线贴图 法线贴图 存储法线的一张贴图,归一化的法线的 xyz 的值被映射成为对应的 RGB 值.归一化的法线值为[-1,1],RGB的每一个分量为无符号的8 ...

  8. 【Unity Shader学习笔记】Unity基础纹理-法线贴图

    1 高度纹理 使用一张纹理改变物体表面法线,为模型提供更多细节. 有两种主要方法: 1.高度映射:使用一张高度纹理(height map)来模拟表面位移(displacement).得到一个修改后的法 ...

  9. 一键批量添加材质的法线贴图-unity插件

    有时候材质做完后需要更改贴图,或者增加贴图,数量少的时候可以一张张添加和修改,数量多的时候就只能代码生成了.原理是通过名字的关联:主贴图和法线贴图大多数只是后缀的不同上,如果不是那是美术规范没做好啊, ...

  10. Unity3d《Shader篇》法线贴图

    效果图 贴图 法线贴图 //代码 Shader "Custom/NormalMap" { Properties { _MainTex ("Texture", 2 ...

随机推荐

  1. 安卓端-APPUI自动化实战【上】

    当前UI自动化测试存在以下问题: 1.投入产出比低:在目前版本快速迭代的大背景下,app更新较快,维护脚本成本高,导致投入产出比低 2.对测试人员要求较高:必须有一定的编程能力 3.运行稳定性较差,断 ...

  2. vuePress2.x 多页面 多目录生成方案

    前言 因为官网介绍的都只有一个'一级标题' 只有一个markdown文件 最终编译后也只有一个html文件,类似于spa 单页项目 如何才有多页项目呢 百度查询 网上插件库有很多,大部分不能用, 后来 ...

  3. 如何在FastAPI中玩转STOMP协议升级,让你的消息传递更高效?

    扫描二维码 关注或者微信搜一搜:编程智域 前端至全栈交流与成长 发现1000+提升效率与开发的AI工具和实用程序:https://tools.cmdragon.cn/ 1. STOMP协议基础 STO ...

  4. 前端开发系列086-Node篇之require

    本文主要介绍require对象(函数)的结构,使用方法和注意点,对模块和CommanJS规范等内容不进行展开. 一.require函数 在Node中,所有的文件都被认为是一个模块.根据来源的不同,我们 ...

  5. 前端开发系列044-基础篇之TypeScript语言特性(四)

    本文主要对TypeScript中的函数进行展开介绍.主要包括以下内容 ❏ 函数的创建和类型 ❏ 函数参数情况说明 ❏ 泛型函数简单介绍 一.函数的创建和类型 函数的创建 函数的创建主要有两种方式:通过 ...

  6. 【乐观锁实现】StampedLock 的乐观读机制

    StampedLock 的乐观读机制主要解决了读多写少场景下,传统读写锁(如 ReentrantReadWriteLock)可能存在的写线程饥饿或性能瓶颈问题.它通过一种"乐观"的 ...

  7. POLIR-Society-Organization-Networking结网{Ice-break/Connection/Relationship/Networking/Organizing

    POLIR-Society-Organization Networking结网{Ice-break/Connection/Relationship/Networking/Organizing How ...

  8. css样式入门学习

    css样式入门学习 一.css是什么 css 又叫层叠样式表 Cascading style sheets CSS层疊样式表是一门标记语言,并不是编程语言,因此不可以自定义变量,不可以引用等,换句话说 ...

  9. freeswitch: 如何指定主叫显示号码

    一.origiante时指定主叫号码 正常情况下,如果在freeswitch控制台,输入类似下面 命令: originate user/1000 &park 被叫收到振铃提示时,显示的号码类似 ...

  10. 关于智能体(AI Agent),不得不看的一篇总结(建议收藏)

    大家好,我是汤师爷,专注AI智能体分享,致力于帮助100W人用智能体创富~ 最近,AI技术的发展可谓是日新月异,尤其是AI智能体这个领域,真是让人眼花缭乱. 不知道你是否和我一样,经常被各种AI智能体 ...