美文网首页程序员OpenGL
四、OpenGL渲染技巧之圆环面绘制及隐藏面消除

四、OpenGL渲染技巧之圆环面绘制及隐藏面消除

作者: 夏天的枫_ | 来源:发表于2020-07-10 18:52 被阅读0次
绘制游泳圈
固定容器—圆环面->游泳圈

前提准备一个OpenGL的工程

  • 1.在OpenGL绘制立体3D图形——Torus主要用的的函数
    绘制游泳圈Torus用到的函数是
/* 
* par1: GLTriangleBatch 容器类,虽说是游泳圈,其最小单位还是有三角形
* par2: 外边缘半径
* par3: 内边缘半径
* par4&5: 主半径和从半径的细分单元
**/
void gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);

绘制的着色器是光源着色器,利用透视投影

/* 
* par1: GLT_SHADER_DEFAULT_LIGHT 默认光源着色器
* par2: 模型视图矩阵
* par3: 投影矩阵
* par4: 基本颜色值
**/
shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRGBA);
  • 2.绘制流程
    OpenGL图元绘制


    OpenGL图元 点、线、面等绘制流程
  • 3.主要函数

//
//  main.cpp
//  OpenGL图元
//  Created by TL on 2020/7/10.
//  Copyright © 2020 tl. All rights reserved.
//

#include "GLTools.h"

#include "GLMatrixStack.h" // 矩阵堆栈
#include "GLFrame.h" // 矩阵变换
#include "GLFrustum.h" // 投影矩阵
#include "GLBatch.h" // 7中不同的图元容器对象
#include "GLGeometryTransform.h" // 几何变换的管道

#include <math.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

//设置角色帧,作为相机
GLFrame             viewFrame;
//使用GLFrustum类来设置透视投影
GLFrustum           viewFrustum;
// 三角形容器类
GLTriangleBatch     torusBatch;
// 模型矩阵堆栈
GLMatrixStack       modelViewMatix;
GLMatrixStack       projectionMatrix;
// 几何变换的管道
GLGeometryTransform transformPipeline;

GLShaderManager     shaderManager;

/// 初始化场景
void SetupRC()
{
    // 1.设置背景色
    glClearColor(0.59f, 0.39f, 0.78f, 1.0f);
    
    // 2.初始化着色器管理器
    shaderManager.InitializeStockShaders();
    
    // 3.将相机移动10个单元:肉眼到物体之间的距离
    viewFrame.MoveForward(10.0f);
    
    // 4.创建游泳圈
    /* void gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
     * par1: GLTriangleBatch 容器帮助类,虽说是游泳圈,其最小单位还是有三角形组成
     * par2: 外边缘半径
     * par3: 内边缘半径
     * par4&5: 主半径和从半径的细分单元数量
     **/
    gltMakeTorus(torusBatch, 1.5f, 0.5, 60, 30);
    
    // 5.点的大小 方便肉眼观察
    glPointSize(4.0f);
    
}

/// 改变窗口大小
/// @param w 宽带
/// @param h 高度
void ChangeSize(int w,int h)
{
    // 1.判断h是否为0
    if (h == 0) {
        h = 1;
    }
    // 2.设置窗口尺寸
    glViewport(0, 0, w, h);
    
    /* 3.设置透视模式,初始化透视矩阵
     * SetPerspective 的参数是y
     * 参数1:垂直方向上的观察者视角度数
     * 参数2:纵横比 w/h
     * 参数3:近裁剪⾯距离 (视角到近裁剪面距离为fNear)
     * 参数4:远裁剪面距离(视角到远裁剪面距离为fFar)
     */
    viewFrustum.SetPerspective(45.0f, float(w)/float(h), 2.0f, 100.0f);
    
    // 4.把透视矩阵加载到透视矩阵对阵中
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    // 5.初始化渲染管线
    transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);
}
/// 方向控制 通过改变Camera 移动,改变视口
/// @param key key description
/// @param x x description
/// @param y y description
void directionOnclick(int key, int x, int y)
{
    // 1.判断方向
    switch (key) {
       case GLUT_KEY_UP:
           // 2.根据方向调整观察者位置
           viewFrame.RotateWorld(m3dDegToRad(-10.0f), 2, 0.0f, 0.0f);
           break;
       case GLUT_KEY_RIGHT:
           viewFrame.RotateWorld(m3dDegToRad(10.0f), 2, 0.0f, 0.0f);
           break;
       case GLUT_KEY_DOWN:
           viewFrame.RotateWorld(m3dDegToRad(-10.0f), 0, 2, 0.0f);
           break;
       case GLUT_KEY_LEFT:
           viewFrame.RotateWorld(m3dDegToRad(10.0f), 0, 2, 0.0f);
           break;
    }

    // 3.重新刷新
    glutPostRedisplay();
    
}

/// 渲染场景
void RenderScene()
{
    // 1.清除窗口、深度缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    
    // 2.把摄像机矩阵压入模型矩阵中->压栈
    modelViewMatix.PushMatrix(viewFrame);
    
    // 3.设置绘图颜色
    GLfloat vRGBA[] = {0.8f,0.73f,0.0f,1.0f};
    
    // 使用平面着色器
//    shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetProjectionMatrix(),vRGBA);
    
    // 使用光源着色器
    shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRGBA);
    // 5.绘制在屏幕上
    torusBatch.Draw();
    
    // 6.出栈 绘制后 恢复
    modelViewMatix.PopMatrix();
    
    // 7.交换缓存区
    glutSwapBuffers();
}

