带纹理的球体实例
2020-07-23 本文已影响0人
夏天的枫_
实现一个球体A自转,使得一个小球体B围绕其公转,并随机生成多个小球,同时实现一个镜面的效果,使得有倒影一般。并给这些球体和地面铺设纹理。
-
实例的最终模样
-
以下是实现步骤
绘制纹理的球体自转与公转
main函数
1.申请一个颜色缓存区、深度缓存区、双缓存区、模板缓存区
2.设置window 的尺寸、名称
3.注册回调函数(改变尺寸)
4.点击空格的调用函数
5.特殊键位函数(上下左右)
6.显示函数
7.绘制
8.删除纹理
其中需要注意的是,使用完纹理对象后,要对其进行释放
int main(int argc ,char* argv[])
{
gltSetWorkingDirectory(argv[0]);
// 初始化
glutInit(&argc, argv);
// 申请一个双缓存区、颜色缓存区、深度缓存区、模板缓存区
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH );
// 设置window的尺寸
glutInitWindowSize(800, 600);
// 创建window的名称
glutCreateWindow("OpenGL Sphere");
// 注册回调函数(改变尺寸)
glutReshapeFunc(ChangeSize);
// 显示函数
glutDisplayFunc(RenderScene);
// 设置特殊键位函数 : 上下左右
glutSpecialFunc(directionKeyOnclick);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Init Error: %s\n", glewGetErrorString(err));
return 1;
}
// 绘制
SetupRC();
// runloop 运行循环
glutMainLoop();
// 删除纹理
shutDownRC();
return 0;
}
SetupRC
- 初始化场景,只执行一次,设置都在此函数中执行设置,包括背景色、着色器的设置、是否开启深度测试以及背面剔除
1.设置背景颜色
2.初始化着色器管理器
3.开启深度测试、背面剔除
4.初始化大球、小球、地板纹理
5.命名纹理对象
6.将TGA文件加载为2D纹理(先绑定纹理,在加载)
/// 初始化场景
void SetupRC()
{
// 1.设置背景色
glClearColor(0.1, 0.1, 0.1f, 1.0f);
// 2.初始化着色器管理器
shaderManager.InitializeStockShaders();
// 开启深度测试
glEnable(GL_DEPTH_TEST);
// 开启背面剔除
glEnable(GL_CULL_FACE);
// 初始化一个球体 球体批次类,半径,片段数,堆积数量
gltMakeSphere(bigSphereBatch, 0.5f, 40, 80);
// 初始化小球
gltMakeSphere(globlueBatch, 0.11f, 15, 30);
//6.设置地板顶点数据&地板纹理
GLfloat texSize = 10.0f;
floorBatch.Begin(GL_TRIANGLE_FAN, 4,1);
floorBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
floorBatch.Vertex3f(-20.f, -0.41f, 20.0f);
floorBatch.MultiTexCoord2f(0, texSize, 0.0f);
floorBatch.Vertex3f(20.0f, -0.41f, 20.f);
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();
for (int i = 0; i < Spheres_Num; i++) {
//y轴不变,X,Z产生随机值
GLfloat x = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
GLfloat z = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
//在y方向,将球体设置为0.0的位置,这使得它们看起来是飘浮在眼睛的高度
//对spheres数组中的每一个顶点,设置顶点数据
spheres[i].SetOrigin(x, 0.0f, z);
}
//8.命名纹理对象
glGenTextures(3, uiTextures);
//9.将TGA文件加载为2D纹理。
//参数1:纹理文件名称
//参数2&参数3:需要缩小&放大的过滤器
//参数4:纹理坐标环绕模式
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);
}
loadTGATexture
1.加载TGA为纹理数据
2.设置问纹理参数(纹理纬度、过滤方式、环绕模式)
3.释放纹理
4.加载Mip
///加载纹理数据
bool loadTGATexture(const char *tgafileName,GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
GLbyte *pBits;
int mWidth, mHeight, mComponents;
GLenum eFormat;
// 1.加载纹理数据
pBits = gltReadTGABits(tgafileName, &mWidth, &mHeight, &mComponents, &eFormat);
if (!pBits) {
return false;
}
// 2.设置纹理参数
/**
* 参数1:纹理维度
* 2: 为S/T坐标设置模式
* 3: wrapMode,环绕模式
*/
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
/**
* 1:纹理维度
* 2: 线性过滤
* 3: wrapMode,环绕模式
*/
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
/**
* 1:纹理维度
* 2: mip贴图层次
* 3: 纹理单元存储的颜色成分(从读取像素图是获得)- 将内部参数comments改为通用压缩纹理格式GL_COMPRESSED_RGB
* 4. 加载纹理宽
* 5:加载纹理高
* 6: 加载纹理的深度
* 7:像素数据的类型 (GL_UNSIGNED_BYTE,每个颜色分量都是一个8位无符号整数)
* 8:指向纹理图像数据的指针
*/
glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB, mWidth, mHeight, 0,
eFormat, GL_UNSIGNED_BYTE, pBits);
// 3.使用完释放pBits
free(pBits);
//只有minFilter 等于以下四种模式,才可以生成Mip贴图
//GL_NEAREST_MIPMAP_NEAREST具有非常好的性能,并且闪烁现象非常弱
//GL_LINEAR_MIPMAP_NEAREST常常用于对游戏进行加速,它使用了高质量的线性过滤器
//GL_LINEAR_MIPMAP_LINEAR 和GL_NEAREST_MIPMAP_LINEAR 过滤器在Mip层之间执行了一些额外的插值,以消除他们之间的过滤痕迹。
//GL_LINEAR_MIPMAP_LINEAR 三线性Mip贴图。纹理过滤的黄金准则,具有最高的精度。
if(minFilter == GL_LINEAR_MIPMAP_LINEAR ||
minFilter == GL_LINEAR_MIPMAP_NEAREST ||
minFilter == GL_NEAREST_MIPMAP_LINEAR ||
minFilter == GL_NEAREST_MIPMAP_NEAREST)
{
//4.加载Mip,纹理生成所有的Mip层
//参数:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
glGenerateMipmap(GL_TEXTURE_2D);
}
return true;
}
ChangeSize函数
- 设置视口大小、透视模式以及初始化矩阵,将透视矩阵加载到透视矩阵对阵中,初始化管线
/// 改变窗口大小
/// @param w 宽带
/// @param h 高度
void ChangeSize(int w,int h)
{
// .设置窗口尺寸
glViewport(0, 0, w, h);
/* 3.设置透视模式,初始化透视矩阵
* SetPerspective 的参数是y
* 参数1:垂直方向上的观察者视角度数
* 参数2:纵横比 w/h
* 参数3:近裁剪⾯距离 (视角到近裁剪面距离为fNear)
* 参数4:远裁剪面距离(视角到远裁剪面距离为fFar)
*/
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f);
// 4.把透视矩阵加载到透视矩阵对阵中
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
modelViewMatix.LoadIdentity();
//设置变换管道以使用两个矩阵堆栈(变换矩阵modelViewMatrix ,投影矩阵projectionMatrix)
//初始化GLGeometryTransform 的实例transformPipeline.通过将它的内部指针设置为模型视图矩阵堆栈 和 投影矩阵堆栈实例,来完成初始化
//当然这个操作也可以在SetupRC 函数中完成,但是在窗口大小改变时或者窗口创建时设置它们并没有坏处。而且这样可以一次性完成矩阵和管线的设置。
// 5.初始化渲染管线
transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);
}
RenderScene函数
- 渲染场景,场景的渲染逻辑,在其中设置定时器,让其作为自转和公转的时间载体;要实现镜面效果,其实就是绘制两次地面+球体。
- 首先,压入单元矩阵,之后绘制观察者,再进行镜面压栈,这一层次压栈是为了将镜面的绘制实体不影响到地面的元素;
- 在镜面绘制时,为其添加反光效果,步骤可以分为将其沿Y轴翻转,再让其平移一段距离,远离正常地面,这个过程中需要将其正面重新设置以下,指定顺时针为正面,绘制完镜面的图元之后,再指定逆时针为正面,并进行镜面出栈。
- 绘制正常的地面,绑定地面纹理,并对其进行混合,绘制完地面之后取消混合。
-绘制地面之上的球体,在出栈单元矩阵恢复。交换缓存区并提交重新显示。
/// 渲染场景
void RenderScene()
{
// 0.设置颜色值-地板 球体
static GLfloat mFloorColor[] = {0.8f,0.8f,0.1f,0.75f};
// 1.清除窗口、深度缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 设置定时器动画
static CStopWatch rotTimer;
float rot = rotTimer.GetElapsedSeconds() * 60.0f;
// 压栈,将单元矩阵压入
modelViewMatix.PushMatrix();
// 3.将观察者提前一轮绘制,不影响下一层的布局
M3DMatrix44f myCamera;
cameraFrame.GetCameraMatrix(myCamera);
modelViewMatix.MultMatrix(myCamera);
// 4. 镜面压栈
modelViewMatix.PushMatrix();
// 5. 添加反光效果
// 翻转y轴
modelViewMatix.Scale(1.0f, -1.0f, 1.0f);
// 使镜面世界与y轴平移一段距离
modelViewMatix.Translate(0.0f, 1.2f, 0.0f);
// 6.指定顺时针为正面
glFrontFace(GL_CW);
// 7. 绘制地面以外其他地方-镜面
drawSphere(rot);
// 8. 恢复为逆时针为正面
glFrontFace(GL_CCW);
// 9.恢复绘制镜面的矩阵
modelViewMatix.PopMatrix();
// 开启混合功能
glEnable(GL_BLEND);
// 10。指定glBlendFunc 颜色混合方程式
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 11.绑定地面纹理
glBindTexture(GL_TEXTURE_2D, uiTextures[0]);
/* 12
纹理着色器——将基本色乘以一个取自纹理单元TextureUnit的纹理
para1:GLT_SHADER_TEXTURE_MODULATE
para2:模型视图投影矩阵
para3: 颜色
para4: 纹理单元 第0层的纹理单元
*/
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_MODULATE,
transformPipeline.GetModelViewProjectionMatrix(),
mFloorColor,
0);
floorBatch.Draw();
// 13.取消混合
glDisable(GL_BLEND);
// 14.绘制地面以外的球体
drawSphere(rot);
// 15.绘制完毕,恢复矩阵
modelViewMatix.PopMatrix();
// 16.交换缓存区
glutSwapBuffers();
// 17.提交重新显示
glutPostRedisplay();
}
drawSphere-——绘制球体
- 定义光源位置后就开始绘制,先给随机生成的小球绑定纹理再绘制;然后绘制自转大球,公转小球。
/// 绘制j球体
/// @param yRot <#yRot description#>
void drawSphere(GLfloat yRot)
{
// 1.定义光源位置和漫反射颜色
static GLfloat vWhite[] = {1.0f,1.0f,1.0f,1.0f};
static GLfloat vLightPosition[] = { 0.0f, 3.0f,0.0, 1.0f };
// 2.绘制小球 绑定纹理
glBindTexture(GL_TEXTURE_2D, uiTextures[2]);
// 3.随机小球
for (int i = 0; i<Spheres_Num; i ++) {
modelViewMatix.PushMatrix();
modelViewMatix.MultMatrix(spheres[I]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
modelViewMatix.GetMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightPosition,
vWhite,
0);
globlueBatch.Draw();
modelViewMatix.PopMatrix(); // 小球绘制完成,再出栈
}
// 4.绘制大球
modelViewMatix.Translate(0.0f, 0.2f, -2.5f);
modelViewMatix.PushMatrix();
modelViewMatix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
glBindTexture(GL_TEXTURE_2D, uiTextures[1]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
modelViewMatix.GetMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightPosition,
vWhite,
0);
bigSphereBatch.Draw();
modelViewMatix.PopMatrix();
// 5.绘制公转的小球
modelViewMatix.PushMatrix();
modelViewMatix.Rotate(yRot * -2.0f, 0.0f, 1.0f, 0.0f);
modelViewMatix.Translate(0.8f, 0.0f, 0.0f);
glBindTexture(GL_TEXTURE_2D, uiTextures[2]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
modelViewMatix.GetMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightPosition,
vWhite,
0);
globlueBatch.Draw();
modelViewMatix.PopMatrix();
}
directionKeyOnclick
-
通过控制按键控制视角的前后移动和旋转
/// 方向控制 通过改变Camera 移动,改变视口
/// @param key key description
/// @param x x description
/// @param y y description
void directionKeyOnclick(int key,int x,int y)
{
float linear = 0.1f;
float angular = float(m3dDegToRad(5.0f));
if (key == GLUT_KEY_UP) {
//MoveForward 平移
cameraFrame.MoveForward(linear);
}
if (key == GLUT_KEY_DOWN) {
cameraFrame.MoveForward(-linear);
}
if (key == GLUT_KEY_LEFT) {
//RotateWorld 旋转
cameraFrame.RotateWorld(angular, 0.0f, 1.0f, 0.0f);
}
if (key == GLUT_KEY_RIGHT) {
cameraFrame.RotateWorld(-angular, 0.0f, 1.0f, 0.0f);
}
glutPostRedisplay();
}
至此,就完成了带纹理的球体实例的实现。带纹理的球体实例demo