法线贴图和切线空间

切线空间法线贴图生成和应用的理解 一、基于切线空间坐标系的法线贴图理解:使用法线贴图是因为在低模下想获得高模凹凸表面光照效果。因为基于模型坐标系的法线贴图,在换到不同的模型下时候法线贴图不能复用。故引入切线空间让法线贴图可以在相同网格形状模型(或许是子模型)但不同模型坐标系的多个模型间可以共享法线贴图。以下是我个人对切线空间求取,法线贴图求取,法线贴图计算光照的理解,不对之处欢迎指出:1)低模切线空间求取:假设:dp是网格上某三角... 阅读详情

Normal Maping

法线贴图是一种特殊的texture,法线贴图中每个像素存储的不是rgb颜色值,而是该片元的法向量。
法向量可以通过模拟物体表面的明暗情况(只是改变了明暗而非在高度上做技巧,和高度贴图是不同的),使得渲染出来的物体更有凹凸感,看起来更为真实。同时性能消耗非常小

图1:
这里写图片描述

法线贴图的使用非常方便,只需要当做平常的texture一样对它进行采样,采样的值作为normal而非rgb。
下面是一个简单的GLSL Shader的例子。

#version 330 core

out vec4 fragcolor;

in SharedData
{
    vec3 worldPos;
    vec2 texcoord;

}fs_in;

uniform vec3 lightPos;
uniform vec3 viewPos;
uniform sampler2D diffuse_map;
uniform sampler2D normal_map;

void main()
{
    vec3 color = texture(diffuse_map, fs_in.texcoord).rgb;
    //采样的值作为normal
    vec3 normal = texture(normal_map, fs_in.texcoord).rgb;
    //为了把[0,1]的value映射到[-1,1]
    normal = normalize(normal * 2.0 - 1.0); 

    vec3 lightDir = normalize(fs_in.worldPos - lightPos);
    vec3 viewDir = normalize(fs_in.worldPos - viewPos);

    //ambient
    vec3 ambient = 0.2f * color;

    //diffuse
    float diff = dot(-lightDir, normal) * 0.5 + 0.5;
    vec3 diffuse = diff * color; 

    //specular
    vec3 half = -normalize(lightDir + viewDir);
    float spec = pow(max(dot(normal, half), 0.0f), 32.0f);
    vec3 specualr = spec * color;

    fragcolor = vec4(ambient + diffuse + specualr, 1.0f);
}

渲染出来的物体很有凹凸感于是看起来更为真实:
图2:
这里写图片描述
(上图对应的法线贴图)
图3:
这里写图片描述
你所能找到的法线贴图几乎都是偏蓝色的,这是因为法向量几乎都是指向Z轴的正向的,也就是说在(0,0,1)之间波动,蓝色的分量更大一些所以整个图看起来是蓝色的。

到现在为止一切都很好,可是在学习法线贴图的时候,有一个无论如何都绕不开的概念称为切线空间。那么我们为什么还要自找麻烦呢?

Tangent Space

原由和概念

其实不是我们自找麻烦,是因为真的有很多麻烦要处理。

上面的例子最后渲染的结果很好是因为原来的图片中的砖块的法线向量恰好是指向Z轴的正向的,但是如果原图是一个坑坑洼洼的不规则复杂地形,例如下图所示:

图4:
这里写图片描述
表面的法向量用红色标识,如果有无数方向不同的法向量,难不成我们还得给一张原图制作无数张法线贴图吗?显然这不现实。

另一个稍微有点难的解决方案是,在一个不同的坐标空间中进行光照,这个坐标空间里,法线贴图向量总是指向这个坐标空间的正z方向;所有的光照向量都相对与这个正z方向进行变换。这样我们就能始终使用同样的法线贴图,不管朝向问题。这个坐标空间叫做切线空间(tangent space)。

法线贴图中的法线向量在切线空间中,法线永远指着正z方向。切线空间是位于三角形表面之上的空间(切线空间是顶点构造的一个局部坐标系,每一个三角面定义的切线空间是不一样的)因此,切线空间的一大好处是我们可以为任何类型的表面计算出一个这样的矩阵,由此我们可以把切线空间的z方向和表面的法线方向对齐。

