OpenGL代码学习(7)--开始接触3D效果

注意:需要在配置好OpenGL的编程环境中运行下列代码,环境配置文章可参考:

OpenGL在Mac项目上的配置

下面的代码,直接放置在main.cpp文件中即可:

#include "GLTools.h"
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLGeometryTransform.h"
#include <math.h>
#include <glut/glut.h>

GLShaderManager shaderManager;
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;
GLFrame cameraFrame;// 摄像机的位置
GLFrame objectFrame;// 物体的位置
GLFrustum viewFrustum;// 投影矩阵

//三角形扇的批次
GLBatch triangleFanBatch;

//变换管线
GLGeometryTransform transformPipeline;

//颜色
GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };

//初始化三角形扇批次
void SetupTriangleFanBatch() {
    // 用代码生成三角形扇的顶点位置
    // x,y,z 坐标的点
    GLfloat vPoints[9][3];//??
    int nVerts = 0;
    GLfloat r = 3.0f;
    vPoints[nVerts][0] = 0.0f;
    vPoints[nVerts][1] = 0.0f;
    vPoints[nVerts][2] = 0.0f;

    for (GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI / 6.0f) {
        nVerts++;
        vPoints[nVerts][0] = float(cos(angle)) * r;
        vPoints[nVerts][1] = float(sin(angle)) * r;
        vPoints[nVerts][2] = -r;
    }
    //使三角形扇闭合
    nVerts++;
    vPoints[nVerts][0] = r;
    vPoints[nVerts][1] = 0;
    vPoints[nVerts][2] = 0.0f;
    
    printf("顶点数据的个数:~%d\n", nVerts);

    /*
     点 -> GL_POINTS:屏幕上单独的点
     线段 -> GL_LINES:每对顶点定义一条线段
     线条 -> GL_LINE_STRIP:从起始点依次经过所有后续点的线条
     闭合线条 -> GL_LINE_LOOP:起始点和终点相连的线条
     三角形 -> GL_TRIANGLES:每 3 个顶点定义一个三角形
     三角形条带 -> GL_TRIANGLE_STRIP:共用一个条带上顶点的一组三角形
     三角形扇 -> GL_TRIANGLE_FAN:以圆点为中心呈扇形的共用相邻顶点的一组三角形
     */
    //三角形扇批次初始化
    triangleFanBatch.Begin(GL_TRIANGLE_FAN, 8);
    triangleFanBatch.CopyVertexData3f(vPoints);
    triangleFanBatch.End();
    
    glPointSize(5.f);
}

//为程序作一次性的设置
void SetupRC() {
    //设置窗口背景颜色
    glClearColor(0.7f, 0.7f, 0.7f, 1.0f);

    //初始化着色器管理器
    shaderManager.InitializeStockShaders();

    //开启深度测试
    glEnable(GL_DEPTH_TEST);// 如果去掉这行代码,依然能3D效果,点的颜色不再是绿色,而是黑色

    //设置变换管线以使用两个矩阵堆栈
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);// 这行代码去掉的话,代码出错

    /*
     默认情况下,透视投影中的观察者位置处于原点(0,0,0),并沿着z轴负方向看向屏幕里面,一般通过moveForward方法来调整观察者位置,moveForward默认的朝向是-z轴,所有向屏幕里面移动传正数值,向屏幕外即+z轴,需要传负数值
     */
    //移动摄像机的位置
    cameraFrame.MoveForward(-15.0f);//-25:看得更近    -10:看得更远

    //准备画图要用的批次
    SetupTriangleFanBatch();
}

//画三角形
void DrawTriangleBatch(GLBatch* pBatch) {
    //画绿色面
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
    pBatch->Draw();

    //开启调节片段的深度值,使深度值产生偏移而不实际改变 3D 空间的物理位置
    glPolygonOffset(-1.0f, -1.0f);
    glEnable(GL_POLYGON_OFFSET_LINE);

    //开启线条的抗锯齿
    glEnable(GL_LINE_SMOOTH);

    //开启颜色混合
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    //多边形模式切换为前后面的线模式
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    //画边界黑线
    glLineWidth(2.5f);
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    pBatch->Draw();

    //还原绘画环境
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glDisable(GL_POLYGON_OFFSET_LINE);
    glLineWidth(1.0f);
    glDisable(GL_BLEND);
    glDisable(GL_LINE_SMOOTH);
}

//渲染画面
void RenderScene(void) {
    //清除一个或一组特定的缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    //保存当前的模型视图矩阵 (单位矩阵)
    modelViewMatrix.PushMatrix();

    //MultMatrix: 用一个矩阵乘以矩阵堆栈的顶部矩阵,相乘得到的结果随后将存储在堆栈的顶部
    //处理模型相对于摄像机的位置
    M3DMatrix44f mCamera;
    // GetCameraMatrix是GLFrame的一个函数,通常会用这个来进行设置,通过此函数来获取一个观察者变换后的矩阵
    cameraFrame.GetCameraMatrix(mCamera);
    modelViewMatrix.MultMatrix(mCamera);

    //处理模型自身的旋转
    M3DMatrix44f mObjectFrame;
    // GetCameraMatrix是GLFrame的一个函数,通常会用这个来进行设置,通过此函数来获取一个观察者变换后的矩阵
    objectFrame.GetCameraMatrix(mObjectFrame);
    modelViewMatrix.MultMatrix(mObjectFrame);

    //画图
    DrawTriangleBatch(&triangleFanBatch);

    // 还原以前的模型视图矩阵 (单位矩阵)
    modelViewMatrix.PopMatrix();

    //将在后台缓冲区进行渲染,然后在结束时交换到前台
    glutSwapBuffers();
}

