55void QSSGRenderReflectionMap::addReflectionMapEntry(qint32 probeIdx,
const QSSGRenderReflectionProbe &probe)
57 QRhi *rhi = m_context.rhiContext()->rhi();
62 QRhiTexture::Format rhiFormat = QRhiTexture::RGBA16F;
64 const QByteArray rtName = probe.debugObjectName.toLatin1();
66 const int mapRes = 1 << probe.reflectionMapRes;
67 QSize pixelSize(mapRes, mapRes);
68 QSSGReflectionMapEntry *pEntry = reflectionMapEntry(probeIdx);
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));
78 pEntry = &m_reflectionMapList.back();
82 pEntry->m_needsRender =
true;
84 if (probe.hasScheduledUpdate)
85 pEntry->m_rendered =
false;
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);
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;
102 Q_ASSERT(pEntry->m_rhiRenderTargets.size() == 6);
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;
111 for (
const auto face : QSSGRenderTextureCubeFaces) {
112 QRhiTextureRenderTarget *&rt(pEntry->m_rhiRenderTargets[quint8(face)]);
114 QRhiColorAttachment att(pEntry->m_rhiCube);
115 att.setLayer(quint8(face));
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);
125 qWarning(
"Failed to build reflection map render target");
127 rt->setName(rtName + QByteArrayLiteral(
" reflection cube face: ") + QSSGBaseTypeHelpers::displayName(face));
130 if (!pEntry->m_prefilterPipeline) {
131 const QSize mapSize = pEntry->m_rhiCube->pixelSize();
133 int mipmapCount = rhi->mipLevelsForSize(mapSize);
134 mipmapCount = qMin(mipmapCount, 6);
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);
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;
160 pEntry->m_rhiPrefilterRenderTargetsMap.insert(mipLevel, renderTargets);
163 const auto &prefilterShaderStages = m_context.shaderCache()->getBuiltInRhiShaders().getRhiReflectionprobePreFilterShader();
165 const QSSGRhiSamplerDescription samplerMipMapDesc {
169 QRhiSampler::ClampToEdge,
170 QRhiSampler::ClampToEdge,
174 QRhiSampler *cubeSampler = m_context.rhiContext()->sampler(samplerMipMapDesc);
176 QRhiVertexInputLayout inputLayout;
177 inputLayout.setBindings({
178 { 3 *
sizeof(
float) }
180 inputLayout.setAttributes({
181 { 0, 0, QRhiVertexInputAttribute::Float3, 0 }
184 int ubufElementSize = rhi->ubufAligned(128);
185 pEntry->m_prefilterVertBuffer = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufElementSize * 6);
186 pEntry->m_prefilterVertBuffer->create();
188 const int uBufSamplesSize = 16 * prefilterSampleCount + 8;
189 int uBufSamplesElementSize = rhi->ubufAligned(uBufSamplesSize);
190 pEntry->m_prefilterFragBuffer = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, uBufSamplesElementSize * mipmapCount);
191 pEntry->m_prefilterFragBuffer->create();
193 pEntry->m_prefilterPipeline = rhi->newGraphicsPipeline();
194 pEntry->m_prefilterPipeline->setCullMode(QRhiGraphicsPipeline::Front);
195 pEntry->m_prefilterPipeline->setFrontFace(QRhiGraphicsPipeline::CCW);
196 pEntry->m_prefilterPipeline->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
197 pEntry->m_prefilterPipeline->setShaderStages({
198 *prefilterShaderStages->vertexStage(),
199 *prefilterShaderStages->fragmentStage()
202 pEntry->m_prefilterSrb = rhi->newShaderResourceBindings();
203 pEntry->m_prefilterSrb->setBindings({
204 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, pEntry->m_prefilterVertBuffer, 128),
205 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, pEntry->m_prefilterFragBuffer, uBufSamplesSize),
206 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, pEntry->m_rhiCube, cubeSampler)
208 pEntry->m_prefilterSrb->create();
210 pEntry->m_prefilterPipeline->setVertexInputLayout(inputLayout);
211 pEntry->m_prefilterPipeline->setShaderResourceBindings(pEntry->m_prefilterSrb);
212 pEntry->m_prefilterPipeline->setRenderPassDescriptor(pEntry->m_rhiPrefilterRenderPassDesc);
213 if (!pEntry->m_prefilterPipeline->create())
214 qWarning(
"failed to create pre-filter reflection map pipeline state");
216 const auto &irradianceShaderStages = m_context.shaderCache()->getBuiltInRhiShaders().getRhienvironmentmapPreFilterShader(
false );
218 pEntry->m_irradiancePipeline = rhi->newGraphicsPipeline();
219 pEntry->m_irradiancePipeline->setCullMode(QRhiGraphicsPipeline::Front);
220 pEntry->m_irradiancePipeline->setFrontFace(QRhiGraphicsPipeline::CCW);
221 pEntry->m_irradiancePipeline->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
222 pEntry->m_irradiancePipeline->setShaderStages({
223 *irradianceShaderStages->vertexStage(),
224 *irradianceShaderStages->fragmentStage()
227 int ubufIrradianceSize = rhi->ubufAligned(20);
228 pEntry->m_irradianceFragBuffer = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufIrradianceSize);
229 pEntry->m_irradianceFragBuffer->create();
231 pEntry->m_irradianceSrb = rhi->newShaderResourceBindings();
232 pEntry->m_irradianceSrb->setBindings({
233 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, pEntry->m_prefilterVertBuffer, 128),
234 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, pEntry->m_irradianceFragBuffer, 20),
235 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, pEntry->m_rhiCube, cubeSampler)
237 pEntry->m_irradianceSrb->create();
239 pEntry->m_irradiancePipeline->setShaderResourceBindings(pEntry->m_irradianceSrb);
240 pEntry->m_irradiancePipeline->setVertexInputLayout(inputLayout);
241 pEntry->m_irradiancePipeline->setRenderPassDescriptor(pEntry->m_rhiPrefilterRenderPassDesc);
242 if (!pEntry->m_irradiancePipeline->create())
243 qWarning(
"failed to create irradiance reflection map pipeline state");
246 pEntry->m_timeSlicing = probe.timeSlicing;
247 pEntry->m_probeIndex = probeIdx;
248 Q_QUICK3D_PROFILE_ASSIGN_ID(&probe, pEntry);
439void QSSGReflectionMapEntry::renderMips(QSSGRhiContext *rhiCtx)
441 auto *rhi = rhiCtx->rhi();
442 auto *cb = rhiCtx->commandBuffer();
444 auto *rub = rhi->nextResourceUpdateBatch();
445 rub->generateMips(m_rhiCube);
446 QRhiBuffer *vertexBuffer = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer,
sizeof(cube));
447 vertexBuffer->create();
448 vertexBuffer->deleteLater();
449 rub->uploadStaticBuffer(vertexBuffer, cube);
450 cb->resourceUpdate(rub);
452 const QRhiCommandBuffer::VertexInput vbufBinding(vertexBuffer, 0);
454 int ubufElementSize = rhi->ubufAligned(128);
456 const int uBufSamplesSize = 16 * prefilterSampleCount + 8;
457 int uBufSamplesElementSize = rhi->ubufAligned(uBufSamplesSize);
458 int uBufIrradianceElementSize = rhi->ubufAligned(20);
461 QMatrix4x4 mvp = rhi->clipSpaceCorrMatrix();
462 mvp.perspective(90.0f, 1.0f, 0.1f, 10.0f);
464 auto lookAt = [](
const QVector3D &eye,
const QVector3D ¢er,
const QVector3D &up) {
465 QMatrix4x4 viewMatrix;
466 viewMatrix.lookAt(eye, center, up);
469 QVarLengthArray<QMatrix4x4, 6> views;
470 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(1.0, 0.0, 0.0), QVector3D(0.0f, -1.0f, 0.0f)));
471 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(-1.0, 0.0, 0.0), QVector3D(0.0f, -1.0f, 0.0f)));
472 if (rhi->isYUpInFramebuffer()) {
473 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 1.0, 0.0), QVector3D(0.0f, 0.0f, 1.0f)));
474 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, -1.0, 0.0), QVector3D(0.0f, 0.0f, -1.0f)));
476 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, -1.0, 0.0), QVector3D(0.0f, 0.0f, -1.0f)));
477 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 1.0, 0.0), QVector3D(0.0f, 0.0f, 1.0f)));
479 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 0.0, 1.0), QVector3D(0.0f, -1.0f, 0.0f)));
480 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 0.0, -1.0), QVector3D(0.0f, -1.0f, 0.0f)));
482 rub = rhi->nextResourceUpdateBatch();
483 for (
const auto face : QSSGRenderTextureCubeFaces) {
484 rub->updateDynamicBuffer(m_prefilterVertBuffer, quint8(face) * ubufElementSize, 64, mvp.constData());
485 rub->updateDynamicBuffer(m_prefilterVertBuffer, quint8(face) * ubufElementSize + 64, 64, views[quint8(face)].constData());
488 const QSize mapSize = m_rhiCube->pixelSize();
490 int mipmapCount = rhi->mipLevelsForSize(mapSize);
491 mipmapCount = qMin(mipmapCount, 6);
493 const float resolution = mapSize.width();
494 QVarLengthArray<QVector4D, prefilterSampleCount> sampleDirections;
497 for (
int mipLevel = 0; mipLevel < mipmapCount - 1; ++mipLevel) {
498 Q_ASSERT(mipmapCount - 2);
499 const float roughness =
float(mipLevel) /
float(mipmapCount - 2);
500 float invTotalWeight = 0.0f;
501 uint sampleCount = 0;
503 sampleDirections.clear();
504 fillPrefilterValues(roughness, resolution, sampleDirections, invTotalWeight, sampleCount);
506 rub->updateDynamicBuffer(m_prefilterFragBuffer, mipLevel * uBufSamplesElementSize, 16 * prefilterSampleCount, sampleDirections.constData());
507 rub->updateDynamicBuffer(m_prefilterFragBuffer, mipLevel * uBufSamplesElementSize + 16 * prefilterSampleCount, 4, &invTotalWeight);
508 rub->updateDynamicBuffer(m_prefilterFragBuffer, mipLevel * uBufSamplesElementSize + 16 * prefilterSampleCount + 4, 4, &sampleCount);
511 const float roughness = 0.0f;
512 const float lodBias = 0.0f;
513 const int distribution = 0;
514 const int sampleCount = resolution / 4;
516 rub->updateDynamicBuffer(m_irradianceFragBuffer, 0, 4, &roughness);
517 rub->updateDynamicBuffer(m_irradianceFragBuffer, 4, 4, &resolution);
518 rub->updateDynamicBuffer(m_irradianceFragBuffer, 4 + 4, 4, &lodBias);
519 rub->updateDynamicBuffer(m_irradianceFragBuffer, 4 + 4 + 4, 4, &sampleCount);
520 rub->updateDynamicBuffer(m_irradianceFragBuffer, 4 + 4 + 4 + 4, 4, &distribution);
523 cb->resourceUpdate(rub);
526 for (
int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
527 if (mipLevel > 0 && m_timeSlicing == QSSGRenderReflectionProbe::ReflectionTimeSlicing::AllFacesAtOnce)
528 mipLevel = m_timeSliceFrame;
530 for (
auto face : QSSGRenderTextureCubeFaces) {
531 if (m_timeSlicing == QSSGRenderReflectionProbe::ReflectionTimeSlicing::IndividualFaces)
532 face = m_timeSliceFace;
534 cb->beginPass(m_rhiPrefilterRenderTargetsMap[mipLevel][quint8(face)], QColor(0, 0, 0, 1), { 1.0f, 0 },
nullptr, rhiCtx->commonPassFlags());
535 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(m_rhiPrefilterRenderTargetsMap[mipLevel][quint8(face)]));
536 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
537 if (mipLevel < mipmapCount - 1) {
539 cb->setGraphicsPipeline(m_prefilterPipeline);
540 cb->setVertexInput(0, 1, &vbufBinding);
541 cb->setViewport(QRhiViewport(0, 0, m_prefilterMipLevelSizes[mipLevel].width(), m_prefilterMipLevelSizes[mipLevel].height()));
542 QVector<QPair<
int, quint32>> dynamicOffsets = {
543 { 0, quint32(ubufElementSize * quint8(face)) },
544 { 2, quint32(uBufSamplesElementSize * mipLevel) }
546 cb->setShaderResources(m_prefilterSrb, 2, dynamicOffsets.constData());
549 cb->setGraphicsPipeline(m_irradiancePipeline);
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(uBufIrradianceElementSize) }
556 cb->setShaderResources(m_irradianceSrb, 1, dynamicOffsets.constData());
558 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
560 QSSGRHICTX_STAT(rhiCtx, draw(36, 1));
561 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DRenderCall, 36llu | (1llu << 32), profilingId);
563 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
564 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QSSG_RENDERPASS_NAME(
"reflection_map", mipLevel, face));
566 if (m_timeSlicing == QSSGRenderReflectionProbe::ReflectionTimeSlicing::IndividualFaces)
570 if (mipLevel > 0 && m_timeSlicing == QSSGRenderReflectionProbe::ReflectionTimeSlicing::AllFacesAtOnce) {
572 if (m_timeSliceFrame >= mipmapCount)
573 m_timeSliceFrame = 1;