欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

OpenGL学习笔记三(旋转带纹理的花托,球体,地板,点光源)

程序员文章站 2024-03-24 11:16:22
...
#include <GLTools.h>
#include <GLFrame.h>
#include <GLMatrixStack.h>
#include <GLGeometryTransform.h>
#include <GLShaderManager.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <StopWatch.h>

#define FREEGLUT_STATIC
#include <GL/glut.h>

#define SPHERE_NUM 30

GLFrame cameraFrame;
GLMatrixStack modelViewStack;
GLMatrixStack projectionStack;
GLGeometryTransform transformPipeLine;
GLShaderManager shaderManager;
GLFrustum viewFrustum;
GLFrame spheres[SPHERE_NUM];

GLBatch batch_floor;
GLTriangleBatch batch_torus;
GLTriangleBatch batch_sphere;

GLfloat vWhite[] = {1.0f, 1.0f, 1.0f, 0.75f};
const char *texFileName[] = {"Marble.tga", "Marslike.tga", "MoonLike.tga"};
GLuint texIdArray[3] = {0};

//加载纹理并设置相关参数
bool LoadTgaTexture(const char *fileName, GLenum filterParam, GLenum wrapParam)
{
	GLbyte *pbits = NULL;
	GLint width, height, components;
	GLenum eFormat;
	//从tga文件获取纹理图像的地址和相关参数
	pbits = gltReadTGABits(fileName, &width, &height, &components, &eFormat);
	if (NULL == pbits)
	{
		return false;
	}
	//设置缩放算法
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterParam);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterParam);
	//设置纹理边界外的填充方法
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapParam);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapParam);
	//根据获取的相关参数创建2D纹理
	glTexImage2D(GL_TEXTURE_2D, 0, components, width, height, 0, eFormat, GL_UNSIGNED_BYTE, pbits);
	free(pbits);
	return true;
}

bool SetupRC()
{
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	shaderManager.InitializeStockShaders();
	//开启深度测试
	glEnable(GL_DEPTH_TEST);
	//剔除背面渲染
	glEnable(GL_CULL_FACE);
	//生成3个纹理ID
	glGenTextures(3, texIdArray);

	glBindTexture(GL_TEXTURE_2D, texIdArray[0]);
	if (!LoadTgaTexture(texFileName[0], GL_LINEAR, GL_REPEAT))
	{
		return false;
	}
	glBindTexture(GL_TEXTURE_2D, texIdArray[1]);
	if (!LoadTgaTexture(texFileName[1], GL_LINEAR, GL_CLAMP_TO_EDGE))
	{
		return false;
	}
	glBindTexture(GL_TEXTURE_2D, texIdArray[2]);
	if (!LoadTgaTexture(texFileName[2], GL_LINEAR, GL_CLAMP_TO_EDGE))
	{
		return false;
	}
	//纹理坐标和顶点坐标的加载顺序不能反过来
	batch_floor.Begin(GL_TRIANGLE_FAN, 4, 1);
	batch_floor.MultiTexCoord2f(0, 0.0f, 0.0f);
	batch_floor.Vertex3f(-20.0f, -0.4f, 20.0f);

	batch_floor.MultiTexCoord2f(0, 10.0f, 0.0f);
	batch_floor.Vertex3f(20.0f, -0.4f, 20.0f);

	batch_floor.MultiTexCoord2f(0, 10.0f, 10.0f);
	batch_floor.Vertex3f(20.0f, -0.4f, -20.0f);
	
	batch_floor.MultiTexCoord2f(0, 0.0f, 10.0f);
	batch_floor.Vertex3f(-20.0f, -0.4f, -20.0f);
	batch_floor.End();

	//初始化花托的顶点批次
	gltMakeTorus(batch_torus, 0.4f, 0.15f, 40, 20);
	//初始化球体的顶点批次
	gltMakeSphere(batch_sphere, 0.1f, 26, 13);
	//初始化X-Z平面零散球体的坐标
	for (int n=0; n<SPHERE_NUM; n++)
	{
		GLfloat x = ((GLfloat)((rand() % 400) - 200) * 0.1f);
		GLfloat z = ((GLfloat)((rand() % 400) - 200) * 0.1f);
		spheres[n].SetOrigin(x, 0.0, z);
	}
	return true;
}