//特殊按键(功能键或者方向键)监听
void SpecialKeys(int key, int x, int y) {

    //上、下、左、右按键,3D 旋转
    /*
     * RotateWorld(float fAngle, float x, float y, float z)
     * fAngle: 旋转弧度, x/y/z:以哪个坐标轴旋转
     * m3dDegToRad:角度 -> 弧度
     */
    switch (key) {
        case GLUT_KEY_UP: objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f); break;
        case GLUT_KEY_DOWN: objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f); break;
        case GLUT_KEY_LEFT: objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f); break;
        case GLUT_KEY_RIGHT: objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f); break;
        default:
            break;
    }

    //触发渲染
    glutPostRedisplay();
}

//窗口大小改变时接受新的宽度和高度
void ChangeSize(int width, int height) {

    // 防止下面除法的除数为0导致的闪退
    if(height == 0) height = 1;

    //设置视图窗口位置
    glViewport(0, 0, width, height);

    // 创建投影矩阵,并将它载入到投影矩阵堆栈中
    /* 显示3D图形,有远小近大的效果
     参数1:垂直方向上的视场角度
     参数2:窗口的纵横比 = w / h
     参数3:近裁剪面距离
     参数4:远裁剪面距离
     */
    viewFrustum.SetPerspective(35.0f, float(width) / float(height), 1.0f, 500.0f);
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());

    // 在模型视图矩阵顶部载入单位矩阵
    modelViewMatrix.LoadIdentity();
}

//程序入口
int main(int argc, char* argv[]) {
    //设置当前工作目录,针对MAC OS X
    gltSetWorkingDirectory(argv[0]);

    //初始化GLUT库
    glutInit(&argc, argv);

    /*初始化渲染模式,其中标志GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分别指
     双缓冲窗口、RGBA颜色模式、深度测试、模板缓冲区*/
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);

    //初始化窗口大小
    glutInitWindowSize(800, 720);
    //创建窗口
    glutCreateWindow("GL_POINTS");

    //注册回调函数
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    glutSpecialFunc(SpecialKeys);

    //确保驱动程序的初始化中没有出现任何问题
    GLenum err = glewInit();
    if(GLEW_OK != err) {
        fprintf(stderr, "glew error:%s\n", glewGetErrorString(err));
        return 1;
    }

    //初始化设置
    SetupRC();

    //进入调用循环
    glutMainLoop();
    
    return 0;
}

#pragma mark - 代码逻辑整理
/*
 1 UseStockShader函数肯定是关键,核心。所以先从该函数入手。
 
 2 共有两处地方使用到了该函数
 (1)shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
 (2)shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
 
 //变换管线
 GLGeometryTransform transformPipeline;
 
 //设置变换管线以使用两个矩阵堆栈
 transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
 
 
 GLMatrixStack modelViewMatrix;
 
 modelViewMatrix.PushMatrix();
 
 modelViewMatrix.MultMatrix(mCamera);
 (1)GLFrame cameraFrame;
 (2)//移动摄像机的位置
 cameraFrame.MoveForward(-15.0f);
 (3)//处理模型相对于摄像机的位置
 M3DMatrix44f mCamera;
 cameraFrame.GetCameraMatrix(mCamera);
 modelViewMatrix.MultMatrix(mCamera);
 
 modelViewMatrix.MultMatrix(mObjectFrame);
 (1)GLFrame objectFrame;
 (2)//处理模型自身的旋转
 M3DMatrix44f mObjectFrame;
 objectFrame.GetCameraMatrix(mObjectFrame);
 modelViewMatrix.MultMatrix(mObjectFrame);
 (3)switch (key) {
     case GLUT_KEY_UP: objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f); break;
     case GLUT_KEY_DOWN: objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f); break;
     case GLUT_KEY_LEFT: objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f); break;
     case GLUT_KEY_RIGHT: objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f); break;
     default:
         break;
 }
 
 modelViewMatrix.PopMatrix();
 
 modelViewMatrix.LoadIdentity();
 
 
 GLMatrixStack projectionMatrix;
 
 projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
 (1)GLFrustum viewFrustum;
 (2)// 创建投影矩阵,并将它载入到投影矩阵堆栈中
 viewFrustum.SetPerspective(35.0f, float(width) / float(height), 1.0f, 500.0f);
 projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
 
 */

运行上面的代码,得到的部分效果图示:

 

 

 

 

 

 

 

posted @ 2021-05-01 18:34  码出境界  阅读(213)  评论(0编辑  收藏  举报