OGL(教程24)——阴影映射2——代码结构梳理

项目地址:[email protected]:yichichunshui/ShadowMap2.git

本节主要是分析之前章节中使用的类、方法,以及如何连接shader、如何编写摄像机类,本文以及阴影映射的例子为入口,详细分析下OpenGL中如何加载模型、如何使用shader渲染一个物体,这将对今后的进一步的学习起到至关重要的作用。

在梳理代码结构之前,首先要回答下为什么选择OGL(教程24)——阴影映射2的项目进行分析。首先随着翻译的进行,一节一节音效不够深刻这是主要原因,其问题就是在于理解了对原理稍微了解,但是对于用程序实现还是十分陌生,OGL教程翻译,共计53节,如果继续翻译下去,势必理解还是不够深刻,所以还是应该以实际程序为参考进行编码层次的分析,这样才能更加深刻的理解原理。其次,随着我的翻译进行,发现阴影映射中包含了比较完整的、清晰的代码结构了。包括模型加载、图片加载、摄像机类、shader管理类,这些初步已经成为后面代码的工具类,所以此时如果能够对代码有清晰的认识,那么势必基础打的更加牢靠。

1、文件的个数统计:
头文件:13个
源文件:11个

2、头文件的功能介绍:
callbacks.h——接口类,里面包含的是方法的声明
camera.h——摄像机类
glut_backend.h——GLUT的一些初始化的工作,单独提到一个类中
lighting_technique.h——包括三类光源,平行光、点光、聚光灯,处理灯光的一些方法
math_3d.h——处理向量和矩阵的一些运算
mesh.h——包括顶点的定义、网格的定义
pipeline.h——渲染光线的操作,比如旋转、缩放、透视等
shadow_map_fbo.h——帧缓冲对象的初始化、绑定操作等
shadow_map_technique.h——阴影映射实现
technique.h——把shader的添加、链接、获取shader属性位置等封装为一个类
texture.h——贴图处理类
unistd.h——unix用到的头文件
util.h——工具类

3、main.cpp分析

int main(int argc, char** argv)
{
    GLUTBackendInit(argc, argv);

这个函数在glut_backend.cpp中,方法如下:

void GLUTBackendInit(int argc, char** argv){
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH);
    glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);
}

这个在第一节窗口中已经介绍过,它是GLUT的初始化函数,接收的参数可以从命令传入。glutInitDisplayMode设置了显示的设置,这里使用双缓冲,颜色模式为RGBA,开启深度缓冲,由于我们的阴影映射需要开启深度缓冲。glutSetOption设置了GLUT_ACTION_ON_WINDOW_CLOSE和GLUT_ACTION_GLUTMAINLOOP_RETURNS,它保证了glutMainLoop()退出后,继续执行其后的代码,防止glutMainLoop造成的内存泄漏。

总结:在初始化GLUT的其他方法调用之前,首先是初始化、设置显示模式、设置一些选项用以防止内存泄漏。

  if (!GLUTBackendCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, 32, false, "OpenGL tutors")) {
        return 1;
    }

WINDOW_WIDTH和WINDOW_HEIGHT是宏,其值为你想要的数值。这里指窗口的实际大小。GLUTBackendCreateWindow的内容如下:

bool GLUTBackendCreateWindow(unsigned int Width, unsigned int Height, unsigned int bpp, bool isFullScreen, const char* pTitle){
    if (isFullScreen){
        char ModeString[64] = {0};
        snprintf(ModeString, sizeof(ModeString), "%dx%d@%d", Width, Height, bpp);
        glutGameModeString(ModeString);
        glutEnterGameMode();
    }

接收的参数是窗口的宽度、高度,是否全屏,窗口的标题,这里的第三个参数bpp是每个像素的位数。
如果是全屏那么开辟长度为64的字符数组,其全屏模式设置字符串为%dx%d@%d,就是1280x1024@32,32位真彩色。然后调用glutGameModeString设置全屏模式。设置全屏模式之后,就进入游戏模式:glutEnterGameMode()。

扫描二维码关注公众号,回复: 3839952 查看本文章
 else {
        glutInitWindowSize(Width, Height);
        glutCreateWindow(pTitle);
    }

如果不是全屏模式,那么直接初始化窗口:glutInitWindowSize(Width, Height);然后指定窗口的标题,也只有非全屏模式才有窗口的标题。

总结:在创建窗口的时候,区分了全屏和非全屏。全屏需要设置游戏字符串,然后进入游戏模式;非全屏创建窗口,然后设置标题。

    GLenum res = glewInit();
    if (res != GLEW_OK){
        fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));
        return false;
    }

    return true;
}

glewInit是OpenGL的扩展库,用以正确加载OpenGL函数。然后是检测是否初始化成功。成功返回true,否则直接返回false。
回到main函数。

Main* pApp = new Main();

new出一个Main对象。

Main的成员变量9个,分别是:
LightingTechnique指针
ShadowMapTechnique指针
Camera指针
float m_scale缩放
SpotLight 聚光灯对象
Mesh网格指针
Mesh地面网格指针
ShadowMapFBO对象
Texture指针

