Затенение швов в соответствии с индексом наложения в OpenGL - PullRequest
0 голосов
/ 09 мая 2019

У меня проблемы с тестовой программой QtWidgets для визуализации соединений в старых данных сетки FVF, выделяя выбранные пользователем отдельные соединения.Я сократил программу и включил все части ниже.

Программа загружает сетку, состоящую из данных массива вершин и элементов, отображает ее в виде белого каркаса, но окрашивает все вершины указанного индекса смешивания в красный цвет.Индексы смешивания присутствуют в данных вершин.Они собраны и представлены в виде списка.Щелчок по индексу переадресует этот индекс в фрагментный шейдер, который переопределяет цвет по умолчанию, белый, с красным, для вершин с соответствующим индексом наложения.

Вот пример программы, окрашивающей вершины с индексом наложения0:

enter image description here

Цветная часть имеет смысл как корень скелета.

Проблема в том, что это работает только тогда, когдавыбранный индекс смешивания равен 0. Выбор любого другого индекса приводит к тому, что каркас отображается белым цветом.Ожидаемое поведение заключается в том, что другие части сетки будут окрашены в красный цвет.Данные вершин подтверждаются правильными, так как они работают в исходном приложении и, конечно, не являются источником ошибки.

Я не уверен, как исследовать эту проблему.У меня почти нет опыта в отладке шейдеров - если предположить, что они являются источником ошибки.Одна идея, которая меня интересует, это выравнивание данных.Вот расположение данных FVF в перечислении:

openglwidget.h :

#ifndef OPENGLWIDGET_H
#define OPENGLWIDGET_H

#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLWidget>
#include <QVector3D>

enum VertexLayout {
    XyzOffset = 0,
    NormalOffset = 12,
    TexcoordOffset = 24,
    TangentOffset = 32,
    BinormalOffset = 44,
    BlendIndexOffset0 = 56,
    BlendWeightOffset0 = 57,
    BlendIndexOffset1 = 61,
    BlendIndexWeight1 = 62,
    BlendIndexOffset2 = 66,
    BlendWeightOffset2 = 67,
    Size = 71 // Size in bytes of one raw vertex
};

class OpenGLWidget : public QOpenGLWidget {
    Q_OBJECT
public:
    OpenGLWidget(QWidget* parent=nullptr);
    ~OpenGLWidget() override;
    void initGeometry(const QByteArray& rawVertices, const QByteArray& rawIndices);
    void setIndex(int index);
    void initializeGL() override;
    void paintGL() override;
    void resizeGL(int w, int y) override;
signals:
    void readyForData();
private:
    void initShaders();
    float aspect = 1.33f;
    qint32 numIndices = 0;
    int blendIndex = 0;
    QOpenGLShaderProgram program;
    QOpenGLVertexArrayObject vao;
    QOpenGLBuffer arrayBuf;
    QOpenGLBuffer indexBuf;
    QVector3D eye, target, up;
};

#endif // OPENGLWIDGET_H

В то время как все другие атрибуты являются плавающими, индексы смешивания являются байтами.Я указываю их в initGeometry ниже с помощью GL_UNSIGNED_BYTE:

openglwidget.cpp :

#include "openglwidget.h"

#include <QOpenGLBuffer>
#include <QOpenGLVersionProfile>
#include <QOpenGLWidget>

OpenGLWidget::OpenGLWidget(QWidget* parent)
    :QOpenGLWidget(parent),
     arrayBuf(QOpenGLBuffer::VertexBuffer),
     indexBuf(QOpenGLBuffer::IndexBuffer),
     up(0.0f, 0.0f, 1.0f)
{
}

OpenGLWidget::~OpenGLWidget()
{
}

void OpenGLWidget::initGeometry(const QByteArray& rawVertices, const QByteArray& rawIndices)
{
    vao.create();
    vao.bind();

    arrayBuf.create();
    indexBuf.create();
    arrayBuf.bind();
    arrayBuf.allocate(rawVertices, rawVertices.size());
    indexBuf.bind();
    indexBuf.allocate(rawIndices, rawIndices.size());

    numIndices = rawIndices.size() / 2; // sizeof(qint16)

    int vertexLocation = program.attributeLocation("a_position");
    program.enableAttributeArray(vertexLocation);
    program.setAttributeBuffer(vertexLocation,
                               GL_FLOAT,
                               VertexLayout::XyzOffset,
                               3,
                               VertexLayout::Size);

    int blend0Location = program.attributeLocation("a_blend0");
    program.enableAttributeArray(blend0Location);
    program.setAttributeBuffer(blend0Location,
                               GL_UNSIGNED_BYTE,
                               VertexLayout::BlendIndexOffset0,
                               1,
                               VertexLayout::Size);

    vao.release();

    update();
}

void OpenGLWidget::setIndex(int index)
{
    if (index != blendIndex) {
        blendIndex = index;
        update();
    }
}

