Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qssgrenderray.cpp
Go to the documentation of this file.
1// Copyright (C) 2008-2012 NVIDIA Corporation.
2// Copyright (C) 2019 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4
5#include "qssgrenderray_p.h"
6
7#include <QtQuick3DUtils/private/qssgplane_p.h>
8#include <QtQuick3DUtils/private/qssgutils_p.h>
9#include <QtQuick3DUtils/private/qssgmeshbvh_p.h>
10#include <QtQuick3DRuntimeRender/private/qssgrendermesh_p.h>
11
12#include <optional>
13
15
16// http://www.siggraph.org/education/materials/HyperGraph/raytrace/rayplane_intersection.htm
17
18std::optional<QVector3D> QSSGRenderRay::intersect(const QSSGPlane &inPlane, const QSSGRenderRay &ray)
19{
20 float Vd = QVector3D::dotProduct(inPlane.n, ray.direction);
21 if (std::abs(Vd) < .0001f)
22 return std::nullopt;
23 float V0 = -1.0f * (QVector3D::dotProduct(inPlane.n, ray.origin) + inPlane.d);
24 float t = V0 / Vd;
25 return ray.origin + (ray.direction * t);
26}
27
29 const QSSGRenderRay &ray)
30{
31 using DirectionOp = RayData::DirectionOp;
32 QMatrix4x4 originTransform = globalTransform.inverted();
33 QVector3D transformedOrigin = QSSGUtils::mat44::transform(originTransform, ray.origin);
34 float *outOriginTransformPtr(originTransform.data());
35 outOriginTransformPtr[12] = outOriginTransformPtr[13] = outOriginTransformPtr[14] = 0.0f;
36 const QVector3D &transformedDirection = QSSGUtils::mat44::rotate(originTransform, ray.direction).normalized();
37 static auto getInverseAndDirOp = [](const QVector3D &dir, QVector3D &invDir, DirectionOp (&dirOp)[3]) {
38 for (int i = 0; i != 3; ++i) {
39 const float axisDir = dir[i];
40 dirOp[i] = qFuzzyIsNull(axisDir) ? DirectionOp::Zero : ((axisDir < -std::numeric_limits<float>::epsilon())
41 ? DirectionOp::Swap
42 : DirectionOp::Normal);
43 invDir[i] = qFuzzyIsNull(axisDir) ? 0.0f : (1.0f / axisDir);
44 }
45 };
46 DirectionOp dirOp[3];
47 QVector3D transformedDirectionInvers;
48 getInverseAndDirOp(transformedDirection, transformedDirectionInvers, dirOp);
49 return RayData{ globalTransform, ray, transformedOrigin, transformedDirectionInvers,
50 transformedDirection, { dirOp[0], dirOp[1], dirOp[2] } };
51}
52
54 const HitResult &hit)
55{
56 Q_ASSERT(hit.intersects());
57 Q_ASSERT(hit.bounds != nullptr);
58 const QSSGBounds3 &bounds = *hit.bounds;
59 // Local postion
60 const QVector3D &scaledDir = data.direction * hit.min;
61 const QVector3D &localPosition = scaledDir + data.origin;
62 // ray length squared
63 const QVector3D &globalPosition = QSSGUtils::mat44::transform(data.globalTransform, localPosition);
64 const QVector3D &cameraToLocal = data.ray.origin - globalPosition;
65 const float rayLenSquared = QSSGUtils::vec3::magnitudeSquared(cameraToLocal);
66 // UV coordinates
67 const auto &boundsMin = bounds.minimum;
68 const auto &boundsMax = bounds.maximum;
69 const float xRange = boundsMax.x() - boundsMin.x();
70 const float yRange = boundsMax.y() - boundsMin.y();
71 const QVector2D uvCoords{((localPosition[0] - boundsMin.x()) / xRange), ((localPosition[1] - boundsMin.y()) / yRange)};
72
73 // Since we just intersected with a bounding box, there is no face normal
74 return IntersectionResult(rayLenSquared, uvCoords, globalPosition, localPosition, QVector3D());
75}
76
78 const QSSGBounds3 &bounds)
79{
80 // Intersect the origin with the AABB described by bounds.
81
82 // Scan each axis separately. This code basically finds the distance
83 // from the origin to the near and far bbox planes for a given
84 // axis. It then divides this distance by the direction for that axis to
85 // get a range of t [near,far] that the ray intersects assuming the ray is
86 // described via origin + t*(direction). Running through all three axis means
87 // that you need to min/max those ranges together to find a global min/max
88 // that the pick could possibly be in.
89 float tmax = std::numeric_limits<float>::max();
90 float tmin = std::numeric_limits<float>::min();
91 float origin;
92 const QVector3D *const barray[] { &bounds.minimum, &bounds.maximum };
93
94 for (int axis = 0; axis != 3; ++axis) {
95 origin = data.origin[axis];
96 const bool zeroDir = (data.dirOp[axis] == RayData::DirectionOp::Zero);
97 if (zeroDir && (origin < bounds.minimum[axis] || origin > bounds.maximum[axis])) {
98 // Pickray is roughly parallel to the plane of the slab
99 // so, if the origin is not in the range, we have no intersection
100 return { -1.0f, -1.0f, nullptr };
101 }
102 if (!zeroDir) {
103 // Shrink the intersections to find the closest hit
104 tmax = std::min(((*barray[1-quint8(data.dirOp[axis])])[axis] - origin) * data.directionInvers[axis], tmax);
105 tmin = std::max(((*barray[quint8(data.dirOp[axis])])[axis] - origin) * data.directionInvers[axis], tmin);
106 }
107 }
108
109 return { tmin, tmax, &bounds };
110}
111
112// Möller-Trumbore ray-triangle intersection
113// https://www.graphics.cornell.edu/pubs/1997/MT97.pdf
114// https://fileadmin.cs.lth.se/cs/Personal/Tomas_Akenine-Moller/raytri/
116 const QVector3D &v0,
117 const QVector3D &v1,
118 const QVector3D &v2,
119 float &u,
120 float &v,
121 QVector3D &normal)
122{
123 const float epsilon = std::numeric_limits<float>::epsilon();
124
125 // Compute the Triangle's Edges
126 const QVector3D edge1 = v1 - v0;
127 const QVector3D edge2 = v2 - v0;
128
129 // Compute the vector P as the cross product of the ray direction and edge2
130 const QVector3D P = QVector3D::crossProduct(ray.direction, edge2);
131
132 // Compute the determinant
133 const float determinant = QVector3D::dotProduct(edge1, P);
134
135 QVector3D Q;
136
137 // If determinant is near zero, the ray lies in the plane of the triangle
138 if (determinant > epsilon) {
139 // Compute the vector T from the ray origin to the first vertex of the triangle
140 const QVector3D T = ray.origin - v0;
141
142 // Calculate coordinate u and test bounds
143 u = QVector3D::dotProduct(T, P);
144 if (u < 0.0f || u > determinant)
145 return false;
146
147 // Compute the vector Q as the cross product of vector T and edge1
148 Q = QVector3D::crossProduct(T, edge1);
149
150 // Calculate coordinate v and test bounds
152 if (v < 0.0f || ((u + v) > determinant))
153 return false;
154 } /*else if (determinant < -epsilon) { // This would be if we cared about backfaces
155 // Compute the vector T from the ray origin to the first vertex of the triangle
156 const QVector3D T = ray.origin - v0;
157
158 // Calculate coordinate u and test bounds
159 u = QVector3D::dotProduct(T, P);
160 if (u > 0.0f || u < determinant)
161 return false;
162
163 // Compute the vector Q as the cross product of vector T and edge1
164 Q = QVector3D::crossProduct(T, edge1);
165
166 // Calculate coordinate v and test bounds
167 v = QVector3D::dotProduct(ray.direction, Q);
168 if (v > 0.0f || ((u + v) < determinant))
169 return false;
170 } */else {
171 // Ray is parallel to the plane of the triangle
172 return false;
173 }
174
175 const float invDeterminant = 1.0f / determinant;
176
177 // Calculate the value of t, the parameter of the intersection point along the ray
178 const float t = QVector3D::dotProduct(edge2, Q) * invDeterminant;
179
180 if (t > epsilon) {
181 normal = QVector3D::crossProduct(edge1, edge2).normalized();
182 u *= invDeterminant;
183 v *= invDeterminant;
184 return true;
185 }
186
187 return false;
188}
189
190
192 const QSSGMeshBVHNode *bvh,
193 const QSSGRenderMesh *mesh,
194 QVector<IntersectionResult> &intersections,
195 int depth)
196{
197 if (!bvh || !mesh || !mesh->bvh)
198 return;
199
200 // If this is a leaf node, process it's triangles
201 if (bvh->count != 0) {
202 // If there is an intersection on a leaf node, then test against geometry
203 auto results = intersectWithBVHTriangles(data, mesh->bvh->triangles(), bvh->offset, bvh->count);
204 if (!results.isEmpty())
205 intersections.append(results);
206 return;
207 }
208
210 if (hit.intersects())
211 intersectWithBVH(data, static_cast<const QSSGMeshBVHNode *>(bvh->left), mesh, intersections, depth + 1);
212
214 if (hit.intersects())
215 intersectWithBVH(data, static_cast<const QSSGMeshBVHNode *>(bvh->right), mesh, intersections, depth + 1);
216}
217
218
219
220QVector<QSSGRenderRay::IntersectionResult> QSSGRenderRay::intersectWithBVHTriangles(const RayData &data,
221 const QSSGMeshBVHTriangles &bvhTriangles,
222 int triangleOffset,
223 int triangleCount)
224{
225 Q_ASSERT(bvhTriangles.size() >= size_t(triangleOffset + triangleCount));
226
227 QVector<QSSGRenderRay::IntersectionResult> results;
228
229 for (int i = triangleOffset; i < triangleCount + triangleOffset; ++i) {
230 const auto &triangle = bvhTriangles[i];
231
232 QSSGRenderRay relativeRay(data.origin, data.direction);
233
234 // Use Barycentric Coordinates to get the intersection values
235 float u = 0.f;
236 float v = 0.f;
237 QVector3D normal;
238 const bool intersects = triangleIntersect(relativeRay,
239 triangle.vertex1,
240 triangle.vertex2,
241 triangle.vertex3,
242 u,
243 v,
244 normal);
245 if (intersects) {
246 const float w = 1.0f - u - v;
247 const QVector3D localIntersectionPoint = w * triangle.vertex1 +
248 u * triangle.vertex2 +
249 v * triangle.vertex3;
250
251 const QVector2D uvCoordinate = w * triangle.uvCoord1 +
252 u * triangle.uvCoord2 +
253 v * triangle.uvCoord3;
254 // Get the intersection point in scene coordinates
255 const QVector3D sceneIntersectionPos = QSSGUtils::mat44::transform(data.globalTransform,
256 localIntersectionPoint);
257 const QVector3D hitVector = data.ray.origin - sceneIntersectionPos;
258 // Get the magnitude of the hit vector
259 const float rayLengthSquared = QSSGUtils::vec3::magnitudeSquared(hitVector);
260 results.append(IntersectionResult(rayLengthSquared,
261 uvCoordinate,
262 sceneIntersectionPos,
263 localIntersectionPoint,
264 normal));
265 }
266 }
267
268 // Does not intersect with any of the triangles
269 return results;
270}
271
272std::optional<QVector2D> QSSGRenderRay::relative(const QMatrix4x4 &inGlobalTransform,
273 const QSSGBounds3 &inBounds,
274 QSSGRenderBasisPlanes inPlane) const
275{
276 QMatrix4x4 theOriginTransform = inGlobalTransform.inverted();
277
278 QVector3D theTransformedOrigin = QSSGUtils::mat44::transform(theOriginTransform, origin);
279 float *outOriginTransformPtr(theOriginTransform.data());
280 outOriginTransformPtr[12] = outOriginTransformPtr[13] = outOriginTransformPtr[14] = 0.0f;
281 QVector3D theTransformedDirection = QSSGUtils::mat44::rotate(theOriginTransform, direction);
282
283 // The XY plane is going to be a plane with either positive or negative Z direction that runs
284 // through
285 QVector3D theDirection(0, 0, 1);
286 QVector3D theRight(1, 0, 0);
287 QVector3D theUp(0, 1, 0);
288 switch (inPlane) {
290 break;
292 theDirection = QVector3D(0, 1, 0);
293 theUp = QVector3D(0, 0, 1);
294 break;
296 theDirection = QVector3D(1, 0, 0);
297 theRight = QVector3D(0, 0, 1);
298 break;
299 }
300 QSSGPlane thePlane(theDirection,
301 QVector3D::dotProduct(theDirection, theTransformedDirection) > 0.0f
302 ? QVector3D::dotProduct(theDirection, inBounds.maximum)
303 : QVector3D::dotProduct(theDirection, inBounds.minimum));
304
305 const QSSGRenderRay relativeRay(theTransformedOrigin, theTransformedDirection);
306 std::optional<QVector3D> localIsect = QSSGRenderRay::intersect(thePlane, relativeRay);
307 if (localIsect.has_value()) {
308 float xRange = QVector3D::dotProduct(theRight, inBounds.maximum) - QVector3D::dotProduct(theRight, inBounds.minimum);
309 float yRange = QVector3D::dotProduct(theUp, inBounds.maximum) - QVector3D::dotProduct(theUp, inBounds.minimum);
310 float xOrigin = xRange / 2.0f + QVector3D::dotProduct(theRight, inBounds.minimum);
311 float yOrigin = yRange / 2.0f + QVector3D::dotProduct(theUp, inBounds.minimum);
312 return QVector2D((QVector3D::dotProduct(theRight, *localIsect) - xOrigin) / xRange,
313 (QVector3D::dotProduct(theUp, *localIsect) - yOrigin) / yRange);
314 }
315 return std::nullopt;
316}
317
bool isEmpty() const noexcept
Definition qlist.h:401
void append(parameter_type t)
Definition qlist.h:458
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
QMatrix4x4 inverted(bool *invertible=nullptr) const
Returns the inverse of this matrix.
Class representing 3D range or axis aligned bounding box.
QVector3D minimum
QVector3D maximum
QSSGBounds3 boundingData
Representation of a plane.
Definition qssgplane_p.h:31
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
QVector3D normalized() const noexcept
Returns the normalized unit vector form of this vector.
Definition qvectornd.h:695
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:671
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:670
static constexpr float dotProduct(QVector3D v1, QVector3D v2) noexcept
Returns the dot product of v1 and v2.
Definition qvectornd.h:770
static constexpr QVector3D crossProduct(QVector3D v1, QVector3D v2) noexcept
Returns the cross-product of vectors v1 and v2, which is normal to the plane spanned by v1 and v2.
Definition qvectornd.h:775
QVector3D Q_QUICK3DUTILS_EXPORT transform(const QMatrix4x4 &m, const QVector3D &v)
Definition qssgutils.cpp:86
QVector3D Q_QUICK3DUTILS_EXPORT rotate(const QMatrix4x4 &m, const QVector3D &v)
Definition qssgutils.cpp:75
float Q_QUICK3DUTILS_EXPORT magnitudeSquared(const QVector3D &v)
Definition qssgutils.cpp:28
Combined button and popup list for selecting options.
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:349
GLint GLfloat GLfloat GLfloat v2
GLsizei const GLfloat * v
[13]
GLint GLenum GLsizei GLsizei GLsizei depth
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint GLfloat v0
GLint GLfloat GLfloat v1
GLdouble GLdouble t
Definition qopenglext.h:243
static const qreal epsilon
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
std::vector< QSSGMeshBVHTriangle > QSSGMeshBVHTriangles
QSSGRenderBasisPlanes
#define v0
unsigned char quint8
Definition qtypes.h:46
static const uint tmax
Definition qurlidna.cpp:22
static const uint tmin
Definition qurlidna.cpp:21
QString dir
[11]
std::unique_ptr< QSSGMeshBVH > bvh
static void intersectWithBVH(const RayData &data, const QSSGMeshBVHNode *bvh, const QSSGRenderMesh *mesh, QVector< IntersectionResult > &intersections, int depth=0)
static std::optional< QVector3D > intersect(const QSSGPlane &inPlane, const QSSGRenderRay &ray)
static bool triangleIntersect(const QSSGRenderRay &ray, const QVector3D &v0, const QVector3D &v1, const QVector3D &v2, float &u, float &v, QVector3D &normal)
static IntersectionResult createIntersectionResult(const RayData &data, const HitResult &hit)
QVector3D direction
static RayData createRayData(const QMatrix4x4 &globalTransform, const QSSGRenderRay &ray)
std::optional< QVector2D > relative(const QMatrix4x4 &inGlobalTransform, const QSSGBounds3 &inBounds, QSSGRenderBasisPlanes inPlane) const
static HitResult intersectWithAABBv2(const RayData &data, const QSSGBounds3 &bounds)
static QVector< IntersectionResult > intersectWithBVHTriangles(const RayData &data, const std::vector< QSSGMeshBVHTriangle > &bvhTriangles, int triangleOffset, int triangleCount)