OpenGL之仿“天体”运动渲染球体之间的旋转效果

前文

  • 本文是基于OpenGL之渲染大小球自转和公转的效果完成的,之前的文章已详细地说明了整体的地板的绘制、球体的颜色绘制,以及指定球体之间的旋转和自转等逻辑。
  • 本文在其基础上继续实现图元上纹理的渲染和镜面显示效果,让其更加逼真的渲染出仿照“天体”之间的运动效果。

效果展示

在这里插入图片描述

重点说明

  • SetupRC函数:在原有代码的基础上,增加纹理相关数据及设置,并开启背面剔除;
  • loadTGATexture:将TGA文件加载渲染出2D纹理;
  • RenderScene:镜面球体部分、地板、非镜面球体部分的绘制;
  • Drawsomething:中心大球、静态小球、公转动态小球的绘制。

绘制流程

一、SetupRC()
  • 设置地板顶点数和地板纹理:地板数据由原来的线条相交,更改为仅设置4个顶点坐标&纹理坐标
    GLfloat texSize = 10.0f;
    floorBatch.Begin(GL_TRIANGLE_FAN, 4,1);
    floorBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
    floorBatch.Vertex3f(-20.f, -0.41f, 20.0f);
    
    floorBatch.MultiTexCoord2f(0, texSize, 0.0f);
    floorBatch.Vertex3f(20.0f, -0.41f, 20.f);
    
    floorBatch.MultiTexCoord2f(0, texSize, texSize);
    floorBatch.Vertex3f(20.0f, -0.41f, -20.0f);
    
    floorBatch.MultiTexCoord2f(0, 0.0f, texSize);
    floorBatch.Vertex3f(-20.0f, -0.41f, -20.0f);
    floorBatch.End();
    
  • 命名纹理对象
// 命名纹理对象
    glGenTextures(3, uiTextures);
  • 绑定纹理&加载纹理:glBindTexture绑定,LoadTGATexture方法加载纹理
    /* 将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纹理”
  • 读取纹理: gltReadTGABits
  • 设置纹理参数(S和T的环绕模式、放大/缩小的过滤方式): glTexParameteri
  • 载入纹理 – glTexImage2D
  • 使用完毕释放pBits
  • 加载Mip,纹理生成所有的Mip层
bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode) {
    
    GLbyte *pBits;
    int nWidth, nHeight, nComponents;
    GLenum eFormat;
    
    // 读取纹理数据
    pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
    if(pBits == NULL)
        return false;
    
    /* 设置纹理参数
     * 参数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:wrapMode,环绕模式
     */
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
    
    /* 载入纹理
     * 参数1:纹理维度
     * 参数2:mip贴图层次
     * 参数3:纹理单元存储的颜色成分(从读取像素图是获得)-将内部参数nComponents改为了通用压缩纹理格式GL_COMPRESSED_RGB
     * 参数4:加载纹理宽
     * 参数5:加载纹理高
     * 参数6:加载纹理的深度
     * 参数7:像素数据的数据类型(GL_UNSIGNED_BYTE,每个颜色分量都是一个8位无符号整数)
     * 参数8:指向纹理图像数据的指针
     */
    glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB, nWidth, nHeight, 0,
                 eFormat, GL_UNSIGNED_BYTE, pBits);
    
    // 使用完毕释放pBits
    free(pBits);
    
    /* 只有minFilter等于以下四种模式,才可以生成Mip贴图
     * GL_NEAREST_MIPMAP_NEAREST具有非常好的性能,并且闪烁现象非常弱
     * GL_LINEAR_MIPMAP_NEAREST常用于对游戏进行加速,它使用了高质量的线性过滤器
     * GL_LINEAR_MIPMAP_LINEAR和GL_NEAREST_MIPMAP_LINEAR 过滤器在Mip层之间执行了一些额外的插值,以消除他们之间的过滤痕迹
     * GL_LINEAR_MIPMAP_LINEAR 三线性Mip贴图,纹理过滤的黄金准则,具有最高的精度
     */
    if(minFilter == GL_LINEAR_MIPMAP_LINEAR ||
       minFilter == GL_LINEAR_MIPMAP_NEAREST ||
       minFilter == GL_NEAREST_MIPMAP_LINEAR ||
       minFilter == GL_NEAREST_MIPMAP_NEAREST)
        
    /* 加载Mip,纹理生成所有的Mip层
     * 参数:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
     */
    glGenerateMipmap(GL_TEXTURE_2D);
    
    return true;
}
三、RenderScence()
  • 设定地板的颜色,动画定时器动态渲染以及栈顶压栈和设置观察者;
    // 地板的颜色:一定要设置透明度
    static GLfloat vFloorColor[] = {1.f, 1.f, 0.f, 0.75f};

    // 动画定时器
    static CStopWatch rotTimer;
    float  yPot = rotTimer.GetElapsedSeconds() * 60.f;
    
    // 清除颜色缓存区和深度缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    // 压栈(栈顶):此处压栈的目的是由于观察者矩阵是作用于全局的,为了不影响后续图形的绘制
    modelViewMatrix.PushMatrix();
    
    // 设置观察者矩阵
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    modelViewMatrix.PushMatrix(mCamera);
  • 绘制地板的镜面反光效果
    /* 添加镜面反光效果
     * 翻转Y轴:通过Scale函数,沿着y轴,从+y翻转到-y绝对值相等的坐标
     * 镜面世界围绕Y轴平移一定间距:平移的目的是为了镜面效果更逼真,球体与镜面是有一定间隔的
     */
    modelViewMatrix.Scale(1.f, -1.f, 1.f);
    modelViewMatrix.Translate(0.f, 0.8f, 0.f);
    
    // 指定顺时针为正面:需要绘制的-y轴的镜面部分,所以需要更改系统默认的逆时针正面,改为顺时针为正面,绘制完成后,在恢复默认设置
    glFrontFace(GL_CW);
    
    // 绘制地面以外其他部分(镜面)
    DrawSomething(yPot);
    
    // 恢复为逆时针为正面
    glFrontFace(GL_CCW);
    
    // 绘制地板,恢复矩阵
    modelViewMatrix.PopMatrix();
  • 绘制地板并绑定地板纹理
    // 开启混合功能,绘制地板:开启混合的目的是地板需要与镜面部分进行颜色混合,且地板需要设置一个半透明的基本色,用于颜色混合,如果不设置,则无法看到地板的镜面效果,将看到的是一个实体的地板
    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(),
                                 vFloorColor,
                                 0);
    floorBatch.Draw();
    
    // 取消混合
    glDisable(GL_BLEND);
    
    // 绘制地面以外其他部分
    DrawSomething(yPot);
   
    // 绘制完毕,恢复矩阵
    modelViewMatrix.PopMatrix();
    
    // 执行缓存区交换
    glutSwapBuffers();
    
    glutPostRedisplay();
