美文网首页
OpenGL 案例:球体世界

OpenGL 案例:球体世界

作者: 远方竹叶 | 来源:发表于2020-07-24 10:37 被阅读0次

    球体世界案例是基于 OpenGL 综合案例的学习 的基础上增加了纹理和镜像显示。最终效果图:

    总体流程图可以使用下图来展示:

    对于流程图中涉及到函数主要改动了 SetupRC、RenderScene 函数,增加了 LoadTGATexture、ShutdownRC、drawSomething 函数。

    SetupRC

    主要设置背景颜色、着色器的初始化,顶点、纹理数据的相关设置,在原有的基础上绑定了纹理数据。

    地板

    由原来的线条相交,更改为仅设置4个顶点坐标&纹理坐标

        // 设置地板顶点数据&地板纹理
        GLfloat texSize = 10.0f;
        floorBatch.Begin(GL_TRIANGLE_FAN, 4, 1);
        
        floorBatch.MultiTexCoord2f(0, 0, 0);
        floorBatch.Vertex3f(-20.0f, -0.41f, 20.0f);
        
        floorBatch.MultiTexCoord2f(0, texSize, 0.0f);
        floorBatch.Vertex3f(20.0f, -0.41f, 20.0f);
        
        floorBatch.MultiTexCoord2f(0, texSize, texSize);
        floorBatch.Vertex3f(20.0f, -0.41f, -20.0f);
        
        floorBatch.MultiTexCoord2f(0, 0, texSize);
        floorBatch.Vertex3f(-20.0f, -0.41f, -20.0f);
        
        floorBatch.End();
    
    球体
    • 由于使用了3种纹理,传入纹理个数为3,并传入纹理数组
    glGenTextures(3, uiTextures);
    
    • 绑定纹理&加载纹理
    // 将TGA文件加载为2D纹理。
    //参数1:纹理文件名称
    //参数2&参数3:需要缩小&放大的过滤器
    //参数4:纹理坐标环绕模式
    glBindTexture(GL_TEXTURE_2D, uiTextures[0]);
    LoadTGATexture("marble.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_REPEAT);
        
    glBindTexture(GL_TEXTURE_2D, uiTextures[1]);
    LoadTGATexture("marslike.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE);
        
    glBindTexture(GL_TEXTURE_2D, uiTextures[2]);
    LoadTGATexture("moonlike.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE);
    

    LoadTGATexture

    主要是将 TGA 文件从从内存中读取,加载2D 纹理数据

    读取纹理像素

    根据指定的TGA图片,将其从内存中读取出来,转换为位图数据

    //参数1:纹理文件名称
    //参数2:文件宽度地址
    //参数3:文件高度地址
    //参数4:文件组件地址
    //参数5:文件格式地址
    //pBits: 指向图像数据的指针
    pBytes = gltReadTGABits(szFileName, &newWidth, &newHeight, &newComponents, &eFormate);
    
    设置纹理参数

    设置纹理的边缘环绕模式,以及放大/缩小的过滤方式

    //参数1:纹理维度
    //参数2:为S/T坐标设置模式
    //参数3:wrapMode,环绕模式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
        
    //参数1:纹理维度
    //参数2:线性过滤
    //参数3: 缩小/放大过滤方式.
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
    
    载入纹理

    将图片数据加载为2D纹理

    //参数1:纹理维度
    //参数2:mip贴图层次
    //参数3:纹理单元存储的颜色成分(从读取像素图是获得)
    //参数4:加载纹理宽
    //参数5:加载纹理高
    //参数6:加载纹理的半框
    //参数7:加载纹理的格式
    //参数8:像素数据的数据类型(GL_UNSIGNED_BYTE,每个颜色分量都是一个8位无符号整数)
    //参数9:指向纹理图像数据的指针
    glTexImage2D(GL_TEXTURE_2D, 0, nComponents, nWidth, nHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBits);
    
    释放

    使用完毕释放 pBits

    free(pBytes);
    
    加载Mip 贴图

    只有 minFilter 等于(GL_NEAREST_MIPMAP_NEAREST | GL_LINEAR_MIPMAP_NEAREST | GL_LINEAR_MIPMAP_LINEAR | GL_LINEAR_MIPMAP_LINEAR)四种模式,才可以生成 Mip 贴图

    glGenerateMipmap(GL_TEXTURE_2D);
    

    ShutdownRC

    删除纹理

    glDeleteTextures(3, uiTextures);
    

    RenderScene

    这部分主要包含三部分

    绘制地板以下的部分
    1. push:为了不影响后续图形的绘制,需要压栈
    2. 翻转:通过 Scale 函数沿着 Y 轴翻转,类似镜面效果
    3. 平移:整体沿着 Y 轴平移,为了使镜面效果更加逼真,例如照镜子时,镜子里面的自己与现实中的自己是有一定距离的
    4. 指定正面方向:地板以下的部分,与地板上面的部分,方向是相反的,所以需要更改系统默认的正方向
    5. 绘制:调用封装的函数
    6. 恢复:地板下面的部分绘制完了,需要恢复默认设置,不能影响后续的绘制工作
    7. pop:恢复到堆栈栈顶绘制地板前的状态
        //压栈(镜面)
        modelViewMatrix.PushMatrix();
        
        //---添加反光效果---
        //翻转Y轴
        modelViewMatrix.Scale(1.0f, -1.0f, 1.0f);
        
        //镜面世界围绕Y轴平移一定间距
        modelViewMatrix.Translate(0, 0.8f, 0);
        
        //指定顺时针为正面
        glFrontFace(GL_CW);
        
        //绘制地面以外其他部分(镜面)
        drawSomething(yRot);
        
        //恢复为逆时针为正面
        glFrontFace(GL_CCW);
        
        //绘制镜面,恢复矩阵
        modelViewMatrix.PopMatrix();
    
    绘制地板
    1. 开启混合:因为要有镜面效果,所以需要一个透明的颜色与地板进行颜色混合,如果不进行颜色混合,地板是不透明的,就看不到地板下面的部分。
    2. 指定混合方程式
    3. 绑定地板纹理
    4. 绘制地板
    5. 关闭混合:对应开启
        //开启混合功能(绘制地板)
        glEnable(GL_BLEND);
        
        //指定 glBlendFunc 颜色混合方程式
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        
        //绑定地面纹理
        glBindTexture(GL_TEXTURE_2D, uiTextures[0]);
        
        /*
        纹理调整着色器(将一个基本色乘以一个取自纹理的单元nTextureUnit的纹理)
        参数1:GLT_SHADER_TEXTURE_MODULATE
        参数2:模型视图投影矩阵
        参数3:颜色
        参数4:纹理单元(第0层的纹理单元)
        */
        shaderManager.UseStockShader(GLT_SHADER_TEXTURE_MODULATE, transformPipeline.GetModelViewProjectionMatrix(), vGreenColor, 0);
        
        //开始绘制
        floorBatch.Draw();
        
        //取消混合
        glDisable(GL_BLEND);
    
    绘制地板以上的部分
    1. 绘制
    2. pop:恢复矩阵初始状态
        //绘制地面以外其他部分
        drawSomething(yRot);
        
        //绘制完,恢复矩阵
        modelViewMatrix.PopMatrix();
    

    drawSomething

    主要是针对大球(自转),小球(公转)绘制的封装,因为镜面上下是一样的,无需绘制两遍。主要包含以下步骤

    1. 定义光源位置&漫反射颜色
    2. 绘制悬浮小球(静止)
    3. 绘制大球(自转)
    4. 绘制公转小球

    为了不影响后续的绘制,无论是大球还是小球都需要 Push&Pop。

    完整代码见 Github 球体世界

    相关文章

      网友评论

          本文标题:OpenGL 案例:球体世界

          本文链接:https://www.haomeiwen.com/subject/rnfjlktx.html