问题
Update
The OpenGL version seems to be 4.3 at least according to the following code
QSurfaceFormat format = view.format();
int major = format.majorVersion();
int minor = format.minorVersion();
so geometry shaders should work and the issue seems to be something else.
Original Question
While trying to answer this question related to how to create billboards in Qt3D I encountered an issue I found no solution for.
I used the code from this GitHub repository which contains C++ and QML. It works perfectly and showcases how billboards can be implemented in Qt3D - at least when using QML. This is what a screenshot of the code looks like:
Now, the person asking the question I mentioned needs it to be in C++ so I tried to translate it since every QML class has a corresponding C++ class. I only succeeded to some extend. The original code uses a geometry shader to create the billboards. When I don't include the geometry part of the shader I manage to get the individual points drawn on-screen with a pre-defined color like this (I circled the points so you can see them better):
But as soon as I include the geometry shader all the points vanish. But this exact shader has worked under QML.
I boiled it down to the geometry shader being the issue because when I comment it out I get the white points but when I add it the points are not shown anymore (and obviously the billboards aren't either):
billboardShaderProgram->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl("qrc:/shaders/billboards.vert")));
//billboardShaderProgram->setGeometryShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl("qrc:/shaders/billboards.geom")));
billboardShaderProgram->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl("qrc:/shaders/billboards.frag")));
After having checked the OpenGL version (which seems to be 4.3) the issue must be something else in how I create the objects in C++.
Code
You can find the project here on GitHub.
Alternatively, I'll add the relevant classes here and try to keep them to a minimum.
main.cpp
:
// Includes for framegraph
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DRender/QFrameGraphNode>
#include <Qt3DRender/QRenderSurfaceSelector>
#include <Qt3DRender/QViewport>
#include <Qt3DRender/QCameraSelector>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QCameraLens>
#include <Qt3DRender/QClearBuffers>
#include <Qt3DExtras/QForwardRenderer>
#include <Qt3DExtras/QFirstPersonCameraController>
#include <Qt3DInput/QInputSettings>
#include <Qt3DCore/QEntity>
#include <Qt3DExtras/QPlaneMesh>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DRender/QGeometryRenderer>
#include <Qt3DCore/QTransform>
#include <Qt3DRender/QMaterial>
#include <Qt3DRender/QParameter>
#include <Qt3DRender/QTexture>
#include <Qt3DRender/QTextureImage>
#include <Qt3DRender/QEffect>
#include <Qt3DRender/QTechnique>
#include <Qt3DRender/QRenderPass>
#include <Qt3DRender/QShaderProgram>
#include <Qt3DExtras/QSphereMesh>
#include <Qt3DExtras/QCuboidMesh>
#include <Qt3DRender/QGraphicsApiFilter>
#include <Qt3DExtras/QTextureMaterial>
#include <QSurfaceFormat>
#include <QVector3D>
#include <QColor>
#include <QGuiApplication>
#include "billboardgeometry.h"
#include <QOpenGLContext>
Qt3DExtras::QFirstPersonCameraController * cameraController;
int windowWidth = 1600;
int windowHeight = 800;
Qt3DCore::QEntity *createScene() {
Qt3DCore::QEntity *root = new Qt3DCore::QEntity();
cameraController = new Qt3DExtras::QFirstPersonCameraController(root);
// Add plane
Qt3DCore::QEntity *planeEntity = new Qt3DCore::QEntity(root);
Qt3DExtras::QPlaneMesh *planeMesh = new Qt3DExtras::QPlaneMesh(planeEntity);
planeMesh->setWidth(20);
planeMesh->setHeight(20);
Qt3DExtras::QPhongMaterial *planeMaterial = new Qt3DExtras::QPhongMaterial(planeEntity);
planeMaterial->setAmbient(QColor(0, 0, 0.7 * 255, 0.1 * 255));
planeEntity->addComponent(planeMesh);
planeEntity->addComponent(planeMaterial);
// Add sphere
Qt3DCore::QEntity *sphereEntity = new Qt3DCore::QEntity(root);
Qt3DExtras::QSphereMesh *sphereMesh = new Qt3DExtras::QSphereMesh(sphereEntity);
Qt3DExtras::QPhongMaterial *sphereMaterial = new Qt3DExtras::QPhongMaterial(sphereEntity);
sphereMaterial->setAmbient(Qt::red);
Qt3DCore::QTransform *sphereTransform = new Qt3DCore::QTransform(sphereEntity);
sphereTransform->setTranslation(QVector3D(0., 5., 0.));
sphereEntity->addComponent(sphereMesh);
sphereEntity->addComponent(sphereMaterial);
sphereEntity->addComponent(sphereTransform);
// Add cube
Qt3DCore::QEntity *cubeEntity = new Qt3DCore::QEntity(root);
Qt3DExtras::QCuboidMesh *cubeMesh = new Qt3DExtras::QCuboidMesh(cubeEntity);
Qt3DExtras::QPhongMaterial *cubeMaterial = new Qt3DExtras::QPhongMaterial(cubeEntity);
cubeMaterial->setAmbient(Qt::gray);
Qt3DCore::QTransform *cubeTransform = new Qt3DCore::QTransform();
cubeTransform->setTranslation(QVector3D(2., 2., 5.));
cubeEntity->addComponent(cubeMesh);
cubeEntity->addComponent(cubeMaterial);
cubeEntity->addComponent(cubeTransform);
// Add Billboard
Qt3DCore::QEntity *billboardEntity = new Qt3DCore::QEntity(root);
// Create billboard geometry
QVector<QVector3D> pos;
pos << QVector3D(1, 1, 0);
pos << QVector3D(-1, 2, 8);
pos << QVector3D(1, 1, 7);
pos << QVector3D(0, 0, 4);
BillboardGeometry *billboardGeometry = new BillboardGeometry(billboardEntity);
billboardGeometry->setPoints(pos);
Qt3DRender::QGeometryRenderer *billboardRenderer = new Qt3DRender::QGeometryRenderer(billboardEntity);
billboardRenderer->setPrimitiveType(Qt3DRender::QGeometryRenderer::Points);
billboardRenderer->setGeometry(billboardGeometry);
billboardRenderer->setVertexCount(billboardGeometry->count());
Qt3DCore::QTransform *billboardTransform = new Qt3DCore::QTransform(billboardEntity);
billboardTransform->setTranslation(QVector3D(0., 1.5, 0.));
// Billboard material
// Image of billboard material
Qt3DRender::QMaterial *billboardMaterial = new Qt3DRender::QMaterial(billboardEntity);
Qt3DRender::QTexture2D* texture = new Qt3DRender::QTexture2D();
Qt3DRender::QTextureImage* textureImage = new Qt3DRender::QTextureImage(texture);
textureImage->setSource(QUrl(QStringLiteral("qrc:/success-kid.png")));
texture->addTextureImage(textureImage);
// Parameters of billboard material
Qt3DRender::QParameter* billboardParam1 = new Qt3DRender::QParameter(QStringLiteral("tex0"), texture);
Qt3DRender::QParameter* billboardParam2 = new Qt3DRender::QParameter(QStringLiteral("WIN_SCALE"), QSize(1600, 800));
Qt3DRender::QParameter* billboardParam3 = new Qt3DRender::QParameter(QStringLiteral("BB_SIZE"), QSize(100, 100));
billboardMaterial->addParameter(billboardParam1);
billboardMaterial->addParameter(billboardParam2);
billboardMaterial->addParameter(billboardParam3);
// Effect of material
Qt3DRender::QEffect* billboardEffect = new Qt3DRender::QEffect();
Qt3DRender::QTechnique* billboardTechnique = new Qt3DRender::QTechnique();
billboardTechnique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL);
billboardTechnique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::CoreProfile);
billboardTechnique->graphicsApiFilter()->setMajorVersion(3);
billboardTechnique->graphicsApiFilter()->setMinorVersion(1);
// You need the filter key because the QForwardRenderer employed as the default framegraph by the Qt3DWindow
// extends QTechniqueFilter and filters for this key exactly. Without it, the material gets discarded.
Qt3DRender::QFilterKey* filterKey = new Qt3DRender::QFilterKey(billboardMaterial);
filterKey->setName(QStringLiteral("renderingStyle"));
filterKey->setValue(QStringLiteral("forward"));
billboardTechnique->addFilterKey(filterKey);
Qt3DRender::QRenderPass* billboardRenderPass = new Qt3DRender::QRenderPass();
Qt3DRender::QShaderProgram* billboardShaderProgram = new Qt3DRender::QShaderProgram();
billboardShaderProgram->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl("qrc:/shaders/billboards.vert")));
//billboardShaderProgram->setGeometryShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl("qrc:/shaders/billboards.geom")));
billboardShaderProgram->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl("qrc:/shaders/billboards.frag")));
billboardRenderPass->setShaderProgram(billboardShaderProgram);
billboardTechnique->addRenderPass(billboardRenderPass);
billboardEffect->addTechnique(billboardTechnique);
billboardMaterial->setEffect(billboardEffect);
billboardEntity->addComponent(billboardRenderer);
billboardEntity->addComponent(billboardMaterial);
billboardEntity->addComponent(billboardTransform);
return root;
}
int main(int argc, char* argv[])
{
QGuiApplication app(argc, argv);
Qt3DExtras::Qt3DWindow view;
view.resize(windowWidth, windowHeight);
Qt3DExtras::QForwardRenderer *renderer = (Qt3DExtras::QForwardRenderer *)view.activeFrameGraph();
renderer->setClearColor("black");
Qt3DRender::QCamera *camera = view.camera();
camera->setProjectionType(Qt3DRender::QCameraLens::PerspectiveProjection);
camera->setFieldOfView(45);
// Cast to float to ensure float division
camera->setAspectRatio(windowWidth / (float) windowHeight);
camera->setNearPlane(0.1f);
camera->setFarPlane(100.f);
camera->setPosition(QVector3D(0., 10., 20.));
camera->setViewCenter(QVector3D(0., 0., 0.));
camera->setUpVector(QVector3D(0., 1., 0.));
Qt3DCore::QEntity *root = createScene();
view.setRootEntity(root);
cameraController->setCamera(camera);
view.setTitle("Billboards");
view.show();
return app.exec();
}
billboardgeometry.h
:
#ifndef BILLBOARDGEOMETRY_H
#define BILLBOARDGEOMETRY_H
#include <Qt3DRender/QGeometry>
#include <Qt3DRender/QBuffer>
#include <QVector3D>
class BillboardGeometry : public Qt3DRender::QGeometry
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged)
public:
BillboardGeometry( Qt3DCore::QNode *parent = nullptr );
void setPoints( const QVector<QVector3D> &vertices );
int count();
signals:
void countChanged(int count);
private:
Qt3DRender::QAttribute *mPositionAttribute = nullptr;
Qt3DRender::QBuffer *mVertexBuffer = nullptr;
int mVertexCount = 0;
};
#endif // BILLBOARDGEOMETRY_H
billboardgeometry.cpp
:
#include "billboardgeometry.h"
#include <Qt3DRender/QAttribute>
BillboardGeometry::BillboardGeometry( Qt3DCore::QNode *parent )
: Qt3DRender::QGeometry( parent )
, mPositionAttribute( new Qt3DRender::QAttribute( this ) )
, mVertexBuffer( new Qt3DRender::QBuffer( Qt3DRender::QBuffer::VertexBuffer, this ) )
{
mPositionAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
mPositionAttribute->setBuffer( mVertexBuffer );
mPositionAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
mPositionAttribute->setVertexSize( 3 );
mPositionAttribute->setName( Qt3DRender::QAttribute::defaultPositionAttributeName() );
addAttribute( mPositionAttribute );
}
int BillboardGeometry::count()
{
return mVertexCount;
}
void BillboardGeometry::setPoints(const QVector<QVector3D> &vertices)
{
QByteArray vertexBufferData;
vertexBufferData.resize( vertices.size() * 3 * sizeof( float ) );
float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
int idx = 0;
for ( const auto &v : vertices )
{
rawVertexArray[idx++] = v.x();
rawVertexArray[idx++] = v.y();
rawVertexArray[idx++] = v.z();
}
mVertexCount = vertices.count();
mVertexBuffer->setData( vertexBufferData );
emit countChanged(mVertexCount);
}
billboard.vert
:
#version 150
uniform mat4 modelViewProjection;
in vec3 vertexPosition;
void main(void)
{
gl_Position = modelViewProjection * vec4(vertexPosition, 1);
}
billboard.geom
:
#version 150
layout (points) in;
layout (triangle_strip, max_vertices = 4) out;
uniform mat4 modelViewProjection;
uniform vec2 BB_SIZE; // billboard size in pixels
uniform vec2 WIN_SCALE; // the size of the viewport in pixels
out vec2 UV;
void main (void)
{
vec4 P = gl_in[0].gl_Position;
P /= P.w;
//vec2 size = vec2(0.5,0.5);
vec2 size = BB_SIZE / WIN_SCALE;
gl_Position = P;
gl_Position.xy += vec2(-0.5,-0.5) * size;
UV = vec2(0,0);
EmitVertex();
gl_Position = P;
gl_Position.xy += vec2(0.5,-0.5) * size;
UV = vec2(1,0);
EmitVertex();
gl_Position = P;
gl_Position.xy += vec2(-0.5,+0.5) * size;
UV = vec2(0,1);
EmitVertex();
gl_Position = P;
gl_Position.xy += vec2(+0.5,+0.5) * size;
UV = vec2(1,1);
EmitVertex();
EndPrimitive();
}
billboard.frag
:
#version 150
uniform sampler2D tex0;
in vec2 UV;
void main(void)
{
//gl_FragColor = texture(tex0, UV);
gl_FragColor = vec4(1, 1, 1, 1);
}
shaders.qrc
:
<RCC>
<qresource prefix="/shaders">
<file>billboards.frag</file>
<file>billboards.vert</file>
<file>billboards.geom</file>
</qresource>
</RCC>
billboards.pro
:
TEMPLATE = app
QT += 3dcore 3drender 3dinput 3dquick qml quick 3dquickextras 3dextras
SOURCES += \
main.cpp \
billboardgeometry.cpp
RESOURCES += qml.qrc \
shaders.qrc
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
HEADERS += \
billboardgeometry.h
success-kid.jpg
回答1:
GitHub users wonder-sk and ismailsunni solved this issue for me by pointing to an error in the code:
Qt3DRender::QParameter* billboardParam2 = new Qt3DRender::QParameter(QStringLiteral("WIN_SCALE"), QSize(1600, 800));
Qt3DRender::QParameter* billboardParam3 = new Qt3DRender::QParameter(QStringLiteral("BB_SIZE"), QSize(100, 100));
In these two lines it needs to be QSizeF
and not QSize
- voila the shader is working!
Alternatively, there already exists a C++ port here.
来源:https://stackoverflow.com/questions/63079722/qt3d-geometry-shader-working-in-qml-but-not-in-c