int main(int argc ,char* argv[])
{
    gltSetWorkingDirectory(argv[0]);
    // 初始化
    glutInit(&argc, argv);
    // 申请一个颜色缓存区、深度缓存区、双缓存区、模板缓存区
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    
    // 设置window的尺寸
    glutInitWindowSize(500, 500);
    
    // 创建window的名称
    glutCreateWindow("点点");
    
    // 注册回调函数(改变尺寸)
    glutReshapeFunc(ChangeSize);
    
    
    // 设置特殊键位函数 : 上下左右
    glutSpecialFunc(directionOnclick);
    
    // 显示函数
    glutDisplayFunc(RenderScene);
    
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Init Error: %s\n", glewGetErrorString(err));
        return 1;
    }
    
    // 绘制
    SetupRC();
    
    // runloop 运行循环
    glutMainLoop();
    
    return 0;
}
  • 4.结果


    固定容器—圆环面->游泳圈
  • 5.bug?
    使用方向键查看这个游泳圈,出现了黑色的一坨,立体3D秒出bug。


    bug?

    这就是一个bug,这个bug的原因是:在绘制3D图形时,我们使用光源着色器将其所有的图元组成部分都绘制在屏幕上,在我们旋转图形过程中,我们就看到了其隐藏面,隐藏面的出现是它无法知道该展示正面or反面,所以就出现了这游泳圈出现了一坨一坨的黑色。

  • 6.隐藏面消除(正背面剔除)
    将在旋转过程中出现的隐藏面不可见部分就称为隐藏面消除or正背面剔除;

正面:按照逆时针顶点顺序连接的三角形面
反面:按照顺时针顶点顺序连接的三角形面
(三角形面为OpenGL中图元组成的一个单位,3D图形也是可以有N多三角形绘制组成的,三角形则可由最小单位点连线组成)
其中正背面是可以通过void glFrontFace(GLenum mode),GL_CW: 顺时针方向连接三角形点为正面;默认GL_CCW:逆时针方向连接三角形点为正面
正⾯和背⾯是有三⻆形的顶点定义顺序和观察者⽅向共同决定的.随着观察者的⻆度⽅向的改变,正⾯背⾯会跟着改变

让OpenGL始终将正面朝向观察者,没移动一个角度就渲染它,并将其背面的部分丢弃,这样既达到了隐藏面消除,也节约了片元着色器的使用率间接提升了性能。
步骤:
1.告诉OpenGL顶点连接顺序,则OpenGL即可知道
2.绘制正面,丢弃反面,每移动观察视角就重新渲染
隐藏面消除核心开关代码

// 1.开启正背⾯剔除 
glEnable(GL_CULL_FACE); 
// 2.关闭正背⾯剔除 
glDisable(GL_CULL_FACE); 
// 3.指定剔除⾯:mode 参数的可⽤值为 GL_FRONT、GL_BACK 或 GL_FRONT_AND_BACK 
void glCullFace(GLenum mode);

RenderScene()中加入

glEnable(GL_CULL_FACE);      
glCullFace(GL_BACK);

bug消除


bingo

然而出现了新的一个问题 😨

我是新bug
这将引入深度测试相关知识,这不再是新bug

相关文章

  • 四、OpenGL渲染技巧之圆环面绘制及隐藏面消除

    绘制游泳圈 前提准备一个OpenGL的工程 1.在OpenGL绘制立体3D图形——Torus主要用的的函数绘制游泳...

  • OpenGL渲染技巧(隐藏面消除)

    首先先看一个图片 在绘制3D场景的时候,我们需要决定哪些部分是对观察者可⻅的,或者哪些部分是对观察者不可⻅的.对于...

  • OpenGL 隐藏面消除及混合

    在绘制3D场景的时候,我们需要决定哪些部分是对观察者可见,或者哪些部分是对观察者不可见的。对于不可见的部分,就不应...

  • OpenGL渲染技巧

    OpenGL渲染技巧 了解了OpenGL的渲染流程和常用API后,就可以简单的绘制出图形了。但是在绘制中可能会碰到...

  • Opengl ES之四边形绘制

    四边形的绘制在Opengl ES是很重要的一项技巧,比如做视频播放器时视频的渲染就需要使用到Opengl ES绘制...

  • OpenGL正面&背面剔除与深度测试

    1.隐藏面消失 在绘制图形的时候,图形的背面是观察者不可见的,那么这部分就不应该被渲染出来,这种情况叫“隐藏面消除...

  • OpenGL 阐述隐藏面消除解决方案

    OpenGL 阐述隐藏面消除解决方案 在OpenGL中如果没有设置隐藏背面,就会出现下面这种情况,正面红色和背面黑...

  • 03、OpenGL隐藏面消除

    在渲染过程中可能产生的问题 在绘制3D场景的时候,我们需要决定哪些部分是对观察者可见的,或者哪些部分是对观察者不可...

  • OpenGL隐藏面消除详解

    什么是隐藏面消除? 隐藏面消除:在绘制3D场景的时候,我们需要决定哪些部分是对观察者可见的,或者哪些部分是对观察者...

  • 8、OpenGL初探之OpenGL图像渲染对隐藏面消除的理解

    直接开始正题,还是以我们的甜甜圈为例子,先看一下隐藏面剔除前后的效果,然后再解释一下隐藏面剔除的应用场景和好处 一...

网友评论

    本文标题:四、OpenGL渲染技巧之圆环面绘制及隐藏面消除

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