在立方体内绘制金字塔

时间:2018-09-18 05:30:43

标签: c++ qt opengl qt5

我正在尝试绘制一个“透明”立方体(仅具有边缘),并将一个金字塔放置在此立方体中,问题是当我旋转它时,立方体的边缘在金字塔上绘制。

这里是一个示例

enter image description here

我了解为什么会发生这种情况(因为我先绘制了金字塔,然后再绘制了一个立方体),但是我希望在不需要时不要在金字塔上绘制这些边缘。

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QOpenGLWindow>
#include <QSurfaceFormat>
#include <QOpenGLContext>
#include <QTimer>

#ifdef _DEBUG
#   include <QDebug>
#endif

class MainWindow: public QOpenGLWindow
{
    Q_OBJECT

public:
    MainWindow();

private:
    QOpenGLContext *m_context;
    QTimer m_timer;
    GLfloat m_angle;

private slots:
    void rotate();

protected:
    void initializeGL() override;
    void paintGL() override;

    void paintEvent(QPaintEvent *event) override;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"

MainWindow::MainWindow():
    QOpenGLWindow(),
    m_angle(0.0f)
{
    QSurfaceFormat sf;

    sf.setProfile(QSurfaceFormat::CompatibilityProfile);
    sf.setVersion(2, 1);

    setFormat(sf);
    setSurfaceType(QWindow::OpenGLSurface);
    create();

    m_context = new QOpenGLContext();
    m_context->setFormat(sf);
    m_context->create();
    m_context->makeCurrent(this);

    connect(&m_timer, SIGNAL(timeout()), this, SLOT(rotate()));

    m_timer.start(33);
}

void MainWindow::rotate()
{
    m_angle += 0.5f;

    if (m_angle >= 360.0f) {
        m_angle = 0.0f;
    }

    update();
}

void MainWindow::initializeGL()
{
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
}

void MainWindow::paintGL()
{
    glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glLoadIdentity();
    glRotatef(m_angle, 1.0f, 1.0f, 1.0f);
    glScalef(0.3f, 0.3f, 0.3f);

    glBegin(GL_TRIANGLES);
        glColor3f(1.0f, 0.0f, 0.0f);

        glVertex3f( 0.0f,  1.0f,  0.0f);
        glVertex3f( 1.0f, -1.0f, -1.0f);
        glVertex3f(-1.0f, -1.0f, -1.0f);

        glColor3f(0.0f, 1.0f, 0.0f);

        glVertex3f(-1.0f, -1.0f, 1.0f);
        glVertex3f( 1.0f, -1.0f, 1.0f);
        glVertex3f( 0.0f,  1.0f,  0.0f);

        glColor3f(0.0f, 0.0f, 1.0f);

        glVertex3f(-1.0f, -1.0f,  1.0f);
        glVertex3f( 0.0f,  1.0f,  0.0f);
        glVertex3f(-1.0f, -1.0f, -1.0f);

        glColor3f(1.0f, 1.0f, 0.0f);

        glVertex3f(1.0f, -1.0f,  1.0f);
        glVertex3f(1.0f, -1.0f, -1.0f);
        glVertex3f(0.0f,  1.0f,  0.0f);

        // bottom
        glColor3f(0.0f, 1.0f, 1.0f);

        glVertex3f(-1.0f, -1.0f, -1.0f);
        glVertex3f( 1.0f, -1.0f, -1.0f);
        glVertex3f(-1.0f, -1.0f,  1.0f);

        glVertex3f(-1.0f, -1.0f,  1.0f);
        glVertex3f( 1.0f, -1.0f, -1.0f);
        glVertex3f( 1.0f, -1.0f,  1.0f);
    glEnd();

    // The cube (only edges)

    glScalef(1.5f, 1.5f, 1.5f);
    glColor3f(0.0f, 0.0f, 0.0f);

    glBegin(GL_LINE_LOOP);
        glVertex3f( 1.0f,  1.0f, -1.0f);
        glVertex3f(-1.0f,  1.0f, -1.0f);
        glVertex3f(-1.0f, -1.0f, -1.0f);
        glVertex3f( 1.0f, -1.0f, -1.0f);
    glEnd();

    glBegin(GL_LINE_LOOP);
        glVertex3f( 1.0f,  1.0f, 1.0f);
        glVertex3f(-1.0f,  1.0f, 1.0f);
        glVertex3f(-1.0f, -1.0f, 1.0f);
        glVertex3f( 1.0f, -1.0f, 1.0f);
    glEnd();

    glBegin(GL_LINES);
        glVertex3f(-1.0f, 1.0f, -1.0f);
        glVertex3f(-1.0f, 1.0f, 1.0f);

        glVertex3f(-1.0f, -1.0f, -1.0f);
        glVertex3f(-1.0f, -1.0f, 1.0f);

        glVertex3f(1.0f, 1.0f, -1.0f);
        glVertex3f(1.0f, 1.0f, 1.0f);

        glVertex3f(1.0f, -1.0f, -1.0f);
        glVertex3f(1.0f, -1.0f, 1.0f);
    glEnd();

    glFlush();
}

void MainWindow::paintEvent(QPaintEvent*)
{
    paintGL();
}

main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;