四、DrawSomething()
  • 绘制随机位置的50个小球
    // 定义光源位置&漫反射颜色
    static GLfloat vSphereColor[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static GLfloat vLightPods[] = {0.0f, 3.0f, 0.0f, 1.0f};
       
    // 绘制随机位置的50个小球
    glBindTexture(GL_TEXTURE_2D, uiTextures[2]);
    for (int i = 0; i < NUM_SPHERES; i++) {
        modelViewMatrix.PushMatrix();
        modelViewMatrix.MultMatrix(spheres[i]);
        shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
                                     modelViewMatrix.GetMatrix(),
                                     transformPipeline.GetProjectionMatrix(),
                                     vLightPods,
                                     vSphereColor,
                                     0);
        sphereBatch.Draw();
        modelViewMatrix.PopMatrix();
    }
  • 绘制中心的红色大球
    // 绘制红色大球
    modelViewMatrix.Translate(0.0f, 0.2f, -2.5f);
    modelViewMatrix.PushMatrix();
    modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
    glBindTexture(GL_TEXTURE_2D, uiTextures[1]);
    shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
                                 modelViewMatrix.GetMatrix(),
                                 transformPipeline.GetProjectionMatrix(),
                                 vLightPods,
                                 vSphereColor,
                                 0);
    torusBatch.Draw();
    modelViewMatrix.PopMatrix();
  • 绘制动态蓝色球绕着红色大球转
    // 动态蓝色球绕着红色大球转
    modelViewMatrix.Rotate(yRot * -2.f, 0.f, 1.f, 0.f);
    modelViewMatrix.Translate(0.8f, 0.f, 0.f);
    glBindTexture(GL_TEXTURE_2D, uiTextures[2]);
    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,
                                transformPipeline.GetModelViewMatrix(),
                                transformPipeline.GetProjectionMatrix(),
                                vLightPods,
                                vSphereColor);
    sphereBatch.Draw();
    modelViewMatrix.PopMatrix();
五、ShutdownRC():删除纹理
// 删除纹理
void ShutdownRC(void) {
    glDeleteTextures(3, uiTextures);
}

完整代码传送门

OpenGL之仿“天体”运动渲染球体之间的旋转效果

猜你喜欢

转载自blog.csdn.net/Forever_wj/article/details/107494175