2017-10-18 65 views
1

我正在寻找在Qt3D中创建广告牌的最佳方式。我想要一架飞机,它面向相机,无论它在哪里,并且在相机向前或向后移动时都不会改变尺寸。我已经阅读了如何使用GLSL顶点和几何着色器来做到这一点,但我正在寻找Qt3D方式,除非客户着色器是广告牌最有效和最好的方式。使用Qt3D 2.0的广告牌

我看过了,看起来我可以通过属性在QTransform上设置矩阵,但是我不清楚如何操作矩阵,或者有更好的方法吗?我正在使用C++ API,但QML答案可以。我可以将它移植到C++。

+0

你已经做了什么?编写代码时你面临什么问题? – folibis

回答

4

如果您只想绘制一个广告牌,您可以添加一架飞机并在相机移动时进行旋转。但是,如果您想用数千或数百万个广告牌高效完成此任务,我建议使用自定义着色器。我们这样做是为了在Qt3D中绘制冒充者领域。

但是,我们没有使用几何着色器,因为我们的目标系统不支持几何着色器。相反,我们只使用顶点着色器,将四个顶点放在原点中,并将这些顶点移到着色器上。为了创建许多副本,我们使用了实例化绘图。我们根据球体的位置移动了每组四个顶点。最后,我们移动了每个球体的四个顶点中的每一个,以使它们产生总是面向相机的广告牌。

通过继承QGeometry并创建一个缓冲区函数来创建四个点,所有点都在原点(请参阅spherespointgeometry.cpp)。为每个点提供一个我们稍后可以使用的ID。如果您使用几何着色器,则不需要该ID,您只能创建一个顶点。

class SpheresPointVertexDataFunctor : public Qt3DRender::QBufferDataGenerator 
{ 
public: 
    SpheresPointVertexDataFunctor() 
    { 
    } 

    QByteArray operator()() Q_DECL_OVERRIDE 
    { 
     const int verticesCount = 4; 
     // vec3 pos 
     const quint32 vertexSize = (3+1) * sizeof(float); 

     QByteArray verticesData; 
     verticesData.resize(vertexSize*verticesCount); 
     float *verticesPtr = reinterpret_cast<float*>(verticesData.data()); 

     // Vertex 1 
     *verticesPtr++ = 0.0; 
     *verticesPtr++ = 0.0; 
     *verticesPtr++ = 0.0; 
     // VertexID 1 
     *verticesPtr++ = 0.0; 

     // Vertex 2 
     *verticesPtr++ = 0.0; 
     *verticesPtr++ = 0.0; 
     *verticesPtr++ = 0.0; 
     // VertexID 2 
     *verticesPtr++ = 1.0; 

     // Vertex 3 
     *verticesPtr++ = 0.0; 
     *verticesPtr++ = 0.0; 
     *verticesPtr++ = 0.0; 
     // VertexID3 
     *verticesPtr++ = 2.0; 

     // Vertex 4 
     *verticesPtr++ = 0.0; 
     *verticesPtr++ = 0.0; 
     *verticesPtr++ = 0.0; 
     // VertexID 4 
     *verticesPtr++ = 3.0; 

     return verticesData; 
    } 

    bool operator ==(const QBufferDataGenerator &other) const Q_DECL_OVERRIDE 
    { 
     Q_UNUSED(other); 
     return true; 
    } 

    QT3D_FUNCTOR(SpheresPointVertexDataFunctor) 
}; 

对于真实位置,我们使用了一个单独的QBuffer。我们还设置颜色和比例,但我在这里省略了那些(见spheredata.cpp):

void SphereData::setPositions(QVector<QVector3D> positions, QVector3D color, float scale) 
{ 
    QByteArray ba; 
    ba.resize(positions.size() * sizeof(QVector3D)); 
    SphereVBOData *vboData = reinterpret_cast<QVector3D *>(ba.data()); 
    for(int i=0; i<positions.size(); i++) { 
     QVector3D &position = vboData[i]; 
     position = positions[i]; 
    } 
    m_buffer->setData(ba); 
    m_count = positions.count(); 
} 

然后,在QML中,我们连接在一个QGeometryRenderer缓冲区中的几何形状。这也可以在C++中完成的,如果你喜欢(见 Spheres.qml):

GeometryRenderer { 
    id: spheresMeshInstanced 
    primitiveType: GeometryRenderer.TriangleStrip 
    enabled: instanceCount != 0 
    instanceCount: sphereData.count 

    geometry: SpheresPointGeometry { 
     attributes: [ 
      Attribute { 
       name: "pos" 
       attributeType: Attribute.VertexAttribute 
       vertexBaseType: Attribute.Float 
       vertexSize: 3 
       byteOffset: 0 
       byteStride: (3 + 3 + 1) * 4 
       divisor: 1 
       buffer: sphereData ? sphereData.buffer : null 
      } 
     ] 
    } 
} 

