我们这一节的效果图如下:
我们会给每个模型贴上纹理图,纹理图的使用上一节已经讲过了,我们会实现一个镜面的效果,如上图,每个模型下面都有一个像是通过镜子反射的模型一样。
其实做法很简单,我们只需要把原来的视口矩阵反转如下的代码
modelViewMatrix.Scale(1.0f, -1.0f, 1.0f);
然后把改变多边形的正面,OpenGl默认的多边形的正面是逆时针的,我们在这里改为顺时针,如下代码
glFrontFace(GL_CW);
然后绘制镜面中的物体,绘制完成后把多边形的正面再改回逆时针的
glFrontFace(GL_CCW);
然后我们绘制地板,并且和刚才绘制的镜面中的物体进行混合操作
最后我们再绘制地面上的物体,这样就实现的镜面效果
下面是代码实现
首先是需要包含的头文件 和全局变量
#include <GLTools.h>
#include <GLShaderManager.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLMatrixStack.h>
#include <GLGeometryTransform.h>
#include <StopWatch.h>
#include <math.h>
#include <stdio.h>
#define FREEGLUT_STATIC
#include <GL/glut.h>
#define NUM_SPHERES 50
GLFrame spheres[NUM_SPHERES];
GLShaderManager shaderManager;
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;
GLFrustum viewFrustum;
GLGeometryTransform transformPipeline;
GLFrame cameraFrame;
GLTriangleBatch torusBatch;
GLTriangleBatch sphereBatch;
GLBatch floorBatch;
GLuint uiTextures[3];
然后是主函数main
int main(int argc, char* argv[])
{
//设置工作路径
gltSetWorkingDirectory(argv[0]);
//初始化glut
glutInit(&argc, argv);
//申请一个带有双缓冲区,颜色缓冲区的窗口
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
//窗口大小
glutInitWindowSize(800, 600);
//窗口名字
glutCreateWindow("OpenGL SphereWorld");
//窗口大小改变时的回调函数
glutReshapeFunc(ChangeSize);
//键盘按键响应函数
glutSpecialFunc(SpecialKeys);
//渲染的回调函数
glutDisplayFunc(RenderScene);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
//初始化函数
SetupRC();
//主函数循环
glutMainLoop();
//循环结束后释放内存
ShutdownRC();
return 0;
}
然后是窗口改变大小时的回调函数ChangeSize
void ChangeSize(int nWidth, int nHeight)
{
//设置视口大小
glViewport(0, 0, nWidth, nHeight);
//将视图变换矩阵 和 模型 变换矩阵统一管理起来
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
//设置视口变换矩阵
viewFrustum.SetPerspective(35.0f, float(nWidth) / float(nHeight), 1.0f, 100.0f);
//设置模型变换矩阵
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//设置视图变换矩阵
modelViewMatrix.LoadIdentity();
}
然后是响应键盘方向键的函数SpecialKeys
void SpecialKeys(int key, int x, int y)
{
float linear = 0.1f;
float angular = float(m3dDegToRad(5.0f));
if (key == GLUT_KEY_UP)
cameraFrame.MoveForward(linear);
if (key == GLUT_KEY_DOWN)
cameraFrame.MoveForward(-linear);
if (key == GLUT_KEY_LEFT)
cameraFrame.RotateWorld(angular, 0.0f, 1.0f, 0.0f);
if (key == GLUT_KEY_RIGHT)
cameraFrame.RotateWorld(-angular, 0.0f, 1.0f, 0.0f);
}
下面是初始化函数SetupRC
void SetupRC()
{
//初始化glew
glewInit();
//初始化着色器管理类
shaderManager.InitializeStockShaders();
//开启深度测试
glEnable(GL_DEPTH_TEST);
//开启剔除
glEnable(GL_CULL_FACE);
//清除后台缓存
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// 初始化泳圈模型
gltMakeTorus(torusBatch, 0.4f, 0.15f, 40, 20);
// 初始化小球模型
gltMakeSphere(sphereBatch, 0.1f, 26, 13);
//初始化地板数据
GLfloat texSize = 10.0f;
floorBatch.Begin(GL_TRIANGLE_FAN, 4, 1);
floorBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
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.0f, texSize);
floorBatch.Vertex3f(-20.0f, -0.41f, -20.0f);
floorBatch.End();
// 创建3个纹理对象
glGenTextures(3, uiTextures);
// 绑定纹理1对象
glBindTexture(GL_TEXTURE_2D, uiTextures[0]);
LoadTGATexture("marble.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_REPEAT);
// 绑定纹理2对象
glBindTexture(GL_TEXTURE_2D, uiTextures[1]);
LoadTGATexture("marslike.tga", GL_LINEAR_MIPMAP_LINEAR,
GL_LINEAR, GL_CLAMP_TO_EDGE);
// 绑定纹理3对象
glBindTexture(GL_TEXTURE_2D, uiTextures[2]);
LoadTGATexture("moonlike.tga", GL_LINEAR_MIPMAP_LINEAR,
GL_LINEAR, GL_CLAMP_TO_EDGE);
// 设置50个小球的位置
for(int i = 0; i < NUM_SPHERES; i++)
{
GLfloat x = ((GLfloat)((rand() % 400) - 200) * 0.1f);
GLfloat z = ((GLfloat)((rand() % 400) - 200) * 0.1f);
spheres[i].SetOrigin(x, 0.0f, z);
}
}
下面是加载纹理图片的函数LoadTGATexture
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;
//设置纹理环绕模式
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, magFilter);
//开启紧密包装像素数据模式
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//生成纹理图片
glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB, nWidth, nHeight, 0,
eFormat, GL_UNSIGNED_BYTE, pBits);
free(pBits);
//创建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;
}
下面是具体绘制模型的函数DrawSongAndDance
void DrawSongAndDance(GLfloat yRot) // Called to draw dancing objects
{
static GLfloat vWhite[] = { 1.0f, 1.0f, 1.0f, 1.0f };
static GLfloat vLightPos[] = { 0.0f, 3.0f, 0.0f, 1.0f };
// 得到光源位置
M3DVector4f vLightTransformed;
M3DMatrix44f mCamera;
modelViewMatrix.GetMatrix(mCamera);
m3dTransformVector4(vLightTransformed, vLightPos, mCamera);
// 绘制光源
modelViewMatrix.PushMatrix();
modelViewMatrix.Translatev(vLightPos);
shaderManager.UseStockShader(GLT_SHADER_FLAT,
transformPipeline.GetModelViewProjectionMatrix(),
vWhite);
sphereBatch.Draw();
modelViewMatrix.PopMatrix();
//渲染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(),
vLightTransformed,
vWhite,
0);
sphereBatch.Draw();
modelViewMatrix.PopMatrix();
}
modelViewMatrix.Translate(0.0f, 0.2f, -2.5f);
modelViewMatrix.PushMatrix(); // Saves the translated origin
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(),
vLightTransformed,
vWhite,
0);
torusBatch.Draw();
modelViewMatrix.PopMatrix(); // Erased the rotate
modelViewMatrix.Rotate(yRot * -2.0f, 0.0f, 1.0f, 0.0f);
modelViewMatrix.Translate(0.8f, 0.0f, 0.0f);
//绘制旋转的小球
glBindTexture(GL_TEXTURE_2D, uiTextures[2]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
modelViewMatrix.GetMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightTransformed,
vWhite,
0);
sphereBatch.Draw();
}
下面是程序退出时执行的清理函数ShutdownRC
void ShutdownRC(void)
{
//释放纹理对象
glDeleteTextures(3, uiTextures);
}
基本上每一行代码都有注释,有哪里看不懂的童鞋,我们可以一起讨论 ):