    w.resize(480, 480);
    w.show();

    return a.exec();
}

3 个答案:

答案 0 :(得分:0)

  

...反之亦然,请看一下此屏幕快照。看起来前边缘的行为类似于后边缘。

金字塔的三角形是顺时针设置的,但是默认情况下,逆时针的多边形是朝前的。设置glFrontFace(GL_CW)或更改glVertex3f的顺序。

  

有人可以解释一下为什么三角形渲染四边形吗?

激活Depth Testing时,将根据片段的深度显示片段。将片段中的深度值与当前在帧缓冲区中的匹配样本中的深度值进行比较。条件测试由函数glDepthFunc指定。 glDepthFunc的初始值为GL_LESS

在您的示例中,三角形碎片的深度比四边形碎片的深度小,因此三角形位于四边形之上。

enter image description here

答案 1 :(得分:0)

设置最小深度缓冲区对我来说没有任何改变(可能是因为平台设置已经是24,但是检查这些默认值并非没有价值)。问题在于,立方体边缘的显示顺序仍然看起来不正确:应该更靠近的边缘由金字塔绘制。

enter image description here

因为您对边缘的可见性不做任何事情,所以会导致幻觉:多维数据集必须沿与金字塔的旋转方向相反且垂直的方向旋转。

问题在于金字塔实际上是错误的。让我证明一下:

glBegin(GL_QUADS);//(GL_LINE_LOOP);
    glVertex3f( 1.0f,  1.0f, -1.0f);
    glVertex3f(-1.0f,  1.0f, -1.0f);
    glVertex3f(-1.0f, -1.0f, -1.0f);
    glVertex3f( 1.0f, -1.0f, -1.0f);
glEnd(); 

结果图像为:

enter image description here

多维数据集沿假定方向旋转,它不是由内而外的。那意味着金字塔是。原因之一是我们实际上看到的是背面面孔,而不是正面。

如果我将您的代码转换为模拟透明金字塔,但保存默认的剔除设置:

