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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtquick3d/src/runtimerender/qssgrenderreflectionmap.cpp