[demo5].不画甜甜圈的吃货不是好的程序猿

跳去目录


复习下基本流程

初始化glut模块
加载配置模块
设置窗口大小
给窗口取个名字
注册一个方法监测窗口改变
注册一个方法监测键盘输入
注册一个方法监测屏幕刷新
初始化glew模块并检测是否初始化成功
设置默认值(环境)
加载并启动MainLoop

实际上并不需要完全按照上面的步骤来做,像注册监测的那三个方法可以自由变换顺序。

具体代码:

    //着色器管理类
    GLShaderManager shaderManager;
    //甜甜圈容器
    GLTriangleBatch donutBatch;
    //窗口改变
    void ChangeSize(int w, int h) { }

    //监测键盘输入信号
    void SpecialKeys(int key, int x, int y) { }

    //屏幕刷新
    void RenderScene() { }

    //初始化相关工作
    void SetupRC() { }

    int main( int argc, char *argv[]) {
        
        gltSetWorkingDirectory(argv[0]);
        //初始化glut模块
        glutInit(&argc, argv);
        //加载配置模块
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
        //设置窗口大小
        glutInitWindowSize(400, 400);
        //给窗口取个名字
        glutCreateWindow("There is a donut here");
        //注册一个方法监测窗口改变
        glutReshapeFunc(ChangeSize);
        //注册一个方法监测键盘输入
        glutSpecialFunc(SpecialKeys);
        注册一个方法监测屏幕刷新
        glutDisplayFunc(RenderScene);
        
        //初始化glew模块并检测是否初始化成功
        GLenum err = glewInit();
        if (GLEW_OK != err) {
            fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
            return 1;
        }
        //设置默认值(环境)
        SetupRC();
        //加载并启动MainLoop
        glutMainLoop();
        
        return 0;
    }


绘制甜甜圈的准备工作

1.因为这次是要画一个可以展览的甜甜圈(不然就没有灵魂了),所以我们需要有一个观察者,也就是摄影师(观察者坐标系),且观察者自身也有一个属性(物体坐标系)

GLMatrixStack cameraMatix;
GLFrame cameraFrame;

2.而甜甜圈自身也是有大小的,所以我们需要描绘甜甜圈的属性(物体坐标系)

GLMatrixStack donutMatrix;

3.在将甜甜圈转化为世界坐标系的时候,需要设置甜甜圈的投影规则(称之为管线)

GLGeometryTransform donutTransform;

4.这次需要绘制的是立体的甜甜圈,所以为了有立体感,需要设置投影方式为透视投影,为此,我们需要设置一个全局变量用于记录当前透视状态

GLFrustum viewFrustum;

初始化工作

SetupRC中需要设置背景颜色,初始化全局变量

    glClearColor(240/255.0, 248/255.0, 255/255.0, 1.0f);
    
    shaderManager.InitializeStockShaders();

    
    //将相机向后移动7个单元:肉眼到物体之间的距离
    cameraFrame.MoveForward(7.0);
    
    //创建一个甜甜圈
    //void gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
    //参数1:GLTriangleBatch 容器帮助类
    //参数2:外边缘半径
    //参数3:内边缘半径
    //参数4、5:主半径和从半径的细分单元数量
    gltMakeTorus(donutBatch, 1.0f, 0.3f, 52, 26);

窗口改变时

//1.防止h变为0
    if(h == 0)
        h = 1;
    
    //2.设置视口窗口尺寸
    glViewport(0, 0, w, h);
    
    //3.setPerspective函数的参数是一个从顶点方向看去的视场角度(用角度值表示)
    // 设置透视模式,初始化其透视矩阵
    viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f);
    
    //4.把透视矩阵加载到透视矩阵对阵中
    donutMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    
    //5.初始化渲染管线
    donutTransform.SetMatrixStacks(cameraMatix, donutMatrix);

屏幕刷新时

