OpenGL镜面效果

我们这一节的效果图如下:



我们会给每个模型贴上纹理图,纹理图的使用上一节已经讲过了,我们会实现一个镜面的效果,如上图,每个模型下面都有一个像是通过镜子反射的模型一样。

其实做法很简单,我们只需要把原来的视口矩阵反转如下的代码

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);
    }


基本上每一行代码都有注释,有哪里看不懂的童鞋,我们可以一起讨论 ):











评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值