OpenGL--天空盒

理论基础

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

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

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

1,主程序:

#include "stdafx.h"
#include
#include
#include
#include 

#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;
}

OpenGL--天空盒_第2张图片

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;

你可能感兴趣的:(OpenGL)