Élimination des visages OpenGL

1. Introduction

OpenGL est capable de vérifier tous les visages faisant face au spectateur et de les rendre, en supprimant ceux qui sont tournés vers l'extérieur, ce qui nous évite de nombreux appels de fragment shader (ils sont chers !). Mais nous devons encore dire à OpenGL quelles faces sont les faces avant et quelles faces sont les faces arrière. OpenGL utilise une astuce astucieuse qui analyse l'ordre d'habillage des données de vertex.

2. Ordre d'ambiance

Lorsque nous définissons un ensemble de sommets de triangle, nous les définissons dans un ordre d'habillage spécifique, dans le sens des aiguilles d'une montre ou dans le sens inverse des aiguilles d'une montre. Chaque triangle est composé de 3 sommets, nous allons définir un ordre d'habillage pour ces 3 sommets à partir du milieu du triangle.

Comme vous pouvez le voir, nous définissons d'abord le sommet 1, puis nous pouvons choisir de définir le sommet 2 ou le sommet 3, ce qui définira l'ordre d'enveloppement du triangle.

float vertices[] = {
    // 顺时针
    vertices[0], // 顶点1
    vertices[1], // 顶点2
    vertices[2], // 顶点3
    // 逆时针
    vertices[0], // 顶点1
    vertices[2], // 顶点3
    vertices[1]  // 顶点2  
};

OpenGL utilisera ces informations lors du rendu de la primitive pour déterminer si un triangle est un triangle orienté vers l'avant ou un triangle orienté vers l'extérieur. Par défaut, les triangles définis par des sommets dans le sens inverse des aiguilles d'une montre seront traités comme des triangles vers l'avant.

Dans les données de sommet, nous définissons deux triangles dans le sens inverse des aiguilles d'une montre. Cependant, du point de vue de l'observateur, ce dernier triangle est dans le sens des aiguilles d'une montre, si nous regardons toujours dans l'ordre de 1, 2, 3 dans le champ de vision face à face de l'observateur. Même si nous avons défini les triangles suivants dans le sens inverse des aiguilles d'une montre, c'est maintenant dans le sens des aiguilles d'une montre. C'est la face invisible que nous comptons abattre (jeter) !

3. L'élimination des visages

Pour activer l'élimination des visages, il suffit d'activer l'option GL_CULL_FACE d'OpenGL :

glEnable(GL_CULL_FACE);

Après cette ligne de code, toutes les faces arrière seront supprimées. Nous avons pu économiser plus de 50 % de performances lors du rendu des fragments, mais notez que cela ne fonctionne que pour les formes fermées comme les cubes.

OpenGL nous permet de changer le type de visage qui doit être éliminé, ce qui peut être réalisé en appelant glCullFace :

glCullFace(GL_FRONT);

La fonction glCullFace a trois options disponibles :

  • GL_BACK: Retirez uniquement les faces arrière.
  • GL_FRONT: Supprimez uniquement les faces positives.
  • GL_FRONT_AND_BACK: Éliminer les faces avant et arrière.

La valeur initiale de glCullFace est GL_BACK. En plus des faces qui doivent être supprimées, nous pouvons également indiquer à OpenGL que nous voulons définir la face dans le sens des aiguilles d'une montre (plutôt que la face dans le sens inverse des aiguilles d'une montre) comme face avant en appelant glFrontFace :

glFrontFace(GL_CCW);

La valeur par défaut est GL_CCW, qui représente un ordre d'emballage dans le sens inverse des aiguilles d'une montre, et l'autre option est GL_CW, qui (évidemment) représente un ordre dans le sens des aiguilles d'une montre.

4. Exemples

Voici le graphique original :

Après avoir activé l'élimination des visages :

    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);//剔除背向面
    glFrontFace(GL_CW);//顺时针的环绕顺序是正向面,逆时针的就会被剔除

 

Modifiez l'ordre des sommets :

 

#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLTexture>
#include <QImage>
#include <QOpenGLShaderProgram>

class MyOpenGLWidget : public QOpenGLWidget,public QOpenGLFunctions_3_3_Core
{
public:
    MyOpenGLWidget(QWidget *parent = nullptr);

protected:
    virtual void initializeGL();
    virtual void paintGL();
    virtual void resizeGL(int w, int h);

private:
    QOpenGLTexture *m_wall;

    QOpenGLTexture *m_face;

    QOpenGLShaderProgram *m_program;
};

#endif // MYOPENGLWIDGET_H




#include "myopenglwidget.h"
#include <QMatrix4x4>
#include <QTime>
#include <QTimer>
#include <math.h>
#include <QVector3D>
#include <QVector>