我们需要定义一个空间基来描述这一空间,称为TBN矩阵。
TBN矩阵这三个字母分别代表tangent、bitangent和normal向量。这三个向量两两正交,它们沿一个表面的法线贴图对齐于:(B)上、(T)右、(N)前。
图5:
这里写图片描述
图6:
那么,对于某个给定了uv坐标的三角形面,我们有如下关系:这里写图片描述

根据图5可知,我们想要的切线空间的B是平行于V,T是平行于U的。
设图6中:
E1→=p2−p1
E2→=p3−p1
又E1→,E2→很显然可以用U,V的线性组合来表示,且T,B分别平行于U,V,所以我们可以用T,B的线性组合来表示E1→,E2→

E1→=t1×T⃗ +b1×B⃗ E2→=t2×T⃗ +b2×B⃗ t1=p2.u−p1.u,b1=p2.v−p1.vt2=p3.u−p1.u,b2=p3.v−p1.v

写作矩阵的形式:
[E1E2]=[t1t2b1b2]×[TB]

所以可知TB的结果是:
[TB]=[t1t2b1b2]−1×[E1E2]

继续推导可知:
[TB]=1t1b2−t2b1×[b2−t2−b1t1]×[E1E2]

最后得出的结果进行施密特正交化即可。
下面是一个简单的计算过程的例子:
// positions
        glm::vec3 pos1(-1.0, 1.0, 0.0);
        glm::vec3 pos2(-1.0, -1.0, 0.0);
        glm::vec3 pos3(1.0, -1.0, 0.0);
        glm::vec3 pos4(1.0, 1.0, 0.0);
        // texture coordinates
        glm::vec2 uv1(0.0, 1.0);
        glm::vec2 uv2(0.0, 0.0);
        glm::vec2 uv3(1.0, 0.0);
        glm::vec2 uv4(1.0, 1.0);
        // normal vector
        glm::vec3 nm(0.0, 0.0, 1.0);

        // calculate tangent/bitangent vectors of both triangles
        glm::vec3 tangent1, bitangent1;
        glm::vec3 tangent2, bitangent2;
        // - triangle 1
        glm::vec3 edge1 = pos2 - pos1;
        glm::vec3 edge2 = pos3 - pos1;
        glm::vec2 deltaUV1 = uv2 - uv1;
        glm::vec2 deltaUV2 = uv3 - uv1;


        GLfloat f = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y);

        tangent1.x = f * (deltaUV2.y * edge1.x - deltaUV1.y * edge2.x);
        tangent1.y = f * (deltaUV2.y * edge1.y - deltaUV1.y * edge2.y);
        tangent1.z = f * (deltaUV2.y * edge1.z - deltaUV1.y * edge2.z);
        tangent1 = glm::normalize(tangent1);

        bitangent1.x = f * (-deltaUV2.x * edge1.x + deltaUV1.x * edge2.x);
        bitangent1.y = f * (-deltaUV2.x * edge1.y + deltaUV1.x * edge2.y);
        bitangent1.z = f * (-deltaUV2.x * edge1.z + deltaUV1.x * edge2.z);
        bitangent1 = glm::normalize(bitangent1);

在TangentSpace还是WordSpace计算光照

现在假设我们已经有了TBN变换矩阵,在shader中我们应该怎么使用它呢?
有两种方法。

  1. 把所有在WorldSpace的点变换到TBN空间,在TBN计算光照。(这需要TBN的逆矩阵)
  2. 采样得到的法向量是在TBN空间,我们将这些法向量变换到WorldSpace计算光照。
    (TBN矩阵可以将TangentSpace=>WorldSpace )
    如果按照第二种办法,我们需要把TBN矩阵传到FragmentShader,然后采样NormalMap,采样得到的值乘以TBN变换到WorldSpace。剩下的工作就是在世界空间进行光照计算。
normal = texture(normalMap, fs_in.TexCoords).rgb;
normal = normalize(normal * 2.0 - 1.0);   
normal = normalize(fs_in.TBN * normal);

这样看起来十分美妙也很符合我的逻辑,不过你需要在每次FragmentShader中都多做一次TBN变换到WorldSpace,有点耗费时间。

效率对于Shader来说十分重要。

所以我们选择第一种方法。
在VertexShader中我们计算TBN的逆矩阵,把顶点的TangentSpace坐标记录下来。由于VertexShader的调用次数一定少于FragmentShader,剩下的每个片元的TangentSpace坐标可以由差值自动获取。
它的代码看起来像这样:

