当前位置: 首页 > news >正文

Unity法线贴图原理理解(为什么存在切线空间?存的值是什么?)

Unity法线贴图原理理解(为什么存在切线空间?存的值是什么?)

  • 写在前面
    • 1、为什么用法线贴图?
    • 2、用什么存法线?
    • 3、法线向量为什么存在切线空间?法线贴图存得是什么?
    • 4、法线贴图为什么会偏蓝?
    • 5、如何生成法线贴图?
    • 6、怎样用法线贴图计算光照?
  • 写在后面

写在前面

最近写Shader使用到了法线贴图,一直没搞懂为什么法线贴图是存在切线空间的?以及法线贴图里到底存的是什么?
看来了网上很多博客以及《Unity Shader入门精要》都没太明白。
我当时很疑惑的点是:法线贴图存储的是模型在切线空间下的法线方向。而切线空间z轴是顶点的法线方向,x轴是顶点的切线方向,y轴由法线和切线叉积的副切线。法线贴图到底存的是什么呢?切线空间都把法线拿去当z轴了,还不所有法线都是(0,0,1)了嘛
最终在https://www.gxlsystem.com/hulianwang-1859689.html中得到了比较合理的解释。
下面是真的上面的引用再加上个人的理解,若有错误欢迎讨论。

1、为什么用法线贴图?

精细的模型虽然光照的效果很好,但是凹凸面太多,导致光照计算量太大。而粗模的凹凸面太少,导致光照效果不好。因为光照效果是受模型法线方向影响的,精模凹凸面多->面的法线方向越多样->光照效果好,粗模凹凸面少->面的法线方向多样少->光照效果不好->导致看起来没有凹凸感。
所以人们想到,能不能把高模的法线存起来用在低模上,从而让低模实现类似高模的光照效果。所以发明了法线贴图用来存高模的法线信息。

2、用什么存法线?

法线向量的3个分量xyz正好对应图片的三通道rgb,所以想到用图片去存法线信息,所以这张存了法线信息的图片就叫法线贴图。

3、法线向量为什么存在切线空间?法线贴图存得是什么?

法线用什么坐标系去存呢?
有三种选择方式:世界坐标系、模型坐标系、切线坐标系
世界坐标系:
法线向量相对于世界原点,那么法线向量与模型本身位置,如果模型换个位置,或则换个场景,就出出错,所以不行。
模型坐标系:
法线相对于模型原点,那么如果模型不动,但某个三角形网格变了,面上的法线向量本应该变了。但是模型原点坐标没变,法线向量是相对于模型原点的,所以法线实际是不会变,这就有问题了。并且由于模型空间下的法线向量是相对于这个模型原点的,不同模型的模型坐标系是不一样的,所以也导致这些模型空间下的法线只能应用于这个模型,换一个模型就不行了。
切线坐标系:
既然世界坐标和模型坐标都不太好,那么人们就想能不能让法线依赖于向量既不依赖于场景又不依赖于模型,而是依赖于模型上的面呢?当面变的时候,法线跟着变。从而让法线始终保持处于这个面的上方。人们为了实现这个目标,提出了切线空间。
坐标系定义:对于粗模 (注意是粗模!!!) 的每个顶点,它都有自己的切线空间,这个切线空间的原点就是该原点本身,而z轴是顶点的法线方向,x轴是顶点的切线方向,y轴由法线和切线叉积而得,称为副切线(bitangent)。
切线坐标系储存的值:
储存的是精模法线向量从模型坐标系转到粗模的切线坐标系的向量。也就是用粗模的切线空间储存精模的法线。

这样,由于每个表面顶点都有自己的切线空间,这个切线空间是以粗模顶点的法线方向为z轴,顶点的切线方向为x轴,副切线为y轴,储存的是精模的法线向量。因此这个精模的法线是相对当前粗模该顶点的切线空间的法线,这里我们假设值为(0,0,1),即精模法线与粗模法线重合(因为粗模法线是z轴嘛)。如果粗模发生变化,这时粗模顶点法线和切线变化,那么这个切线空间变了。此时的(0,0,1)依然代表这个变化后的粗模的顶点法线方向,也就是说你只要知道变化后的粗模的法线方向,那么对应的精模的法线也就是这个法线,那么应用在粗模的光照效果依然是精模的光照效果。
这里我画一个示意图,图上的红绿蓝坐标系就是粗模的切线空间。这里我们假设来自于精模的四个四边形变成粗模的一个四边形。我画的粉色框就是精模的四边形,这时这个四边形顶点的法线就是精模法线向量,然后把这个坐标系放到粗模切线坐标系,此时这个法线向量的值就是法线贴图的值。
在这里插入图片描述