void OpenGLWidget::initializeGL()
{
    QSurfaceFormat format;
    QOpenGLVersionProfile profile(format);
    auto functions = context()->versionFunctions(profile);
    QOpenGLWidget::initializeGL();
    functions->initializeOpenGLFunctions();
    glClearColor(0.2f, 0.2f, 0.3f, 1.0f);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    initShaders();
    emit readyForData();
}

void OpenGLWidget::paintGL()
{
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // simple wireframe
    glClearColor(0.2f, 0.2f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Hard-coded pose
    QMatrix4x4 mv;
    eye = QVector3D(-1.50f, 2.33f, 1.43f);
    target = QVector3D(0.50f, 0.28f, 0.94f);
    mv.lookAt(eye, target, up);
    QMatrix4x4 mp;
    mp.perspective(75.0f, aspect, 1.0f, 200.0f);
    QMatrix4x4 mm;
    mm.translate(0.0f, 0.0f, 0.0f);

    program.setUniformValue("mvp_matrix", mp * mv * mm);
    program.setUniformValue("boneToColor", blendIndex);
    vao.bind();
    glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, nullptr);
}

void OpenGLWidget::resizeGL(int w, int h)
{
    aspect = h == 0 ? 1 : w / h;
}

static const char* vertexShaderSource =
    "#version 330 core\n"
    "in vec4 a_position;\n"
    "in int a_blend0;\n"
    "flat out int v_blend0;\n"
    "uniform mat4 mvp_matrix;\n"
    "void main() {\n"
    "gl_Position = mvp_matrix * a_position;\n"
    "v_blend0 = a_blend0;\n"
    "\n}";

static const char* fragmentShaderSource =
    "#version 330 core\n"
    "flat in int v_blend0;\n"
    "out vec4 fragmentColor;\n"
    "uniform int boneToColor;\n"
    "void main() {\n"
    "if (v_blend0 == boneToColor)\n"
    "fragmentColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
    "else\n"
    "fragmentColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
    "\n}";

void OpenGLWidget::initShaders()
{
    if (!program.addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource)) {
        qCritical() << "Failed to load vertex shader";
        close();
    }
    if (!program.addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource)) {
        qCritical() << "Failed to load fragment shader";
        close();
    }
    if (!program.link()) {
        qCritical() << "Failed to link program";
        close();
    }
    if (!program.bind()) {
        qCritical() << "Failed to bind program";
        close();
    }
}

Я понимаю, что GLSL преобразует этот тип в int,Возможно, это преобразование является причиной проблем, но если это так, я не уверен, как это проверить.Возможно, в шейдерах есть какая-то другая ошибка, но тогда работает случай blendIndex == 0.

Стоит отметить, что эта сетка связывает три индекса смешивания и три веса смешивания с каждой вершиной.Программа обращает внимание только на первый индекс смеси.Я слишком новичок в этой теме, чтобы понять, может ли это вызвать проблему.

Хотя эта проблема, вероятно, связана с отсутствием моего понимания OpenGL или базовых данных, я также пометил вопрос с помощьюQt на случай, если в их OpenGL-интерфейсе есть морщины.

Вот остальные файлы, необходимые для запуска проекта:

mainwindow.h :

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class QListWidget;
class QListWidgetItem;
class OpenGLWidget;

class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    MainWindow(QWidget* parent=nullptr);
    ~MainWindow();
private:
    void loadGeometry();
    void onIndexSelected(QListWidgetItem* item);
    QListWidget* selector;
    OpenGLWidget* openGLWidget;
};

#endif // MAINWINDOW_H

mainwindow.cpp :

#include "mainwindow.h"

#include <set>

#include <QByteArray>
#include <QDebug>
#include <QFile>
#include <QHBoxLayout>
#include <QListWidget>

#include "openglwidget.h"

MainWindow::MainWindow(QWidget* parent)
    :QMainWindow(parent),
     selector(new QListWidget(this)),
     openGLWidget(new OpenGLWidget(this))
{
    selector->setFixedWidth(80);
    auto dummy = new QWidget(this);
    auto hbox = new QHBoxLayout(dummy);
    hbox->addWidget(selector);
    hbox->addWidget(openGLWidget);
    setCentralWidget(dummy);
    setMinimumSize(640, 480);

    connect(openGLWidget, &OpenGLWidget::readyForData,
            this, &MainWindow::loadGeometry);
    connect(selector, &QListWidget::itemClicked,
            this, &MainWindow::onIndexSelected);
}

MainWindow::~MainWindow()
{
}

void MainWindow::onIndexSelected(QListWidgetItem* item)
{
    int index = item->text().toInt();
    openGLWidget->setIndex(index);
}

