OpenGL--天空盒

2023-05-16

理论基础

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;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenGL--天空盒 的相关文章

  • 最好的 Curl 学习指南,强烈建议收藏!

    来自 xff1a 阮一峰的网络日志 链接 xff1a http www ruanyifeng com blog 2019 09 curl reference html 简介 curl 是常用的命令行工具 xff0c 用来请求 Web 服务器
  • TCP超时与重传

    1 TCP重传 A 基于时间信息 设置RTO xff1a tcp协议对超时报文的处理响应比较剧烈 xff0c 如 xff1a i 基于拥塞控制机制 xff0c 减小发送窗口大小 限窗 xff1b ii 当一个重传报文段被再次重传时 xff0
  • tcpdump参数用法详解

    一直在linux下开发的人一定会用到tcpdump xff0c 下面就是关于tcpdump的使用方法说明 1 tcpdump的选项 a 将网络地址和广播地址转变成名字 xff1b d 将匹配信息包的代码以人们能够理解的汇编格式给出 xff1
  • 学习图像处理知识---EmguCV3.4图像ArUco Marker Detection--DetectorParameters 结构体

    好久没有更新了图像处理 ArUco Marker Detection 种汉明 海明 码的格子图 用于相机 相机姿态估计之标记检测 在Emgu CV Aruco Namespace 命名空间中 重要的检测结构体DetectorParamete
  • 仿真导航中2d Nav Goal后小车不能到达目标点

    古月老师的课程我在进行仿真导航过程中 xff0c 遇到了小车不能到达我在rviz中指定的2d Nav Goal的目标点 xff0c 并且反复震荡的问题 解决方法如下 xff1a 模型参数里左右轮参数搞反了 xff0c 互换一下即可
  • 超声波传感器测距原理

    超声波 ultrasonic waves xff1a 人类耳朵能听到的声波频率为20HZ xff5e 20KHz 当声波的振动频率大于20KHz或小于20Hz时 xff0c 我们便听不见了 因此 xff0c 我们把频率高于20000赫兹的声
  • stm32串口中断收发数据环形缓冲区的设计

    cpp view plain copy Function Name USART2 IRQHandler Description This function handles USART2 global interrupt request In
  • CMake注意事项

    今天被target link libraries找不到库文件的问题给郁闷了好久 xff0c 后来才发现target link libraries第二个参数 xff08 即需要连接的库 xff09 居然一定要lib作为开头 xff0c 才能在
  • Web后端http请求(带用户名和密码防止401 Unauthorized)

    Java Java这方面的Jar包应该比较多 xff0c 比如HttpClient xff0c 我这里使用最基本的 xff1a java view plain copy 认证信息对象 xff0c 用于包含访问翻译服务的用户名和密码 Auth
  • 开关电源基础——TI电源在电赛中的应用

    开关电源基础 线性稳压器等效电路 如果输入是39V xff0c 输出是13V xff0c 那么效率为33 3 xff0c 过低的效率导致能量的浪费 如何提高线性稳压器的效率呢 xff1f 这是开关电源最原始的设计思想 xff0c 但是我们又
  • QGraphicsView类

    QGraphicsView提供一个显示QGraphicsScene内容的窗口 xff0c 该窗口可以滚动 xff0c 可以在构造时候把场景对象作为参数 xff0c 或者之后使用setScene 来设置view的场景 xff0c 然后调用了s
  • STM32 USART 接收任意长度字符

    近段时间学习到 STM32 USART 部分 xff0c 基本上在接收数据的时候都是采用定长 xff0c 所以一直想实现接收任意长度的字符串 这里的任意长度不是指的无限长 xff0c 而是在自己定义的缓冲区范围之类 比如说缓冲区的大小是 1
  • 关于RS485和RS422总线,一主多从回复信号被拉低收不到反馈数据的问题。

    芯片 xff1a MAX13487EESA xff08 RS485 xff09 这里这个三个电阻不接 AK管不接也行 如果你发现你在总线上挂接两个以上的RS485模块 xff0c 发现总线电压和只接一个时波形幅度降低了 xff0c 就是上面
  • ubuntu16.04 UNIX 网络编程卷一 源码使用

    参考源码目录 README文档 tar xvf unpv13e tar gz 解压 然后进入源码目录 a configure 这一步没有出现问题 b cd lib c make 这一步没有出错 d cd libfree e make 这一步
  • HTTP认证之摘要认证——Digest

    一 概述 Digest认证是为了修复基本认证协议的严重缺陷而设计的 xff0c 秉承 绝不通过明文在网络发送密码 的原则 xff0c 通过 密码摘要 进行认证 xff0c 大大提高了安全性 相对于基本认证 xff0c 主要有如下改进 xff
  • QFramework Pro 开发日志(六)一键生成类图功能介绍

    这个功能连续开发了三天 xff0c 现在完成了一个基本的雏形 先说说 xff0c 为啥做这个功能吧 作为 Unity 开发者 xff0c 不管是在做游戏还是在做工具 方案 学习源码的时候 xff0c 多多少少都会需要魔改一些其他插件 框架
  • HAL库教程9:串口接收不定长数据

    串口收到的两组数据之间 xff0c 往往会有一定的时间间隔 可以判断这个间隔 xff0c 来实现无需结束符 xff0c 无需指定长度 xff0c 串口可接收不定长数据的功能 如果串口在一定的时间内没有收到新的数据 xff0c 可以认为一组数
  • odroid平台——ASUS Xtion Pro Live + Openni + ROS搭建(Xu4升级版)

    之前的文章写了基于odroid xu3的Xtion 43 ROS搭建方法 xff0c 由于xu3停产了 xff0c 只能换用xu4 xff0c 但是换的过程中发现xu4没有usb2 0 xff0c 只有usb3 0 xff0c 但是很遗憾X
  • Tensorflow: Cannot dlopen some GPU libraries. Skipping registering GPU devices...

    Cannot dlopen some GPU libraries Skipping registering GPU devices 很久没搞Tensorflow了 xff0c 又出了一些问题 xff0c 这里作个备份 可能的问题为 xff1
  • 目标检测模型、卷积网络的感受野与分形特征

    概述 最近几年深度学习的快速发展对目标检测 xff08 Object Detection xff09 领域也产生了巨大的影响 xff0c 各种SOTA xff08 State of Art xff09 的模型也层出不穷 xff0c 包括但不

随机推荐