4、法线贴图为什么会偏蓝?

因为法线向量各个分量xyz取值区间是[-1,1],而rgb颜色分量范围是0-1,所以需要映射一下:rgb = (xyz + 1) / 2。
上面提到了法线贴图存的值是精模法线向量从模型坐标系转到粗模的切线坐标系的向量。比如说粗模的三角形数量相对于粗模来说没有减少,也就是粗模和精模是同一个模型。这时因为切线空间的z轴是粗模的法向量,由于假设粗模和精模是同一个模型法线是完全相同的,也就是精模的所有法线向量就是z轴,即值为(0,0,1)。这时的法线贴图所有值都是rgb=(0.5,0.5,1),也就淡蓝色。因此只要粗模相对于精模三角形数量和为没有太大变化,那法线贴图大多数颜色就是(0.5,0.5,1)淡蓝色。

5、如何生成法线贴图?

这个问题也就是再说,如何让精模模型空间下的法线转到粗模切线空间下。也就是我们需要一个变化矩阵让精模法线经过变化矩阵变化的粗模切线空间。
这里直接给结论:从一个空间T变换到空间N的变换矩阵,就是T空间的三个基向量xyz在N空间下的表示。

结论来自https://zhuanlan.zhihu.com/p/361417740

因此从切线空间->模型空间:相当于切线空间中三个基坐标(TBN,T为切线、B为副切线、N为法线)在模型空间三个基坐标上的投影(XYZ)。因此变换矩阵为
在这里插入图片描述
同理从模型空间->切线空间是切线空间->模型空间的转置,因为是逆向操作
在这里插入图片描述
这样我们就得到了从模型空间->切线空间变换矩阵。再用模型空间的法线左乘这个变换矩阵就能得到切线空间的法线了。
注意:因为粗模一个三角面需要对应多个精模的三角面,因此多个法线向量就可以共享一个低模切线坐标系。

6、怎样用法线贴图计算光照?

因为法线贴图是切线空间的,但是光照是在世界空间计算的,因此要计算光照有两种方式。法一:把光照方向和视角法线转到切线空间去计算。法二:把法线从切线空间转到世界空间去计算。
这里我们主要讲法二,把法线贴图中法线转到世界空间去计算光照。
首先我们就要获得从切线空间->世界空间的变换矩阵
切线空间->世界空间:使用法线贴图,如果光照在世界空间中计算,就需要把法线从切线空间转换到世界空间。Matrix = 切线空间三分量在世界空间的表示。
在这里插入图片描述
矩阵再乘上切线空间下的法线,获得世界空间法线。
在这里插入图片描述
对应shader的代码,o.tangent.w是副切线的方向性的。

//粗模顶点法线
float3 worldNormal = mul((float3x3)unity_ObjectToWorld,o.normal);
//粗模顶点切线
float3 worldTangent = mul((float3x3)unity_ObjectToWorld,o.tangent.xyz);
//粗模顶点副切线
float3 worldBinormal = cross(worldNormal,worldTangent)*o.tangent.w;
//切线空间->世界空间的变换矩阵
r.matrixRow1 = float3(worldTangent.x,worldBinormal.x,worldNormal.x);
r.matrixRow2 = float3(worldTangent.y,worldBinormal.y,worldNormal.y);
r.matrixRow3 = float3(worldTangent.z,worldBinormal.z,worldNormal.z);

有了切线空间->世界空间的变换矩阵,我们就只需要把法线贴图的法线向量左乘这个矩阵就能得到世界空间下的法线向量。主要要反映射一下,因为贴图是rgb[0,1]而法线范围是[-1,1]。

//对法线纹理进行采样,取出其值,这是在切线空间的法线
fixed4 bumpColor = tex2D(_BumpTexture,o.uvNormalTexture);
//解出法线纹理,这是对法线纹理的采样结果的一个反映射操作, normal.xy = packednormal.xy * 2 - 1;
fixed3 bump = UnpackNormal(bumpColor);
bump *= _BumpScale;
//得到副切线
bump.z = sqrt(1 - max(0, dot(bump.xy, bump.xy)));
//法线向量从切线空间转到世界空间
bump = fixed3(dot(o.matrixRow1,bump),dot(o.matrixRow2,bump),dot(o.matrixRow3,bump));

