文章目录
- 为什么要有法线贴图
- 原理
- 实施
- MiniEngine注意点
为什么要有法线贴图
先来看一张截图
很容易发现,这个柱子的反光不对,一眼看过去就知道是一张贴图,非常的不真实。
因为我们的柱子模型,本身就是平滑的。当然了可以通过雕刻,使得柱子模型完美的匹配对应的纹理。但工作量太大。
于是就用到了法线贴图技术
原理
- 根据贴图纹理生成一张法线贴图
- 这个法线贴图中每个像素记录的是该点的法向量
- 在PS阶段,通过该点本身的法向量与法线贴图中记录的法向量进行一些计算,来计算出一个新的法向量
- 使用新的法向量参与后边的光照等计算
实施
原书代码已经提供好了法线贴图,这里直接使用就好。
- 我们直接把第18章的代码拷过来作为基础代码。
- 简单调整下代码结果,把新的几张法线贴图加入到纹理队列中
- 跑一次看看结果是否正确。
之所以进行上边的步骤,在于我本身擅长的是迭代式开发。这样一小步一小步的做,期间发现了问题比较容易定位。
运行起来,发现符合预期。
github:
https://github.com/mversace/DirectX12-MiniEngine-Dragon/tree/ecd7c09258f26212e4e77b9c530376f05e8e3d2e
接下来就是shader的修改。
在这里,顶点结构中新添加了切线。
- 在VS中把切线转换到世界坐标系
- 添加源码中的那个计算函数
- 在PS中利用新函数计算出新的法向量
这个新函数,我没有细看,工作就是根据原法线、切线、法线贴图中的法线,计算出一个新的法线
//---------------------------------------------------------------------------------------
// 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 = normalize(tangentW - dot(tangentW, N) * N);float3 B = cross(N, T);float3x3 TBN = float3x3(T, B, N);// Transform from tangent space to world space.float3 bumpedNormalW = mul(normalT, TBN);return bumpedNormalW;
}
然后程序跑起来,纹理不对劲。
开始挺长时间的调试。。。VS的图形调试真的是无限崩溃啊
MiniEngine注意点
最终定位到问题点在于纹理载入函数,对于法线贴图,第二个参数应该传为false。否则会修改存储格式
// 7个纹理m_srvs.resize(7);TextureManager::Initialize(L"Textures/");m_srvs[0] = TextureManager::LoadFromFile(L"bricks2", true)->GetSRV();m_srvs[1] = TextureManager::LoadFromFile(L"bricks2_nmap", false)->GetSRV();m_srvs[2] = TextureManager::LoadFromFile(L"tile", true)->GetSRV();m_srvs[3] = TextureManager::LoadFromFile(L"tile_nmap", false)->GetSRV();m_srvs[4] = TextureManager::LoadFromFile(L"white1x1", true)->GetSRV();m_srvs[5] = TextureManager::LoadFromFile(L"default_nmap", false)->GetSRV();m_srvs[6] = TextureManager::LoadFromFile(L"snowcube1024", true)->GetSRV();
虽然MiniEngine已经足够简单了,但依旧会有一些“坑”,难以想象直接看UE代码会是怎样的体验。
效果图