float vertices[] = {
    //坐标                //纹理坐标
    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
     0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

    0.5f, -0.5f,  0.5f,  1.0f, 0.0f,   //交换这两行顺序
    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,   //交换这两行顺序

     0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
    -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

    -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
    -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
    -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
     0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
     0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
     0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
     0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
     0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
     0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
    -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
};

GLuint indices[] = {
    0, 1, 3,
    1, 2, 3
};

//顶点着色器语言
const GLchar* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"layout (location = 1) in vec2 texCoord;\n"
"out vec2 outTexCoord;\n"
"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"
"void main()\n"
"{\n"
"gl_Position = projection * view * model * vec4(position,1.0);\n"
"outTexCoord = texCoord;\n"
"}\n\0";

//片段着色器语言
//texture函数会使用之前设置的纹理参数对相应的颜色值进行采样
//mix按一定的比例,混合两个纹理颜色
const GLchar* fragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"
"uniform sampler2D ourTexture1;\n"
"uniform sampler2D ourTexture2;\n"
"in vec2 outTexCoord;\n"
"void main()\n"
"{\n"
"color = mix(texture(ourTexture1, outTexCoord),texture(ourTexture2, vec2(outTexCoord.x, outTexCoord.y)),0.45);\n"
"}\n\0";

GLuint VBO, VAO,EBO;
GLuint shaderProgram;

QTimer *timer;

QVector<QVector3D> cubePositions = {
  QVector3D( 0.0f,  0.0f,  0.0f),
  QVector3D( 2.0f,  5.0f, -15.0f),
  QVector3D(-1.5f, -2.2f, -2.5f),
  QVector3D(-3.8f, -2.0f, -12.3f),
  QVector3D( 2.4f, -0.4f, -3.5f),
  QVector3D(-1.7f,  3.0f, -7.5f),
  QVector3D( 1.3f, -2.0f, -2.5f),
  QVector3D( 1.5f,  2.0f, -2.5f),
  QVector3D( 1.5f,  0.2f, -1.5f),
  QVector3D(-1.3f,  1.0f, -1.5f)
};

MyOpenGLWidget::MyOpenGLWidget(QWidget *parent)
    : QOpenGLWidget(parent)
{
    timer = new QTimer();
    timer->start(50);
    connect(timer,&QTimer::timeout,[=]{
        update();
    });
}

void MyOpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();

    m_program = new QOpenGLShaderProgram();
    m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShaderSource);
    m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,fragmentShaderSource);
    m_program->link();

    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);//绑定VAO
    glBindBuffer(GL_ARRAY_BUFFER, VBO);//顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//把顶点数据复制到缓冲的内存中GL_STATIC_DRAW :数据不会或几乎不会改变。

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glGenBuffers(1, &EBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    glBindVertexArray(0);//解绑VAO

    m_wall = new QOpenGLTexture(QImage("./container.jpg").mirrored());
    m_face = new QOpenGLTexture(QImage("./awesomeface.png").mirrored());

    m_program->bind();
    m_program->setUniformValue("ourTexture1",0);
    m_program->setUniformValue("ourTexture2",1);

    //设置透视投影矩阵
    QMatrix4x4 projection;
    projection.perspective(45,(float)(width())/(height()),0.1,100);
    m_program->setUniformValue("projection",projection);

    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);//剔除背向面
    glFrontFace(GL_CW);//顺时针的环绕顺序是正向面,逆时针的就会被剔除

}

void MyOpenGLWidget::paintGL()
{
    glClearColor(0.2f,0.3f,0.3f,1.0f);
    glEnable(GL_DEPTH_TEST);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    QMatrix4x4 model;
    QMatrix4x4 view;

    view.translate(0,0,-4);

    m_program->bind();

    glBindVertexArray(VAO);//绑定VAO

    m_wall->bind(0);
    m_face->bind(1);

    //设置观察矩阵
    m_program->setUniformValue("view",view);

//    foreach(auto pos , cubePositions)
//    {
//        //设置为单位矩阵
//        model.setToIdentity();
//        //平移
//        model.translate(pos);
//        //设置模型矩阵
//        m_program->setUniformValue("model",model);
//        glDrawArrays(GL_TRIANGLES,0,36);
//    }
    model.setToIdentity();
    model.rotate(45,0.3,0.3);
    m_program->setUniformValue("model",model);
    glDrawArrays(GL_TRIANGLES,0,36);
}

void MyOpenGLWidget::resizeGL(int w, int h)
{

}

Je suppose que tu aimes

Origine blog.csdn.net/wzz953200463/article/details/131348231
conseillé
Classement