博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
OpenGL 学习 08 - 球体世界
阅读量:6824 次
发布时间:2019-06-26

本文共 12664 字,大约阅读时间需要 42 分钟。

学习书籍: 密码:fu4w

书籍源码: 密码:oyb4

环境搭建:

PS:因为以后 Demo 源码代码量会越来越多,不太可能全部复制在这里了,我就解释下 Demo 的核心源码,全部源码请前往我的 下载,源码里会有详细注释。

基本概念

三角形批次

GLTriangleBatch 就是专门为了绘制三角形的批次类,它将以更加高效的方式(索引顶点数组)进行组织,并实际上将多边形存储在图形卡(使用定点缓冲区对象)上,这里只需要了解它的用法即可。

角色帧

在场景中移动的物体通常被称为角色,为了方便我们计算角色在 3D 场景中进行移动、旋转等操作的坐标变换,每个角色都有自己的参考帧,即本地对象坐标系,更简洁的表示角色在空间中的坐标和方向。

为了在 3D 场景中表示任何对象的位置和方向,GLTools 为我们实现了该参考帧的数据结构 GLFrame角色帧),包含了空间中的一个位置、一个指向前方的向量和一个指向上方的向量。

class GLFrame {    M3DVector3f vOrigin;	// Where am I?    M3DVector3f vForward;	// Where am I going?    M3DVector3f vUp;		// Which way is up?    ...}复制代码

可能会有疑问说,怎么少了一个指向左边或右边的向量?因为这个指向左边或右边的向量可通过 vForwardvUp 向量叉乘得到。

有了角色帧的概念后,我们就知道照相机实际上就是一个角色帧,可以想象一下,一个人抬着照相机进行拍摄,它拍摄到的东西就是我们在窗口上看到的东西。

变换光线

对于几何图形变换来说,典型情况下我们会设置变换矩阵,将它们传递到着色器,然后让硬件完成所有顶点的变换,但对于光源来说,会有一些不同,因为光源变换是独立于几何变换以外。

光源位置也需要转化到视觉坐标系,但传递到着色器的矩阵是几何图形,而不是光线。想象下,光源就像是一个灯光师,它总是跟随着照相机的一起移动的。

得到光源矩阵:

// 先获取到照相机矩阵M3DMatrix44f mCamera;cameraFrame.GetCameraMatrix(mCamera);// 根据光源位置,得到光源矩阵M3DVector4f vLightPos = { 0.0f, 10.0f, 5.0f, 1.0f };M3DVector4f vLightEyePos;m3dTransformVector4(vLightEyePos, vLightPos, mCamera);复制代码

使用点光源着色器,传入光源矩阵:

shaderManager.UseStockShader(    GLT_SHADER_POINT_LIGHT_DIFF,           transformPipeline.GetModelViewMatrix(),              transformPipeline.GetProjectionMatrix(),     vLightEyePos,     vColor);复制代码

源码解析

更多对象(球、花托、圆柱、圆锥、圆盘)

09-Objects 核心源码如下,全部源码下载:

// 初始化球体的三角形批次,后面参数依次是,球半径,片段数,堆叠数gltMakeSphere(sphereBatch, 3.0, 10, 20);复制代码

// 初始化花托的三角形批次,后面参数依次是,外半径,内半径,片段数,堆叠数gltMakeTorus(torusBatch, 3.0f, 0.75f, 150, 15);复制代码

// 初始化圆柱的三角形批次,后面参数依次是,底部半径,顶部半径,高度,片段数,堆叠数gltMakeCylinder(cylinderBatch, 2.0f, 2.0f, 3.0f, 13, 2);复制代码

// 初始化圆锥的三角形批次,后面参数依次是,底部半径,顶部半径,高度,片段数,堆叠数gltMakeCylinder(coneBatch, 2.0f, 0.0f, 3.0f, 13, 2);复制代码

// 初始化圆盘的三角形批次,后面参数依次是,内半径,外半径,片段数,堆叠数gltMakeDisk(diskBatch, 1.5f, 3.0f, 13, 3);复制代码

球体世界 Lv1(绘制地板、旋转的花托)

13-SphereWorld-Lv1 核心源码如下,全部源码下载:

// 程序初始化void SetupRC() {    // 设置窗口背景颜色为黑色    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);    // 初始化着色器    shaderManager.InitializeStockShaders();    // 开启深度测试    glEnable(GL_DEPTH_TEST);    // 设置多边形模式为前后面线段模式    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);    // 得到花托批次数据    gltMakeTorus(torusBatch, 0.4f, 0.15f, 30, 30);    // 得到方格地板批次数据    floorBatch.Begin(GL_LINES, 324);    for(GLfloat x = -20.0; x <= 20.0f; x+= 0.5) {        floorBatch.Vertex3f(x, -0.55f, 20.0f);        floorBatch.Vertex3f(x, -0.55f, -20.0f);        floorBatch.Vertex3f(20.0f, -0.55f, x);        floorBatch.Vertex3f(-20.0f, -0.55f, x);    }    floorBatch.End();}// 窗口渲染回调void RenderScene(void) {    // 获取2次渲染之间的时间间隔    static CStopWatch    rotTimer;    float yRot = rotTimer.GetElapsedSeconds() * 60.0f;    // 清空缓冲区    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    // 压栈,保持原始矩阵    modelViewMatrix.PushMatrix();    // 绘制地板    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vFloorColor);    floorBatch.Draw();       // 视图矩阵进行平移、旋转后进行绘制花托    modelViewMatrix.Translate(0.0f, 0.0f, -2.5f);    modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vTorusColor);    torusBatch.Draw();        // 出栈,恢复原始矩阵    modelViewMatrix.PopMatrix();    // 因为是双缓冲区模式,后台缓冲区替换到前台缓存区进行显示    glutSwapBuffers();    // 自动触发渲染,达到动画效果    glutPostRedisplay();}复制代码

球体世界 Lv2(加入角色移动和蓝色小球旋转)

14-SphereWorld-Lv2 核心源码如下,全部源码下载:

// 程序初始化void SetupRC() {    // 设置窗口背景颜色为黑色    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);    // 初始化着色器    shaderManager.InitializeStockShaders();    // 开启深度测试    glEnable(GL_DEPTH_TEST);    // 设置多边形模式为前后面线段模式    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);    // 得到花托批次数据    gltMakeTorus(torusBatch, 0.4f, 0.15f, 30, 30);    // 得到旋转小球批次数据【Lv2 增加的代码】    gltMakeSphere(sphereBatch, 0.1f, 26, 13);        // 得到方格地板批次数据    floorBatch.Begin(GL_LINES, 324);    for(GLfloat x = -20.0; x <= 20.0f; x+= 0.5) {        floorBatch.Vertex3f(x, -0.55f, 20.0f);        floorBatch.Vertex3f(x, -0.55f, -20.0f);        floorBatch.Vertex3f(20.0f, -0.55f, x);        floorBatch.Vertex3f(-20.0f, -0.55f, x);    }    floorBatch.End();}// 窗口渲染回调void RenderScene(void) {    // 获取2次渲染之间的时间间隔    static CStopWatch    rotTimer;    float yRot = rotTimer.GetElapsedSeconds() * 60.0f;    // 清空缓冲区    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);        // 原来是没法移动所以压入原始矩阵,但现在角色可以移动通过角色帧获取照相机矩阵,并压入栈中【Lv2 修改的代码块】    M3DMatrix44f mCamera;    cameraFrame.GetCameraMatrix(mCamera);    modelViewMatrix.PushMatrix(mCamera);        // 绘制地板    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vFloorColor);    floorBatch.Draw();        // 视图矩阵进行平移    modelViewMatrix.Translate(0.0f, 0.0f, -2.5f);        // 保持旋转前的视图矩阵,因为如果不在旋转前保存,会导致旋转小球也会带上花托的旋转【Lv2 增加的代码】    modelViewMatrix.PushMatrix();        // 继续进行旋转并进行绘制花托    modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vTorusColor);    torusBatch.Draw();        // 恢复到旋转前的视图矩阵【Lv2 增加的代码】    modelViewMatrix.PopMatrix();        // 绘制旋转小球【Lv2 增加的代码】    modelViewMatrix.Rotate(yRot * -2.0f, 0.0f, 1.0f, 0.0f);    modelViewMatrix.Translate(0.8f, 0.0f, 0.0f);    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vSphereColor);    sphereBatch.Draw();        // 出栈,恢复成原始矩阵    modelViewMatrix.PopMatrix();    // 因为是双缓冲区模式,后台缓冲区替换到前台缓存区进行显示    glutSwapBuffers();    // 自动触发渲染,达到动画效果    glutPostRedisplay();}// 特殊按键点击回调【Lv2 增加的代码】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);}复制代码

球体世界 Lv3(加入随机小球分布场景)

15-SphereWorld-Lv3 核心源码如下,全部源码下载:

// 程序初始化void SetupRC() {    // 设置窗口背景颜色为黑色    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);    // 初始化着色器    shaderManager.InitializeStockShaders();    // 开启深度测试    glEnable(GL_DEPTH_TEST);    // 设置多边形模式为前后面线段模式    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);    // 得到花托批次数据    gltMakeTorus(torusBatch, 0.4f, 0.15f, 30, 30);    // 得到旋转小球批次数据【Lv2 增加的代码】    gltMakeSphere(sphereBatch, 0.1f, 26, 13);    // 得到方格地板批次数据    floorBatch.Begin(GL_LINES, 324);    for(GLfloat x = -20.0; x <= 20.0f; x+= 0.5) {        floorBatch.Vertex3f(x, -0.55f, 20.0f);        floorBatch.Vertex3f(x, -0.55f, -20.0f);        floorBatch.Vertex3f(20.0f, -0.55f, x);        floorBatch.Vertex3f(-20.0f, -0.55f, x);    }    floorBatch.End();        // 随机小球群位置数据生成【Lv3 增加的代码】    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);    }}// 窗口渲染回调void RenderScene(void) {    // 获取2次渲染之间的时间间隔    static CStopWatch    rotTimer;    float yRot = rotTimer.GetElapsedSeconds() * 60.0f;    // 清空缓冲区    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    // 原来是没法移动所以压入原始矩阵,但现在角色可以移动通过角色帧获取照相机矩阵,并压入栈中【Lv2 修改的代码块】    M3DMatrix44f mCamera;    cameraFrame.GetCameraMatrix(mCamera);    modelViewMatrix.PushMatrix(mCamera);    // 绘制地板    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vFloorColor);    floorBatch.Draw();        // 绘制随机小球群,这里的做法是瞬间切换当前角色位置并开始绘制小球,然后切换回原来角色位置【Lv3 增加的代码】    for(int i = 0; i < NUM_SPHERES; i++) {        // 保存当前矩阵状态,为了能切换回原来的角色位置        modelViewMatrix.PushMatrix();        // 调整当前角色位置为随机生成小球的位置        modelViewMatrix.MultMatrix(spheres[I]);        // 绘制蓝色小球        shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vSphereColor);        sphereBatch.Draw();        // 还原矩阵状态,切换为原来角色位置        modelViewMatrix.PopMatrix();    }        // 视图矩阵进行平移    modelViewMatrix.Translate(0.0f, 0.0f, -2.5f);    // 保持旋转前的视图矩阵,因为如果不在旋转前保存,会导致旋转小球也会带上花托的旋转【Lv2 增加的代码】    modelViewMatrix.PushMatrix();    // 继续进行旋转并进行绘制花托    modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vTorusColor);    torusBatch.Draw();    // 恢复到旋转前的视图矩阵【Lv2 增加的代码】    modelViewMatrix.PopMatrix();    // 绘制旋转小球【Lv2 增加的代码】    modelViewMatrix.Rotate(yRot * -2.0f, 0.0f, 1.0f, 0.0f);    modelViewMatrix.Translate(0.8f, 0.0f, 0.0f);    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vSphereColor);    sphereBatch.Draw();    // 出栈,恢复成原始矩阵    modelViewMatrix.PopMatrix();    // 因为是双缓冲区模式,后台缓冲区替换到前台缓存区进行显示    glutSwapBuffers();    // 自动触发渲染,达到动画效果    glutPostRedisplay();}复制代码

球体世界 Lv4(加入点光源着色)

16-SphereWorld-Lv4 核心源码如下,全部源码下载:

// 程序初始化void SetupRC() {    // 设置窗口背景颜色为黑色    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);    // 初始化着色器    shaderManager.InitializeStockShaders();    // 开启深度测试    glEnable(GL_DEPTH_TEST);        // 设置多边形模式为前后面线段模式【Lv4 删除的代码】//    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);        // 得到花托批次数据    gltMakeTorus(torusBatch, 0.4f, 0.15f, 30, 30);    // 得到旋转小球批次数据【Lv2 增加的代码】    gltMakeSphere(sphereBatch, 0.1f, 26, 13);    // 得到方格地板批次数据    floorBatch.Begin(GL_LINES, 324);    for(GLfloat x = -20.0; x <= 20.0f; x+= 0.5) {        floorBatch.Vertex3f(x, -0.55f, 20.0f);        floorBatch.Vertex3f(x, -0.55f, -20.0f);        floorBatch.Vertex3f(20.0f, -0.55f, x);        floorBatch.Vertex3f(-20.0f, -0.55f, x);    }    floorBatch.End();    // 随机小球群位置数据生成【Lv3 增加的代码】    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);    }}// 窗口渲染回调void RenderScene(void) {    // 获取2次渲染之间的时间间隔    static CStopWatch    rotTimer;    float yRot = rotTimer.GetElapsedSeconds() * 60.0f;    // 清空缓冲区    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);        // 原来是没法移动所以压入原始矩阵,但现在角色可以移动通过角色帧获取照相机矩阵,并压入栈中【Lv2 修改的代码块】    M3DMatrix44f mCamera;    cameraFrame.GetCameraMatrix(mCamera);    modelViewMatrix.PushMatrix(mCamera);        // 得到点光源位置 【Lv4 增加的代码】    M3DVector4f vLightPos = { 0.0f, 10.0f, 5.0f, 1.0f };    M3DVector4f vLightEyePos;    m3dTransformVector4(vLightEyePos, vLightPos, mCamera);        // 绘制地板    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vFloorColor);    floorBatch.Draw();    // 绘制随机小球群,这里的做法是瞬间切换当前角色位置并开始绘制小球,然后切换回原来角色位置【Lv3 增加的代码】    for(int i = 0; i < NUM_SPHERES; i++) {        // 保存当前矩阵状态,为了能切换回原来的角色位置        modelViewMatrix.PushMatrix();        // 调整当前角色位置为随机生成小球的位置        modelViewMatrix.MultMatrix(spheres[I]);        // 绘制蓝色小球        // 设置着色器为点光源着色器【Lv4 调整的代码】        shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,                                      transformPipeline.GetModelViewMatrix(),                                     transformPipeline.GetProjectionMatrix(),                                      vLightEyePos,                                      vSphereColor);        sphereBatch.Draw();        // 还原矩阵状态,切换为原来角色位置        modelViewMatrix.PopMatrix();    }    // 视图矩阵进行平移    modelViewMatrix.Translate(0.0f, 0.0f, -2.5f);    // 保持旋转前的视图矩阵,因为如果不在旋转前保存,会导致旋转小球也会带上花托的旋转【Lv2 增加的代码】    modelViewMatrix.PushMatrix();    // 继续进行旋转并进行绘制花托    modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);    // 设置着色器为点光源着色器【Lv4 调整的代码】    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,                                  transformPipeline.GetModelViewMatrix(),                                 transformPipeline.GetProjectionMatrix(),                                  vLightEyePos,                                  vTorusColor);    torusBatch.Draw();    // 恢复到旋转前的视图矩阵【Lv2 增加的代码】    modelViewMatrix.PopMatrix();    // 绘制旋转小球【Lv2 增加的代码】    modelViewMatrix.Rotate(yRot * -2.0f, 0.0f, 1.0f, 0.0f);    modelViewMatrix.Translate(0.8f, 0.0f, 0.0f);    // 设置着色器为点光源着色器【Lv4 调整的代码】    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,                                  transformPipeline.GetModelViewMatrix(),                                 transformPipeline.GetProjectionMatrix(),                                  vLightEyePos,                                  vSphereColor);    sphereBatch.Draw();    // 出栈,恢复成原始矩阵    modelViewMatrix.PopMatrix();    // 因为是双缓冲区模式,后台缓冲区替换到前台缓存区进行显示    glutSwapBuffers();    // 自动触发渲染,达到动画效果    glutPostRedisplay();}复制代码

上面的 Demo 源码全部都放在我的 上,大家可以去下载和调试。

有什么问题可以在下方评论区提出,写得不好可以提出你的意见,我会合理采纳的,O(∩_∩)O哈哈~,求关注求赞

转载于:https://juejin.im/post/5b13cfcb6fb9a01e554bf9fe

你可能感兴趣的文章
linux 下查找文件或者内容常用命令
查看>>
Linux常用系统调用表
查看>>
linux x86_64要注意的问题
查看>>
批处理中的call与start的个人学习心得
查看>>
BGP反射(RR)
查看>>
×××运算取ceiling
查看>>
搜索引擎的前世今生
查看>>
JSP
查看>>
经典排序算法 - 地精排序Gnome Sort
查看>>
mysql rand函数
查看>>
24种编程语言的Hello World程序
查看>>
Java中main函数参数String args[] 和 String[] args 区别
查看>>
Jarvis Oj Pwn 学习笔记-Tell Me Something
查看>>
【WP7进阶】——XNA游戏精灵的动画
查看>>
cat echo 输入多行文字至文本中
查看>>
puppet FAQ
查看>>
linux 基础命令(1)
查看>>
MySQL学习足迹记录01--SOURCE,SHOW
查看>>
DataInputStream与DataOutputStream的简单使用
查看>>
sql根据某一字段查询不重复记录,同时要查询出所有满足条件的字段信息
查看>>