球体世界案例是基于 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
这部分主要包含三部分
绘制地板以下的部分
- push:为了不影响后续图形的绘制,需要压栈
- 翻转:通过 Scale 函数沿着 Y 轴翻转,类似镜面效果
- 平移:整体沿着 Y 轴平移,为了使镜面效果更加逼真,例如照镜子时,镜子里面的自己与现实中的自己是有一定距离的
- 指定正面方向:地板以下的部分,与地板上面的部分,方向是相反的,所以需要更改系统默认的正方向
- 绘制:调用封装的函数
- 恢复:地板下面的部分绘制完了,需要恢复默认设置,不能影响后续的绘制工作
- 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();
绘制地板
- 开启混合:因为要有镜面效果,所以需要一个透明的颜色与地板进行颜色混合,如果不进行颜色混合,地板是不透明的,就看不到地板下面的部分。
- 指定混合方程式
- 绑定地板纹理
- 绘制地板
- 关闭混合:对应开启
//开启混合功能(绘制地板)
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);
绘制地板以上的部分
- 绘制
- pop:恢复矩阵初始状态
//绘制地面以外其他部分
drawSomething(yRot);
//绘制完,恢复矩阵
modelViewMatrix.PopMatrix();
drawSomething
主要是针对大球(自转),小球(公转)绘制的封装,因为镜面上下是一样的,无需绘制两遍。主要包含以下步骤
1. 定义光源位置&漫反射颜色
2. 绘制悬浮小球(静止)
3. 绘制大球(自转)
4. 绘制公转小球
为了不影响后续的绘制,无论是大球还是小球都需要 Push&Pop。
完整代码见 Github 球体世界
网友评论