OpenGL-公转与自转以及填充纹理案例

本次属于对之前学习的OpenGL相关知识的一个综合运用,效果如下:

主要包含了一下功能:

  1. 大球的自转
  2. 小球的公转
  3. 随机分散的小球
  4. 镜面效果
  5. 增加纹理效果

源码分析

用到的变量

#define NUM_SPHERES 50

GLFrame spheres[NUM_SPHERES];

GLShaderManager shaderManager; // 着色器管理器

GLMatrixStack modelViewMatrix; // 模型视图矩阵

GLMatrixStack projectionMatrix; // 投影矩阵

GLFrustum viewFrustum; // 视景体

GLGeometryTransform transformPipeline; // 几何图形变换管道

GLTriangleBatch torusBatch; //自转大球的批处理类

GLBatch floorBatch; // 地板批处理类

GLTriangleBatch sphereBatch; //小球以及公转球的批处理类

GLFrame cameraFrame; //角色帧照相机角色帧(全局照相机实例)

GLuint uiTextures[3]; //纹理标记数组

main函数

main函数是程序的入口函数,主要负责进行一些相关的初始化工作以及注册对应的回调函数,没什么好说的,详细代码可见DEMO。主要需要关注是:

int main(int argc, char* argv[]) {

...

//重塑函数

glutReshapeFunc(ChangeSize);

//绘制

glutDisplayFunc(RenderScene);

//特殊按键

glutSpecialFunc(SpecialKeys);

...

SetupRC();

glutMainLoop();

ShutdownRC();

return 0;

}

ChangeSize

当屏幕大小改变或者初始化的时候会调用此回调函数。主要负责根据当前屏幕的尺寸设置视口以及投影矩阵相关的工作。

void ChangeSize(int nWidth, int nHeight)

{

//1.设置视口

glViewport(0, 0, nWidth, nHeight);

//2.设置投影方式

viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);

//3.将投影矩阵加载到投影矩阵堆栈,

projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());

modelViewMatrix.LoadIdentity();

//4.将投影矩阵堆栈和模型视图矩阵对象设置到管道中

transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);

}

SetupRC

main函数执行,完成和视图绘制相关的初始化工作,主要包括:

  1. 设置背景颜色
  2. 初始化着色器
  3. 开启深度测试、正背面剔除
  4. 设置批次类和相关顶点信息

地板需要手动调用floorBatch进行相关顶点和纹理坐标的设置。而大球和小球可以通过系统提供的API直接进行设置,具体代码见DEMO。同时因为随机小球的位置不在原点,还需要我们使用GLFramesetOrigin方法对随机小球的位置进行设置。

  1. 加载和绑定纹理

大球、小球和地板采用不同的纹理,因此使用uiTextures数组来保存对应的纹理数据。

void SetupRC()

{

//1.设置清屏颜色到颜色缓存区

glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

//2.初始化着色器管理器

shaderManager.InitializeStockShaders();

//3.开启深度测试/背面剔除

glEnable(GL_DEPTH_TEST);

glEnable(GL_CULL_FACE);

//4.设置大球球

gltMakeSphere(torusBatch, 0.4f, 40, 80);

//5.设置小球(公转自转)

gltMakeSphere(sphereBatch, 0.1f, 26, 13);

//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();

//7.随机小球球顶点坐标数据

for (int i = 0; i < NUM_SPHERES; 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:纹理坐标环绕模式

//因为OpenGL是一个状态机,我理解绑定相关于是指定了某一个纹理,同时将对应的tga文件和该纹理联系起来

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

}

RenderScene

绘制场景的核心功能在于模型视图矩阵堆栈的压栈出栈操作以及对应的着色器的使用。具体流程如下:

关于模型视图矩阵堆栈的补充

在此案例中:

  1. 观察者最先入栈,因为观察者是全局的观察
  2. 压栈,坐标系y轴进行翻转
  3. 压栈,绘制随机小球,因为小球涉及到了不同的位置,因此需要单独进行压栈,每一个小球的绘制都需要进行一次压栈和出栈。
  4. 绘制随机小球之后,堆栈恢复到步骤2的状态。
  5. 压栈,绘制自转大球,因为大球涉及到了旋转,同时不能影响下面的操作,因此大球的绘制需要压栈和出栈。
  6. 绘制大球以后,堆栈恢复到步骤2
  7. 压栈,绘制公转小球,因为公转小球涉及到旋转和平移,不能影响下面的操作,因此需要压栈和出栈。
  8. 绘制公转小球后,堆栈恢复到步骤2,此时镜面部分已经绘制完毕,需要恢复坐标轴,即Y轴正向为朝上,此时需要出栈,堆栈恢复到1。
  9. 绘制地板,因为地板不涉及到变化,无需压栈和出栈。
  10. 绘制镜面向上的部分。此时步骤和步骤3-7重复。
  11. 整体绘制完毕后,出栈。以确保不影响下一次的绘制。

以上是 OpenGL-公转与自转以及填充纹理案例 的全部内容, 来源链接: utcz.com/a/34458.html

回到顶部