void MainWindow::initializeGL()
{
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

/*...*/

    glFrontFace(GL_CCW);
    glBegin(GL_TRIANGLES);
        glColor4f(1.0f, 0.0f, 0.0f, 0.3f);

        glVertex3f( 0.0f,  1.0f,  0.0f);
        glVertex3f( 1.0f, -1.0f, -1.0f);
        glVertex3f(-1.0f, -1.0f, -1.0f);

        glColor4f(0.0f, 1.0f, 0.0f, 0.3f);

        glVertex3f(-1.0f, -1.0f, 1.0f);
        glVertex3f( 1.0f, -1.0f, 1.0f);
        glVertex3f( 0.0f,  1.0f,  0.0f);

        glColor4f(0.0f, 0.0f, 1.0f, 0.3f);

        glVertex3f(-1.0f, -1.0f,  1.0f);
        glVertex3f( 0.0f,  1.0f,  0.0f);
        glVertex3f(-1.0f, -1.0f, -1.0f);

        glColor4f(1.0f, 1.0f, 0.0f, 0.3f);

        glVertex3f(1.0f, -1.0f,  1.0f);
        glVertex3f(1.0f, -1.0f, -1.0f);
        glVertex3f(0.0f,  1.0f,  0.0f);

        // bottom
        glColor4f(0.0f, 1.0f, 1.0f, 0.3f);

        glVertex3f(-1.0f, -1.0f, -1.0f);
        glVertex3f( 1.0f, -1.0f, -1.0f);
        glVertex3f(-1.0f, -1.0f,  1.0f);

        glVertex3f(-1.0f, -1.0f,  1.0f);
        glVertex3f( 1.0f, -1.0f, -1.0f);
        glVertex3f( 1.0f, -1.0f,  1.0f);
    glEnd();
    glFrontFace(GL_CW);
    glBegin(GL_TRIANGLES);
        glColor4f(1.0f, 0.0f, 0.0f, 0.6f);

        glVertex3f( 0.0f,  1.0f,  0.0f);
        glVertex3f( 1.0f, -1.0f, -1.0f);
        glVertex3f(-1.0f, -1.0f, -1.0f);

        glColor4f(0.0f, 1.0f, 0.0f, 0.6f);

        glVertex3f(-1.0f, -1.0f, 1.0f);
        glVertex3f( 1.0f, -1.0f, 1.0f);
        glVertex3f( 0.0f,  1.0f,  0.0f);

        glColor4f(0.0f, 0.0f, 1.0f, 0.6f);

        glVertex3f(-1.0f, -1.0f,  1.0f);
        glVertex3f( 0.0f,  1.0f,  0.0f);
        glVertex3f(-1.0f, -1.0f, -1.0f);

        glColor4f(1.0f, 1.0f, 0.0f, 0.6f);

        glVertex3f(1.0f, -1.0f,  1.0f);
        glVertex3f(1.0f, -1.0f, -1.0f);
        glVertex3f(0.0f,  1.0f,  0.0f);

        // bottom
        glColor4f(0.0f, 1.0f, 1.0f, 0.6f);

        glVertex3f(-1.0f, -1.0f, -1.0f);
        glVertex3f( 1.0f, -1.0f, -1.0f);
        glVertex3f(-1.0f, -1.0f,  1.0f);

        glVertex3f(-1.0f, -1.0f,  1.0f);
        glVertex3f( 1.0f, -1.0f, -1.0f);
        glVertex3f( 1.0f, -1.0f,  1.0f);
    glEnd();

我得到图片

enter image description here

我仍然看到金字塔如何旋转,其所有边缘以及“后面”的那条立方体线已被剪切(因为混合对深度测试没有任何作用)。

但是现在,如果您查看金字塔的“粗体”面孔-我有意制作了不可见的面孔,而没有通过给它们提供较大的alpha值来融合粗体-您会发现它们以与立方体相同的方向正确旋转。这意味着您的网格(金字塔)实际上是“由内而外”倒置的。使用非凸形图形会更明显,而使用凸形图形会产生相反旋转的错觉。

所有您需要做的是更改顶点的顺序或切换到金字塔的GL_CW(默认为GL_CCW)。 enter image description here

如果考虑到脸部颜色,我想这是金字塔的预期外观。如果不是,则意味着您假设Z坐标较大的顶点距离投影平面更远。与身份投影矩阵和GL_LESS深度测试功能相反。请注意,如果仅切换一个轴的方向,则实际上会使所有与旋转方向有关的规则反转,逆时针变为顺时针。

注意:我用于透明度错觉的技巧不适用于非凸面图形。为什么?要使用颜色混合,您必须按照其深度测试的顺序绘制面,以手动进行。我的肮脏技巧确实会绘制所有面孔两次,但剔除会删除第一次通过时面对相机的脸部,并删除第二次通过时面对相机的脸部。对于不符合顺序的非凸面-有些脸部朝向相机,但比某些“隐藏”的脸部顺序错误。需要做更多的工作。灵活管道的使用确实改变了OpenGL处理​​它的方式。

答案 2 :(得分:0)

我终于做到了:

#include "mainwindow.h"

MainWindow::MainWindow():
    QOpenGLWindow(),
    m_angle(0.0f)
{
    QSurfaceFormat sf;

    sf.setProfile(QSurfaceFormat::CompatibilityProfile);
    sf.setDepthBufferSize(24);
    sf.setVersion(2, 1);

    setSurfaceType(QWindow::OpenGLSurface);
    setFormat(sf);
    create();

    m_context = new QOpenGLContext(this);
    m_context->setFormat(sf);
    m_context->create();
    m_context->makeCurrent(this);

    connect(&m_timer, SIGNAL(timeout()), this, SLOT(rotate()));

    m_timer.start(33);
}

void MainWindow::rotate()
{
    m_angle += 0.5f;

    if (m_angle >= 360.0f) {
        m_angle = 0.0f;
    }

    update();
}

void MainWindow::initializeGL()
{
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClearDepth(-1.0f);

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_GEQUAL);
}

