53void QSSGRenderReflectionMap::addReflectionMapEntry(qint32 probeIdx,
const QSSGRenderReflectionProbe &probe)
55 QRhi *rhi = m_context.rhiContext()->rhi();
60 QRhiTexture::Format rhiFormat = QRhiTexture::RGBA16F;
62 const QByteArray rtName = probe.debugObjectName.toLatin1();
64 const int mapRes = 1 << probe.reflectionMapRes;
65 QSize pixelSize(mapRes, mapRes);
66 QSSGReflectionMapEntry *pEntry = reflectionMapEntry(probeIdx);
69 QRhiRenderBuffer *depthStencil = allocateRhiReflectionRenderBuffer(rhi, QRhiRenderBuffer::DepthStencil, pixelSize);
70 QRhiTexture *map = allocateRhiReflectionTexture(rhi, rhiFormat, pixelSize, QRhiTexture::RenderTarget | QRhiTexture::CubeMap
71 | QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips);
72 QRhiTexture *prefiltered = allocateRhiReflectionTexture(rhi, rhiFormat, pixelSize, QRhiTexture::RenderTarget | QRhiTexture::CubeMap
73 | QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips);
74 m_reflectionMapList.push_back(QSSGReflectionMapEntry::withRhiCubeMap(probeIdx, map, prefiltered, depthStencil));
76 pEntry = &m_reflectionMapList.back();
80 pEntry->m_needsRender =
true;
82 if (probe.hasScheduledUpdate)
83 pEntry->m_rendered =
false;
85 if (!pEntry->m_rhiDepthStencil || mapRes != pEntry->m_rhiCube->pixelSize().width()) {
86 pEntry->destroyRhiResources();
87 pEntry->m_rhiDepthStencil = allocateRhiReflectionRenderBuffer(rhi, QRhiRenderBuffer::DepthStencil, pixelSize);
88 pEntry->m_rhiCube = allocateRhiReflectionTexture(rhi, rhiFormat, pixelSize, QRhiTexture::RenderTarget | QRhiTexture::CubeMap
89 | QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips);
90 pEntry->m_rhiPrefilteredCube = allocateRhiReflectionTexture(rhi, rhiFormat, pixelSize, QRhiTexture::RenderTarget | QRhiTexture::CubeMap
91 | QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips);
95 if (pEntry->m_rhiRenderTargets.isEmpty()) {
96 pEntry->m_rhiRenderTargets.resize(6);
97 for (
int i = 0; i < 6; ++i)
98 pEntry->m_rhiRenderTargets[i] =
nullptr;
100 Q_ASSERT(pEntry->m_rhiRenderTargets.size() == 6);
102 if (pEntry->m_skyBoxSrbs.isEmpty()) {
103 pEntry->m_skyBoxSrbs.resize(6);
104 for (
int i = 0; i < 6; ++i)
105 pEntry->m_skyBoxSrbs[i] =
nullptr;
109 for (
const auto face : QSSGRenderTextureCubeFaces) {
110 QRhiTextureRenderTarget *&rt(pEntry->m_rhiRenderTargets[quint8(face)]);
112 QRhiColorAttachment att(pEntry->m_rhiCube);
113 att.setLayer(quint8(face));
114 QRhiTextureRenderTargetDescription rtDesc;
115 rtDesc.setColorAttachments({ att });
116 rtDesc.setDepthStencilBuffer(pEntry->m_rhiDepthStencil);
117 rt = rhi->newTextureRenderTarget(rtDesc);
118 rt->setDescription(rtDesc);
119 if (!pEntry->m_rhiRenderPassDesc)
120 pEntry->m_rhiRenderPassDesc = rt->newCompatibleRenderPassDescriptor();
121 rt->setRenderPassDescriptor(pEntry->m_rhiRenderPassDesc);
123 qWarning(
"Failed to build reflection map render target");
125 rt->setName(rtName + QByteArrayLiteral(
" reflection cube face: ") + QSSGBaseTypeHelpers::displayName(face));
128 if (!pEntry->m_prefilterPipeline) {
129 const QSize mapSize = pEntry->m_rhiCube->pixelSize();
131 int mipmapCount = rhi->mipLevelsForSize(mapSize);
132 mipmapCount = qMin(mipmapCount, 6);
135 for (
int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
136 const QSize levelSize = QSize(mapSize.width() * std::pow(0.5, mipLevel),
137 mapSize.height() * std::pow(0.5, mipLevel));
138 pEntry->m_prefilterMipLevelSizes.insert(mipLevel, levelSize);
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(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(
"Failed to build prefilter cube map render target");
156 renderTargets << renderTarget;
158 pEntry->m_rhiPrefilterRenderTargetsMap.insert(mipLevel, renderTargets);
161 const auto &prefilterShaderStages = m_context.shaderCache()->getBuiltInRhiShaders().getRhiReflectionprobePreFilterShader();
163 const QSSGRhiSamplerDescription samplerMipMapDesc {
167 QRhiSampler::ClampToEdge,
168 QRhiSampler::ClampToEdge,
172 const QSSGRhiSamplerDescription samplerDesc {
176 QRhiSampler::ClampToEdge,
177 QRhiSampler::ClampToEdge,
181 QRhiSampler *sampler = m_context.rhiContext()->sampler(samplerDesc);
182 QRhiSampler *cubeSampler = m_context.rhiContext()->sampler(samplerMipMapDesc);
184 QRhiVertexInputLayout inputLayout;
185 inputLayout.setBindings({
186 { 3 *
sizeof(
float) }
188 inputLayout.setAttributes({
189 { 0, 0, QRhiVertexInputAttribute::Float3, 0 }
192 int ubufElementSize = rhi->ubufAligned(128);
193 pEntry->m_prefilterVertBuffer = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufElementSize * 6);
194 pEntry->m_prefilterVertBuffer->create();
196 const int uBufSamplesSize = 16 * prefilterSampleCount + 8;
197 int uBufSamplesElementSize = rhi->ubufAligned(uBufSamplesSize);
198 pEntry->m_prefilterFragBuffer = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, uBufSamplesElementSize * mipmapCount);
199 pEntry->m_prefilterFragBuffer->create();
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()
210 pEntry->m_prefilterSrb = rhi->newShaderResourceBindings();
211 pEntry->m_prefilterSrb->setBindings({
212 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, pEntry->m_prefilterVertBuffer, 128),
213 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, pEntry->m_prefilterFragBuffer, uBufSamplesSize),
214 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, pEntry->m_rhiCube, cubeSampler)
216 pEntry->m_prefilterSrb->create();
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(
"failed to create pre-filter reflection map pipeline state");
224 const auto &irradianceShaderStages = m_context.shaderCache()->getBuiltInRhiShaders().getRhienvironmentmapPreFilterShader(
false );
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()
235 int ubufIrradianceSize = rhi->ubufAligned(20);
236 pEntry->m_irradianceFragBuffer = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufIrradianceSize);
237 pEntry->m_irradianceFragBuffer->create();
239 pEntry->m_irradianceSrb = rhi->newShaderResourceBindings();
240 pEntry->m_irradianceSrb->setBindings({
241 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, pEntry->m_prefilterVertBuffer, 128),
242 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, pEntry->m_irradianceFragBuffer, 20),
243 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, pEntry->m_rhiCube, sampler)
245 pEntry->m_irradianceSrb->create();
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(
"failed to create irradiance reflection map pipeline state");
254 pEntry->m_timeSlicing = probe.timeSlicing;
255 pEntry->m_probeIndex = probeIdx;
256 Q_QUICK3D_PROFILE_ASSIGN_ID(&probe, pEntry);
447void QSSGReflectionMapEntry::renderMips(QSSGRhiContext *rhiCtx)
449 auto *rhi = rhiCtx->rhi();
450 auto *cb = rhiCtx->commandBuffer();
452 auto *rub = rhi->nextResourceUpdateBatch();
453 rub->generateMips(m_rhiCube);
454 QRhiBuffer *vertexBuffer = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer,
sizeof(cube));
455 vertexBuffer->create();
456 vertexBuffer->deleteLater();
457 rub->uploadStaticBuffer(vertexBuffer, cube);
458 cb->resourceUpdate(rub);
460 const QRhiCommandBuffer::VertexInput vbufBinding(vertexBuffer, 0);
462 int ubufElementSize = rhi->ubufAligned(128);
464 const int uBufSamplesSize = 16 * prefilterSampleCount + 8;
465 int uBufSamplesElementSize = rhi->ubufAligned(uBufSamplesSize);
466 int uBufIrradianceElementSize = rhi->ubufAligned(20);
469 QMatrix4x4 mvp = rhi->clipSpaceCorrMatrix();
470 mvp.perspective(90.0f, 1.0f, 0.1f, 10.0f);
472 auto lookAt = [](
const QVector3D &eye,
const QVector3D ¢er,
const QVector3D &up) {
473 QMatrix4x4 viewMatrix;
474 viewMatrix.lookAt(eye, center, up);
477 QVarLengthArray<QMatrix4x4, 6> views;
478 views.append(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(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(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(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 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, 0.0, 1.0), QVector3D(0.0f, -1.0f, 0.0f)));
488 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 rub = rhi->nextResourceUpdateBatch();
491 for (
const auto face : QSSGRenderTextureCubeFaces) {
492 rub->updateDynamicBuffer(m_prefilterVertBuffer, quint8(face) * ubufElementSize, 64, mvp.constData());
493 rub->updateDynamicBuffer(m_prefilterVertBuffer, quint8(face) * ubufElementSize + 64, 64, views[quint8(face)].constData());
496 const QSize mapSize = m_rhiCube->pixelSize();
498 int mipmapCount = rhi->mipLevelsForSize(mapSize);
499 mipmapCount = qMin(mipmapCount, 6);
501 const float resolution = mapSize.width();
502 QVarLengthArray<QVector4D, prefilterSampleCount> sampleDirections;
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;
511 sampleDirections.clear();
512 fillPrefilterValues(roughness, resolution, sampleDirections, invTotalWeight, sampleCount);
514 rub->updateDynamicBuffer(m_prefilterFragBuffer, mipLevel * uBufSamplesElementSize, 16 * prefilterSampleCount, sampleDirections.constData());
515 rub->updateDynamicBuffer(m_prefilterFragBuffer, mipLevel * uBufSamplesElementSize + 16 * prefilterSampleCount, 4, &invTotalWeight);
516 rub->updateDynamicBuffer(m_prefilterFragBuffer, mipLevel * uBufSamplesElementSize + 16 * prefilterSampleCount + 4, 4, &sampleCount);
519 const float roughness = 0.0f;
520 const float lodBias = 0.0f;
521 const int distribution = 0;
522 const int sampleCount = resolution / 4;
524 rub->updateDynamicBuffer(m_irradianceFragBuffer, 0, 4, &roughness);
525 rub->updateDynamicBuffer(m_irradianceFragBuffer, 4, 4, &resolution);
526 rub->updateDynamicBuffer(m_irradianceFragBuffer, 4 + 4, 4, &lodBias);
527 rub->updateDynamicBuffer(m_irradianceFragBuffer, 4 + 4 + 4, 4, &sampleCount);
528 rub->updateDynamicBuffer(m_irradianceFragBuffer, 4 + 4 + 4 + 4, 4, &distribution);
531 cb->resourceUpdate(rub);
534 for (
int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
535 if (mipLevel > 0 && m_timeSlicing == QSSGRenderReflectionProbe::ReflectionTimeSlicing::AllFacesAtOnce)
536 mipLevel = m_timeSliceFrame;
538 for (
auto face : QSSGRenderTextureCubeFaces) {
539 if (m_timeSlicing == QSSGRenderReflectionProbe::ReflectionTimeSlicing::IndividualFaces)
540 face = m_timeSliceFace;
542 cb->beginPass(m_rhiPrefilterRenderTargetsMap[mipLevel][quint8(face)], QColor(0, 0, 0, 1), { 1.0f, 0 },
nullptr, rhiCtx->commonPassFlags());
543 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(m_rhiPrefilterRenderTargetsMap[mipLevel][quint8(face)]));
544 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
545 if (mipLevel < mipmapCount - 1) {
547 cb->setGraphicsPipeline(m_prefilterPipeline);
548 cb->setVertexInput(0, 1, &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) }
554 cb->setShaderResources(m_prefilterSrb, 2, dynamicOffsets.constData());
557 cb->setGraphicsPipeline(m_irradiancePipeline);
558 cb->setVertexInput(0, 1, &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) }
564 cb->setShaderResources(m_irradianceSrb, 1, dynamicOffsets.constData());
566 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
568 QSSGRHICTX_STAT(rhiCtx, draw(36, 1));
569 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DRenderCall, 36llu | (1llu << 32), profilingId);
571 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
572 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QSSG_RENDERPASS_NAME(
"reflection_map", mipLevel, face));
574 if (m_timeSlicing == QSSGRenderReflectionProbe::ReflectionTimeSlicing::IndividualFaces)
578 if (mipLevel > 0 && m_timeSlicing == QSSGRenderReflectionProbe::ReflectionTimeSlicing::AllFacesAtOnce) {
580 if (m_timeSliceFrame >= mipmapCount)
581 m_timeSliceFrame = 1;