无参数构造函数:

 Main()
    {
        m_pLightingEffect = NULL;
        m_pShadowMapEffect = NULL;
        m_pGameCamera = NULL;
        m_pMesh = NULL;
        m_pQuad = NULL;
        m_scale = 0.0f;
        m_pGroundTex = NULL;

        m_spotLight.AmbientIntensity = 0.1f;
        m_spotLight.DiffuseIntensity = 0.9f;
        m_spotLight.Color = Vector3f(1.0f, 1.0f, 1.0f);
        m_spotLight.Attenuation.Linear = 0.01f;
        m_spotLight.Position  = Vector3f(-20.0, 20.0, 1.0f);
        m_spotLight.Direction = Vector3f(1.0f, -1.0f, 0.0f);
        m_spotLight.Cutoff =  20.0f;
    }

析构函数:

    virtual ~Main()
    {
        SAFE_DELETE(m_pLightingEffect);
        SAFE_DELETE(m_pShadowMapEffect);
        SAFE_DELETE(m_pGameCamera);
        SAFE_DELETE(m_pMesh);
        SAFE_DELETE(m_pQuad);
        SAFE_DELETE(m_pGroundTex);
    }

六个指针的析构。

Main* pApp = new Main();

此句代码目前只执行到了Main的无参数构造函数。

  if (!pApp->Init()) {
        return 1;
    }

重点来了,进入Main的Init方法。

    bool Init()
    {
        Vector3f Pos(3.0f, 8.0f, -10.0f);
        Vector3f Target(0.0f, -0.2f, 1.0f);
        Vector3f Up(0.0, 1.0f, 0.0f);

        if (!m_shadowMapFBO.Init(WINDOW_WIDTH, WINDOW_HEIGHT)) {
            return false;
        }

        m_pGameCamera = new Camera(WINDOW_WIDTH, WINDOW_HEIGHT, Pos, Target, Up);

定义了摄像机的位置、目标、向上向量。

    if (!m_shadowMapFBO.Init(WINDOW_WIDTH, WINDOW_HEIGHT)) {
            return false;
        }

m_shadowMapFBO是阴影映射对象,它是对象,不是指针,所以不为空。调用其Init方法。
ShadowMapFBO的成员两个:
GLuint m_fbo;
GLuint m_shadowMap;

构造函数:

ShadowMapFBO::ShadowMapFBO()
{
    m_fbo = 0;
    m_shadowMap = 0;
}

然后我们进入ShadowMapFBO的Init方法。

bool ShadowMapFBO::Init(unsigned int WindowWidth, unsigned int WindowHeight)
{
    glGenFramebuffers(1, &m_fbo);
     glGenTextures(1, &m_shadowMap);
     glBindTexture(GL_TEXTURE_2D, m_shadowMap);
     glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, WindowWidth, WindowHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);

创建一个fbo对象,创建一个贴图m_shadowMap,绑定贴图到GL_TEXTURE_2D纹理目标。glTexImage2D,设定纹理的格式、大小等。

 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

总结:对图片做了三步:创建、绑定目标、设定格式、设置偏移和裁剪方式。

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo);

绑定m_fbo到目标GL_DRAW_FRAMEBUFFER。

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_shadowMap, 0);

绑定铁路到目标:GL_FRAMEBUFFER,最后一个参数是贴图的mipmap等级0是原始尺寸。第二个参数用以说明此贴图用来存储的是深度信息。

总结:创建fbo对象、创建贴图对象、绑定贴图对象到2D目标、设置贴图格式、设置贴图偏移模式、绑定fbo对象到GL_DRAW_FRAMEBUFFER目标、绑定贴图到GL_DRAW_FRAMEBUFFER。
这个部分参考:http://ogldev.atspace.co.uk/www/tutorial23/tutorial23.html

 glDrawBuffer(GL_NONE);

阴影映射,不需要绘制颜色缓冲,所以设置为GL_NONE。
最后检查FBO对象绑定状态:

GLenum Status = glCheckFramebufferStatus(GL_FRAMEBUFFER);

    if (Status != GL_FRAMEBUFFER_COMPLETE){
        printf("FB error, status: 0x%x\n", Status);
        return false;
    }

    return true;
}

再次回到Main的Init方法:

 m_pGameCamera = new Camera(WINDOW_WIDTH, WINDOW_HEIGHT, Pos, Target, Up);

这个是初始化摄像机。
摄像机类的成员8个:


    Vector3f m_pos;  //位置
    Vector3f m_target; //目标
    Vector3f m_up; //向上向量

    int m_windowWidth; //窗口宽度
    int m_windowHeight; //窗口高度

    float m_AngleH; //水平角度
    float m_AngleV; //垂直角度

    Vector2i m_mousePos; //鼠标位置

有参数构造函数1:

Camera::Camera(int WindowWidth, int WindowHeight, const Vector3f& Pos, const Vector3f& Target, const Vector3f& Up)
{
    m_windowWidth  = WindowWidth;
    m_windowHeight = WindowHeight;
    m_pos = Pos;

    m_target = Target;
    m_target.Normalize();

    m_up = Up;
    m_up.Normalize();

    Init();
}

最后的Init方法,

猜你喜欢

转载自blog.csdn.net/wodownload2/article/details/83547101
今日推荐