OpenGL的渲染技巧

384 阅读4分钟

1.绘制甜甜圈,并通过键盘控制旋转

  • 定义变量
//设置角色帧,作为相机
GLFrame             viewFrame;
//使用GLFrustum类来设置透视投影
GLFrustum           viewFrustum;
//三角形批次类
GLTriangleBatch     torusBatch;
//模型视图矩阵
GLMatrixStack       modelViewMatix;
//投影矩阵
GLMatrixStack       projectionMatrix;
//着色器管道
GLGeometryTransform transformPipeline;
//着色器管理器
GLShaderManager     shaderManager;
  • main函数,初始化GLUT,初始化显示模式,创建窗口,设置回调函数,开启runloop
int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[0]);
    
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Geometry Test Program");
    glutReshapeFunc(ChangeSize);
    glutSpecialFunc(SpecialKeys);
    glutDisplayFunc(RenderScene);
    
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }
    
    SetupRC();
    
    glutMainLoop();
    return 0;
}

  • SetupRC 设置背景色,初始化着色器管理器,设置观察者视角,创建甜甜圈
void SetupRC()
{
    //1.设置背景颜色
    glClearColor(0.3f, 0.3f, 0.3f, 1.0f );
    
    //2.初始化着色器管理器
    shaderManager.InitializeStockShaders();
    
    //3.将相机向后移动10个单元:肉眼到物体之间的距离
    viewFrame.MoveForward(10);
    
    
    //4.创建一个甜甜圈
    //void gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
    //参数1:GLTriangleBatch 容器帮助类
    //参数2:外边缘半径
    //参数3:内边缘半径
    //参数4、5:主半径和从半径的细分单元数量
    gltMakeTorus(torusBatch, 1.0f, 0.3f, 52, 26);
    
    //5.点的大小(方便点填充时,肉眼观察)
    glPointSize(4.0f);
}
  • ChangeSize窗口改变大小时调用,设置视口,投影模式,初始化投影矩阵和渲染管线
void ChangeSize(int w, int h)
{
    //1.防止h变为0
    if(h == 0)
        h = 1;
    
    //2.设置视口窗口尺寸
    glViewport(0, 0, w, h);
    

    // 3.设置透视模式,初始化其透视矩阵
    viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f);
    
    //4.把透视矩阵加载到透视矩阵对阵中
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    
    //5.初始化渲染管线
    transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);
}
  • RenderScene渲染场景(清缓存,设置MVP矩阵,渲染颜色,图元组合模式)
void RenderScene()
{
    //1.清除窗口和深度缓冲区
    //不清空颜色/深度缓冲区时.渲染会造成什么问题-->残留数据,颜色混合
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    //2.把摄像机矩阵压入模型矩阵中
    modelViewMatix.PushMatrix(viewFrame);

    //3.设置绘图颜色
    GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    
    //4.
    //使用平面着色器
    //参数1:平面着色器
    //参数2:模型视图投影矩阵
    //参数3:颜色
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vRed);
    
    //5.绘制
    torusBatch.Draw();

    //6.出栈 绘制完成恢复
    modelViewMatix.PopMatrix();
    
    //7.交换缓存区
    glutSwapBuffers();
}
  • SpecialKeys通过上下左右键移动控制观察者角度
void SpecialKeys(int key, int x, int y)
{
    //1.判断方向
    if(key == GLUT_KEY_UP)
        //2.根据方向调整观察者位置 绕Y轴选择-5度
        viewFrame.RotateWorld(m3dDegToRad(-5.0), 1.0f, 0.0f, 0.0f);
    
    if(key == GLUT_KEY_DOWN) //绕Y轴选择5度
        viewFrame.RotateWorld(m3dDegToRad(5.0), 1.0f, 0.0f, 0.0f);
    
    if(key == GLUT_KEY_LEFT) //绕X轴选择-5度
        viewFrame.RotateWorld(m3dDegToRad(-5.0), 0.0f, 1.0f, 0.0f);
    
    if(key == GLUT_KEY_RIGHT)//绕X轴选择5度
        viewFrame.RotateWorld(m3dDegToRad(5.0), 0.0f, 1.0f, 0.0f);
    
    //3.重新刷新
    glutPostRedisplay();
}

渲染过程产⽣的问题

问题原因:

a) 从观察者视角,不可见部分也进行了渲染--->不可见区域应该丢弃,即隐藏面消除(Hidden surface elimination)

b) 从观察者视角,位于同一个平面的像素点,渲染出现问题--->开启深度测试

2. 油画渲染

先绘制场景中的离观察者较远的物体,再绘制较近的物体

弊端:

远近离观察者一致时,无法处理--->深度测试

多余的去绘制 --->正背面剔除

3. 正面&背⾯剔除

何为正面?

默认从观察者角度,逆时针的面为正面,开发者也可以修改和指定正面,但此状态是全局的,一处修改其他渲染位置同时遵循指定的正面规则

正面&背⾯剔除,就是检查所有正面朝向观察者的面,渲染它们.而丢弃背面朝向的面. 同时用户也可以选择剔除哪一个面

//开启表⾯剔除(默认背面剔除)
void glEnable(GL_CULL_FACE);

//关闭表面剔除(默认背面剔除)
void glDisable(GL_CULL_FACE);


//⽤户选择剔除那个面(正面/背面) 
//mode参数为: GL_FRONT,GL_BACK,GL_FRONT_AND_BACK ,默认GL_BACK ⽤户指定绕序那个为正面
void glCullFace(GLenum mode);

//指定正面
//GL_CW:指定顺时针环绕的多边形为正面;
//GL_CCW:指定逆时针环绕的多边形为正面
//mode参数为: GL_CW,GL_CCW,默认值:GL_CCW
void glFrontFace(GLenum mode);

//剔除正面,方法一:
glCullFace(GL_BACK);
glFrontFace(GL_CW); 

//剔除正面,方法二:
glCullFace(GL_FRONT);

4. 深度测试

关于深度测试,详见浅谈图形图像渲染中的深度缓冲区和深度测试

6. 裁剪

只刷新屏幕上发生变化的部分--> 指定一个裁剪框,不在裁剪框的片元被丢弃,只有裁剪区域内的片元才有可能进入帧缓冲。

//1 开启裁剪测试 
glEnable(GL_SCISSOR_TEST);

//2.关闭裁剪测试 
glDisable(GL_SCISSOR_TEST);

//3.指定裁剪窗⼝
void glScissor(Glint x,Glint y,GLSize width,GLSize height);
//x,y:指定裁剪框左下角位置; width , height:指定裁剪尺⼨

7. 颜⾊混合

我们把OpenGL 渲染时会把颜⾊值存在颜⾊缓存区中,每个片段的深度值也是放在深度缓冲区。当深度 缓冲区被关闭时,新的颜色将简单的覆盖原来颜色缓存区存在的颜色值,当深度缓冲区再次打开时,新 的颜⾊⽚段只有当它们比原来的值更更接近邻近的裁剪平面才会替换原来的颜色片段。

颜色混合的详解见: 浅析OpenGL的颜色混合