OpenGL--天空盒

    xiaoxiao2021-12-15  28

    理论基础

    1,目前虚拟场景中天空建模常用的方法有天空顶(SkyDome:半球形)和天空盒(SkyBox:长方体)两种方法。其本质都是摄像机处在一个盒子中间,这个盒子通过纹理贴图形成的虚拟世界场景。其中天空盒绘制技术非常简单,因此被广泛应用。然而,有时也会存在一些问题,例如使用雾效时,如果雾被设置在观察者的旁边,天空盒将减淡甚至消失。另一个更坑爹的问题是雾会聚积在天空盒的顶点处,从而使天空盒的多边形暴露无遗。小心地调整雾化参数或细分天空盒的每一个面可以减少这些问题,但这样会大大影响性能。此时,就可用天空顶来替代天空盒了。它是个半球形场景,因此就不会有什么边界暴露问题了。本节主要介绍天空盒相关技术。

    2,天空盒其实就是一个覆盖场景四周的长方体,但它的各个面上贴有表示天空的纹理图片,即四周的4面纹理的边与顶面纹理的边相连,同时四面纹理前后相连,纹理大小要是2的N次方(32,64,128,…),如下:

    天空盒示例(部分辅助类见上一节)

    1,主程序:

    #include "stdafx.h" #include<gl/glut.h> #include<gl/glu.h> #include<gl/gl.h> #include <gl\GLAUX.h> #include "Camera.h" #include "SkyBox.h" Camera m_Camera; CSkyBox m_SkyBox; void init(void) { /** 用户自定义的初始化过程 */ glClearColor(0.0f, 0.0f, 0.0f, 0.5f); glClearDepth(1.0f); glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glShadeModel(GL_SMOOTH); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); /** 启用纹理 */ glEnable(GL_TEXTURE_2D); /** 初始化天空 */ if (!m_SkyBox.Init()) { MessageBox(NULL, (LPCWSTR)"初始化天空失败!", (LPCWSTR)"错误", MB_OK); exit(0); } /** 设置摄像机 */ m_Camera.setCamera(500, 35, 400, 501, 35, 400, 0, 1, 0); } void display(void) { /** 用户自定义的绘制过程 */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); /** 放置摄像机 */ m_Camera.setLook(); /** 绘制天空 */ m_SkyBox.CreateSkyBox(0, 0, 0, 1.0, 0.5, 1.0); glFlush(); /**< 强制执行所有的OpenGL命令 */ } void ChangeSize(int width, int height) { glViewport(0, 0, width, height); /**< 重新设置视口 */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f, (GLfloat)width / (GLfloat)height, 0.1f, 4000.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void motion(int x, int y) { m_Camera.setViewByMouse(); glutPostRedisplay(); } void keyboard(unsigned char key, int x, int y) { switch (key) { case 27: exit(0); break; case '1': m_Camera.setSpeed(0.2f); break; case '2': m_Camera.setSpeed(1.0f); break; case 'w': m_Camera.moveCamera(m_Camera.getSpeed()); break; case 's': m_Camera.moveCamera(-m_Camera.getSpeed()); break; case 'a': m_Camera.yawCamera(-m_Camera.getSpeed()); break; case 'd': m_Camera.yawCamera(m_Camera.getSpeed()); break; } glutPostRedisplay(); printf("key::%d", key); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_RGB); glutInitWindowSize(800, 600); glutInitWindowPosition((GetSystemMetrics(SM_CXSCREEN) >> 1) - 400, (GetSystemMetrics(SM_CYSCREEN) >> 1) - 300); glutCreateWindow("天空盒"); init(); glutReshapeFunc(ChangeSize); glutDisplayFunc(display); glutMotionFunc(motion); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; }

    2,天空盒实现类

    #ifndef __SKYBOX_H__ #define __SKYBOX_H__ #include "stdafx.h" #include "CBMPLoader.h" #include "Vector.h" #include "Camera.h" #define GL_CLAMP_TO_EDGE 0x812F /** 天空盒类 */ class CSkyBox { public: /** 构造函数 */ CSkyBox(); ~CSkyBox(); /** 初始化 */ bool Init(); /** 渲染天空 */ void CreateSkyBox(float x, float y, float z, float width, float height, float length); private: CBMPLoader m_texture[6]; /**< 天空盒纹理 */ }; #endif ///__SKYBOX_H__ #include "SkyBox.h" CSkyBox::CSkyBox() { } CSkyBox::~CSkyBox() { /** 删除纹理对象及其占用的内存 */ for(int i =0 ;i< 6; i++) { m_texture[i].FreeImage(); glDeleteTextures(1,&m_texture[i].ID); } } /** 天空盒初始化 */ bool CSkyBox::Init() { char filename[128] ; /**< 用来保存文件名 */ char *bmpName[] = { "back","front","bottom","top","left","right"}; for(int i=0; i< 6; i++) { sprintf(filename,"data/%s",bmpName[i]); strcat(filename,".bmp"); if(!m_texture[i].LoadBitmap(filename)) /**< 载入位图文件 */ { MessageBox(NULL, (LPCWSTR)"装载位图文件失败!", (LPCWSTR)"错误", MB_OK); /**< 如果载入失败则弹出对话框 */ exit(0); } glGenTextures(1, &m_texture[i].ID); /**< 生成一个纹理对象名称 */ glBindTexture(GL_TEXTURE_2D, m_texture[i].ID); /**< 创建纹理对象 */ /** 控制滤波: */ /* 其中GL_TEXTURE_WRAP_S,GL_TEXTURE_WRAP_T通常可设置为GL_REPEAT或GL_CLAMP两种方式。 当待填充的多边形大于纹理的时候,GL_REPEAT表示多余的部分用重复的方式填充;GL_CLAMP表示多余的部分用相连边缘的相邻像素填充。 在实际绘制中,我们一般采用GL_CLAMP_EDGE来处理,这就消除了接缝处的细线,增强了天空盒的真实感。 */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); /** 创建纹理 */ gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, m_texture[i].imageWidth, m_texture[i].imageHeight, GL_RGB, GL_UNSIGNED_BYTE, m_texture[i].image); } return true; } /** 构造天空盒 */ void CSkyBox::CreateSkyBox(float x, float y, float z, float box_width, float box_height, float box_length) { /** 获得场景中光照状态 */ GLboolean lp; glGetBooleanv(GL_LIGHTING,&lp); /** 计算天空盒长 宽 高 */ float width = MAP * box_width/8; float height = MAP * box_height/8; float length = MAP * box_length/8; /** 计算天空盒中心位置 */ x = x+ MAP/8 - width / 2; y = y+ MAP/24 - height / 2; z = z+ MAP/8 - length / 2; glDisable(GL_LIGHTING); /**< 关闭光照 */ /** 开始绘制 */ glPushMatrix(); glTranslatef(-x,-y,-z); /** 绘制背面 */ glBindTexture(GL_TEXTURE_2D, m_texture[0].ID); glBegin(GL_QUADS); /** 指定纹理坐标和顶点坐标 */ glTexCoord2f(1.0f, 0.0f); glVertex3f(x + width, y, z); glTexCoord2f(1.0f, 1.0f); glVertex3f(x + width, y + height, z); glTexCoord2f(0.0f, 1.0f); glVertex3f(x, y + height, z); glTexCoord2f(0.0f, 0.0f); glVertex3f(x, y, z); glEnd(); /** 绘制前面 */ glBindTexture(GL_TEXTURE_2D, m_texture[1].ID); glBegin(GL_QUADS); /** 指定纹理坐标和顶点坐标 */ glTexCoord2f(1.0f, 0.0f); glVertex3f(x, y, z + length); glTexCoord2f(1.0f, 1.0f); glVertex3f(x, y + height, z + length); glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y + height, z + length); glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y, z + length); glEnd(); /** 绘制底面 */ glBindTexture(GL_TEXTURE_2D, m_texture[2].ID); glBegin(GL_QUADS); /** 指定纹理坐标和顶点坐标 */ glTexCoord2f(1.0f, 0.0f); glVertex3f(x, y, z); glTexCoord2f(1.0f, 1.0f); glVertex3f(x, y, z + length); glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y, z + length); glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y, z); glEnd(); /** 绘制顶面 */ glBindTexture(GL_TEXTURE_2D, m_texture[3].ID); glBegin(GL_QUADS); /** 指定纹理坐标和顶点坐标 */ glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y + height, z); glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y + height, z + length); glTexCoord2f(1.0f, 0.0f); glVertex3f(x, y + height, z + length); glTexCoord2f(1.0f, 1.0f); glVertex3f(x, y + height, z); glEnd(); /** 绘制左面 */ glBindTexture(GL_TEXTURE_2D, m_texture[4].ID); glBegin(GL_QUADS); /** 指定纹理坐标和顶点坐标 */ glTexCoord2f(1.0f, 1.0f); glVertex3f(x, y + height, z); glTexCoord2f(0.0f, 1.0f); glVertex3f(x, y + height, z + length); glTexCoord2f(0.0f, 0.0f); glVertex3f(x, y, z + length); glTexCoord2f(1.0f, 0.0f); glVertex3f(x, y, z); glEnd(); /** 绘制右面 */ glBindTexture(GL_TEXTURE_2D, m_texture[5].ID); glBegin(GL_QUADS); /** 指定纹理坐标和顶点坐标 */ glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y, z); glTexCoord2f(1.0f, 0.0f); glVertex3f(x + width, y, z + length); glTexCoord2f(1.0f, 1.0f); glVertex3f(x + width, y + height, z + length); glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y + height, z); glEnd(); glPopMatrix(); /** 绘制结束 */ if(lp) /** 恢复光照状态 */ glEnable(GL_LIGHTING); }

    注: /* 定义地面网格 / const unsigned int MAP_WIDTH = 1024; const unsigned int CELL_WIDTH = 16; const unsigned int MAP = MAP_WIDTH * CELL_WIDTH / 2;

    转载请注明原文地址: https://ju.6miu.com/read-1000080.html

    最新回复(0)