У меня проблемы с тестовой программой 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-интерфейсе есть морщины.
Вот остальные файлы, необходимые для запуска проекта:
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