最后,我们创建自定义的着色器绘制广告牌。请注意,因为我们正在绘制冒充者球体,所以增加了广告牌尺寸以处理来自难看角度的片段着色器中的光线追踪。一般来说,您可能不需要2.0*0.6因素。

顶点着色器:

#version 330 

in vec3 vertexPosition; 
in float vertexId; 
in vec3 pos; 
in vec3 col; 
in float scale; 

uniform vec3 eyePosition = vec3(0.0, 0.0, 0.0); 

uniform mat4 modelMatrix; 
uniform mat4 mvp; 

out vec3 modelSpherePosition; 
out vec3 modelPosition; 
out vec3 color; 
out vec2 planePosition; 
out float radius; 
vec3 makePerpendicular(vec3 v) { 
    if(v.x == 0.0 && v.y == 0.0) { 
     if(v.z == 0.0) { 
      return vec3(0.0, 0.0, 0.0); 
     } 
     return vec3(0.0, 1.0, 0.0); 
    } 
    return vec3(-v.y, v.x, 0.0); 
} 

void main() { 
    vec3 position = vertexPosition + pos; 
    color = col; 
    radius = scale; 
    modelSpherePosition = (modelMatrix * vec4(position, 1.0)).xyz; 

    vec3 view = normalize(position - eyePosition); 
    vec3 right = normalize(makePerpendicular(view)); 
    vec3 up = cross(right, view); 

    float texCoordX = 1.0 - 2.0*(float(vertexId==0.0) + float(vertexId==2.0)); 
    float texCoordY = 1.0 - 2.0*(float(vertexId==0.0) + float(vertexId==1.0)); 
    planePosition = vec2(texCoordX, texCoordY); 

    position += 2*0.6*(-up - right)*(scale*float(vertexId==0.0)); 
    position += 2*0.6*(-up + right)*(scale*float(vertexId==1.0)); 
    position += 2*0.6*(up - right)*(scale*float(vertexId==2.0)); 
    position += 2*0.6*(up + right)*(scale*float(vertexId==3.0)); 

    vec4 modelPositionTmp = modelMatrix * vec4(position, 1.0); 
    modelPosition = modelPositionTmp.xyz; 

    gl_Position = mvp*vec4(position, 1.0); 
} 

片段着色器:

#version 330 

in vec3 modelPosition; 
in vec3 modelSpherePosition; 
in vec3 color; 
in vec2 planePosition; 
in float radius; 

out vec4 fragColor; 

uniform mat4 modelView; 
uniform mat4 inverseModelView; 
uniform mat4 inverseViewMatrix; 
uniform vec3 eyePosition; 
uniform vec3 viewVector; 

void main(void) { 
    vec3 rayDirection = eyePosition - modelPosition; 
    vec3 rayOrigin = modelPosition - modelSpherePosition; 

    vec3 E = rayOrigin; 
    vec3 D = rayDirection; 

    // Sphere equation 
    //  x^2 + y^2 + z^2 = r^2 
    // Ray equation is 
    //  P(t) = E + t*D 
    // We substitute ray into sphere equation to get 
    //  (Ex + Dx * t)^2 + (Ey + Dy * t)^2 + (Ez + Dz * t)^2 = r^2 
    float r2 = radius*radius; 
    float a = D.x*D.x + D.y*D.y + D.z*D.z; 
    float b = 2.0*E.x*D.x + 2.0*E.y*D.y + 2.0*E.z*D.z; 
    float c = E.x*E.x + E.y*E.y + E.z*E.z - r2; 

    // discriminant of sphere equation 
    float d = b*b - 4.0*a*c; 
    if(d < 0.0) { 
     discard; 
    } 

    float t = (-b + sqrt(d))/(2.0*a); 
    vec3 sphereIntersection = rayOrigin + t * rayDirection; 

    vec3 normal = normalize(sphereIntersection); 
    vec3 normalDotCamera = color*dot(normal, normalize(rayDirection)); 

    float pi = 3.1415926535897932384626433832795; 

    vec3 position = modelSpherePosition + sphereIntersection; 

    // flat red 
    fragColor = vec4(1.0, 0.0, 0.0, 1.0); 
} 

它已经有一段时间,因为我们第一次实现这一点,有可能是更容易的方法,现在做到这一点,但是这应该给你对你需要的作品有了一个想法。

+0

谢谢你这个极好的答案!在研究过程中,我实际上碰到了你的GitHub回购。很高兴有一个解释,以配合它。 –

+0

不客气!我实际上是在想着这个仓库,并且它可能更加便于用户使用和记录。很高兴你发布了这个问题,并提醒我再看看它。如果您还有其他问题,请告诉我。 – dragly