Qt 6.11 is out! See what's new in the release
blog
3D calculate normals
-
I wanted to create a custom mesh. But the lighting is not working. Can anybody help me to calculate the right normals? The second problem is, that not all triangles are drawn, when I use a color. With textures they are drawn...
main.qml
import QtQuick import QtQuick3D import QtQuick3D.Helpers import MyCustomGeometry Window { width: 800 height: 600 visible: true title: qsTr("Basic Scene") View3D { camera: camera anchors.fill: parent width: 800 height: 600 x: 0 y: 0 id: view environment: SceneEnvironment { clearColor: "#ffffff" backgroundMode: SceneEnvironment.Color antialiasingMode: SceneEnvironment.ProgressiveAA antialiasingQuality: SceneEnvironment.VeryHigh depthTestEnabled: true } DirectionalLight { eulerRotation.x: -20 eulerRotation.y: 110 shadowMapQuality : Light.ShadowMapQualityHigh castsShadow: true brightness: 5 } Node { id: originNode PerspectiveCamera { id: cameraNode z: 100 fieldOfView: 45 } } OrbitCameraController { anchors.fill: parent origin: originNode camera: cameraNode } Model { position: Qt.vector3d(0, -50, 0) scale: Qt.vector3d(5, 5, 1) eulerRotation.x: -90 source: "#Rectangle" materials: [ PrincipledMaterial { baseColor: "green"; } ] receivesShadows: true } Model { id: sphere objectName: "sphere" visible: true usedInBakedLighting : true geometry: MyCustomGeometry {} materials: [DefaultMaterial {diffuseColor: "blue"}] eulerRotation.x: 90 eulerRotation.z: 45 // materials: [ // DefaultMaterial { // objectName: "" // Texture { // id: baseColorMap // source: "qrc:/qt_logo_rect.png" // } // //lightProbe: baseColorMap // cullMode: DefaultMaterial.NoCulling // diffuseMap: baseColorMap // specularAmount: 0.5 // // lighting : DefaultMaterial.FragmentLighting // } // ] } } }main.cpp
#include <MyCustomGeometry.h> #include <QGuiApplication> #include <QQmlApplicationEngine> int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); qmlRegisterType<MyCustomGeometry>("MyCustomGeometry", 1, 0, "MyCustomGeometry"); QQmlApplicationEngine engine; const QUrl url(u"qrc:/untitled9/main.qml"_qs); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); return app.exec(); }mycustomgeometry.h
#ifndef MYCUSTOMGEOMETRY_H #define MYCUSTOMGEOMETRY_H #include <QQuick3DGeometry> class MyCustomGeometry : public QQuick3DGeometry { Q_OBJECT public: MyCustomGeometry(); virtual ~MyCustomGeometry(); QVector3D GetNormal(QVector3D a, QVector3D b, QVector3D c); signals: public slots: void setData(QByteArray vertexData, QByteArray indexData); }; #endif // MYCUSTOMGEOMETRY_Hmycustomgeometry.cpp
#include "mycustomgeometry.h" #include <QVector3D> MyCustomGeometry::MyCustomGeometry() { QVector3D vectors[4] { QVector3D (-10.0f, -10.0f, -10.0f), QVector3D (10.0f, -10.0f, -10.0f), QVector3D (0.0f, -10.0f, -20.0f), QVector3D (-5.0f, -5.0f, -25.0f) }; QByteArray i; i.resize(3 * 3 * sizeof(int)); int *p1 = reinterpret_cast<int *>(i.data()); *p1++ = 0; *p1++ = 3; *p1++ = 2; *p1++ = 0; *p1++ = 1; *p1++ = 3; *p1++ = 1; *p1++ = 2; *p1++ = 3; QByteArray v; v.resize((3 + 3 + 2) * 4 * sizeof(float)); float *p = reinterpret_cast<float *>(v.data()); QVector3D normals[4] { QVector3D (1.0f, 1.0f, 1.0f), QVector3D (1.0f, 1.0f, 1.0f), QVector3D (1.0f, 1.0f, 1.0f), QVector3D (1.0f, 1.0f, 1.0f) }; for (int i = 0; i < 4; i++) { auto prev = i == 0 ? vectors[3] : vectors[i-1]; auto next = i == 3 ? vectors[0] : vectors[i+1]; auto current = vectors[i]; normals[i] = GetNormal(current, prev, next); } // vertex *p++ = vectors[0].x(); *p++ = vectors[0].y(); *p++ = vectors[0].z(); // normals *p++ = normals[0].x(); *p++ = normals[0].y(); *p++ = normals[0].z(); // texture *p++ = 0.25f; *p++ = 0.0f; // vertex *p++ = vectors[1].x(); *p++ = vectors[1].y(); *p++ = vectors[1].z(); // normals *p++ = normals[1].x(); *p++ = normals[1].y(); *p++ = normals[1].z(); // texture *p++ = 2.0f; *p++ = 0.0f; // vertex *p++ = vectors[2].x(); *p++ = vectors[2].y(); *p++ = vectors[2].z(); // normals *p++ = normals[2].x(); *p++ = normals[2].y(); *p++ = normals[2].z(); // texture *p++ = 0.5f; *p++ = 2.0f; // vertex *p++ = vectors[3].x(); *p++ = vectors[3].y(); *p++ = vectors[3].z(); // normals *p++ = normals[3].x(); *p++ = normals[3].y(); *p++ = normals[3].z(); // texture *p++ = 0.5f; *p++ = 2.0f; setData(v, i); } MyCustomGeometry::~MyCustomGeometry() { } QVector3D MyCustomGeometry::GetNormal(QVector3D a, QVector3D b, QVector3D c) { QVector3D normVec; auto dir = normVec.crossProduct(b - a, c - a); dir.normalize(); return dir; } void MyCustomGeometry::setData(QByteArray vertexData, QByteArray indexData) { clear(); setPrimitiveType(PrimitiveType::Triangles); addAttribute(Attribute::PositionSemantic, 0, Attribute::F32Type); addAttribute(Attribute::IndexSemantic, 0, Attribute::U32Type); addAttribute(Attribute::NormalSemantic, 3 * sizeof(float), Attribute::F32Type); addAttribute(QQuick3DGeometry::Attribute::TexCoordSemantic, 6 * sizeof(float), QQuick3DGeometry::Attribute::F32Type); setVertexData(vertexData); setIndexData(indexData); setStride(sizeof(float) * (3 + 3 + 2)); QVector3D boundsMin, boundsMax; auto vertices = reinterpret_cast<const float*>(vertexData.constData()); for(size_t i = 0; i < vertexData.length() / sizeof(float); i += 8) { for(size_t j = 0; j < 3; j++) { boundsMin[j] = (std::min(vertices[i + j], i == 0 ? vertices[i + j] : boundsMin[j])); boundsMax[j] = (std::max(vertices[i + j], i == 0 ? vertices[i + j] : boundsMax[j])); } } setBounds(boundsMin, boundsMax); }