//1.清除窗口和深度缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     
    //2.把摄像机矩阵压入模型矩阵中
    cameraMatix.PushMatrix(cameraFrame);
     
    //3.设置绘图颜色
    GLfloat colorDonut[] = { 255/255.0, 215/255.0, 0/255.0, 1.0f };
     
     //4.
     //使用平面着色器
     //参数1:平面着色器
     //参数2:模型视图投影矩阵
     //参数3:颜色
    // shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vRed);
     
     //使用默认光源着色器
     //通过光源、阴影效果跟提现立体效果
     //参数1:GLT_SHADER_DEFAULT_LIGHT 默认光源着色器
     //参数2:模型视图矩阵
     //参数3:投影矩阵
     //参数4:基本颜色值
     shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, donutTransform.GetModelViewMatrix(), donutTransform.GetProjectionMatrix(), colorDonut);
     
     //5.绘制
     donutBatch.Draw();

     //6.出栈 绘制完成恢复
     cameraMatix.PopMatrix();
     
     //7.交换缓存区
     glutSwapBuffers();
不会动的甜甜圈

Run一下,发现甜甜圈出现了,开心~


但是,这是一个不会动的甜甜圈!!!
为了让他动起来,我们必须进行操作
SpecialKey

//监测键盘输入信号
void SpecialKeys(int key, int x, int y) {
    
    //w 设置为后空翻吧,就是围绕x轴向后旋转
    if(key == 119)
        cameraFrame.RotateWorld(m3dDegToRad(-5.0), 1.0f, 0.0f, 0.0f);
    //s 设置为前空翻吧,就是围绕x轴向前旋转
    if(key == 115)
        cameraFrame.RotateWorld(m3dDegToRad(5.0), 1.0f, 0.0f, 0.0f);
    //a 设置为直立顺时针转圈,就是围绕y轴顺时针旋转
    if(key == 97)
        cameraFrame.RotateWorld(m3dDegToRad(-5.0), 0.0f, 1.0f, 0.0f);
    //d 设置为直立顺时针转圈,就是围绕y轴逆时针旋转
    if(key == 100)
        cameraFrame.RotateWorld(m3dDegToRad(5.0), 0.0f, 1.0f, 0.0f);
    //q 设置为正向中心顺时针转圈,就是围绕z轴逆时针旋转
    if(key == 113)
        cameraFrame.RotateWorld(m3dDegToRad(-5.0), 0.0f, 0.0f, 1.0f);
    //e 设置为正向中心逆时针转圈,就是围绕z轴逆时针旋转
    if(key == 101)
        cameraFrame.RotateWorld(m3dDegToRad(5.0), 0.0f, 0.0f, 1.0f);
    
    //3.重新刷新
    glutPostRedisplay();
}

再Run一下,棒~想要的效果出来了!


又又又又又但是!在旋转的时候,出现了很多黑色的东西,色香味色先挂了,哪里还有食欲?想要解决它,首先得了解它是什么。

背面未剔除

在现实世界中,一个甜甜圈还没开始吃的时候,应该只能看到它脆脆的皮,而不可能看到它松松的肉,但是在我们绘制的甜甜圈中,我们看到了它松松的肉,这是不正常的。
在OpenGL的世界中,所描绘的每一个物体都是有它的正面和背面的,正面不会被遮挡,而背面会被遮挡住,用这样的方式来模拟现实世界中不透明的物体的成像。
因此现在的这种情况,我们只需要打开OpenGL自身的背面剔除功能就好了,所以在RenderScene绘制的时候我们需要设置背面剔除.

glEnable(GL_CULL_FACE);

再run一次,发现不再出现黑色了,


但是又来了,这次,黑色没有了,可旋转到某一个方向的时候,出现了透视的现象,这是为什么呢?

image.png

在这次的甜甜圈的世界里,因为采用的是油画的画法,即先画底下涂层的,再画上面涂层,因此,图形的正面与背面是已经设定好了的,所以在旋转的时候,由于正面位于背面的涂层下面,但是正面是不会被背面所遮挡的,所以便出现了透视的效果
该怎么做呢?先看一下下一篇OpenGL的深度测试把,深度测试可以帮我们定位很多问题!


跳去目录

你可能感兴趣的:([demo5].不画甜甜圈的吃货不是好的程序猿)