开场白
本文在我之前的文章OpenGL-公转自转demo中的demo为基础,添加纹理。效果图:
简述逻辑
SetupRC
在初始设置函数SetupRC中,增加设置纹理的代码。示例中有三个纹理,分别是地板,大球,小球。
void SetupRC() {
//......
//大球小球初始化相关代码
//......
//申请3个纹理对象
glGenTextures(3, uiTextures);
//设置纹理状态模式:2D
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);
}
三个纹理的设置相似,所以将设置纹理的相关函数进行自定义函数封装,
bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFileter, GLenum wrapMode)
- 参数1:文件名
- 参数2:缩小过滤方式
- 参数3:放大过滤方式
- 参数4:环绕方式
*/
bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFileter, GLenum wrapMode){
//定义局部变量
GLbyte *pBits;
int nWidth, nHeight, nComponents;
GLenum eFormat;
//加载TGA文件,获取TGA文件的宽、高、组件、格式,并返回一个纹理图像数据的指针
pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
if (pBits == NULL) {
return false;
}
//设置纹理的S、T的环绕方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
//设置纹理放大、缩小的过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFileter);
/**
载入纹理:
参数1:纹理2D模式
参数2:mip贴图层次,通常设置为0
参数3:纹理单元存储成分,使用压缩RGB格式
参数4、5:纹理宽、高
参数6:纹理深度
参数7:像素格式
参数8:像素数据的数据类型(GL_UNSIGNED_BYTE,每个颜色分量都是一个8位无符号整数)
参数9:指向纹理图像数据的指针
*/
glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB, nWidth, nHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBits);
free(pBits);
/*
只有minFilter等于这四种模式的时候,纹理生成所有mip层。
因为GL_LINEAR和GL_NEAREST模式下只有基层纹理会加载,所以生成所有mip层也没用
*/
if (minFilter == GL_LINEAR_MIPMAP_LINEAR ||
minFilter == GL_LINEAR_MIPMAP_NEAREST ||
minFilter == GL_NEAREST_MIPMAP_LINEAR ||
minFilter == GL_NEAREST_MIPMAP_NEAREST) {
glGenerateMipmap(GL_TEXTURE_2D);
}
return true;
}
RenderScene
将绘制的代码封装成了函数中,因为有倒影的原因,整个球体世界需要绘制两遍,中间用地面相隔开。倒影的世界运动轨迹是完全相反的,并且地面和倒影要有一个混合的效果。
void RenderScene(void) {
static GLfloat vFloorColor[] = {1.0, 1.0, 0.0, 0.75};
static CStopWatch rotTimer;
float yRot = rotTimer.GetElapsedSeconds() * 60;
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
modelViewMatrix.PushMatrix();
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.MultMatrix(mCamera);
modelViewMatrix.PushMatrix(mCamera);
//倒影
modelViewMatrix.Scale(1, -1, 1);
modelViewMatrix.Translate(0, 0.8, 0);
glFrontFace(GL_CW);
drawSomething(yRot);
glFrontFace(GL_CCW);
modelViewMatrix.PopMatrix();
//地面-与倒影进行了混合
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindTexture(GL_TEXTURE_2D, uiTextures[0]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_MODULATE, transformPipeline.GetModelViewProjectionMatrix(), vFloorColor, 0);
floorBatch.Draw();
glDisable(GL_BLEND);
//地面上面的部分
drawSomething(yRot);
modelViewMatrix.PopMatrix();
//将在后台缓冲区进行渲染,然后在结束时交换到前台
glutSwapBuffers();
//9触发重新绘制
glutPostRedisplay();
}
- 地板颜色vFloorColor要有一定的透明度,为了与倒影进行混合
- 倒影设置就是进行翻转y轴
modelViewMatrix.Scale(1, -1, 1);
- 因为倒影的运动方向是反的,所以需要设置顶点绕序
glFrontFace(GL_CW);
- 调用自定义封装函数
drawSomething(yRot);
- 顶掉绕序修改回来
glFrontFace(GL_CCW);
,因为一会还有绘制地面上放的球体。 - 绘制地面
- 绘制地面上面的球体,调用自定义封装函数
drawSomething(yRot);
==drawSomething()函数就是球体的绘制过程,可以下载demo看看==
网友评论