美文网首页
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 案例:球体世界

    球体世界案例是基于 OpenGL 综合案例的学习 的基础上增加了纹理和镜像显示。最终效果图: 总体流程图可以使用下...

  • OpenGL纹理案例-球体世界

    开场白 本文在我之前的文章OpenGL-公转自转demo中的demo为基础,添加纹理。效果图: 简述逻辑 Setu...

  • OpenGL案例08:球体世界

    本案例是基于案例06:大球自转+小球公转+移动[https://www.jianshu.com/p/4896498...

  • OpenGL绘制球体世界

    本文主要记录使用所学OpenGL相关知识绘制OpenGL中的一个经典案例--球体世界,最终实现效果如下: 准备工作...

  • OpenGL ----- 球体世界

  • opengl-球体的绘制

    opengl-pbr 光照一讲里,以球体为案例进行说明。球体的绘制第一次接触理解花了点时间 一、球体坐标分解 按照...

  • OpenGL 球体公转+自转案例

    声明变量 声明一个着色器管理器实例,为模型视图矩阵和投影矩阵声明GLMatrixStack实例,使用GLFrust...

  • 八.OpenGL:球体世界

    主要的内容如下:SetupRC:纹理相关数据的设置LoadTGATexture:读取TGA文件并加载为2D纹理数据...

  • 案例分析5:OpenGL下的球体世界

    本案例是基于案例分析2:大球自转+小球公转+移动的基础上增加了纹理和镜像显示,最终的效果如图所示 对应代码地址纹理...

  • OpenGL学习之路(5.0) 三角形批次类案例

    通过三角形批次类画图形示例 球体 环面 圆柱 圆锥体 磁盘 代码案例 该代码案例需要搭建OpenGL环境

网友评论

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

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