这个网站详细介绍了整个加载过程包括代码,这里只对需要额外理解的细节做补充。
等矩形贴图是将球体的贴图展开成正方形,一个例子如下所示:
要变成的环境映射贴图的GLSL代码如下:
#version 330 core
out vec4 FragColor;
in vec3 localPos;
uniform sampler2D equirectangularMap;
const vec2 invAtan = vec2(0.1591, 0.3183);
vec2 SampleSphericalMap(vec3 v)
{
vec2 uv = vec2(atan(v.z, v.x), asin(v.y));
uv *= invAtan;
uv += 0.5;
return uv;
}
void main()
{
vec2 uv = SampleSphericalMap(normalize(localPos)); // make sure to normalize localPos
vec3 color = texture(equirectangularMap, uv).rgb;
FragColor = vec4(color, 1.0);
}
其中atan(v.z, v.x)的取值为-
π
\pi
π-
π
\pi
π,为了使取值范围变为-0.5~0.5,需要乘以系数0.1591,asin(v.y)也是同理。
接下来的工作就是获取GLSL代码中的v了,假想我们的相机在球体中心,则要生成立方体贴图我们需要在上下左右前后6个面的方向对hdr贴图进行采样。
对6个面的方向进行记录
glm::mat4 captureViews[] =
{
glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)),
glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f)),
glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f))
};
glm::lookAt的第一个参数指定相机位置,第二个参数指定相机朝向,第三个参数指定朝向向量的正y方向,不妨想象一下,在captureViews[0]所代表的lookAt矩阵,你面朝着坐标系-x方向,自己的头指向+y方向,但是OpenGL加载纹理时是从-y方向开始进行的,所以应该反一下,所以所有captureViews的第三个lookAt参数的y方向应该翻转由正变负。
这种翻转在learnopengl-cn有描述:
理解captureViews[2]、captureViews[3]第三个参数的一个好办法是想想你只有躺着或趴着才能看到天花板或者地板对吧,这时候你的头指向的就是OpenGL坐标系中的±Z轴方向了。