void DrawSongAndDance(GLfloat yRot)
{
	static GLfloat vLightPos[] = {0.0f, 3.0f, 0.0f, 1.0f};

	//绘制点光源,PushMatrix和PopMatrix要成对使用
	modelViewStack.PushMatrix();
	modelViewStack.Translatev(vLightPos);
	shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeLine.GetModelViewProjectionMatrix(), vWhite);
	batch_sphere.Draw();
	modelViewStack.PopMatrix();

	//绘制随机球体
	glBindTexture(GL_TEXTURE_2D, texIdArray[1]);
	for (int n=0; n<SPHERE_NUM; n++)
	{
		modelViewStack.PushMatrix();
		modelViewStack.MultMatrix(spheres[n]);
		modelViewStack.Rotate(3*yRot, 0.0f, 1.0f, 0.0f);
		shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF, transformPipeLine.GetModelViewMatrix(),
			transformPipeLine.GetProjectionMatrix(), vLightPos, vWhite, 0);
		batch_sphere.Draw();
		modelViewStack.PopMatrix();
	}

	modelViewStack.PushMatrix();
	//在当前视点位置的前上方绘制花托和绕行球体,不然视点位置会看不到
	modelViewStack.Translate(0.0f, 0.2f, -3.5f);

	//绘制旋转花托
	modelViewStack.PushMatrix();
	modelViewStack.Rotate(yRot, 0.0f, 1.0f, 0.0f);
	shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF, transformPipeLine.GetModelViewMatrix(),
		transformPipeLine.GetProjectionMatrix(), vLightPos, vWhite, 0);
	batch_torus.Draw();
	modelViewStack.PopMatrix();

	//绘制绕花托旋转的球体
	modelViewStack.PushMatrix();
	//以2倍反向速度旋转,注意旋转和平移的顺序不能反,不同顺序效果不一样
	modelViewStack.Rotate(-2*yRot, 0.0f, 1.0f, 0.0f);
	modelViewStack.Translate(0.8f, 0.0f, 0.0f);
	shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF, transformPipeLine.GetModelViewMatrix(),
		transformPipeLine.GetProjectionMatrix(), vLightPos, vWhite, 0);
	batch_sphere.Draw();
	modelViewStack.PopMatrix();
	modelViewStack.PopMatrix();
}

void RenderScene()
{
	static CStopWatch rotTimer;
	//1秒钟转60度角的速度
	float yRot = rotTimer.GetElapsedSeconds() * 60.0f;

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	modelViewStack.PushMatrix();
	//乘视图矩阵
	M3DMatrix44f mCamera;
	cameraFrame.GetCameraMatrix(mCamera);
	modelViewStack.MultMatrix(mCamera);

	//翻转、往下平移
	modelViewStack.PushMatrix();
	modelViewStack.Scale(1.0f, -1.0f, 1.0f);
	modelViewStack.Translate(0.0f, 0.8f, 0.0f);
	
	//绘制地板下的倒影,因为Y轴朝下了,所以背面剔除的方向应也要改变
	glFrontFace(GL_CW);
	DrawSongAndDance(yRot);
	//倒影绘制完成,恢复提出面方向
	glFrontFace(GL_CCW);
	modelViewStack.PopMatrix();

	//绘制地板
	//开启混合
	glEnable(GL_BLEND);
	//设置混合计算方程
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	//绑定地板纹理,绘制地板
	glBindTexture(GL_TEXTURE_2D, texIdArray[0]);
	static GLfloat vFloorColor[] = {1.0f, 1.0f, 1.0f, 0.75f};
	shaderManager.UseStockShader(GLT_SHADER_TEXTURE_MODULATE, 
		transformPipeLine.GetModelViewProjectionMatrix(), vFloorColor, 0);
	batch_floor.Draw();
	glDisable(GL_BLEND);

	//绘制地板上的球体、花托
	DrawSongAndDance(yRot);
	modelViewStack.PopMatrix();
	//将本次渲染的动画替换上一次渲染的动画
	glutSwapBuffers();
	//渲染后循环触发,保证动画连续
	glutPostRedisplay();
}

void SpecialKeys(int key, int x, int y)
{
	GLfloat delta = 0.1f;
	GLfloat angular = float(m3dDegToRad(5.0f));
	//设置方向键的前进后退和左右方向键的绕Y轴方向旋转
	switch(key)
	{
	case GLUT_KEY_UP:
		cameraFrame.MoveForward(delta);
		break;
	case GLUT_KEY_DOWN:
		cameraFrame.MoveForward(-delta);
		break;
	case GLUT_KEY_LEFT:
		cameraFrame.RotateWorld(angular, 0.0f, 1.0f, 0.0f);
		break;
	case GLUT_KEY_RIGHT:
		cameraFrame.RotateWorld(-angular, 0.0f, 1.0f, 0.0f);
		break;
	default:
		break;
	}
}

void ChangeSize(int w, int h)
{
	glViewport(0, 0, w, h);
	//设置投影矩阵的视景体
	viewFrustum.SetPerspective(35.0f, float(w)/h, 1.0f, 100.0f);
	projectionStack.LoadMatrix(viewFrustum.GetProjectionMatrix());
	//模型视图矩阵设置单位矩阵
	modelViewStack.LoadIdentity();
	//将投影矩阵和模型视图矩阵设置到渲染管线对象
	transformPipeLine.SetMatrixStacks(modelViewStack, projectionStack);
}

int main(int argc, char *argv[])
{
	glutInit(&argc, argv);
	//设置显示模式
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	//创建窗口
	glutInitWindowSize(800, 600);
	glutCreateWindow("show");
	//设置窗口尺寸回调函数
	glutReshapeFunc(ChangeSize);
	//设置特殊键回调函数
	glutSpecialFunc(SpecialKeys);
	//设置渲染回调函数
	glutDisplayFunc(RenderScene);

	GLenum ret = glewInit();
	if (GLEW_OK != ret)
	{
		fprintf(stderr, "glew error: %s", glewGetErrorString(ret));
		return 1;
	}
	//初始化函数
	if (!SetupRC())
	{
		return 1;
	}
	//接收一些外部事件,如键盘鼠标之类的,关闭窗口是退出
	glutMainLoop();
	//删除全局纹理对象
	glDeleteTextures(3, texIdArray);

	return 0;
}

匹配的纹理打包文件可以在此处下载https://download.csdn.net/download/gk_2014/10643475
这个示例基于《OpenGLchao超级宝典(第5版)》这本书