作者:i_dovelemon
日期:2018-05-30
来源:CSDN
主题:Radiosity Normal Mapping, Tangent Space
引言
前面一篇文章,我们讲述了如何通过Radiosity算法,实现旧式的光照贴图烘焙。正如前面我们讲述的,最终我们希望实现的是Source引擎中带有凹凸效果的烘焙(题图对比)。
我们知道,Normal Mapping是一种增添表面细节的方法。旧式的光照贴图丢失了这种细节。原因是普通的Normal Mapping,需要光照方向,但是对于烘焙的贴图来说没有固定的光照方向,所以丢失了凹凸的信息。为此,Valve在Half Life 2中提出了Radiosity Normal Mapping的改进方法,以此来增加烘焙贴图的表面细节。
Radiosity Normal Mapping
在传统的光照贴图烘焙中,我们保存的是每一个patch的法线所朝向的半个立方体范围内的入射光照的结果。Valve将此种方法进行了扩展,他们实验出了一组正交基,如图所示:
![这里写图片描述](https://i-blog.csdnimg.cn/blog_migrate/03bcf33b4279584bd53a9fbdb3e19e80.jpeg)
与旧式不同的是,需要计算以这三个切空间正交基为法线的patch的颜色。也就是说,相对于以前只保存一个颜色的光照贴图,现在需要保存这三个法线方向的光照颜色。
当我们获取了这样的光照贴图之后,在实际使用的时候,只要简单的获取对应的法线贴图中的法线数据与三个基向量做点积,并以此为权重进行光照贴图的混合,如图所示:
![这里写图片描述](https://i-blog.csdnimg.cn/blog_migrate/e780983e382fa1804371edc09a344e3b.jpeg)
整个过程并没有多复杂,通过这样的方式就能够近似的模拟带有Normal Maping的效果了。
实现
实现中也没有什么特别的地方,只要注意Valve给出的向量实际是patch法线所在的切空间,所以我们在进行光照贴图计算的时候,需要将这三个正交基变换到世界坐标来,如下代码所示:
void PrepareLightPatch() {
memset(m_Patch, 0, sizeof(m_Patch));
// Calculate uv for every patch
for (int32_t h = 0; h < kLightMapHeight; h++) {
for (int32_t w = 0; w < kLightMapWidth; w++) {
m_Patch[h][w].uv = math::Vector((w + 0.5f) * 1.0f / kLightMapWidth, (kLightMapHeight - h - 0.5f) * 1.0f / kLightMapHeight, 0.0f);
}
}
// Collect all faces
struct Face {
struct {
math::Vector uv;
math::Vector pos;
math::Vector normal;
math::Vector tangent;
math::Vector binormal;
} vertex[3];
};
std::vector<Face> faces;
faces.clear();
scene::ModelEffectParam effectParam;
scene::ModelMaterialParam materialParam;
float* vertexBuf = NULL;