这样我的就得到了法线贴图,然后用这张法线贴图去做漫反射光照即可。

fixed3 diffuseColor = _LightColor0.rgb*mainTextureColor.rgb*max(0,dot(normalize(bump),normalize(_WorldSpaceLightPos0.xyz)));

完整代码
见https://blog.csdn.net/iiiiiiimp/article/details/125505828中的Phong光照模型(顶点片元Shader、表面体Shader)
在这里插入图片描述

写在后面

看来一天的博客,才有点弄明白了。由此可见《深海》的图形学技术功底。大家去看看《深海》吧!博主当时哭得好惨!听说12亿才回本!!!
在这里插入图片描述

相关文章:

Unity法线贴图原理理解(为什么存在切线空间?存的值是什么?)

Unity法线贴图原理理解(为什么存在切线空间?存的值是什么?)写在前面1、为什么用法线贴图?2、用什么存法线?3、法线向量为什么存在切线空间?法线贴图存得是什么?4、法线贴图为什么会偏蓝&#xf…...

【JavaWeb】传输层协议——UDP + TCP

目录 UDP协议 UDP协议结构 UDP的特点 TCP协议 TCP协议结构 TCP的特点 TCP的十个核心机制 确认应答 超时重传 连接管理 滑动窗口 流量控制 阻塞控制 延迟应答 捎带应答 粘包问题 异常处理 UDP协议 UDP协议结构 源端口:存储的是发送方的端口号。 目的…...

C++ 中是用来修饰:内置类型变量、自定义对象、成员函数、返回值、函数参数

const 是 constant 的缩写,本意是不变的,不易改变的意思。在 C 中是用来修饰内置类型变量,自定义对象,成员函数,返回值,函数参数。 一. const修饰 普通类型的变量 const int a 7; int b a; // 正确 …...

av 146 002

61. 一个新的敏捷项目经理正试图确定团队该如何执行一个发布计划的进度。哪种工具可以更深入地了解团队的进展? A. 发布计划系统 B. 产品路线图。 C. 看板。 D. 燃尽图 62. 你的项目发起人找到你,让你知道他正在考虑给你项目中的一位高级工程师颁发1000美元的现…...

小红书用户画像 | 小红书数据平台

小红书的用户画像是小红书品牌营销的必备技能,也是小红书推广种草的一个重要前提。通过对小红书用户画像进行分析,对品牌进行精准营销,实现更高的流量转化。 2022小红书粉丝人群画像 千瓜数据在2022年发布的千瓜活跃用户画像趋势报告中分析了…...

【STM32笔记】低功耗模式下GPIO、外设、时钟省电配置避坑

【STM32笔记】低功耗模式下GPIO、外设、时钟省电配置避坑 前文: blog.csdn.net/weixin_53403301/article/details/128216064 【STM32笔记】HAL库低功耗模式配置(ADC唤醒无法使用、低功耗模式无法烧录解决方案) blog.csdn.net/weixin_534033…...

Linux内存分区(swap)

目录 1、使用物理分区创建内存交换分区 2、使用文件创建内存交换文件 当硬件的设备资源充足的话,那么swap是不会被我们的系统所使用到的,所以swap会被利用到的时刻通常就是物理内存不足的情况 我们知道CPU所读取的数据都来自于内存,那么当…...

第六章——抽样分布

文章目录1、统计量的定义2、常用的统计量3、经验分布函数4、正态总体常用统计量的分布4.1、卡方分布4.1.1、卡方分布的定义4.1.2、卡方分布的性质4.2、t分布4.2.1、t分布的定义4.2.2、t分布的性质4.3、F分布4.3.1、F分布的定义4.3.2、F分布的性质5、正态总体的样本均值与样本方…...

蓝桥云课-声网编程赛(声网编程竞赛7月专场)题解

比赛题目快速链接:https://www.lanqiao.cn/contests/lqENT02/challenges/ 让时钟转起来(考点:css:transform) // index.js function main() {// 题解前理解一个东西:// 时针每过一小时,转30 原…...

Java高手速成 | Java web 实训之投票系统

01、投票系统的案例需求 在本篇中,我们将制作一个投票系统,让学生给自己喜爱的老师投票。该系统由1个界面组成,系统运行,出现投票界面,如图所示: ▍显示效果 在这个界面中,标题为:“欢迎给教师投票”;在界面上有一个表格,显示了各位教师的编号、姓名、得票数;其中…...

排序的基本概念