void MainWindow::loadGeometry()
{
    // Read raw geometry from file:
    QFile inf("boneShaderTestGeometry.dat");
    if (!inf.open(QIODevice::ReadOnly)) {
        qCritical() << "Failed to open geometry file";
        return;
    }
    QDataStream ins(&inf);
    ins.setByteOrder(QDataStream::LittleEndian);

    qint32 numVerts;
    ins >> numVerts;
    QByteArray rawVertices(numVerts * VertexLayout::Size, 0);
    ins.readRawData(rawVertices.data(), rawVertices.size());

    qint32 numIndices;
    ins >> numIndices;
    QByteArray rawIndices(numIndices * 2 /* sizeof(qint16) */, 0);
    ins.readRawData(rawIndices.data(), rawIndices.size());

    // Parse raw vertices for blend indices:
    std::set<char> blendIndices;
    for (int i=0; i<numVerts; ++i) {
        auto offset = VertexLayout::Size * i + VertexLayout::BlendIndexOffset0;
        blendIndices.insert(rawVertices[offset]);
    }
    // Populate selector:
    for (auto blendIndex: blendIndices)
        selector->addItem(QString::number(blendIndex));
    selector->setCurrentRow(0);

    // Forward raw geometry:
    openGLWidget->initGeometry(rawVertices, rawIndices);
}

main.cpp :

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

BoneShaderTest.pro :

QT       += core gui opengl
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = BoneShaderTest
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
CONFIG += c++11
SOURCES += \
        main.cpp \
        mainwindow.cpp \
    openglwidget.cpp
HEADERS += \
        mainwindow.h \
    openglwidget.h
LIBS += opengl32.lib
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

Необработанные данные сетки на tinyupload.com

Ответы [ 2 ]

1 голос
/ 09 мая 2019

Я посмотрел, и не так много, кажется, неправильно.Единственное, что может быть проблемой, это вызов:

program.setAttributeBuffer(blend0Location,
                           GL_UNSIGNED_BYTE,
                           VertexLayout::BlendIndexOffset0,
                           1,
                           VertexLayout::Size);

Если вы указываете целочисленный тип без знака для индекса смешивания, то убедитесь, что вы делаете это с помощью:glVertexArrayAttrib I Format (а не glVertexArrayAttribFormat).Но с другой стороны, если это работает для случая, когда индекс равен нулю, это как бы указывает на то, что что-то идет не так при установке унифицированного значения индекса.

При отслеживании подобных вещей обычно рекомендуется подключиться к API отладки OpenGL, чтобы он выводил текущие ошибки.В худшем случае вы всегда можете вернуться к старому подходу glGetError и обернуть все вызовы в GL простым макросом проверки, чтобы увидеть, не происходит ли каких-либо ошибок.

inline void checkGlError(const char* const file, const char* const op, int line)
{
  switch(glGetError())
  {
  case GL_NO_ERROR:  return;
  case GL_INVALID_ENUM: fprintf(stderr, "file: %s\n\t%s: line %d: GL_INVALID_ENUM\n", file, op, line); break;
  case GL_INVALID_VALUE: fprintf(stderr, "file: %s\n\t%s: line %d: GL_INVALID_VALUE\n", file, op, line); break;
  case GL_INVALID_OPERATION: fprintf(stderr, "file: %s\n\t%s: line %d: GL_INVALID_OPERATION\n", file, op, line); break;
  case GL_INVALID_FRAMEBUFFER_OPERATION: fprintf(stderr, "file: %s\n\t%s: line %d: GL_INVALID_FRAMEBUFFER_OPERATION\n", file, op, line); break;
  case GL_OUT_OF_MEMORY: fprintf(stderr, "file: %s\n\t%s: line %d: GL_OUT_OF_MEMORY\n", file, op, line); break;
  default: fprintf(stderr, "file: %s\n\t%s: line %d: unknown Gl error\n", file, op, line); break;
  }
}

#define CHECK_GL_ERROR(X) X; checkGlError(__FILE__, #X, __LINE__);
0 голосов
/ 09 мая 2019

Советы Робтеблока привели к решению. Два изменения были необходимы.

Сначала необходимо было использовать glVertexAttribIPointer для правильной обработки целочисленного типа:

program.setAttributeBuffer(blend0Location,
                           GL_UNSIGNED_BYTE,
                           VertexLayout::BlendIndexOffset0,
                           1,
                           VertexLayout::Size);

было заменено на:

#include <QOpenGLFunctions_4_0_Core>

auto fun = new QOpenGLFunctions_4_0_Core();
fun->initializeOpenGLFunctions();
fun->glVertexAttribIPointer(blend0Location, 1, GL_UNSIGNED_BYTE,
                       VertexLayout::Size, reinterpret_cast<const void*>(qintptr(VertexLayout::BlendIndexOffset0)));

Во-вторых, нужно было обновить шейдеры. Вершинный шейдер:

#version 330 core
in vec4 a_position;
in int a_blend0;
flat out int v_blend0;
uniform mat4 mvp_matrix;
void main() {
    gl_Position = mvp_matrix * a_position;
    v_blend0 = a_blend0;
}

и фрагментный шейдер:

#version 330 core
flat in int v_blend0;
out vec4 fragmentColor;
uniform int boneToColor;
void main() {
    if (v_blend0 == boneToColor)
        fragmentColor = vec4(1.0, 0.0, 0.0, 1.0);
    else
        fragmentColor = vec4(1.0, 1.0, 1.0, 1.0);
}
...