四、OpenGL案例分析

2020-07-10  本文已影响0人  随意昵称你能懂得

本文将以金字塔为例,对使用OpenGL绘制图形的代码做一些简单分析。


金字塔.png

金字塔实现

必要属性

//着色管理器
GLShaderManager shaderManager;
//模型矩阵
GLMatrixStack modelViewMatrix;
//投影矩阵
GLMatrixStack projectionMatrix;
//相机位置
GLFrame cameraFrame;
//模型位置
GLFrame objectFrame;

//投影矩阵
GLFrustum viewFrustum;

//金色塔容器
GLBatch triangleBatch;

//几何变换的管道
GLGeometryTransform transformPipeline;

//红色表面
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
//黑色比酷昂
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };

main函数

需要初始化一些功能,再绑定一些按键和方法,然后初始化显示数据,最后开启循环。

int main(int argc, char* argv[])
{
    //设置当前目录
    gltSetWorkingDirectory(argv[0]);
    //初始化glut
    glutInit(&argc, argv);
    //申请一个颜色缓存区、深度缓存区、双缓存区、模板缓存区
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    //设置视图窗口的尺寸
    glutInitWindowSize(800, 600);
    //创建视图窗口的名称
    glutCreateWindow("GL_TRIANGLES");
    //监听视图窗口改变大小
    glutReshapeFunc(ChangeSize);
    //监听按键,例如空格
    glutKeyboardFunc(KeyPressFunc);
    //监听特殊键位,例如上下左右
    glutSpecialFunc(SpecialKeys);
    //监听屏幕刷新,处理屏幕刷新
    glutDisplayFunc(RenderScene);
    
    //判断一下是否能初始化glew库,确保项目能正常使用OpenGL 框架
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }
    
    //自定义绘制方法,自行实现
    SetupRC();
    
    //runloop运行循环
    glutMainLoop();
    return 0;
}

自定义绘制方法

设置改场景下的部分设置,并且初始化金字塔数据

void SetupRC()
{
    //设置背景色
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f );
    //初始化着色管理器
    shaderManager.InitializeStockShaders();
    //打开深度测试
    glEnable(GL_DEPTH_TEST);
    //设置变换管线以使用两个矩阵堆栈
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    //设置相机位置
    cameraFrame.MoveForward(-15.0f);
    
    //通过三角形创建金字塔
    GLfloat vPyramid[12][3] = {
        -2.0f, 0.0f, -2.0f,
        2.0f, 0.0f, -2.0f,
        0.0f, 4.0f, 0.0f,

        2.0f, 0.0f, -2.0f,
        2.0f, 0.0f, 2.0f,
        0.0f, 4.0f, 0.0f,

        2.0f, 0.0f, 2.0f,
        -2.0f, 0.0f, 2.0f,
        0.0f, 4.0f, 0.0f,

        -2.0f, 0.0f, 2.0f,
        -2.0f, 0.0f, -2.0f,
        0.0f, 4.0f, 0.0f
        
    };
    
    //GL_TRIANGLES 每3个顶点定义一个新的三角形
    triangleBatch.Begin(GL_TRIANGLES, 12);
    triangleBatch.CopyVertexData3f(vPyramid);
    triangleBatch.End();
}

显示函数

进行矩阵变换,中间留出正在渲染金字塔的方法

void RenderScene(void) {
    // 清理缓存区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
   
    //矩形压栈
    modelViewMatrix.PushMatrix();
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    
    //矩阵乘以矩阵堆栈的顶部矩阵,相乘的结果随后简存储在堆栈的顶部
    modelViewMatrix.MultMatrix(mCamera);
    
    M3DMatrix44f mObjectFrame;
    //只要使用 GetMatrix 函数就可以获取矩阵堆栈顶部的值,这个函数可以进行2次重载。用来使用GLShaderManager 的使用。或者是获取顶部矩阵的顶点副本数据
    objectFrame.GetMatrix(mObjectFrame);
    
    //矩阵乘以矩阵堆栈的顶部矩阵,相乘的结果随后简存储在堆栈的顶部
    modelViewMatrix.MultMatrix(mObjectFrame);
    
    //渲染金字塔方法
    DrawWireFramedBatch(&triangleBatch);

    //还原到以前的模型视图矩阵(单位矩阵)
    modelViewMatrix.PopMatrix();
    
    // 进行缓冲区交换
    glutSwapBuffers();
}

渲染金字塔

先画表面,然后画边框,设置一些优化属性,需要还原回来。

void DrawWireFramedBatch(GLBatch* pBatch)
{
    //金字塔表面
    //提供着色器的Uniform值-平面着色器
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vRed);
    pBatch->Draw();
    
    //黑色边框
    glPolygonOffset(-1.0f, -1.0f);// 偏移深度,在同一位置要绘制填充和边线,会产生z冲突,所以要偏移
    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);
    
    //提供着色器的Uniform值-平面着色器
    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 SpecialKeys(int key, int x, int y) {
    
    if(key == GLUT_KEY_UP)
        objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
    
    if(key == GLUT_KEY_DOWN)
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
    
    if(key == GLUT_KEY_LEFT)
        objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
    
    if(key == GLUT_KEY_RIGHT)
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
    
    glutPostRedisplay();
}

窗口大小改变

该方法必须设置绑定,不然会导致显示不出东西

void ChangeSize(int w, int h)
{
    glViewport(0, 0, w, h);
    //创建投影矩阵,并将它载入投影矩阵堆栈中
    viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    
    //调用顶部载入单元矩阵
    modelViewMatrix.LoadIdentity();
}
上一篇下一篇

猜你喜欢

热点阅读