按数据存储介质:内部排序和外部排序按比较器个数:串行排序和并行排序按主要操作:比较排序和基数排序插入排序:基本思想:每步将一个待排序的对象,按其关键码大小,插入到前面已经排好序的一组对象…...

面试笔试资料--Java

这里写自定义目录标题1.同步和异步有何异同?在什么情况下分别使用他们?举例说明1.同步和异步有何异同?在什么情况下分别使用他们?举例说明 1.1概念 Java中交互方式分为同步和异步两种:   同步交互:指发送…...

基于TC377的MACL-ADC General配置解读

目录标题一、MACL-ADC General1.Config Variant与AdcConfigSet2. AdcGeneral3.AdcPublishedInformation二、最终对应达芬奇生成内容一、MACL-ADC General 1.Config Variant与AdcConfigSet Config Variant :变体配置,默认选择VariantPostBuild就好了&…...

error: src refspec master does not match any.处理方案

问题描述 在使用git bash指令将项目上传到github时,总是遇到一些错误无法解决。 下面是我遇到的一个问题 error: src refspec master does not match any. error: failed to push some refs to XXXX.git 原因分析: 错误:SRC ReFSPEC主控器不…...

防火墙有关iptables的知识点

基本概念 什么是防火墙 在计算中,防火墙是基于预定安全规则来监视和控制传入和传出网络流量的网络安全系统。该计算机流入流出的所有网络通信均要经过此防火墙。防火墙对流经它的网络通信进行扫描,这样能够过滤掉一些攻击,以免其在目标计算机…...

路肩石水渠机在施工公路项目中工艺特点的匹配

新建公路路肩项目在目前公路项目中的技术手段和实现方式,大多数依靠机械设备来机械来进行,还有一部分通过人工传统的预制作业和安装模式来进行,两种工艺特点的对比来说对于补充完善建设手段和效果实现有很重要的意义. 其中采用了机械设备进行一次成型制作的过程,按照设计需求匹…...

JS 动态爱心(HTML+CSS+JS)

✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...

钉钉配置事件订阅(Python)

钉钉配置事件订阅 0.需求分析 需要实现钉钉企业通讯录同步至企业微信通讯录,这就需要用到钉钉的事件与回调 1.配置应用 登陆开放平台 https://open-dev.dingtalk.com/去企业内部开发里面,先创建个应用,后面都借用这个应用来调接口 创建完…...

Linux-Udev机制

一:Udev概述 udev 是一个用户空间的设备管理器,用于为事件设置处理程序。作为守护进程, udev 接收的事件主要由 linux 内核生成,这些事件是外部设备产生的物理事件。总之, udev 探测外设和热插拔,将设备控制权传递给内核,例如加载内核模块或设备固件。udev 是一个用户空…...

ERP是什么?中小商户有必要用吗?秦丝、金蝶、管家婆哪家强?

ERP系统刚开始传入中国的时候,基本上只有超大型或大型企业有条件实施,不过最近几年随着小微企业、中小商户的信息化需求不断增长,ERP软件已慢慢被普遍使用。但是仍然有不少中小商户,还没搞清楚ERP到底是什么,看到大家都…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...

windows系统MySQL安装文档

概览&#xff1a;本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容&#xff0c;为学习者提供全面的操作指导。关键要点包括&#xff1a; 解压 &#xff1a;下载完成后解压压缩包&#xff0c;得到MySQL 8.…...

永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器

一、原理介绍 传统滑模观测器采用如下结构&#xff1a; 传统SMO中LPF会带来相位延迟和幅值衰减&#xff0c;并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF)&#xff0c;可以去除高次谐波&#xff0c;并且不用相位补偿就可以获得一个误差较小的转子位…...

Leetcode33( 搜索旋转排序数组)

题目表述 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...

k8s从入门到放弃之Pod的容器探针检测

k8s从入门到放弃之Pod的容器探针检测 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;容器探测是指kubelet对容器执行定期诊断的过程&#xff0c;以确保容器中的应用程序处于预期的状态。这些探测是保障应用健康和高可用性的重要机制。Kubernetes提供了两种种类型…...

PostgreSQL 与 SQL 基础:为 Fast API 打下数据基础

在构建任何动态、数据驱动的Web API时&#xff0c;一个稳定高效的数据存储方案是不可或缺的。对于使用Python FastAPI的开发者来说&#xff0c;深入理解关系型数据库的工作原理、掌握SQL这门与数据库“对话”的语言&#xff0c;以及学会如何在Python中操作数据库&#xff0c;是…...