本文为L_Ares个人写作,包括图片皆为个人亲自操作,以任何形式转载请表明原文出处。
本节是对第十五节的卫星图代码进行了纹理的填充,加上了镜面效果,算是对之前所学习的重点知识的综合复习。
//
// main.cpp
// 11球体
//
// Created by EasonLi on 2020/9/6.
// Copyright © 2020 EasonLi. All rights reserved.
//
#include <stdio.h>
#pragma mark - 引用类
#include "GLTools.h"
#include "GLShaderManager.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLMatrixStack.h"
#include "GLGeometryTransform.h"
#include "StopWatch.h"
#include "math3d.h"
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
#pragma mark - 常用变量
//着色器管理器
GLShaderManager shaderManager;
//视景体
GLFrustum viewFrustum;
//观察者(视点/摄像机)参考帧
GLFrame cameraFrame;
//模型视图矩阵堆栈
GLMatrixStack modelViewMatrixStack;
//投影矩阵堆栈
GLMatrixStack projectionMatrixStack;
//变换管道
GLGeometryTransform transformPipeLine;
//地板批次类
GLBatch floorBatch;
//小球批次类
GLTriangleBatch sBallBatch;
//大球批次类
GLTriangleBatch lBallBatch;
//小球个数
#define SMALL_BALL_COUNT 50
//纹理数量
#define TEXTURE_COUNT 3
//小球坐标数组
GLFrame smallBallFrame[SMALL_BALL_COUNT];
//纹理标记数组
GLuint nTextures[TEXTURE_COUNT];
//点光源位置
static GLfloat nLightPos[] = {0.f,3.f,0.f,1.f};
//漫反射颜色
static GLfloat nWhite[] = {1.f,1.f,1.f,1.f};
//地板颜色数组
static GLfloat nfloorColor[] = {1.f,1.f,0.f,0.75f};
#pragma mark - 函数
//初建或重塑窗口
void ChangeSize(int w , int h)
{
//避免除数为0
if (h == 0) {
h = 1;
}
//设置视口大小
glViewport(0, 0, w, h);
//设置投影方式和参数
viewFrustum.SetPerspective(36.f, float(w)/float(h), 1.f, 120.f);
//获取投影矩阵并放置到投影矩阵堆栈栈顶
projectionMatrixStack.LoadMatrix(viewFrustum.GetProjectionMatrix());
//设置变换管道
transformPipeLine.SetMatrixStacks(modelViewMatrixStack, projectionMatrixStack);
}
//从TGA文件读取纹理、加载纹理、设置纹理
bool LoadTextureFromTGA (const char *fileName , GLenum minFilter , GLenum magFilter , GLenum wrapMode)
{
//定义字节类型变量,用来存储从tga文件读取出来的纹理数据
GLbyte *nByte;
//定义变量,用来存储从tga文件中读取出的纹理的各项属性
int nWidth,nHeight,nComponent;
GLenum nFormat;
//从tga文件中读取纹理
nByte = gltReadTGABits(fileName, &nWidth, &nHeight, &nComponent, &nFormat);
if (nByte == NULL) {
printf("没有纹理数据");
return false;
}
//设置纹理属性
//设置纹理过滤器
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
//设置纹理环绕模式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
//载入纹理
glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB, nWidth, nHeight, 0, nFormat, GL_UNSIGNED_BYTE, nByte);
//使用完了纹理数据就可以释放了
free(nByte);
//生成Mipmap贴图
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;
}
//设置地板
void SetFloor ()
{
//参数
//(1).图元绘制方式
//(2).顶点数量
//(3).纹理数组数量
floorBatch.Begin(GL_TRIANGLE_FAN, 4,1);
//设置纹理坐标和顶点坐标
//这里将纹理坐标扩大了,属于特殊的情况,因为tga纹理有点小,但是地板有点大,所以放
//大一点
floorBatch.MultiTexCoord2f(0, 0.f, 0.f);
floorBatch.Vertex3f(-20.f, -0.5f, 20.f);
floorBatch.MultiTexCoord2f(0, 10.f, 0.f);
floorBatch.Vertex3f(20.f, -0.5f, 20.f);
floorBatch.MultiTexCoord2f(0, 10.f, 10.f);
floorBatch.Vertex3f(20.f, -0.5f, -20.f);
floorBatch.MultiTexCoord2f(0, 0.f, 10.f);
floorBatch.Vertex3f(-20.f, -0.5f, -20.f);
floorBatch.End();
}
//设置渲染环境
void SetUpRC ()
{
//设置清屏颜色
glClearColor(0.f, 0.f, 0.f, 1.f);
//初始化着色器管理器
shaderManager.InitializeStockShaders();
//开启正背面剔除和深度测试
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
/********************************设置地板*********************************/
//设置地板顶点和纹理顶点数据
SetFloor();
//生成纹理标记,分配纹理对象
glGenTextures(3, nTextures);
//绑定纹理对象(地板)
glBindTexture(GL_TEXTURE_2D, nTextures[0]);
//读取纹理、设置纹理、载入纹理
LoadTextureFromTGA("Marble.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_REPEAT);
/********************************设置大球*********************************/
//设置大球属性
gltMakeSphere(lBallBatch, 0.5f, 60, 120);
//绑定纹理对象(大球)
glBindTexture(GL_TEXTURE_2D, nTextures[1]);
//读取纹理、设置纹理、载入纹理
LoadTextureFromTGA("Marslike.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE);
/********************************设置公转小球*********************************/
//设置公转小球属性
gltMakeSphere(sBallBatch, 0.2f, 36, 72);
//绑定纹理对象(公转小球)
glBindTexture(GL_TEXTURE_2D, nTextures[2]);
//读取纹理、设置纹理、载入纹理
LoadTextureFromTGA("MoonLike.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE);
/********************************设置随机位置小球*********************************/
for (int i = 0; i < SMALL_BALL_COUNT; i++) {
GLfloat x = ((rand() % 400) - 200) * 0.1f;
GLfloat z = ((rand() % 400) - 200) * 0.1f;
smallBallFrame[i].SetOrigin(x, 0.f, z);
}
}
//绘制除地板以外的其他图形
void DrawOthers (GLfloat yRot)
{
/********************************绘制随机位置小球*********************************/
glBindTexture(GL_TEXTURE_2D, nTextures[2]);
for (int i = 0; i < SMALL_BALL_COUNT; i++) {
modelViewMatrixStack.PushMatrix();
modelViewMatrixStack.MultMatrix(smallBallFrame[i]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,transformPipeLine.GetModelViewMatrix(),transformPipeLine.GetProjectionMatrix(),nLightPos,nWhite,0);
sBallBatch.Draw();
modelViewMatrixStack.PopMatrix();
}
/********************************绘制大球*********************************/
//先将大球从观察者位置移动一个距离,不然最开始就看不到整体的大球了
modelViewMatrixStack.Translate(0.f, 0.2f, -2.5f);
//压栈,之所以在移动了大球的位置后压栈,是因为公转小球要围绕着大球,公转小球的位置是要相对于大球位置,而不是观察者
modelViewMatrixStack.PushMatrix();
//大球围绕y轴做旋转
modelViewMatrixStack.Rotate(yRot, 0.f, 1.f, 0.f);
//绑定一下大球的纹理
glBindTexture(GL_TEXTURE_2D, nTextures[1]);
//设置着色器
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,transformPipeLine.GetModelViewMatrix(),transformPipeLine.GetProjectionMatrix(),nLightPos,nWhite,0);
//绘制大球
lBallBatch.Draw();
//绘制完大球就出栈
modelViewMatrixStack.PopMatrix();
/********************************绘制公转小球*********************************/
//压栈
modelViewMatrixStack.PushMatrix();
//让小球旋转的速度变成2倍,先旋转再平移,不然是没有公转效果的,因为平移是物体坐标系在世界坐标系中平移
modelViewMatrixStack.Rotate(-2 * yRot, 0.f, 1.f, 0.f);
//相对大球再在x轴多平移出来一些距离,这样才能看到小球
modelViewMatrixStack.Translate(0.8f, 0.f, 0.f);
glBindTexture(GL_TEXTURE_2D, nTextures[2]);
//设置着色器
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,transformPipeLine.GetModelViewMatrix(),transformPipeLine.GetProjectionMatrix(),nLightPos,nWhite,0);
sBallBatch.Draw();
modelViewMatrixStack.PopMatrix();
}
//渲染
void RenderScene ()
{
//定义定时器
static CStopWatch nTimer;
//获取每秒的旋转角度
GLfloat yRot = nTimer.GetElapsedSeconds() * 60.f;
//清空缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//压栈
modelViewMatrixStack.PushMatrix();
//定义一个矩阵变量,用以获取观察者矩阵
M3DMatrix44f mCamera;
//获取观察者矩阵并放入矩阵变量中
cameraFrame.GetCameraMatrix(mCamera);
//将观察者矩阵变化放入模型视图矩阵堆栈栈顶
modelViewMatrixStack.MultMatrix(mCamera);
/********************************绘制镜面部分*********************************/
//镜面其实就是画了两遍,只不过相对于x和z轴构成的平面是对称的,所以要将Y轴的正方向旋转180度
//为了让镜面和非镜面的旋转方向是一样的,可以把正背面的顺时针逆时针调换,因为球体的背面旋转和正面旋转刚好是相反的
//所以,先压栈
modelViewMatrixStack.PushMatrix();
//然后旋转Y轴180度,可以稍微再做下缩放,这样更逼真
modelViewMatrixStack.Scale(1.f, -0.9f, 1.f);
//让镜面往下面移动多一点,不然和非镜面的图形会重叠
modelViewMatrixStack.Translate(0.f, 1.2f, 0.f);
//设置顺时针旋转为正面
glFrontFace(GL_CW);
//绘制镜面
DrawOthers(yRot);
//绘制完成把正面换回顺时针
glFrontFace(GL_CCW);
//出栈
modelViewMatrixStack.PopMatrix();
/********************************绘制地板部分*********************************/
//开启混合
glEnable(GL_BLEND);
//指定颜色混合方程式
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//绑定一下纹理,因为绘制的不只是一个图形,避免图形上的纹理出现错误
glBindTexture(GL_TEXTURE_2D, nTextures[0]);
//设置着色器管理器
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_MODULATE,transformPipeLine.GetModelViewProjectionMatrix(),nfloorColor,0);
//绘制地板
floorBatch.Draw();
//绘制完成,取消混合
glDisable(GL_BLEND);
/********************************绘制非镜面部分*********************************/
//绘制其他部分(非镜面)
DrawOthers(yRot);
/****************************************************************************/
//绘制完成,出栈
modelViewMatrixStack.PopMatrix();
//交换缓冲区
glutSwapBuffers();
//因为球每秒都要旋转,所以发送渲染通知
glutPostRedisplay();
}
//特殊键位
void SpecialKeys (int key , int x , int y)
{
//每次前后移动的距离
GLfloat nStep = 0.1f;
//每次旋转的角度
GLfloat nRot = (GLfloat)(m3dDegToRad(5.f));
switch (key) {
case GLUT_KEY_UP:
cameraFrame.MoveForward(nStep);
break;
case GLUT_KEY_DOWN:
cameraFrame.MoveForward(-nStep);
break;
case GLUT_KEY_LEFT:
cameraFrame.RotateWorld(nRot, 0.f, 1.f, 0.f);
break;
case GLUT_KEY_RIGHT:
cameraFrame.RotateWorld(-nRot, 0.f, 1.f, 0.f);
break;
}
}
//关闭纹理
void ShutdownTexture ()
{
glDeleteTextures(TEXTURE_COUNT, nTextures);
}
#pragma mark - main
int main (int argc,char *argv[])
{
//设置工作目录和项目目录在同一文件夹下
gltSetWorkingDirectory(argv[0]);
//初始化glut
glutInit(&argc, argv);
//初始化显示模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_STENCIL | GLUT_DEPTH);
//初始化窗口尺寸
glutInitWindowSize(600, 600);
//创建窗口并命名
glutCreateWindow("球体");
//注册函数
//重塑函数
glutReshapeFunc(ChangeSize);
//渲染函数
glutDisplayFunc(RenderScene);
//特殊键位
glutSpecialFunc(SpecialKeys);
//初始化Glew库
GLenum status = glewInit();
if (status != GLEW_OK) {
printf("glew init error : %s \n",glewGetErrorString(status));
return 1;
}
//设置渲染环境
SetUpRC();
//建立消息循环
glutMainLoop();
//关闭纹理
ShutdownTexture();
return 0;
}
效果图如图1.1所示:
1.1.png纹理图网盘链接,里面M开头的三个文件就是这节要用到的纹理图tga,提取码uhtv
。
网友评论