2017-10-18 2 views
1

Qt3D에서 광고 게시판을 만드는 가장 좋은 방법을 찾고 있습니다. 나는 카메라가 어디에 있든 얼굴을 향하고 비행기가 앞으로 또는 뒤로 움직일 때 크기가 변하지 않는 비행기를 원합니다. GLSL 버텍스 및 지오메트리 셰이더를 사용하여이 작업을 수행하는 방법을 읽었지만 고객 쉐이더가 빌보드의 가장 효율적이고 효과적인 방법이 아니면 Qt3D 방식을 찾고 있습니다.Qt3D 2.0을 사용한 Billboarding

나는 보았고, 속성을 통해 QTransform에 매트릭스를 설정할 수있는 것처럼 보였지만 매트릭스를 조작하는 방법이 분명하지 않거나 더 좋은 방법이있을 수 있습니까? 나는 C++ api를 사용하고 있지만 QML 응답은 그렇게 할 것이다. C++로 포팅 할 수 있습니다.

+0

무엇을 이미 했습니까? 코드를 작성하는 동안 어떤 문제에 직면 했습니까? – folibis

답변

4

광고판을 하나만 그리려면 비행기를 추가하고 카메라가 움직일 때마다 회전 시키십시오. 그러나 수천 또는 수백만 개의 게시판에서이 작업을 효율적으로 수행하려면 맞춤 셰이더를 사용하는 것이 좋습니다. 우리는 Qt3D에서 사기극 구체를 그리기 위해이 작업을 수행했습니다.

그러나 형상 쉐이더를 지원하지 않는 시스템을 대상으로했기 때문에 형상 쉐이더를 사용하지 않았습니다. 대신에 우리는 4 개의 꼭지점을 원점에 놓고 꼭짓점 쉐이더만을 사용하여 쉐이더로 옮겼습니다. 많은 복사본을 만들려면 인스턴스 드로잉을 사용했습니다. 우리는 구체의 위치에 따라 4 개의 꼭지점을 이동했습니다. 마지막으로 각 구의 네 꼭지점을 이동하여 항상 카메라를 향한 광고 게시판으로 만듭니다.

QGeometry를 서브 클래 싱하여 시작하고 4 점을 생성하는 버퍼 펑터를 작성했습니다 (모두 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

관련 문제