void MainWindow::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // The Pyramid

    glLoadIdentity();
    glRotatef(m_angle, 1.0f, 1.0f, 1.0f);
    glScalef(0.33f, 0.33f, 0.33f);

    glEnable(GL_CULL_FACE);
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    glBegin(GL_TRIANGLES);
       // Front
       glColor3f(1.0f, 0.0f, 0.0f);
       glVertex3f( 0.0f,  1.0f, 0.0f);
       glVertex3f(-1.0f, -1.0f, 1.0f);
       glVertex3f( 1.0f, -1.0f, 1.0f);

       // Back
       glColor3f(0.0f, 1.0f, 0.0f);
       glVertex3f( 0.0f,  1.0f,  0.0f);
       glVertex3f( 1.0f, -1.0f, -1.0f);
       glVertex3f(-1.0f, -1.0f, -1.0f);

       // Left
       glColor3f(1.0f, 1.0f, 0.0f);
       glVertex3f( 0.0f,  1.0f,  0.0f);
       glVertex3f(-1.0f, -1.0f, -1.0f);
       glVertex3f(-1.0f, -1.0f,  1.0f);

       // Right
       glColor3f(1.0f, 0.0f, 1.0f);
       glVertex3f(0.0f,  1.0f,  0.0f);
       glVertex3f(1.0f, -1.0f,  1.0f);
       glVertex3f(1.0f, -1.0f, -1.0f);

       // Bottom
       glColor3f(0.0f, 1.0f, 1.0f);

       glVertex3f(-1.0f, -1.0f, -1.0f);
       glVertex3f( 1.0f, -1.0f, -1.0f);
       glVertex3f(-1.0f, -1.0f,  1.0f);

       glVertex3f(-1.0f, -1.0f,  1.0f);
       glVertex3f( 1.0f, -1.0f, -1.0f);
       glVertex3f( 1.0f, -1.0f,  1.0f);
    glEnd();

    // The Cube (edges)

    glLoadIdentity();
    glRotatef(m_angle, 1.0f, 1.0f, 1.0f);
    glScalef(0.5f, 0.5f, 0.5f);

    glDisable(GL_CULL_FACE);
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    glColor3f(0.0f, 0.0f, 0.0f);

    glBegin(GL_QUADS);
        // Front
        glVertex3f( 1.0f,  1.0f, -1.0f);
        glVertex3f(-1.0f,  1.0f, -1.0f);
        glVertex3f(-1.0f, -1.0f, -1.0f);
        glVertex3f( 1.0f, -1.0f, -1.0f);

        // Back
        glVertex3f( 1.0f,  1.0f, 1.0f);
        glVertex3f( 1.0f, -1.0f, 1.0f);
        glVertex3f(-1.0f, -1.0f, 1.0f);
        glVertex3f(-1.0f,  1.0f, 1.0f);

        // Left
        glVertex3f(-1.0f,  1.0f,  1.0f);
        glVertex3f(-1.0f, -1.0f,  1.0f);
        glVertex3f(-1.0f, -1.0f, -1.0f);
        glVertex3f(-1.0f,  1.0f, -1.0f);

        // Right
        glVertex3f(1.0f,  1.0f,  1.0f);
        glVertex3f(1.0f,  1.0f, -1.0f);
        glVertex3f(1.0f, -1.0f, -1.0f);
        glVertex3f(1.0f, -1.0f,  1.0f);
    glEnd();

    glFlush();
}

void MainWindow::paintEvent(QPaintEvent*)
{
    paintGL();
}

此解决方案可能远非理想,但它确实满足了我的需要(任何提示都会感激)。

演示图片

我要感谢@Swift-Friday Pie和@Asaq提供了如此详细的答案,这有助于我更多地了解opengl的工作原理。

如果有人感兴趣-我在Linux(Fedora 28)上,图形卡是Intel HD Graphics 530(Skylake GT2)。