立方体映射(cube-map)纹理是一种特殊类型的纹理,用于环境映射,使用一组图像并把他们作为立方体的面。立方体映射的6个面用正方形并且大小相同的6个子纹理表示。要从立方体纹理中采样的时候,使用的纹理坐标是3维,并且被看做来自原点的方向!方向指向用来读取纹理的立方体映射表面的位置。在这里例子中,有关于环境映射的代码,主要思想是通过观察向量和表面的法向量反射来确定采样的纹理坐标。
通过将一个新的纹理名绑定到GL_TEXTURE_CUBE_MAP纹理目标,然后调用glTexStorage2D()以GL_TEXTUR_CUBE_MAP作为参数为立方体纹理映射分配储存空间,这个调用将为立方体纹理映射的所有6个面分配内存。分配内存后,立方体纹理映射用6个特殊的目标集合表示,分别是:GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z.或者调用glTexImage2D()以6个特殊标识符分别分配6个面的内存,设置数据。
for (int face = 0; face < 6; face++)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
level,
image->internalFormat,
image->mip[level].width, image->mip[level].height,
0,
image->format, image->type,
ptr + image->sliceStride * face);
}
GL_TEXTURE_CUBE_MAP_POSITIVE_X... 是6个连续的整数。
glEnable( GL_TEXTURE_CUBE_MAP_SEAMLESS ):开启GL_TEXTURE_CUBE_MAP_SEAMLESS在天空盒子的边缘将对两个交界面进行采样混合,否则边缘将采用单边像素,会出现明显的裂缝。
实例代码:例子代码中实现了天空盒子和环境映射。在关键地方有简单的注释。
#include "cubeTexture\cubeTexture.h" #include "vbm.h" #include "vutils.h" #include "vmath.h" #include "vermilion.h" namespace cubeTexture { float aspect = 800.0/600.0f; GLuint skybox_prog; GLuint object_prog; GLuint vao; GLuint cube_vbo; GLuint cube_element_buffer; GLuint tex; GLint skybox_rotate_loc; GLint object_mat_mvp_loc; GLint object_mat_mv_loc; VBObject object; void cubeTextureInit() { skybox_prog = glCreateProgram(); static const char skybox_shader_vs[] = "#version 330 core\n" "\n" "layout (location = 0) in vec3 in_position;\n" "\n" "out vec3 tex_coord;\n" "\n" "uniform mat4 tc_rotate;\n" "\n" "void main(void)\n" "{\n" " gl_Position = tc_rotate * vec4(in_position, 1.0);\n" " tex_coord = in_position;\n" "}\n"; static const char skybox_shader_fs[] = "#version 330 core\n" "\n" "in vec3 tex_coord;\n" "\n" "layout (location = 0) out vec4 color;\n" "\n" "uniform samplerCube tex;\n" "\n" "void main(void)\n" "{\n" " color = texture(tex, tex_coord);\n" "}\n"; vglAttachShaderSource(skybox_prog, GL_VERTEX_SHADER, skybox_shader_vs); vglAttachShaderSource(skybox_prog, GL_FRAGMENT_SHADER, skybox_shader_fs ); glLinkProgram( skybox_prog ); static const char object_shader_vs[] = "#version 330 core\n" "\n" "layout (location = 0) in vec4 in_position;\n" "layout (location = 1) in vec3 in_normal;\n" "\n" "out vec3 vs_fs_normal;\n" "out vec3 vs_fs_position;\n" "\n" "uniform mat4 mat_mvp;\n" "uniform mat4 mat_mv;\n" "\n" "void main(void)\n" "{\n" " gl_Position = mat_mvp * in_position;\n" " vs_fs_normal = mat3(mat_mv) * in_normal;\n" " vs_fs_position = (mat_mv * in_position).xyz;\n" "}\n"; static const char object_shader_fs[] = "#version 330 core\n" "\n" "in vec3 vs_fs_normal;\n" "in vec3 vs_fs_position;\n" "\n" "layout (location = 0) out vec4 color;\n" "\n" "uniform samplerCube tex;\n" "\n" "void main(void)\n" "{\n" " vec3 tc = reflect(vs_fs_position, normalize(vs_fs_normal));\n"//这里的原理其实是根据摄像机与观察点形成的向量(默认摄像机在(0,0,0,)), //和观察点顶点法线计算最终顶点纹理坐标。有点类似于关照反射的计算。 //并且这里的所有的计算也是在观察空间内计算的,因为我们之前就进行了,model view变换。 " color = vec4(0.3, 0.2, 0.1, 1.0) + vec4(0.97, 0.83, 0.79, 0.0) * texture(tex, tc);\n" "}\n"; object_prog = glCreateProgram(); vglAttachShaderSource( object_prog, GL_VERTEX_SHADER, object_shader_vs ); vglAttachShaderSource( object_prog, GL_FRAGMENT_SHADER, object_shader_fs ); glLinkProgram( object_prog ); glGenBuffers( 1, &cube_vbo ); glBindBuffer( GL_ARRAY_BUFFER, cube_vbo ); static const GLfloat cube_vertices[] = { -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f }; static const GLushort cube_indices[] = { 0, 1, 2, 3, 6, 7, 4, 5, // First strip 2, 6, 0, 4, 1, 5, 3, 7 // Second strip }; glBufferData( GL_ARRAY_BUFFER, sizeof(cube_vertices), cube_vertices, GL_STATIC_DRAW ); glGenVertexArrays( 1, &vao ); glBindVertexArray( vao ); glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) ); glEnableVertexAttribArray(0); glGenBuffers( 1, &cube_element_buffer ); glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, cube_element_buffer ); glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof(cube_indices), cube_indices, GL_STATIC_DRAW ); skybox_rotate_loc = glGetUniformLocation(skybox_prog, "tc_rotate"); object_mat_mvp_loc = glGetUniformLocation(object_prog, "mat_mvp"); object_mat_mv_loc = glGetUniformLocation(object_prog, "mat_mv"); vglImageData image; tex = vglLoadTexture("../8edlib/media/TantolundenCube.dds", 0, &image); vglUnloadImage(&image); object.LoadFromVBM("../8edlib/media/unit_torus.vbm", 0, 1, 2 ); } void cubeTextureUpdate( float delta ) { } void cubeTextureDisplay() { static const unsigned int start_time = GetTickCount(); float t = float((GetTickCount() - start_time)) / float(0x3FFF); static float time = 1.0; time += 0.01; static const vmath::vec3 X(1.0f, 0.0f, 0.0f); static const vmath::vec3 Y(0.0f, 1.0f, 0.0f); static const vmath::vec3 Z(0.0f, 0.0f, 1.0f); vmath::mat4 tc_matrix( vmath::mat4::identity() ); glClearColor( 0.0f, 0.25f, 0.3f, 1.0f ); glClearDepth( 1.0f ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glEnable( GL_DEPTH_TEST ); glDepthFunc( GL_LEQUAL ); glDisable( GL_CULL_FACE ); glUseProgram( skybox_prog ); glEnable( GL_TEXTURE_CUBE_MAP_SEAMLESS );//开启时,两个面的交界处会进行双边线性采样来确定边缘像素,关闭时,将只采用单边值进行设置。 tc_matrix = vmath::perspective( 35.0f, 1.0f / aspect, 0.1f, 100.0f ) * tc_matrix; glUniformMatrix4fv( skybox_rotate_loc, 1, GL_FALSE, tc_matrix ); glBindVertexArray(vao); glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, cube_element_buffer ); glDrawElements( GL_TRIANGLE_STRIP, 8, GL_UNSIGNED_SHORT, NULL ); glDrawElements( GL_TRIANGLE_STRIP, 8, GL_UNSIGNED_SHORT, BUFFER_OFFSET( 8 * sizeof(GLushort) ) ); glUseProgram( object_prog ); tc_matrix = vmath::translate( vmath::vec3(0.0f, 0.0f, -4.0f) ) * vmath::rotate(80.0f * 3.0f * time, Y) * vmath::rotate(70.0f * 3.0f * time, Z); glUniformMatrix4fv( object_mat_mv_loc, 1, GL_FALSE, tc_matrix ); tc_matrix = vmath::perspective(35.0f, 1.0f / aspect, 0.1f, 100.0f) * tc_matrix; glUniformMatrix4fv( object_mat_mvp_loc, 1, GL_FALSE, tc_matrix ); glClear( GL_DEPTH_BUFFER_BIT ); object.Render(); GLenum e; e = glGetError(); } };
运行效果: