美文网首页
OpenGL从零开始 -- 画一个金字塔

OpenGL从零开始 -- 画一个金字塔

作者: 尤先森 | 来源:发表于2019-05-14 22:46 被阅读0次

上一篇文章已经简单介绍了OpenGL的环境搭建、并且画出了一个三角形,今天这篇文章,就在三角形的基础上,把三角形变成金字塔吧。效果图如下

image.png
在这之前建议先看看这篇文章 《OpenGL / OpenGL ES 下专业名词解析》
了解一下。

搭出金字塔的需求

简单构思一下

  1. 我们要搭成这么一个金字塔,至少需要4面三角形。
  2. 我们需要让这个金字塔动起来,看他的效果,要不然运行起来就只是一个三角形而已。

属性声明

// 存储着色器管理工具类
GLShaderManager     shaderManager;
// 投影矩阵堆栈
GLMatrixStack       modelViewMatrix;
// 模型视图矩阵堆栈
GLMatrixStack       projectionMatrix;
// 设置观察者坐标
GLFrame             cameraFrame;
// 设置图形环绕时,视图坐标
GLFrame             objectFrame;
//设置图片绘制时的投影方式
GLFrustum           viewFrustum;
//容器类
GLBatch             triangleBatch

//几何变换的管道
GLGeometryTransform transformPipeline;
//一个黑色 一个绿色
GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };

main方法

刚开始的时候,main方法基本上就都是这几个方法
(到后面会不会变我就不知道了 ^ _ ^)

int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[0]);
    glutInit(&argc, argv);
    //申请一个颜色缓存区、深度缓存区、双缓存区、模板缓存区
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    //设置window 的尺寸
    glutInitWindowSize(800, 600);
    //创建window的名称
    glutCreateWindow("GL_TRIANGLES");
    //注册回调函数(改变尺寸)
    glutReshapeFunc(ChangeSize);
    //特殊键位函数(上下左右)控制旋转
    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;
}

然后我们就按顺序一个一个的实现这些方法

ChangeSize

自定义函数,通过glutReshaperFunc(函数名)注册为重塑函数。当屏幕⼤小发生变化或者第⼀次创建窗口时,会调用该函数调整窗口⼤小/视⼝大小。

// 窗口已更改大小,或刚刚创建。无论哪种情况,我们都需要
// 使用窗口维度设置视口和投影矩阵.
void ChangeSize(int w, int h)
{
    glViewport(0, 0, w, h);
    //设置投影矩阵
    viewFrustum.SetPerspective(35, float(w)/float(h), 1, 500);
    //加载投影矩阵
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    //加载单元矩阵
    modelViewMatrix.LoadIdentity();
}

RenderScene

⾃定义函数,通过glutDisplayFunc(函数名)注册为显示渲染函数。当屏幕发生变化或者开发者主动渲染会调用此函数。

// 召唤场景
void RenderScene(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    modelViewMatrix.PushMatrix();
    
    M3DMatrix44f camera;
    cameraFrame.GetCameraMatrix(camera);
    modelViewMatrix.MultMatrix(camera);
    
    
    M3DMatrix44f obj;
    objectFrame.GetMatrix(obj);
    modelViewMatrix.MultMatrix(obj);
    
    shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vGreen);
    triangleBatch.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将多边形正面或者背面设为线框模式,实现线框渲染
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    //设置线条宽度
    glLineWidth(2.5f);
    
    /* GLShaderManager 中的Uniform 值——平面着色器
     参数1:平面着色器
     参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
     --transformPipeline.GetModelViewProjectionMatrix() 获取的
     GetMatrix函数就可以获得矩阵堆栈顶部的值
     参数3:颜色值(黑色)
     */
    
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    triangleBatch.Draw();
    
    // 复原原本的设置
    ////通过调用glPolygonMode将多边形正面或者背面设为全部填充模式
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glDisable(GL_POLYGON_OFFSET_LINE);
    glLineWidth(1.0f);
    glDisable(GL_BLEND);
    glDisable(GL_LINE_SMOOTH);
    
    
    modelViewMatrix.PopMatrix();
    glutSwapBuffers();
}

SpecialKeys

特殊键位处理(上、下、左、右移动),这里用来控制图形旋转。

void SpecialKeys(int key, int x, int y)
{
    
    if(key == GLUT_KEY_UP)
        //围绕一个指定的X,Y,Z轴旋转。
        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();
}

setupRC

自定义函数,设置需要渲染的图形的相关顶点数据、颜色数据等等。

void SetupRC()
{
    //设置背景颜色
    glClearColor(0.9, 0.9, 0.9, 1);
    //初始化shaderManager
    shaderManager.InitializeStockShaders();
    //开启深度测试(后面再说是干啥的)
    glEnable(GL_DEPTH_TEST);
    //设置变换管线以使用两个矩阵堆栈
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    //把相机往后移,要不然可能会看不到图,感兴趣的可以改改数值就懂了是啥意思了。
    cameraFrame.MoveForward(-20);
    GLfloat points[] = {
        1,1,0,
        0,1,0,
        1,0,0,
    };
    
    //通过三角形创建金字塔
    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,
    };
    triangleBatch.Begin(GL_TRIANGLES, 12);
    triangleBatch.CopyVertexData3f(vPyramid);
    triangleBatch.End();
    
}

运行效果

image.png

会发现,我们创建的4个三角形确确实实组成了一个金字塔,但是这个金字塔是露底的。那么如果想要把底封起来,就又需要另外两个三角形来组成底部的方形。
如果有想法可以自己搞一下,这里也提供一下另外两个三角形的顶点坐标。
只要改一下这些代码就行啦。

 //通过三角形创建金字塔
    GLfloat vPyramid[18][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,
        
        -2.0f, 0.0f, -2.0f,
        2.0f, 0.0f, -2.0f,
        2.0f, 0.0f, 2.0f,
        
        2.0f, 0.0f, 2.0f,
        -2.0f, 0.0f, -2.0f,
        -2.0f, 0.0f, 2.0f,
    };
    triangleBatch.Begin(GL_TRIANGLES, 18);
    triangleBatch.CopyVertexData3f(vPyramid);
    triangleBatch.End();

再看看运行效果(这么看起来可能有点抽象,发挥想象力,哈哈)


image.png

同理,只要改一改顶点坐标,似乎就可以组成各种奇奇怪怪的形状了,有想法的同志可以发挥想象力。^ _ ^
好咯,这篇文章就先到这咯。

相关文章

网友评论

      本文标题:OpenGL从零开始 -- 画一个金字塔

      本文链接:https://www.haomeiwen.com/subject/dilyaqtx.html