#version 330 core

layout(location = 0) in vec3 pos;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 texcoord;
layout(location = 3) in vec3 tangent;
layout(location = 4) in vec3 bitangent;

out SharedData
{
    vec3 worldPos;
    vec3 tangentLightPos;
    vec3 tangentViewPos;
    vec3 tangentPos;
    vec2 texcoord;

}vs_out;


uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;    

uniform vec3 lightPos;
uniform vec3 viewPos;

void main()
{
    gl_Position = projection * view * model * vec4(pos, 1.0f);
    vs_out.worldPos = vec3(model * vec4(pos, 1.0f));
    vs_out.texcoord = texcoord;

    vec3 T = normalize(tangent);
    vec3 B = normalize(bitangent);
    vec3 N = normalize(normal);//应当始终保证n是指向z的正向,即N必须为(0, 0, 1)?

    mat3 TBN = transpose(mat3(T,B,N));//正交矩阵的逆矩阵是逆矩阵

    vs_out.tangentLightPos = TBN * lightPos;
    vs_out.tangentViewPos = TBN * viewPos;
    vs_out.tangentPos = TBN * vs_out.worldPos;

}

两种方法的最终展现的效果是一样的,但是效率方面第一种方法会更占优势。
图7:
这里写图片描述

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

相关推荐

切线空间与UV坐标

最近在看法线贴图相关的内容,一直没怎么搞懂切线空间是个什么玩意,关于切线空间的Z轴我们都知道是模型顶点本身的法线方向,但是另外两个轴切线(Tangent)和副切线(Bitangent)向量却很难让人理解,网上最多的说法就是切线(Tangent)与UV坐标的U方向对齐,副切线(Bitangent)与UV坐标的V方向对齐。这就很抽象!!!,UV坐标变化本身就是一个二维空间的向量,而切线方向是一个三维空...

Hui Ye的博客 4711

一文彻底掌握什么是法线贴图、切线空间、TBN矩阵

normal mapping,bump mapping ,tangent space 切线空间1、什么是切线空间?2、为什么需要法线贴图3、有了法线贴图为什么需要切线空间的法线的贴图4、计算TBN5、Tangent-Space的优点

记录值得记录的东西 7323

tinyrenderer-切线空间法线贴图

tinyrenderer渲染器实现 学习笔记

Mhypnos的博客 1000

UE4_材质基础_切线空间与法线贴图

学习笔记,不喜勿喷,侵权立删,祝愿大家生活越来越好!

Growthofnotes的博客 3761

OpenGL核心技术之切线空间

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,优快云社区专家,特邀编辑,畅销书作者;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。优快云视频网址:http://edu.youkuaiyun.com/lecturer/144 继续接着OpenGL核心技术之法线贴图介绍,法线贴图中的法线向量在切线空间中,法线永远指着正z方向。切线空间是位于

海洋个人博客 8359

云计算学习——5G网络技术

华为eNSP网络设备模拟器的基础安装及简单使用

qq_42520799的博客 1445

法线贴图技术原理与实践

本文讲述了法线贴图技术的原理,切线空间的概念,切线空间和世界空间的转换,计算顶点切线的方法,并给出了在切线空间和世界空间使用法线贴图进行光照的shader例子。

聚焦游戏和图形引擎开发技术 1591

【ESP32-S3量产指南】基于Flash下载工具实现Release模式Flash加密与安全配置

本文详细介绍了ESP32-S3量产阶段如何通过Flash下载工具实现Release模式下的Flash加密与安全配置。从加密模式选择、工具配置到分区表优化,提供了一套完整的量产安全解决方案,帮助开发者有效防止固件泄露和篡改,确保设备安全。

weixin_28730131的博客 231

切线空间(tangent space)&法线贴图(normal map)

切线空间(tangent space)&法线贴图(normal map) 1。对象空间,纹理空间,切线空间。 空间这个概念,并不一定是通常说的看得见摸得着的几何空间。在老白看来,空间就是N个维度组合的说法,而1个维度就是一种认识视角(认识的结果就叫坐标)。举个例子,对所有地球人,可以以年龄和性别两个维度构造一个二维空间,可以说每个人的年龄和性别就是在这个二维空间的坐标。如果需要

netbaixc的专栏 5940

gitlab远程子仓库配置SSH - 保姆级教程

