[DirectX12学习笔记] 法线贴图

本文介绍了法线贴图的基本概念,包括其作用和处理方法。在顶点着色器中,法线和切线被转换到世界坐标系,通过施密特正交化计算得到切线空间到局部空间的变换矩阵。文章详细讲解了如何计算顶点切线,并在C++和Shader中实现法线贴图的应用,利用法线贴图的Alpha通道存储光泽度,以增强渲染效果。
摘要由CSDN通过智能技术生成
  • 注意!本文是在下几年前入门期间所写(young and naive),其中许多表述可能不正确,为防止误导,请各位读者仔细鉴别。

法线贴图


法线贴图简介

法线贴图我们都很熟悉了,法线一般是存在切线空间里的,一般来说大多数位置的法线方向都是和面方向偏差不大,所以法线贴图总是蓝蓝的,也就是对应rgb(0,0.5,0)。
法线的三个维度的取值范围一般都是[-1,1],而贴图采样出来的范围都是[0,1],所以我们要处理一下,把采样结果乘2减1。

下面简述一下我们要怎么使用法线贴图,首先我们要知道每个顶点的切线方向(u的偏导方向),然后在顶点着色器里把切线和法线都变换到世界坐标。然后世界坐标里就可以得到插值后的切线和法线,用这两输入值作施密特正交化得到切线空间的三个轴的坐标,也就得到了切线空间到局部空间的变换矩阵,然后把贴图上采样得到的法线变换到世界坐标来计算光照。

现在问题来了,我们要怎么知道每个顶点的切线方向呢?和顶点法线类似的,三角形法线法线可以用三角形的两边叉乘得到,顶点法线是所有经过这个顶点的三角面的法线的均值。而顶点切线,就是每个经过这个顶点的面的切线均值。现在问题就变成了求三角面的切线。首先我们规定一点,切向和uv的u的方向是一致的,每个顶点信息里都存了对应的uv,根据这一点我们可以求切线。
首先我们有三角形的两个边,我们把这两边对应的顶点的uv作差,有
(Δu0, Δv0) = (u1 − u0, v1 − v0)
(Δu1, Δv1) = (u2 − u0, v2 − v0)
然后观察下图
在这里插入图片描述
其中N是normal,T是tangent,B是bitangent,TBN就是切线空间,然后u的方向与T一致,v则和B一致,那么有
在这里插入图片描述
写成矩阵形式
在这里插入图片描述
Δu0、Δv0、Δu1、Δv1通过顶点uv作差得到,e0和e1就是局部坐标系下的边向量,顶点坐标作差就可以得到,现在我们就可以反解出T和B向量
在这里插入图片描述
其中
在这里插入图片描述
然后面的切线有了,顶点的切线就取下均值就可以得到,这个步骤可以在我们载入模型的时候完成,算好后就存在顶点的结构体里。
接下来我们考虑切线空间到世界空间的变换矩阵,因为要用这个矩阵来变换采样得到的法线到世界空间,首先我们知道T,B,N在局部空间的坐标,那么局部空间到切线空间的便便就是这三个向量拼在一起,那么切线到局部的变换则是这个矩阵的逆,又因为这个矩阵是正交阵,逆就是转置,所以切线空间到局部空间的变换矩阵是
在这里插入图片描述
这个矩阵在乘上世界矩阵,就是切线空间到世界空间的变换矩阵,注意这里我们只用来变换向量,所以只要3x3。
在这里插入图片描述
可以看出,切线空间到世界空间的变换矩阵其实就是世界坐标系下的T,B,N三个向量拼起来。
但是把T,B,N变换到世界坐标系下,这三个向量可能就不正交了,所以我们一般只在定点里存T和N,B则是临时算出来,T和N变换到世界坐标系后再施密特正交化,再算出B,这样就可以保证这个变换矩阵是正交阵了。

法线贴图demo

接下来我们实现一个应用了法线贴图的场景demo。

首先C++部分和之前基本上没什么区别,只要在材质里面多记录一个法线贴图的index,然后传到shader里就行。

接下来是shader部分,我们在Common.hlsl中封装一个用来将法线贴图变换到世界空间的函数。如下

//---------------------------------------------------------------------------------------
// Transforms a normal map sample to world space.
//---------------------------------------------------------------------------------------
float3 NormalSampleToWorldSpace(float3 normalMapSample, float3 unitNormalW, float3 tangentW)
{
   
	// Uncompress each component from [0,1] to [-1,1].
	float3 normalT = 2.0f*normalMapSample - 1.0f;

	// Build orthonormal basis.
	float3 N = unitNormalW;
	float3 T 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值