【问题标题】:Shading joints according to blend index in OpenGL根据OpenGL中的混合索引对关节进行着色
【发布时间】:2019-05-09 03:08:13
【问题描述】:

我无法使用 QtWidgets 测试程序来可视化旧 FVF 网格数据中的关节,突出显示用户选择的单个关节。我已经减少了程序并包括了下面的所有部分。

程序加载由顶点和元素数组数据组成的网格,将其渲染为白色线框,但将指定混合索引的所有顶点着色为红色。混合索引存在于顶点数据中。它们被收集并显示在列表小部件中。单击索引将该索引转发到片段着色器,该着色器会覆盖具有匹配混合索引的顶点的默认颜色(白色和红色)。

这是一个程序为混合索引为 0 的顶点着色的示例:

彩色部分作为骨架的根是有意义的。

问题在于,这仅在选择的混合索引为 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 接口出现一些问题。

以下是运行项目所需的剩余文件:

ma​​inwindow.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

ma​​inwindow.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);
}

ma​​in.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

The raw mesh data at tinyupload.com

【问题讨论】:

    标签: c++ qt animation opengl glsl


    【解决方案1】:

    我已经看过了,似乎没有很多错误。 可能的唯一问题是调用:

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

    如果要为混合索引指定无符号整数类型,请确保使用:glVertexArrayAttribIFormat(而不是 glVertexArrayAttribFormat)。但是话又说回来,如果它适用于索引为零的情况,则表明在设置统一索引值时出现了问题。

    在跟踪此类内容时,通常最好连接到 OpenGL 调试 API 以使其输出当前错误。在最坏的情况下,您总是可以退回到旧的 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__);
    

    【讨论】:

    • 您对整数版本 glVertexArrayAttribIFormat 的提示是第一条线索。第二条线索是您对设置统一值的问题的评论,这导致更仔细地查看原始着色器中的类型,其中存在严重错误。谢谢。
    【解决方案2】:

    robthebloke 给出的提示导致了解决方案。有两个改变是必要的。

    首先,必须使用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);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-04
      • 2017-01-20
      • 1970-01-01
      • 1970-01-01
      • 2021-09-06
      • 2015-11-26
      • 1970-01-01
      相关资源
      最近更新 更多