在网上搜索了一些方法,但是不够详细,走了好多弯路,所以记录下配置SSH的详细过程吧。

是菠萝的博客 1万+

c++11 无锁队列

/************************************************************************* > File Name: lockfreequeue.cpp > Author: > Mail: > Created Time: Wed 08 Aug 2018 11:38:08 AM CST ********...

qq_35728402的博客 5024

Unity的URP下法线计算

法线贴图计算的HLSL版本

阿赵的博客 1394

深入解析Unity3D中PostLateUpdate突然占用大量时间的原因

在Unity3D的生命周期中,是一个在LateUpdate之后被调用的方法。它通常用于处理一些需要在所有更新(包括物理更新和动画更新)之后进行的逻辑。由于它在游戏循环的后期阶段被调用,因此它通常用于处理与渲染直接相关的任务,如相机调整、UI更新等。突然占用大量时间的问题可能由多种原因引起,包括复杂的逻辑处理、频繁的资源加载、不恰当的渲染优化、插件或第三方库的副作用以及内存泄漏或垃圾回收等。为了解决这个问题,开发者需要仔细审查和优化中的代码,同时利用Unity提供的工具来监控和分析游戏性能。

Yori_22的博客 981

法线贴图为何选切线空间?

摘要: 切线空间法线贴图以模型表面局部坐标系(TBN)存储法线方向,使其能适应模型变形(如动画),保持细节与表面同步。相比世界/物体空间法线贴图,其优势在于:1) 动态适配,法线随表面运动;2) 高复用性,同一贴图适用于不同姿态模型;3) 制作便捷,只需关注表面凹凸。因此,切线空间法线贴图成为游戏和动画领域的通用方案,尤其适合需要变形或复用的场景,而其他空间法线贴图仅适用于静态对象。

本博客聚焦游戏开发技巧、创业实战心得及大学热门课程干货。这里既有技术深度,也有思维广度,无论你是开发者、创业者还是高校学子,都能在这里找到适合自己的成长之路! 553

形位公差标注的常见误区与优化策略

本文深入解析机械设计中形位公差标注的常见误区与优化策略,特别针对NX2406用户提供实用技巧。从标注冗余、基准选择到公差值设定等五大误区入手,结合工程图实例展示如何提升标注精度与效率,帮助工程师避免常见错误,优化设计流程,确保零件功能实现与制造可行性。

info6的博客 245

Vue3 - 超详细“纯前端“将文件上传到阿里云OSS对象存储,最新阿里云前端直传音频、视频、图片、文件、office文档等任意文件,vue3单个或批量从本地选择文件,秒传到阿里云oss无需后端接口!

vue3,阿里云oss存储,前端直传oss,上传图片/office文档/音视频/zip压缩包到阿里云oss,项目中上传文件到阿里云oss详细教程,如何将本地文件上传到阿里云OSS存储,纯前端实现阿里云OSS上传,纯前端直传至阿里OSS对象存储教程,如何解决上传文件到阿里云OSS本地环境调试出现跨域问题,直传阿里云oss跨域报错解决,前端直接上传文件到oss服务端,不经过后端直传,浏览器直接上传文件,阿里云oss上传sdk官方文档,阿里云错误,解决这个问题,上传很慢,一直签名失败,权限,不经过后端,纯前端直传

🏆 前端领域优质创作者 1万+

法线贴图纹理相关学习之切线空间的理解

在学习法线贴图纹理的时候无法避免的遇到了切线空间的学习和理解的问题,在此记录下自己理解的过程。 1. 法线纹理的目的和存在的意义: 是在底模上表现出高模的各种凹凸细节,而为什么要表现凹凸细节呢,很简单,就是为了光照计算,模拟出高模的凹凸细节,光照的时候可以更加接近现实的质感。 如果直接在项目中使用高模的话就不存在这个法线纹理贴图的问题了,因为高模本身的细节足够细致,光照只需要按照模型本身的...

KEYNES的失乐园 1252

[系统安全] 一.什么是逆向分析、逆向分析基础及经典扫雷游戏逆向

娜璋AI安全之家于2020年8月18日开通,将专注于Python和安全技术,主要分享Web渗透、系统安全、CVE复现、威胁情报分析、人工智能、大数据分析、恶意代码检测等文章。真心想把自己近十年的所学所做所感分享出来,与大家一起进步。 系统安全系列作者将深入研究恶意样本分析、逆向分析、攻防实战等,通过在线笔记和实践操作的形式分享与博友们学习,希望能与您一起进步。第一篇文章先带领大家学习什么是逆向分析,然后详细讲解逆向分析的典型应用,接着通过OllyDbg工具逆向分析经典的游戏扫雷,再通过Cheat Engin

杨秀璋的专栏 2万+

从“法线贴图的意义”到“切线空间公式的推导与验证”

目标:讨论法线贴图的意义,讨论切线空间的意义,推导切线空间的计算公式,根据公式编写代码,将其计算结果与其他美术软件计算的结果进行比较以验证公式的正确性。

主要记录了工作之余胡乱学习的知识,大部分是入门级别的内容。 主要是为了自己未来有用到时能快速上手,相当于是个备忘录。 当然,也很开心其他人可以从中找到对自己有用的信息。^_^ 2254

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Unity3D 法线转换&切线空间

Unity3D 法线转换&切线空间

YF云飞的博客 1万+

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

切线空间?切线空间的作用到底是什么?

关于切线空间的理解

qq_39691716的博客 1641

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

切线空间(Tangent Space) 的计算与应用

转:http://windsmoon.com/2017/11/28/%E5%88%87%E7%BA%BF%E7%A9%BA%E9%97%B4-Tangent-Space-%E7%9A%84%E8%AE%A1%E7%AE%97%E4%B8%8E%E5%BA%94%E7%94%A8/?utm_medium=social&utm_source=ZHShareTargetIDMore 概要 本...

pzh的博客 1万+

理解Unity法线贴图的切线空间存储方式

最近再学习一些unity shader的一些东西,看到法线贴图那里突然不是很理解,经过查找资料,现在也是恍然大悟,也是深深敬佩发明切线空间法线贴图那个大神! 话不多说,直接说一下,关于法线贴图,主要有两种,一种是基于模型空间的法线信息存储,一种就是基于切线空间的信息存储。我们这里着重来说一下第二种。 什么是切线空间? 切线空间就是,基于模型上的一个顶点建立的坐标空间,它的X轴

永远在身边的专栏 5122

技术美术知识学习_06:关于法线贴图详解

在切线空间中,X 、Y 、Z三个坐标的向量取值范围为(-1,1),而位图上R G B三个值的取值范围(0,1),需要进行转换,因为模型的法线方向大部分是相同的,即Z轴方向为(0,0,1),映射到颜色空间是(0.5,0.5,1),所以你会看到法线贴图为什么是蓝紫色。法线的坐标刚好与平面垂直,它的坐标是(0,0,1),如果把它转换成一张图片里的颜色那么(0,0,1)就相当于RGB里的B,也就是Blue,因此使用蓝色来表示一个物体的表面法线方向。(切线空间),顾名思义,切线空间法线是基于每个面的切线方向。

qq_17775727的博客 5047

S04 切线空间与法线纹理

在学习法线贴图纹理的时候无法避免的遇到了切线空间的学习和理解的问题,在此记录下自己理解的过程。 法线纹理的目的和存在的意义: 是在底模上表现出高模的各种凹凸细节,而为什么要表现凹凸细节呢,很简单,就是为了光照计算,模拟出高模的凹凸细节,光照的时候可以更加接近现实的质感。 如果直接在项目中使用高模的话就不存在这个法线纹理贴图的问题了,因为高模本身的细节足够细致,光照只需要按照模型本身的法线来计算...

江陵野少的博客 528

OpenGL学习笔记一之高级光照篇六 法线贴图

转载自https://learnopengl-cn.github.io/05%20Advanced%20Lighting/04%20Normal%20Mapping/ 本节暂未进行完全的重写,错误可能会很多。如果可能的话,请对照原文进行阅读。如果有报告本节的错误,将会延迟至重写之后进行处理。 我们的场景中已经充满了多边形物体,其中每个都可能由成百上千平坦的三角形组成。我们以向三角形上附加纹理...

飞飞飞的博客 1033
上一篇: [Modern OpenGL]谈谈VAO、VBO、IBO
下一篇: 使用Visual Studio一劳永逸式配置OpenGL开发环境(GLFW3+GLAD)
SixDayCoder
博客等级 码龄12年 98粉丝 64原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值