概念:
立方体纹理是一种特殊的纹理技术,它用6幅二维纹理图像构成一个以原点为中心的纹理立方体。对于每个片段,纹理坐标(s, t, r)被当作方向向量看待,每个纹素(texel)都表示从原点所看到的纹理立方体上的图像。
指定6个面的纹理图像:
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA,
imageSize, imageSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, image1);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA,
imageSize, imageSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, image4);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA,
imageSize, imageSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, image2);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA,
imageSize, imageSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, image5);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA,
imageSize, imageSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, image3);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA,
imageSize, imageSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, image6);
指定立方体纹理对象的参数:
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S,
GL_REPEAT);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T,
GL_REPEAT);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R,
GL_REPEAT);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER,
GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,
GL_NEAREST);
为了创建立方体纹理图像数据,可以在场景的原点上防止一架照相机,然后把相机一次对准各个轴的正方向和负方向,拍摄6幅视野为90(fovy=90)度的"快照"。这些快照把3D空间划分为6个在原点相交的平头截体(frustum)。
纹理纹素的获取算法
正如前面所述,对于每个片段,纹理坐标(s, t, r)被当作方向向量看待,每个纹素(texel)都表示从原点所看到的纹理立方体上的图像。下面实现代码来自mesa3d里面,源文件路径为Mesa-8.0.4\src\mesa\swrast\s_texfilter.c
/**********************************************************************/
/* Texture Cube Map Sampling Functions */
/**********************************************************************/
/**
* Choose one of six sides of a texture cube map given the texture
* coord (rx,ry,rz). Return pointer to corresponding array of texture
* images.
*/
static const struct gl_texture_image **
choose_cube_face(const struct gl_texture_object *texObj,
const GLfloat texcoord[4], GLfloat newCoord[4])
{
/*
major axis
direction target sc tc ma
---------- ------------------------------- --- --- ---
+rx TEXTURE_CUBE_MAP_POSITIVE_X_EXT -rz -ry rx
-rx TEXTURE_CUBE_MAP_NEGATIVE_X_EXT +rz -ry rx
+ry TEXTURE_CUBE_MAP_POSITIVE_Y_EXT +rx +rz ry
-ry TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT +rx -rz ry
+rz TEXTURE_CUBE_MAP_POSITIVE_Z_EXT +rx -ry rz
-rz TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT -rx -ry rz
*/
const GLfloat rx = texcoord[0];
const GLfloat ry = texcoord[1];
const GLfloat rz = texcoord[2];
const GLfloat arx = FABSF(rx), ary = FABSF(ry), arz = FABSF(rz);
GLuint face;
GLfloat sc, tc, ma;
if (arx >= ary && arx >= arz) {
if (rx >= 0.0F) {
face = FACE_POS_X;
sc = -rz;
tc = -ry;
ma = arx;
}
else {
face = FACE_NEG_X;
sc = rz;
tc = -ry;
ma = arx;
}
}
else if (ary >= arx && ary >= arz) {
if (ry >= 0.0F) {
face = FACE_POS_Y;
sc = rx;
tc = rz;
ma = ary;
}
else {
face = FACE_NEG_Y;
sc = rx;
tc = -rz;
ma = ary;
}
}
else {
if (rz > 0.0F) {
face = FACE_POS_Z;
sc = rx;
tc = -ry;
ma = arz;
}
else {
face = FACE_NEG_Z;
sc = -rx;
tc = -ry;
ma = arz;
}
}
{
const float ima = 1.0F / ma;
newCoord[0] = ( sc * ima + 1.0F ) * 0.5F;
newCoord[1] = ( tc * ima + 1.0F ) * 0.5F;
}
return (const struct gl_texture_image **) texObj->Image[face];
}
static void
sample_nearest_cube(struct gl_context *ctx,
const struct gl_texture_object *tObj, GLuint n,
const GLfloat texcoords[][4], const GLfloat lambda[],
GLfloat rgba[][4])
{
GLuint i;
(void) lambda;
for (i = 0; i < n; i++) {
const struct gl_texture_image **images;
GLfloat newCoord[4];
images = choose_cube_face(tObj, texcoords[i], newCoord);
sample_2d_nearest(ctx, tObj, images[tObj->BaseLevel],
newCoord, rgba[i]);
}
}
static void
sample_linear_cube(struct gl_context *ctx,
const struct gl_texture_object *tObj, GLuint n,
const GLfloat texcoords[][4],
const GLfloat lambda[], GLfloat rgba[][4])
{
GLuint i;
(void) lambda;
for (i = 0; i < n; i++) {
const struct gl_texture_image **images;
GLfloat newCoord[4];
images = choose_cube_face(tObj, texcoords[i], newCoord);
sample_2d_linear(ctx, tObj, images[tObj->BaseLevel],
newCoord, rgba[i]);
}
}
static void
sample_cube_nearest_mipmap_nearest(struct gl_context *ctx,
const struct gl_texture_object *tObj,
GLuint n, const GLfloat texcoord[][4],
const GLfloat lambda[], GLfloat rgba[][4])
{
GLuint i;
ASSERT(lambda != NULL);
for (i = 0; i < n; i++) {
const struct gl_texture_image **images;
GLfloat newCoord[4];
GLint level;
images = choose_cube_face(tObj, texcoord[i], newCoord);
/* XXX we actually need to recompute lambda here based on the newCoords.
* But we would need the texcoords of adjacent fragments to compute that
* properly, and we don't have those here.
* For now, do an approximation: subtracting 1 from the chosen mipmap
* level seems to work in some test cases.
* The same adjustment is done in the next few functions.
*/
level = nearest_mipmap_level(tObj, lambda[i]);
level = MAX2(level - 1, 0);
sample_2d_nearest(ctx, tObj, images[level], newCoord, rgba[i]);
}
}
static void
sample_cube_linear_mipmap_nearest(struct gl_context *ctx,
const struct gl_texture_object *tObj,
GLuint n, const GLfloat texcoord[][4],
const GLfloat lambda[], GLfloat rgba[][4])
{
GLuint i;
ASSERT(lambda != NULL);
for (i = 0; i < n; i++) {
const struct gl_texture_image **images;
GLfloat newCoord[4];
GLint level = nearest_mipmap_level(tObj, lambda[i]);
level = MAX2(level - 1, 0); /* see comment above */
images = choose_cube_face(tObj, texcoord[i], newCoord);
sample_2d_linear(ctx, tObj, images[level], newCoord, rgba[i]);
}
}
static void
sample_cube_nearest_mipmap_linear(struct gl_context *ctx,
const struct gl_texture_object *tObj,
GLuint n, const GLfloat texcoord[][4],
const GLfloat lambda[], GLfloat rgba[][4])
{
GLuint i;
ASSERT(lambda != NULL);
for (i = 0; i < n; i++) {
const struct gl_texture_image **images;
GLfloat newCoord[4];
GLint level = linear_mipmap_level(tObj, lambda[i]);
level = MAX2(level - 1, 0); /* see comment above */
images = choose_cube_face(tObj, texcoord[i], newCoord);
if (level >= tObj->_MaxLevel) {
sample_2d_nearest(ctx, tObj, images[tObj->_MaxLevel],
newCoord, rgba[i]);
}
else {
GLfloat t0[4], t1[4]; /* texels */
const GLfloat f = FRAC(lambda[i]);
sample_2d_nearest(ctx, tObj, images[level ], newCoord, t0);
sample_2d_nearest(ctx, tObj, images[level+1], newCoord, t1);
lerp_rgba(rgba[i], f, t0, t1);
}
}
}
static void
sample_cube_linear_mipmap_linear(struct gl_context *ctx,
const struct gl_texture_object *tObj,
GLuint n, const GLfloat texcoord[][4],
const GLfloat lambda[], GLfloat rgba[][4])
{
GLuint i;
ASSERT(lambda != NULL);
for (i = 0; i < n; i++) {
const struct gl_texture_image **images;
GLfloat newCoord[4];
GLint level = linear_mipmap_level(tObj, lambda[i]);
level = MAX2(level - 1, 0); /* see comment above */
images = choose_cube_face(tObj, texcoord[i], newCoord);
if (level >= tObj->_MaxLevel) {
sample_2d_linear(ctx, tObj, images[tObj->_MaxLevel],
newCoord, rgba[i]);
}
else {
GLfloat t0[4], t1[4];
const GLfloat f = FRAC(lambda[i]);
sample_2d_linear(ctx, tObj, images[level ], newCoord, t0);
sample_2d_linear(ctx, tObj, images[level+1], newCoord, t1);
lerp_rgba(rgba[i], f, t0, t1);
}
}
}
如何指定纹理坐标
1. 使用vertex的法线作为uv坐标; (GL_NORMAL_MAP)
用于天空盒贴图
2. 使用法线和相机到vertex的向量,推导出入射光向量作为uv坐标 (GL_REFLECTION_MAP)
用于物体反射周围环境颜色
Relfect模式shader:
// Reflection Shader
// Vertex Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130
// Incoming per vertex... position and normal
in vec4 vVertex;
in vec3 vNormal;
uniform mat4 mvpMatrix;
uniform mat4 mvMatrix;
uniform mat3 normalMatrix;
uniform mat4 mInverseCamera;
// Texture coordinate to fragment program
smooth out vec3 vVaryingTexCoord;
void main(void)
{
// Normal in Eye Space
vec3 vEyeNormal = normalMatrix * vNormal;
// Vertex position in Eye Space
vec4 vVert4 = mvMatrix * vVertex;
vec3 vEyeVertex = normalize(vVert4.xyz / vVert4.w);
// Get reflected vector
vec4 vCoords = vec4(reflect(vEyeVertex, vEyeNormal), 1.0);
// Rotate by flipped camera
vCoords = mInverseCamera * vCoords;
vVaryingTexCoord.xyz = normalize(vCoords.xyz);
// Don't forget to transform the geometry!
gl_Position = mvpMatrix * vVertex;
}
// Reflection Shader
// Fragment Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130
out vec4 vFragColor;
uniform samplerCube cubeMap;
smooth in vec3 vVaryingTexCoord;
void main(void)
{
vFragColor = texture(cubeMap, vVaryingTexCoord.stp);
}
运行截图:
下面的图为6个面的纹理贴图,注意图像是上下倒置的哦!(看上面的s,t,r的代码)
POS_X:
NEG_X:
POS_Y:
NEG_Y:
POS_Z:
NEG_Z: