Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qssgrenderreflectionmap.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
6#include <QtQuick3DRuntimeRender/private/qssgrenderreflectionprobe_p.h>
7#include <QtQuick3DRuntimeRender/private/qssgrenderlayer_p.h>
8#include <QtQuick3DRuntimeRender/private/qssglayerrenderdata_p.h>
10
12
13const int prefilterSampleCount = 16;
14
15QSSGRenderReflectionMap::QSSGRenderReflectionMap(const QSSGRenderContextInterface &inContext)
16 : m_context(inContext)
17{
18}
19
20QSSGRenderReflectionMap::~QSSGRenderReflectionMap()
21{
22 releaseCachedResources();
23}
24
25void QSSGRenderReflectionMap::releaseCachedResources()
26{
27 for (QSSGReflectionMapEntry &entry : m_reflectionMapList)
28 entry.destroyRhiResources();
29
30 m_reflectionMapList.clear();
31}
32
34 QRhiTexture::Format format,
35 const QSize &size,
36 QRhiTexture::Flags flags = {})
37{
38 auto texture = rhi->newTexture(format, size, 1, flags);
39 if (!texture->create())
40 qWarning("Failed to create reflection map texture of size %dx%d", size.width(), size.height());
41 return texture;
42}
43
45 QRhiRenderBuffer::Type type,
46 const QSize &size)
47{
48 auto renderBuffer = rhi->newRenderBuffer(type, size, 1);
49 if (!renderBuffer->create())
50 qWarning("Failed to build depth-stencil buffer of size %dx%d", size.width(), size.height());
51 return renderBuffer;
52}
53
54
55void QSSGRenderReflectionMap::addReflectionMapEntry(qint32 probeIdx, const QSSGRenderReflectionProbe &probe)
56{
57 QRhi *rhi = m_context.rhiContext()->rhi();
58 // Bail out if there is no QRhi, since we can't add entries without it
59 if (!rhi)
60 return;
61
62 QRhiTexture::Format rhiFormat = QRhiTexture::RGBA16F;
63
64 const QByteArray rtName = probe.debugObjectName.toLatin1();
65
66 const int mapRes = 1 << probe.reflectionMapRes;
67 QSize pixelSize(mapRes, mapRes);
68 QSSGReflectionMapEntry *pEntry = reflectionMapEntry(probeIdx);
69
70 if (!pEntry) {
71 QRhiRenderBuffer *depthStencil = allocateRhiReflectionRenderBuffer(rhi, QRhiRenderBuffer::DepthStencil, pixelSize);
72 QRhiTexture *map = allocateRhiReflectionTexture(rhi, rhiFormat, pixelSize, QRhiTexture::RenderTarget | QRhiTexture::CubeMap
73 | QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips);
74 QRhiTexture *prefiltered = allocateRhiReflectionTexture(rhi, rhiFormat, pixelSize, QRhiTexture::RenderTarget | QRhiTexture::CubeMap
75 | QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips);
76 m_reflectionMapList.push_back(QSSGReflectionMapEntry::withRhiCubeMap(probeIdx, map, prefiltered, depthStencil));
77
78 pEntry = &m_reflectionMapList.back();
79 }
80
81 if (pEntry) {
82 pEntry->m_needsRender = true;
83
84 if (probe.hasScheduledUpdate)
85 pEntry->m_rendered = false;
86
87 if (!pEntry->m_rhiDepthStencil || mapRes != pEntry->m_rhiCube->pixelSize().width()) {
88 pEntry->destroyRhiResources();
89 pEntry->m_rhiDepthStencil = allocateRhiReflectionRenderBuffer(rhi, QRhiRenderBuffer::DepthStencil, pixelSize);
90 pEntry->m_rhiCube = allocateRhiReflectionTexture(rhi, rhiFormat, pixelSize, QRhiTexture::RenderTarget | QRhiTexture::CubeMap
91 | QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips);
92 pEntry->m_rhiPrefilteredCube = allocateRhiReflectionTexture(rhi, rhiFormat, pixelSize, QRhiTexture::RenderTarget | QRhiTexture::CubeMap
93 | QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips);
94 }
95
96 // Additional graphics resources: samplers, render targets.
97 if (pEntry->m_rhiRenderTargets.isEmpty()) {
98 pEntry->m_rhiRenderTargets.resize(6);
99 for (int i = 0; i < 6; ++i)
100 pEntry->m_rhiRenderTargets[i] = nullptr;
101 }
102 Q_ASSERT(pEntry->m_rhiRenderTargets.size() == 6);
103
104 if (pEntry->m_skyBoxSrbs.isEmpty()) {
105 pEntry->m_skyBoxSrbs.resize(6);
106 for (int i = 0; i < 6; ++i)
107 pEntry->m_skyBoxSrbs[i] = nullptr;
108 }
109
110
111 for (const auto face : QSSGRenderTextureCubeFaces) {
112 QRhiTextureRenderTarget *&rt(pEntry->m_rhiRenderTargets[quint8(face)]);
113 if (!rt) {
114 QRhiColorAttachment att(pEntry->m_rhiCube);
115 att.setLayer(quint8(face)); // 6 render targets, each referencing one face of the cubemap
116 QRhiTextureRenderTargetDescription rtDesc;
117 rtDesc.setColorAttachments({ att });
118 rtDesc.setDepthStencilBuffer(pEntry->m_rhiDepthStencil);
119 rt = rhi->newTextureRenderTarget(rtDesc);
120 rt->setDescription(rtDesc);
121 if (!pEntry->m_rhiRenderPassDesc)
122 pEntry->m_rhiRenderPassDesc = rt->newCompatibleRenderPassDescriptor();
123 rt->setRenderPassDescriptor(pEntry->m_rhiRenderPassDesc);
124 if (!rt->create())
125 qWarning("Failed to build reflection map render target");
126 }
127 rt->setName(rtName + QByteArrayLiteral(" reflection cube face: ") + QSSGBaseTypeHelpers::displayName(face));
128 }
129
130 if (!pEntry->m_prefilterPipeline) {
131 const QSize mapSize = pEntry->m_rhiCube->pixelSize();
132
133 int mipmapCount = rhi->mipLevelsForSize(mapSize);
134 mipmapCount = qMin(mipmapCount, 6); // don't create more than 6 mip levels
135
136 // Create a renderbuffer for each mip level
137 for (int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
138 const QSize levelSize = QSize(mapSize.width() * std::pow(0.5, mipLevel),
139 mapSize.height() * std::pow(0.5, mipLevel));
140 pEntry->m_prefilterMipLevelSizes.insert(mipLevel, levelSize);
141 // Setup Render targets (6 * mipmapCount)
142 QVarLengthArray<QRhiTextureRenderTarget *, 6> renderTargets;
143 for (const auto face : QSSGRenderTextureCubeFaces) {
144 QRhiColorAttachment att(pEntry->m_rhiPrefilteredCube);
145 att.setLayer(quint8(face));
146 att.setLevel(mipLevel);
147 QRhiTextureRenderTargetDescription rtDesc;
148 rtDesc.setColorAttachments({att});
149 auto renderTarget = rhi->newTextureRenderTarget(rtDesc);
150 renderTarget->setName(rtName + QByteArrayLiteral(" reflection prefilter mip/face ")
151 + QByteArray::number(mipLevel) + QByteArrayLiteral("/") + QSSGBaseTypeHelpers::displayName(face));
152 renderTarget->setDescription(rtDesc);
153 if (!pEntry->m_rhiPrefilterRenderPassDesc)
154 pEntry->m_rhiPrefilterRenderPassDesc = renderTarget->newCompatibleRenderPassDescriptor();
155 renderTarget->setRenderPassDescriptor(pEntry->m_rhiPrefilterRenderPassDesc);
156 if (!renderTarget->create())
157 qWarning("Failed to build prefilter cube map render target");
158 renderTargets << renderTarget;
159 }
160 pEntry->m_rhiPrefilterRenderTargetsMap.insert(mipLevel, renderTargets);
161 }
162
163 const auto &prefilterShaderStages = m_context.shaderCache()->getBuiltInRhiShaders().getRhiReflectionprobePreFilterShader();
164
165 const QSSGRhiSamplerDescription samplerMipMapDesc {
166 QRhiSampler::Linear,
167 QRhiSampler::Linear,
168 QRhiSampler::Linear,
169 QRhiSampler::ClampToEdge,
170 QRhiSampler::ClampToEdge,
171 QRhiSampler::Repeat
172 };
173
174 const QSSGRhiSamplerDescription samplerDesc {
175 QRhiSampler::Linear,
176 QRhiSampler::Linear,
177 QRhiSampler::None,
178 QRhiSampler::ClampToEdge,
179 QRhiSampler::ClampToEdge,
180 QRhiSampler::Repeat
181 };
182
183 QRhiSampler *sampler = m_context.rhiContext()->sampler(samplerDesc);
184 QRhiSampler *cubeSampler = m_context.rhiContext()->sampler(samplerMipMapDesc);
185
186 QRhiVertexInputLayout inputLayout;
187 inputLayout.setBindings({
188 { 3 * sizeof(float) }
189 });
190 inputLayout.setAttributes({
191 { 0, 0, QRhiVertexInputAttribute::Float3, 0 }
192 });
193
194 int ubufElementSize = rhi->ubufAligned(128);
195 pEntry->m_prefilterVertBuffer = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufElementSize * 6);
196 pEntry->m_prefilterVertBuffer->create();
197
198 const int uBufSamplesSize = 16 * prefilterSampleCount + 8;
199 int uBufSamplesElementSize = rhi->ubufAligned(uBufSamplesSize);
200 pEntry->m_prefilterFragBuffer = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, uBufSamplesElementSize * mipmapCount);
201 pEntry->m_prefilterFragBuffer->create();
202
203 pEntry->m_prefilterPipeline = rhi->newGraphicsPipeline();
204 pEntry->m_prefilterPipeline->setCullMode(QRhiGraphicsPipeline::Front);
205 pEntry->m_prefilterPipeline->setFrontFace(QRhiGraphicsPipeline::CCW);
206 pEntry->m_prefilterPipeline->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
207 pEntry->m_prefilterPipeline->setShaderStages({
208 *prefilterShaderStages->vertexStage(),
209 *prefilterShaderStages->fragmentStage()
210 });
211
212 pEntry->m_prefilterSrb = rhi->newShaderResourceBindings();
213 pEntry->m_prefilterSrb->setBindings({
214 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, pEntry->m_prefilterVertBuffer, 128),
215 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, pEntry->m_prefilterFragBuffer, uBufSamplesSize),
216 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, pEntry->m_rhiCube, cubeSampler)
217 });
218 pEntry->m_prefilterSrb->create();
219
220 pEntry->m_prefilterPipeline->setVertexInputLayout(inputLayout);
221 pEntry->m_prefilterPipeline->setShaderResourceBindings(pEntry->m_prefilterSrb);
222 pEntry->m_prefilterPipeline->setRenderPassDescriptor(pEntry->m_rhiPrefilterRenderPassDesc);
223 if (!pEntry->m_prefilterPipeline->create())
224 qWarning("failed to create pre-filter reflection map pipeline state");
225
226 const auto &irradianceShaderStages = m_context.shaderCache()->getBuiltInRhiShaders().getRhienvironmentmapPreFilterShader(false /* isRGBE */);
227
228 pEntry->m_irradiancePipeline = rhi->newGraphicsPipeline();
229 pEntry->m_irradiancePipeline->setCullMode(QRhiGraphicsPipeline::Front);
230 pEntry->m_irradiancePipeline->setFrontFace(QRhiGraphicsPipeline::CCW);
231 pEntry->m_irradiancePipeline->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
232 pEntry->m_irradiancePipeline->setShaderStages({
233 *irradianceShaderStages->vertexStage(),
234 *irradianceShaderStages->fragmentStage()
235 });
236
237 int ubufIrradianceSize = rhi->ubufAligned(20);
238 pEntry->m_irradianceFragBuffer = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufIrradianceSize);
239 pEntry->m_irradianceFragBuffer->create();
240
241 pEntry->m_irradianceSrb = rhi->newShaderResourceBindings();
242 pEntry->m_irradianceSrb->setBindings({
243 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, pEntry->m_prefilterVertBuffer, 128),
244 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, pEntry->m_irradianceFragBuffer, 20),
245 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, pEntry->m_rhiCube, sampler)
246 });
247 pEntry->m_irradianceSrb->create();
248
249 pEntry->m_irradiancePipeline->setShaderResourceBindings(pEntry->m_irradianceSrb);
250 pEntry->m_irradiancePipeline->setVertexInputLayout(inputLayout);
251 pEntry->m_irradiancePipeline->setRenderPassDescriptor(pEntry->m_rhiPrefilterRenderPassDesc);
252 if (!pEntry->m_irradiancePipeline->create())
253 qWarning("failed to create irradiance reflection map pipeline state");
254 }
255
256 pEntry->m_timeSlicing = probe.timeSlicing;
257 pEntry->m_probeIndex = probeIdx;
258 Q_QUICK3D_PROFILE_ASSIGN_ID(&probe, pEntry);
259 }
260}
261
262void QSSGRenderReflectionMap::addTexturedReflectionMapEntry(qint32 probeIdx, const QSSGRenderReflectionProbe &probe)
263{
264 QSSGReflectionMapEntry *pEntry = reflectionMapEntry(probeIdx);
265 const QSSGRenderImageTexture probeTexture = m_context.bufferManager()->loadRenderImage(probe.texture, QSSGBufferManager::MipModeFollowRenderImage);
266 if (!pEntry) {
267 if (probeTexture.m_texture)
268 m_reflectionMapList.push_back(QSSGReflectionMapEntry::withRhiTexturedCubeMap(probeIdx, probeTexture.m_texture));
269 else
270 addReflectionMapEntry(probeIdx, probe);
271 } else {
272 if (pEntry->m_rhiDepthStencil)
273 pEntry->destroyRhiResources();
274 if (probeTexture.m_texture)
275 pEntry->m_rhiPrefilteredCube = probeTexture.m_texture;
276 }
277}
278
279QSSGReflectionMapEntry *QSSGRenderReflectionMap::reflectionMapEntry(int probeIdx)
280{
281 Q_ASSERT(probeIdx >= 0);
282
283 for (int i = 0; i < m_reflectionMapList.size(); i++) {
284 QSSGReflectionMapEntry *pEntry = &m_reflectionMapList[i];
285 if (pEntry->m_probeIndex == quint32(probeIdx))
286 return pEntry;
287 }
288
289 return nullptr;
290}
291
292QSSGReflectionMapEntry::QSSGReflectionMapEntry()
293 : m_probeIndex(std::numeric_limits<quint32>::max())
294{
295}
296
297// Vertex data for rendering reflection cube map
298static const float cube[] = {
299 -1.0f,-1.0f,-1.0f, // -X side
300 -1.0f,-1.0f, 1.0f,
301 -1.0f, 1.0f, 1.0f,
302 -1.0f, 1.0f, 1.0f,
303 -1.0f, 1.0f,-1.0f,
304 -1.0f,-1.0f,-1.0f,
305
306 -1.0f,-1.0f,-1.0f, // -Z side
307 1.0f, 1.0f,-1.0f,
308 1.0f,-1.0f,-1.0f,
309 -1.0f,-1.0f,-1.0f,
310 -1.0f, 1.0f,-1.0f,
311 1.0f, 1.0f,-1.0f,
312
313 -1.0f,-1.0f,-1.0f, // -Y side
314 1.0f,-1.0f,-1.0f,
315 1.0f,-1.0f, 1.0f,
316 -1.0f,-1.0f,-1.0f,
317 1.0f,-1.0f, 1.0f,
318 -1.0f,-1.0f, 1.0f,
319
320 -1.0f, 1.0f,-1.0f, // +Y side
321 -1.0f, 1.0f, 1.0f,
322 1.0f, 1.0f, 1.0f,
323 -1.0f, 1.0f,-1.0f,
324 1.0f, 1.0f, 1.0f,
325 1.0f, 1.0f,-1.0f,
326
327 1.0f, 1.0f,-1.0f, // +X side
328 1.0f, 1.0f, 1.0f,
329 1.0f,-1.0f, 1.0f,
330 1.0f,-1.0f, 1.0f,
331 1.0f,-1.0f,-1.0f,
332 1.0f, 1.0f,-1.0f,
333
334 -1.0f, 1.0f, 1.0f, // +Z side
335 -1.0f,-1.0f, 1.0f,
336 1.0f, 1.0f, 1.0f,
337 -1.0f,-1.0f, 1.0f,
338 1.0f,-1.0f, 1.0f,
339 1.0f, 1.0f, 1.0f,
340
341 0.0f, 1.0f, // -X side
342 1.0f, 1.0f,
343 1.0f, 0.0f,
344 1.0f, 0.0f,
345 0.0f, 0.0f,
346 0.0f, 1.0f,
347
348 1.0f, 1.0f, // -Z side
349 0.0f, 0.0f,
350 0.0f, 1.0f,
351 1.0f, 1.0f,
352 1.0f, 0.0f,
353 0.0f, 0.0f,
354
355 1.0f, 0.0f, // -Y side
356 1.0f, 1.0f,
357 0.0f, 1.0f,
358 1.0f, 0.0f,
359 0.0f, 1.0f,
360 0.0f, 0.0f,
361
362 1.0f, 0.0f, // +Y side
363 0.0f, 0.0f,
364 0.0f, 1.0f,
365 1.0f, 0.0f,
366 0.0f, 1.0f,
367 1.0f, 1.0f,
368
369 1.0f, 0.0f, // +X side
370 0.0f, 0.0f,
371 0.0f, 1.0f,
372 0.0f, 1.0f,
373 1.0f, 1.0f,
374 1.0f, 0.0f,
375
376 0.0f, 0.0f, // +Z side
377 0.0f, 1.0f,
378 1.0f, 0.0f,
379 0.0f, 1.0f,
380 1.0f, 1.0f,
381 1.0f, 0.0f,
382};
383
384float radicalInverseVdC(uint bits)
385{
386 bits = (bits << 16u) | (bits >> 16u);
387 bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
388 bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
389 bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
390 bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
391 return float(bits) * 2.3283064365386963e-10; // / 0x100000000
392}
393
394QVector2D hammersley(uint i, uint N)
395{
396 return QVector2D(float(i) / float(N), radicalInverseVdC(i));
397}
398
399QVector3D importanceSampleGGX(QVector2D xi, float roughness)
400{
401 float a = roughness*roughness;
402
403 float phi = 2.0f * M_PI * xi.x();
404 float cosTheta = sqrt((1.0f - xi.y()) / (1.0f + (a*a - 1.0f) * xi.y()));
405 float sinTheta = sqrt(1.0f - cosTheta * cosTheta);
406
407 // from spherical coordinates to cartesian coordinates
408 return QVector3D(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
409}
410
411float distributionGGX(float nDotH, float roughness)
412{
413 float a = roughness * roughness;
414 float a2 = a * a;
415 float nDotH2 = nDotH * nDotH;
416
417 float nom = a2;
418 float denom = nDotH2 * (a2 - 1.0f) + 1.0f;
419 denom = M_PI * denom * denom;
420
421 return nom / denom;
422}
423
424void fillPrefilterValues(float roughness, float resolution,
425 QVarLengthArray<QVector4D, prefilterSampleCount> &sampleDirections,
426 float &invTotalWeight, uint &sampleCount)
427{
428 for (int i = 0; i < prefilterSampleCount * 8; ++i) {
429 const QVector2D xi = hammersley(i, prefilterSampleCount);
430 const QVector3D half = importanceSampleGGX(xi, roughness);
431 QVector3D light = 2.0f * half.z() * half - QVector3D(0, 0, 1);
432 light.normalize();
433 const float D = distributionGGX(half.z(), roughness);
434 const float pdf = D * half.z() / (4.0f * half.z()) + 0.0001f;
435 const float saTexel = 4.0f * M_PI / (6.0f * resolution * resolution);
436 const float saSample = 1.0f / (float(prefilterSampleCount) * pdf + 0.0001f);
437 float mipLevel = roughness == 0.0f ? 0.0f : 0.5f * log2(saSample / saTexel);
438 if (light.z() > 0) {
439 sampleDirections.append(QVector4D(light, mipLevel));
440 invTotalWeight += light.z();
441 sampleCount++;
442 if (sampleCount >= prefilterSampleCount)
443 break;
444 }
445 }
446 invTotalWeight = 1.0f / invTotalWeight;
447}
448
449void QSSGReflectionMapEntry::renderMips(QSSGRhiContext *rhiCtx)
450{
451 auto *rhi = rhiCtx->rhi();
452 auto *cb = rhiCtx->commandBuffer();
453
454 auto *rub = rhi->nextResourceUpdateBatch();
455 rub->generateMips(m_rhiCube);
456 QRhiBuffer *vertexBuffer = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(cube));
457 vertexBuffer->create();
458 vertexBuffer->deleteLater();
459 rub->uploadStaticBuffer(vertexBuffer, cube);
460 cb->resourceUpdate(rub);
461
462 const QRhiCommandBuffer::VertexInput vbufBinding(vertexBuffer, 0);
463
464 int ubufElementSize = rhi->ubufAligned(128);
465
466 const int uBufSamplesSize = 16 * prefilterSampleCount + 8;
467 int uBufSamplesElementSize = rhi->ubufAligned(uBufSamplesSize);
468 int uBufIrradianceElementSize = rhi->ubufAligned(20);
469
470 // Uniform Data
471 QMatrix4x4 mvp = rhi->clipSpaceCorrMatrix();
472 mvp.perspective(90.0f, 1.0f, 0.1f, 10.0f);
473
474 auto lookAt = [](const QVector3D &eye, const QVector3D &center, const QVector3D &up) {
475 QMatrix4x4 viewMatrix;
476 viewMatrix.lookAt(eye, center, up);
477 return viewMatrix;
478 };
479 QVarLengthArray<QMatrix4x4, 6> views;
480 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(1.0, 0.0, 0.0), QVector3D(0.0f, -1.0f, 0.0f)));
481 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(-1.0, 0.0, 0.0), QVector3D(0.0f, -1.0f, 0.0f)));
482 if (rhi->isYUpInFramebuffer()) {
483 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 1.0, 0.0), QVector3D(0.0f, 0.0f, 1.0f)));
484 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, -1.0, 0.0), QVector3D(0.0f, 0.0f, -1.0f)));
485 } else {
486 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, -1.0, 0.0), QVector3D(0.0f, 0.0f, -1.0f)));
487 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 1.0, 0.0), QVector3D(0.0f, 0.0f, 1.0f)));
488 }
489 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 0.0, 1.0), QVector3D(0.0f, -1.0f, 0.0f)));
490 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 0.0, -1.0), QVector3D(0.0f, -1.0f, 0.0f)));
491
492 rub = rhi->nextResourceUpdateBatch();
493 for (const auto face : QSSGRenderTextureCubeFaces) {
494 rub->updateDynamicBuffer(m_prefilterVertBuffer, quint8(face) * ubufElementSize, 64, mvp.constData());
495 rub->updateDynamicBuffer(m_prefilterVertBuffer, quint8(face) * ubufElementSize + 64, 64, views[quint8(face)].constData());
496 }
497
498 const QSize mapSize = m_rhiCube->pixelSize();
499
500 int mipmapCount = rhi->mipLevelsForSize(mapSize);
501 mipmapCount = qMin(mipmapCount, 6);
502
503 const float resolution = mapSize.width();
504 QVarLengthArray<QVector4D, prefilterSampleCount> sampleDirections;
505
506 // set the samples uniform buffer data
507 for (int mipLevel = 0; mipLevel < mipmapCount - 1; ++mipLevel) {
508 Q_ASSERT(mipmapCount - 2);
509 const float roughness = float(mipLevel) / float(mipmapCount - 2);
510 float invTotalWeight = 0.0f;
511 uint sampleCount = 0;
512
513 sampleDirections.clear();
514 fillPrefilterValues(roughness, resolution, sampleDirections, invTotalWeight, sampleCount);
515
516 rub->updateDynamicBuffer(m_prefilterFragBuffer, mipLevel * uBufSamplesElementSize, 16 * prefilterSampleCount, sampleDirections.constData());
517 rub->updateDynamicBuffer(m_prefilterFragBuffer, mipLevel * uBufSamplesElementSize + 16 * prefilterSampleCount, 4, &invTotalWeight);
518 rub->updateDynamicBuffer(m_prefilterFragBuffer, mipLevel * uBufSamplesElementSize + 16 * prefilterSampleCount + 4, 4, &sampleCount);
519 }
520 {
521 const float roughness = 0.0f; // doesn't matter for irradiance
522 const float lodBias = 0.0f;
523 const int distribution = 0;
524 const int sampleCount = resolution / 4;
525
526 rub->updateDynamicBuffer(m_irradianceFragBuffer, 0, 4, &roughness);
527 rub->updateDynamicBuffer(m_irradianceFragBuffer, 4, 4, &resolution);
528 rub->updateDynamicBuffer(m_irradianceFragBuffer, 4 + 4, 4, &lodBias);
529 rub->updateDynamicBuffer(m_irradianceFragBuffer, 4 + 4 + 4, 4, &sampleCount);
530 rub->updateDynamicBuffer(m_irradianceFragBuffer, 4 + 4 + 4 + 4, 4, &distribution);
531 }
532
533 cb->resourceUpdate(rub);
534
535 // Render
536 for (int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
537 if (mipLevel > 0 && m_timeSlicing == QSSGRenderReflectionProbe::ReflectionTimeSlicing::AllFacesAtOnce)
538 mipLevel = m_timeSliceFrame;
539
540 for (auto face : QSSGRenderTextureCubeFaces) {
541 if (m_timeSlicing == QSSGRenderReflectionProbe::ReflectionTimeSlicing::IndividualFaces)
542 face = m_timeSliceFace;
543
544 cb->beginPass(m_rhiPrefilterRenderTargetsMap[mipLevel][quint8(face)], QColor(0, 0, 0, 1), { 1.0f, 0 }, nullptr, rhiCtx->commonPassFlags());
545 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(m_rhiPrefilterRenderTargetsMap[mipLevel][quint8(face)]));
546 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
547 if (mipLevel < mipmapCount - 1) {
548 // Specular pre-filtered Cube Map levels
549 cb->setGraphicsPipeline(m_prefilterPipeline);
550 cb->setVertexInput(0, 1, &vbufBinding);
551 cb->setViewport(QRhiViewport(0, 0, m_prefilterMipLevelSizes[mipLevel].width(), m_prefilterMipLevelSizes[mipLevel].height()));
552 QVector<QPair<int, quint32>> dynamicOffsets = {
553 { 0, quint32(ubufElementSize * quint8(face)) },
554 { 2, quint32(uBufSamplesElementSize * mipLevel) }
555 };
556 cb->setShaderResources(m_prefilterSrb, 2, dynamicOffsets.constData());
557 } else {
558 // Diffuse Irradiance
559 cb->setGraphicsPipeline(m_irradiancePipeline);
560 cb->setVertexInput(0, 1, &vbufBinding);
561 cb->setViewport(QRhiViewport(0, 0, m_prefilterMipLevelSizes[mipLevel].width(), m_prefilterMipLevelSizes[mipLevel].height()));
562 QVector<QPair<int, quint32>> dynamicOffsets = {
563 { 0, quint32(ubufElementSize * quint8(face)) },
564 { 2, quint32(uBufIrradianceElementSize) }
565 };
566 cb->setShaderResources(m_irradianceSrb, 1, dynamicOffsets.constData());
567 }
568 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
569 cb->draw(36);
570 QSSGRHICTX_STAT(rhiCtx, draw(36, 1));
571 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DRenderCall, 36llu | (1llu << 32), profilingId);
572 cb->endPass();
573 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
574 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QSSG_RENDERPASS_NAME("reflection_map", mipLevel, face));
575
576 if (m_timeSlicing == QSSGRenderReflectionProbe::ReflectionTimeSlicing::IndividualFaces)
577 break;
578 }
579
580 if (mipLevel > 0 && m_timeSlicing == QSSGRenderReflectionProbe::ReflectionTimeSlicing::AllFacesAtOnce) {
581 m_timeSliceFrame++;
582 if (m_timeSliceFrame >= mipmapCount)
583 m_timeSliceFrame = 1;
584 break;
585 }
586 }
587 cb->debugMarkEnd();
588}
589
590QSSGReflectionMapEntry QSSGReflectionMapEntry::withRhiTexturedCubeMap(quint32 probeIdx, QRhiTexture *prefiltered)
591{
592 QSSGReflectionMapEntry e;
593 e.m_probeIndex = probeIdx;
594 e.m_rhiPrefilteredCube = prefiltered;
595 return e;
596}
597
598QSSGReflectionMapEntry QSSGReflectionMapEntry::withRhiCubeMap(quint32 probeIdx,
599 QRhiTexture *cube,
600 QRhiTexture *prefiltered,
601 QRhiRenderBuffer *depthStencil)
602{
603 QSSGReflectionMapEntry e;
604 e.m_probeIndex = probeIdx;
605 e.m_rhiCube = cube;
606 e.m_rhiPrefilteredCube = prefiltered;
607 e.m_rhiDepthStencil = depthStencil;
608 return e;
609}
610
611void QSSGReflectionMapEntry::destroyRhiResources()
612{
613 delete m_rhiCube;
614 m_rhiCube = nullptr;
615 // Without depth stencil the prefiltered cubemap is assumed to be not owned here and shouldn't be deleted
616 if (m_rhiDepthStencil)
617 delete m_rhiPrefilteredCube;
618 m_rhiPrefilteredCube = nullptr;
619 delete m_rhiDepthStencil;
620 m_rhiDepthStencil = nullptr;
621
622 qDeleteAll(m_rhiRenderTargets);
623 m_rhiRenderTargets.clear();
624 delete m_rhiRenderPassDesc;
625 m_rhiRenderPassDesc = nullptr;
626
627 delete m_prefilterPipeline;
628 m_prefilterPipeline = nullptr;
629 delete m_irradiancePipeline;
630 m_irradiancePipeline = nullptr;
631 delete m_prefilterSrb;
632 m_prefilterSrb = nullptr;
633 delete m_irradianceSrb;
634 m_irradianceSrb = nullptr;
635 delete m_prefilterVertBuffer;
636 m_prefilterVertBuffer = nullptr;
637 delete m_prefilterFragBuffer;
638 m_prefilterFragBuffer = nullptr;
639 delete m_irradianceFragBuffer;
640 m_irradianceFragBuffer = nullptr;
641 delete m_rhiPrefilterRenderPassDesc;
642 m_rhiPrefilterRenderPassDesc = nullptr;
643 for (const auto &e : std::as_const(m_rhiPrefilterRenderTargetsMap))
644 qDeleteAll(e);
645 m_rhiPrefilterRenderTargetsMap.clear();
646 m_prefilterMipLevelSizes.clear();
647}
648
649QT_END_NAMESPACE
Combined button and popup list for selecting options.
float distributionGGX(float nDotH, float roughness)
static QRhiRenderBuffer * allocateRhiReflectionRenderBuffer(QRhi *rhi, QRhiRenderBuffer::Type type, const QSize &size)
QT_BEGIN_NAMESPACE const int prefilterSampleCount
static QRhiTexture * allocateRhiReflectionTexture(QRhi *rhi, QRhiTexture::Format format, const QSize &size, QRhiTexture::Flags flags={})
void fillPrefilterValues(float roughness, float resolution, QVarLengthArray< QVector4D, prefilterSampleCount > &sampleDirections, float &invTotalWeight, uint &sampleCount)
float radicalInverseVdC(uint bits)
static const float cube[]
QVector3D importanceSampleGGX(QVector2D xi, float roughness)
QVector2D hammersley(uint i, uint N)