diff options
| author | Jonas Karlsson <[email protected]> | 2025-01-07 23:12:35 +0100 |
|---|---|---|
| committer | Qt Cherry-pick Bot <[email protected]> | 2025-01-13 16:07:58 +0000 |
| commit | d7256b29a1719e772dd6437eb086cc6d25f18cf6 (patch) | |
| tree | 8c723f496da58f74a2b259391b9f5f4ec7ba3a79 | |
| parent | 0f5722f44f6419cdd30f91d231ea5223131e3cdd (diff) | |
Fix broken shadow mapping with orthographic camera
The issue was that QSSGRenderCamera::calculateViewProjectionMatrix is
assuming the camera is a perspective camera. Instead of using this
method we use the original frustum points of the camera and move them
forward or backwards according to the split t values.
The same issue was present when calculating the radius of the frustum
when locked texels are active.
This change also makes the clipNear be the same as the camera's instead
of being hardcoded and clipFar not be limited.
This change also moves the pcfRadius expansion out of the frustum
calculation and does it on the shadowmap bounding box instead.
Fixes: QTBUG-132081
Pick-to: 6.8
Change-Id: Ic7cc725609946ab5adf7e23cf943e06c2a01b968
Reviewed-by: Eskil Abrahamsen Blomfeldt <[email protected]>
(cherry picked from commit e67c6bd6c86922bd833d9440a6c53d31380a5ed3)
Reviewed-by: Qt Cherry-pick Bot <[email protected]>
| -rw-r--r-- | src/runtimerender/rendererimpl/qssgrenderhelpers.cpp | 155 | ||||
| -rw-r--r-- | tests/baseline/data/shadows/CSMConesCylindersHighNear.qml | 218 | ||||
| -rw-r--r-- | tests/baseline/data/shadows/CustomCamera.qml | 117 | ||||
| -rw-r--r-- | tests/baseline/data/shadows/FrustumCamera.qml | 116 | ||||
| -rw-r--r-- | tests/baseline/data/shadows/Orthographic.qml | 111 | ||||
| -rw-r--r-- | tests/baseline/data/shadows/OrthographicLockedTexels.qml | 112 |
6 files changed, 729 insertions, 100 deletions
diff --git a/src/runtimerender/rendererimpl/qssgrenderhelpers.cpp b/src/runtimerender/rendererimpl/qssgrenderhelpers.cpp index f67e4c00..8286d6a9 100644 --- a/src/runtimerender/rendererimpl/qssgrenderhelpers.cpp +++ b/src/runtimerender/rendererimpl/qssgrenderhelpers.cpp @@ -129,91 +129,44 @@ std::pair<QSSGBounds3, QSSGBounds3> RenderHelpers::calculateSortedObjectBounds(c return { boundsCasting, boundsReceiving }; } -static QSSGBoxPoints computeFrustumBounds(const QSSGRenderCamera &inCamera) +static QSSGBoxPoints computeFrustumBounds(const QMatrix4x4 &projection) { - QMatrix4x4 viewProjection; - inCamera.calculateViewProjectionMatrix(viewProjection); - bool invertible = false; - QMatrix4x4 inv = viewProjection.inverted(&invertible); + QMatrix4x4 inv = projection.inverted(&invertible); Q_ASSERT(invertible); + // The frustum points will be in this orientation + // + // bottom top + // 4__________5 7__________6 + // \ / \ / + // \ / \ / + // \____/ \____/ + // 0 1 3 2 return { inv.map(QVector3D(-1, -1, -1)), inv.map(QVector3D(+1, -1, -1)), inv.map(QVector3D(+1, +1, -1)), inv.map(QVector3D(-1, +1, -1)), inv.map(QVector3D(-1, -1, +1)), inv.map(QVector3D(+1, -1, +1)), inv.map(QVector3D(+1, +1, +1)), inv.map(QVector3D(-1, +1, +1)) }; } -static QSSGBoxPoints computeFrustumBoundsWithNearFar(const QSSGRenderCamera &inCamera, float clipNear, float clipFar, float pcfRadius) +static QSSGBoxPoints sliceFrustum(const QSSGBoxPoints &frustumPoints, float t0, float t1) { - QMatrix4x4 viewProjection; - inCamera.calculateViewProjectionMatrix(viewProjection, clipNear, clipFar); - - bool invertible = false; - QMatrix4x4 inv = viewProjection.inverted(&invertible); - Q_ASSERT(invertible); - - QSSGBoxPoints pts = { inv.map(QVector3D(-1, -1, -1)), inv.map(QVector3D(+1, -1, -1)), - inv.map(QVector3D(+1, +1, -1)), inv.map(QVector3D(-1, +1, -1)), - inv.map(QVector3D(-1, -1, +1)), inv.map(QVector3D(+1, -1, +1)), - inv.map(QVector3D(+1, +1, +1)), inv.map(QVector3D(-1, +1, +1)) }; - - if (pcfRadius > 0.f) { - // Expand the frustum in all directions to cover the pcf radius - const QVector3D origin = inv.map(QVector3D(0, 0, 0)); - const QVector3D x = (inv.map(QVector3D(1, 0, 0)) - origin).normalized() * pcfRadius; - const QVector3D y = (inv.map(QVector3D(0, 1, 0)) - origin).normalized() * pcfRadius; - const QVector3D z = (inv.map(QVector3D(0, 0, 1)) - origin).normalized() * pcfRadius; - - // bottom top - // 4__________5 7__________6 - // \ / \ / - // \ / \ / - // \____/ \____/ - // 0 1 3 2 - // left - pts[0] -= x; - pts[3] -= x; - pts[7] -= x; - pts[4] -= x; - // right - pts[1] += x; - pts[5] += x; - pts[6] += x; - pts[2] += x; - // top - pts[3] += y; - pts[2] += y; - pts[6] += y; - pts[7] += y; - // bottom - pts[0] -= y; - pts[1] -= y; - pts[5] -= y; - pts[4] -= y; - // back - pts[0] -= z; - pts[1] -= z; - pts[2] -= z; - pts[3] -= z; - // front - pts[4] += z; - pts[5] += z; - pts[6] += z; - pts[7] += z; + QSSGBoxPoints pts; + for (int i = 0; i < 4; ++i) { + const QVector3D forward = frustumPoints[i + 4] - frustumPoints[i]; + pts[i] = frustumPoints[i] + forward * t0; + pts[i + 4] = frustumPoints[i] + forward * t1; } - return pts; } -static std::unique_ptr<QSSGRenderCamera> computeShadowCameraFromFrustum(const QSSGRenderCamera &inCamera, - const QMatrix4x4 &lightMatrix, +static std::unique_ptr<QSSGRenderCamera> computeShadowCameraFromFrustum(const QMatrix4x4 &lightMatrix, const QMatrix4x4 &lightMatrixInverted, const QVector3D &lightPivot, const QVector3D &lightForward, const QVector3D &lightUp, const float shadowMapResolution, const float pcfRadius, - float clipRange, + const QSSGBoxPoints &frustumPoints, float frustumStartT, float frustumEndT, float frustumRadius, @@ -239,14 +192,11 @@ static std::unique_ptr<QSSGRenderCamera> computeShadowCameraFromFrustum(const QS return result; }; - const float clipNear = 1.0f + clipRange * frustumStartT; - const float clipFar = 1.0f + clipRange * frustumEndT; - - QSSGBoxPoints frustumPoints = computeFrustumBoundsWithNearFar(inCamera, clipNear, clipFar, pcfRadius); + QSSGBoxPoints frustumPointsSliced = sliceFrustum(frustumPoints, frustumStartT, frustumEndT); if (drawCascades) - ShadowmapHelpers::addDebugFrustum(frustumPoints, QColorConstants::Black, debugDrawSystem); + ShadowmapHelpers::addDebugFrustum(frustumPointsSliced, QColorConstants::Black, debugDrawSystem); - QList<QVector3D> receivingSliced = ShadowmapHelpers::intersectBoxByFrustum(frustumPoints, + QList<QVector3D> receivingSliced = ShadowmapHelpers::intersectBoxByFrustum(frustumPointsSliced, receivingBox.toQSSGBoxPoints(), drawSceneCascadeIntersection ? debugDrawSystem : nullptr, QColorConstants::DarkGray); @@ -276,6 +226,9 @@ static std::unique_ptr<QSSGRenderCamera> computeShadowCameraFromFrustum(const QS castReceiveBounds.minimum.setZ(zMin); } + // Expand to fit pcf radius + castReceiveBounds.fatten(pcfRadius); + QVector3D boundsCenterWorld = lightMatrixInverted.map(castReceiveBounds.center()); QVector3D boundsDims = castReceiveBounds.dimensions(); boundsDims.setZ(boundsDims.z() * 1.01f); // Expand slightly in z direction to avoid pancaking precision errors @@ -319,6 +272,8 @@ static QVarLengthArray<std::unique_ptr<QSSGRenderCamera>, 4> setupCascadingCamer const QSSGRenderLight *inLight, const int shadowMapResolution, const float pcfRadius, + const float clipNear, + const float clipFar, const QSSGBounds3 &castingObjectsBox, const QSSGBounds3 &receivingObjectsBox, bool lockShadowmapTexels, @@ -329,6 +284,9 @@ static QVarLengthArray<std::unique_ptr<QSSGRenderCamera>, 4> setupCascadingCamer Q_ASSERT(inLight->type == QSSGRenderLight::Type::DirectionalLight); QVarLengthArray<std::unique_ptr<QSSGRenderCamera>, 4> result; + if (clipNear >= clipFar || qFuzzyCompare(clipNear, clipFar)) + return result; + const QVector3D lightDir = inLight->getDirection(); const QVector3D lightPivot = inLight->pivot; @@ -345,26 +303,17 @@ static QVarLengthArray<std::unique_ptr<QSSGRenderCamera>, 4> setupCascadingCamer lightMatrix.setRow(3, QVector4D(0.0f, 0.0f, 0.0f, 1.0f)); QMatrix4x4 lightMatrixInverted = lightMatrix.inverted(); - const float clipFar = qMax(2.0f, qMin(inLight->m_shadowMapFar, inCamera.clipFar)); - constexpr float clipNear = 1.0f; - const float clipRange = clipFar - clipNear; + const float farScale = (clipFar - clipNear) / (inCamera.clipFar - inCamera.clipNear); + + QMatrix4x4 viewProjection(Qt::Uninitialized); + inCamera.calculateViewProjectionMatrix(viewProjection); + const QSSGBoxPoints frustum = computeFrustumBounds(viewProjection); + const QSSGBoxPoints frustumUntransformed = lockShadowmapTexels ? computeFrustumBounds(inCamera.projection) : QSSGBoxPoints(); // We calculate the radius of the cascade without rotation or translation so we always get // the same floating point value. const auto calcFrustumRadius = [&](float t0, float t1) -> float { - const float f = clipNear + clipRange * t1; - const float n = clipNear + clipRange * t0; - QMatrix4x4 proj = inCamera.projection; - proj(2, 2) = -(f + n) / (f - n); - proj(2, 3) = -2 * f * n / (f - n); - bool invertible = false; - QMatrix4x4 inv = proj.inverted(&invertible); - Q_ASSERT(invertible); - - QSSGBoxPoints pts = { inv.map(QVector3D(-1, -1, -1)), inv.map(QVector3D(+1, -1, -1)), - inv.map(QVector3D(+1, +1, -1)), inv.map(QVector3D(-1, +1, -1)), - inv.map(QVector3D(-1, -1, +1)), inv.map(QVector3D(+1, -1, +1)), - inv.map(QVector3D(+1, +1, +1)), inv.map(QVector3D(-1, +1, +1)) }; + const QSSGBoxPoints pts = sliceFrustum(frustumUntransformed, t0 * farScale, t1 * farScale); QVector3D center = QVector3D(0.f, 0.f, 0.f); for (QVector3D point : pts) { @@ -396,17 +345,16 @@ static QVarLengthArray<std::unique_ptr<QSSGRenderCamera>, 4> setupCascadingCamer const auto computeFrustums = [&](const QVarLengthArray<float, 3> &splits) { for (const auto &range : computeSplitRanges(splits)) { const float frustumRadius = lockShadowmapTexels ? calcFrustumRadius(range.first, range.second) : 0.0f; - auto camera = computeShadowCameraFromFrustum(inCamera, - lightMatrix, + auto camera = computeShadowCameraFromFrustum(lightMatrix, lightMatrixInverted, lightPivot, forward, up, shadowMapResolution, pcfRadius, - clipRange, - range.first, - range.second, + frustum, + range.first * farScale, + range.second * farScale, frustumRadius, lockShadowmapTexels, castingObjectsBox, @@ -1502,24 +1450,28 @@ void RenderHelpers::rhiRenderShadowMap(QSSGRhiContext *rhiCtx, QVarLengthArray<std::unique_ptr<QSSGRenderCamera>, 4> cascades; if (light->type == QSSGRenderLight::Type::DirectionalLight) { const float pcfRadius = light->m_softShadowQuality == QSSGRenderLight::SoftShadowQuality::Hard ? 0.f : light->m_pcfFactor; + const float clipNear = camera.clipNear; + const float clipFar = qMin(light->m_shadowMapFar, camera.clipFar); + const float clipRange = clipFar - clipNear; cascades = setupCascadingCamerasForShadowMap(disableShadowCameraUpdate ? *debugCamera : camera, light, size.width(), pcfRadius, + clipNear, + clipFar, castingObjectsBox, receivingObjectsBox, light->m_lockShadowmapTexels, debugDrawSystem, drawCascades, drawSceneCascadeIntersection); - const float shadowMapFar = qMax(2.0f, qMin(light->m_shadowMapFar, camera.clipFar)); // Write the split distances from value 0 in the z-axis of the eye view-space - pEntry->m_csmSplits[0] = shadowMapFar * (light->m_csmNumSplits > 0 ? light->m_csmSplit1 : 1.0f); - pEntry->m_csmSplits[1] = shadowMapFar * (light->m_csmNumSplits > 1 ? light->m_csmSplit2 : 1.0f); - pEntry->m_csmSplits[2] = shadowMapFar * (light->m_csmNumSplits > 2 ? light->m_csmSplit3 : 1.0f); - pEntry->m_csmSplits[3] = shadowMapFar * 1.0f; - pEntry->m_shadowMapFar = shadowMapFar; + pEntry->m_csmSplits[0] = clipNear + clipRange * (light->m_csmNumSplits > 0 ? light->m_csmSplit1 : 1.0f); + pEntry->m_csmSplits[1] = clipNear + clipRange * (light->m_csmNumSplits > 1 ? light->m_csmSplit2 : 1.0f); + pEntry->m_csmSplits[2] = clipNear + clipRange * (light->m_csmNumSplits > 2 ? light->m_csmSplit3 : 1.0f); + pEntry->m_csmSplits[3] = clipNear + clipRange * 1.0f; + pEntry->m_shadowMapFar = clipFar; } else if (light->type == QSSGRenderLight::Type::SpotLight) { auto spotlightCamera = std::make_unique<QSSGRenderCamera>(QSSGRenderCamera::Type::PerspectiveCamera); spotlightCamera->fov = qDegreesToRadians(light->m_coneAngle * 2.0f); @@ -1566,8 +1518,11 @@ void RenderHelpers::rhiRenderShadowMap(QSSGRhiContext *rhiCtx, cb->endPass(); QSSGRHICTX_STAT(rhiCtx, endRenderPass()); - if (drawDirectionalLightShadowBoxes) - ShadowmapHelpers::addDirectionalLightDebugBox(computeFrustumBounds(*cascadeCamera), debugDrawSystem); + if (drawDirectionalLightShadowBoxes) { + QMatrix4x4 viewProjection(Qt::Uninitialized); + cascadeCamera->calculateViewProjectionMatrix(viewProjection); + ShadowmapHelpers::addDirectionalLightDebugBox(computeFrustumBounds(viewProjection), debugDrawSystem); + } } Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("shadow_map")); } else { diff --git a/tests/baseline/data/shadows/CSMConesCylindersHighNear.qml b/tests/baseline/data/shadows/CSMConesCylindersHighNear.qml new file mode 100644 index 00000000..7260ee5e --- /dev/null +++ b/tests/baseline/data/shadows/CSMConesCylindersHighNear.qml @@ -0,0 +1,218 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick3D +import QtQuick3D.Helpers + +Item { + id: window + visible: true + width: 1200 + height: 720 + + PerspectiveCamera { + id: camera1 + position: Qt.vector3d(76.1126, 982.409, 1126.55) + eulerRotation: Qt.vector3d(-25.3464, -1.051, 0) + clipFar: 15000 + clipNear: 2000 + } + + SceneEnvironment { + id: environment1 + clearColor: "lightblue" + backgroundMode: SceneEnvironment.Color + antialiasingMode: SceneEnvironment.MSAA + antialiasingQuality: SceneEnvironment.High + } + + property list<int> offsets: [0, width/4, 2*width/4, 3*width/4] + + function addConesCylindersTriple(parentNode, z_pos) { + const newObject = Qt.createQmlObject(` + import QtQuick + import QtQuick3D + + Node { + property var z_positions: [0, 100, 200] + + PrincipledMaterial { + id: material + baseColor: "gray" + } + + Model { + source: "#Cone" + position: Qt.vector3d(0, 450, z_positions[0]) + eulerRotation.z: 180 + scale.y: 5 + materials: material + } + + Model { + source: "#Cone" + position.z: z_positions[1] + scale.y: 2.5 + materials: material + } + + Model { + source: "#Cylinder" + position: Qt.vector3d(0, 175, z_positions[2]) + materials: material + scale.y: 3.5 + } + } + `, + parentNode, + "ConesCylinders" + ); + newObject.z_positions = [z_pos, z_pos - 125, z_pos - 250]; + } + + View3D { + width: parent.width/4 + height: parent.height + x: offsets[0] + camera: camera1 + environment: environment1 + DirectionalLight { + castsShadow: true + shadowFactor: 100 + eulerRotation: Qt.vector3d(-40, -120, 0) + csmNumSplits: 0 + shadowMapQuality: Light.ShadowMapQualityHigh + } + + Model { + source: "#Cube" + scale: Qt.vector3d(25, 0.01, 135) + z: -5500 + materials: DefaultMaterial { + diffuseColor: "gray" + } + castsShadows: false + } + + Node { + Component.onCompleted: { + var z_pos = 0 + for (var i = 0; i < 25; i++) { + addConesCylindersTriple(this, z_pos) + z_pos -= 450 + } + } + } + } + + View3D { + width: parent.width/4 + height: parent.height + x: offsets[1] + + camera: camera1 + environment: environment1 + DirectionalLight { + castsShadow: true + shadowFactor: 100 + eulerRotation: Qt.vector3d(-40, -120, 0) + csmNumSplits: 1 + shadowMapQuality: Light.ShadowMapQualityHigh + } + + Model { + source: "#Cube" + scale: Qt.vector3d(25, 0.01, 135) + z: -5500 + materials: DefaultMaterial { + diffuseColor: "gray" + } + castsShadows: false + } + + Node { + Component.onCompleted: { + var z_pos = 0 + for (var i = 0; i < 25; i++) { + addConesCylindersTriple(this, z_pos) + z_pos -= 450 + } + } + } + } + + View3D { + width: parent.width/4 + height: parent.height + x: offsets[2] + + camera: camera1 + environment: environment1 + DirectionalLight { + castsShadow: true + shadowFactor: 100 + eulerRotation: Qt.vector3d(-40, -120, 0) + csmNumSplits: 2 + shadowMapQuality: Light.ShadowMapQualityHigh + } + + Model { + source: "#Cube" + scale: Qt.vector3d(25, 0.01, 135) + z: -5500 + materials: DefaultMaterial { + diffuseColor: "gray" + } + castsShadows: false + } + + Node { + Component.onCompleted: { + var z_pos = 0 + for (var i = 0; i < 25; i++) { + addConesCylindersTriple(this, z_pos) + z_pos -= 450 + } + } + } + } + + View3D { + width: parent.width/4 + height: parent.height + x: offsets[3] + + camera: camera1 + environment: environment1 + DirectionalLight { + castsShadow: true + shadowFactor: 100 + eulerRotation: Qt.vector3d(-40, -120, 0) + csmNumSplits: 3 + shadowMapQuality: Light.ShadowMapQualityHigh + } + + Model { + source: "#Cube" + scale: Qt.vector3d(25, 0.01, 135) + z: -5500 + materials: DefaultMaterial { + diffuseColor: "gray" + } + castsShadows: false + } + + Node { + Component.onCompleted: { + var z_pos = 0 + for (var i = 0; i < 25; i++) { + addConesCylindersTriple(this, z_pos) + z_pos -= 450 + } + } + } + } +} diff --git a/tests/baseline/data/shadows/CustomCamera.qml b/tests/baseline/data/shadows/CustomCamera.qml new file mode 100644 index 00000000..94a5c436 --- /dev/null +++ b/tests/baseline/data/shadows/CustomCamera.qml @@ -0,0 +1,117 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QtQuick3D +import QtQuick3D.Helpers + +Item { + id: window + visible: true + width: 1200 + height: 720 + + SceneEnvironment { + id: sceneEnvironment + clearColor: "lightblue" + backgroundMode: SceneEnvironment.Color + antialiasingMode: SceneEnvironment.MSAA + antialiasingQuality: SceneEnvironment.High + } + + CustomCamera { + id: camera1 + eulerRotation: Qt.vector3d(-10.4172, 7.45044, 0) + position: Qt.vector3d(381.119, 842.535, 955.253) + + property real near: 10.0 + property real far: 10000.0 + property real fov: 60.0 * Math.PI / 180.0 + projection: Qt.matrix4x4(Math.cos(fov / 2) / Math.sin(fov / 2) * (window.height / window.width), 0, 0, 0, + 0, Math.cos(fov / 2) / Math.sin(fov / 2), 0, 0, + 0, 0, -(near + far) / (far - near), -(2.0 * near * far) / (far - near), + 0, 0, -1, 0); + } + + Node { + id: sceneA + DirectionalLight { + castsShadow: true + eulerRotation: Qt.vector3d(-40, -120, 0) + csmNumSplits: 2 + shadowMapQuality: Light.ShadowMapQualityLow + csmBlendRatio: 0.05 + shadowBias: 20 + pcfFactor: 0 + softShadowQuality: Light.Hard + shadowMapFar: camera1.far + } + + Model { + id: ground + source: "#Cube" + scale: Qt.vector3d(25, 0.01, 135) + z: -5500 + materials: DefaultMaterial { + diffuseColor: "gray" + } + castsShadows: false + } + + Node { + Component.onCompleted: { + + var z_pos = 0 + for (var i = 0; i < 25; i++) { + var conesAndCylinderTrio = Qt.createQmlObject(` + import QtQuick + import QtQuick3D + + Node { + property var z_positions: [` + z_pos + `,` + (z_pos -125) + `,` + (z_pos - 250) + `] + + PrincipledMaterial { + id: material + baseColor: "gray" + } + + Model { + source: "#Cone" + position: Qt.vector3d(0, 450, z_positions[0]) + eulerRotation.z: 180 + scale.y: 5 + materials: material + } + + Model { + source: "#Cone" + position.z: z_positions[1] + scale.y: 2.5 + materials: material + } + + Model { + source: "#Cylinder" + position: Qt.vector3d(0, 175, z_positions[2]) + materials: material + scale.y: 3.5 + } + }`, + this, + "snippet" + i + ); + z_pos -= 450 + } + } + } + } + + View3D { + id: view + width: parent.width + height: parent.height + camera: camera1 + environment: sceneEnvironment + importScene: sceneA + } +} diff --git a/tests/baseline/data/shadows/FrustumCamera.qml b/tests/baseline/data/shadows/FrustumCamera.qml new file mode 100644 index 00000000..db72c53e --- /dev/null +++ b/tests/baseline/data/shadows/FrustumCamera.qml @@ -0,0 +1,116 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QtQuick3D +import QtQuick3D.Helpers + +Item { + id: window + visible: true + width: 1200 + height: 720 + + SceneEnvironment { + id: sceneEnvironment + clearColor: "lightblue" + backgroundMode: SceneEnvironment.Color + antialiasingMode: SceneEnvironment.MSAA + antialiasingQuality: SceneEnvironment.High + } + + FrustumCamera { + id: camera1 + eulerRotation: Qt.vector3d(-10.4172, 7.45044, 0) + position: Qt.vector3d(381.119, 842.535, 955.253) + + clipNear: 100 + clipFar: 10000 + top: 10 + bottom: -10 + left: -10 + right: 10 + } + + Node { + id: sceneA + DirectionalLight { + castsShadow: true + eulerRotation: Qt.vector3d(-40, -120, 0) + csmNumSplits: 2 + shadowMapQuality: Light.ShadowMapQualityLow + csmBlendRatio: 0.05 + shadowBias: 20 + pcfFactor: 0 + softShadowQuality: Light.Hard + shadowMapFar: camera1.clipFar + } + + Model { + id: ground + source: "#Cube" + scale: Qt.vector3d(25, 0.01, 135) + z: -5500 + materials: DefaultMaterial { + diffuseColor: "gray" + } + castsShadows: false + } + + Node { + Component.onCompleted: { + + var z_pos = 0 + for (var i = 0; i < 25; i++) { + var conesAndCylinderTrio = Qt.createQmlObject(` + import QtQuick + import QtQuick3D + + Node { + property var z_positions: [` + z_pos + `,` + (z_pos -125) + `,` + (z_pos - 250) + `] + + PrincipledMaterial { + id: material + baseColor: "gray" + } + + Model { + source: "#Cone" + position: Qt.vector3d(0, 450, z_positions[0]) + eulerRotation.z: 180 + scale.y: 5 + materials: material + } + + Model { + source: "#Cone" + position.z: z_positions[1] + scale.y: 2.5 + materials: material + } + + Model { + source: "#Cylinder" + position: Qt.vector3d(0, 175, z_positions[2]) + materials: material + scale.y: 3.5 + } + }`, + this, + "snippet" + i + ); + z_pos -= 450 + } + } + } + } + + View3D { + id: view + width: parent.width + height: parent.height + camera: camera1 + environment: sceneEnvironment + importScene: sceneA + } +} diff --git a/tests/baseline/data/shadows/Orthographic.qml b/tests/baseline/data/shadows/Orthographic.qml new file mode 100644 index 00000000..3befdd70 --- /dev/null +++ b/tests/baseline/data/shadows/Orthographic.qml @@ -0,0 +1,111 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QtQuick3D +import QtQuick3D.Helpers + +Item { + id: window + visible: true + width: 1200 + height: 720 + + SceneEnvironment { + id: sceneEnvironment + clearColor: "lightblue" + backgroundMode: SceneEnvironment.Color + antialiasingMode: SceneEnvironment.MSAA + antialiasingQuality: SceneEnvironment.High + } + + OrthographicCamera { + id: camera1 + verticalMagnification: 0.35 + horizontalMagnification: 0.35 + eulerRotation: Qt.vector3d(-10.4172, 7.45044, 0) + position: Qt.vector3d(381.119, 842.535, 955.253) + } + + Node { + id: sceneA + DirectionalLight { + castsShadow: true + eulerRotation: Qt.vector3d(-40, -120, 0) + csmNumSplits: 2 + shadowMapQuality: Light.ShadowMapQualityLow + csmBlendRatio: 0.05 + shadowBias: 20 + pcfFactor: 0 + softShadowQuality: Light.Hard + shadowMapFar: camera1.clipFar + } + + Model { + id: ground + source: "#Cube" + scale: Qt.vector3d(25, 0.01, 135) + z: -5500 + materials: DefaultMaterial { + diffuseColor: "gray" + } + castsShadows: false + } + + Node { + Component.onCompleted: { + + var z_pos = 0 + for (var i = 0; i < 25; i++) { + var conesAndCylinderTrio = Qt.createQmlObject(` + import QtQuick + import QtQuick3D + + Node { + property var z_positions: [` + z_pos + `,` + (z_pos -125) + `,` + (z_pos - 250) + `] + + PrincipledMaterial { + id: material + baseColor: "gray" + } + + Model { + source: "#Cone" + position: Qt.vector3d(0, 450, z_positions[0]) + eulerRotation.z: 180 + scale.y: 5 + materials: material + } + + Model { + source: "#Cone" + position.z: z_positions[1] + scale.y: 2.5 + materials: material + } + + Model { + source: "#Cylinder" + position: Qt.vector3d(0, 175, z_positions[2]) + materials: material + scale.y: 3.5 + } + }`, + this, + "snippet" + i + ); + z_pos -= 450 + } + } + } + } + + View3D { + id: view + width: parent.width + height: parent.height + camera: camera1 + environment: sceneEnvironment + importScene: sceneA + } +} diff --git a/tests/baseline/data/shadows/OrthographicLockedTexels.qml b/tests/baseline/data/shadows/OrthographicLockedTexels.qml new file mode 100644 index 00000000..a59bd78a --- /dev/null +++ b/tests/baseline/data/shadows/OrthographicLockedTexels.qml @@ -0,0 +1,112 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QtQuick3D +import QtQuick3D.Helpers + +Item { + id: window + visible: true + width: 1200 + height: 720 + + SceneEnvironment { + id: sceneEnvironment + clearColor: "lightblue" + backgroundMode: SceneEnvironment.Color + antialiasingMode: SceneEnvironment.MSAA + antialiasingQuality: SceneEnvironment.High + } + + OrthographicCamera { + id: camera1 + verticalMagnification: 0.35 + horizontalMagnification: 0.35 + eulerRotation: Qt.vector3d(-10.4172, 7.45044, 0) + position: Qt.vector3d(381.119, 842.535, 955.253) + } + + Node { + id: sceneA + DirectionalLight { + castsShadow: true + eulerRotation: Qt.vector3d(-40, -120, 0) + csmNumSplits: 2 + shadowMapQuality: Light.ShadowMapQualityHigh + csmBlendRatio: 0.05 + shadowBias: 20 + pcfFactor: 0 + softShadowQuality: Light.Hard + shadowMapFar: camera1.clipFar + lockShadowmapTexels: true + } + + Model { + id: ground + source: "#Cube" + scale: Qt.vector3d(25, 0.01, 135) + z: -5500 + materials: DefaultMaterial { + diffuseColor: "gray" + } + castsShadows: false + } + + Node { + Component.onCompleted: { + + var z_pos = 0 + for (var i = 0; i < 25; i++) { + var conesAndCylinderTrio = Qt.createQmlObject(` + import QtQuick + import QtQuick3D + + Node { + property var z_positions: [` + z_pos + `,` + (z_pos -125) + `,` + (z_pos - 250) + `] + + PrincipledMaterial { + id: material + baseColor: "gray" + } + + Model { + source: "#Cone" + position: Qt.vector3d(0, 450, z_positions[0]) + eulerRotation.z: 180 + scale.y: 5 + materials: material + } + + Model { + source: "#Cone" + position.z: z_positions[1] + scale.y: 2.5 + materials: material + } + + Model { + source: "#Cylinder" + position: Qt.vector3d(0, 175, z_positions[2]) + materials: material + scale.y: 3.5 + } + }`, + this, + "snippet" + i + ); + z_pos -= 450 + } + } + } + } + + View3D { + id: view + width: parent.width + height: parent.height + camera: camera1 + environment: sceneEnvironment + importScene: sceneA + } +} |
