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 const QSSGRhiSamplerDescription samplerDesc {
178 QRhiSampler::ClampToEdge,
179 QRhiSampler::ClampToEdge,
183 QRhiSampler *sampler = m_context.rhiContext()->sampler(samplerDesc);
184 QRhiSampler *cubeSampler = m_context.rhiContext()->sampler(samplerMipMapDesc);
186 QRhiVertexInputLayout inputLayout;
187 inputLayout.setBindings({
188 { 3 *
sizeof(
float) }
190 inputLayout.setAttributes({
191 { 0, 0, QRhiVertexInputAttribute::Float3, 0 }
194 int ubufElementSize = rhi->ubufAligned(128);
195 pEntry->m_prefilterVertBuffer = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufElementSize * 6);
196 pEntry->m_prefilterVertBuffer->create();
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();
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()
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)
218 pEntry->m_prefilterSrb->create();
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");
226 const auto &irradianceShaderStages = m_context.shaderCache()->getBuiltInRhiShaders().getRhienvironmentmapPreFilterShader(
false );
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()
237 int ubufIrradianceSize = rhi->ubufAligned(20);
238 pEntry->m_irradianceFragBuffer = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufIrradianceSize);
239 pEntry->m_irradianceFragBuffer->create();
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)
247 pEntry->m_irradianceSrb->create();
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");
256 pEntry->m_timeSlicing = probe.timeSlicing;
257 pEntry->m_probeIndex = probeIdx;
258 Q_QUICK3D_PROFILE_ASSIGN_ID(&probe, pEntry);
449void QSSGReflectionMapEntry::renderMips(QSSGRhiContext *rhiCtx)
451 auto *rhi = rhiCtx->rhi();
452 auto *cb = rhiCtx->commandBuffer();
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);
462 const QRhiCommandBuffer::VertexInput vbufBinding(vertexBuffer, 0);
464 int ubufElementSize = rhi->ubufAligned(128);
466 const int uBufSamplesSize = 16 * prefilterSampleCount + 8;
467 int uBufSamplesElementSize = rhi->ubufAligned(uBufSamplesSize);
468 int uBufIrradianceElementSize = rhi->ubufAligned(20);
471 QMatrix4x4 mvp = rhi->clipSpaceCorrMatrix();
472 mvp.perspective(90.0f, 1.0f, 0.1f, 10.0f);
474 auto lookAt = [](
const QVector3D &eye,
const QVector3D ¢er,
const QVector3D &up) {
475 QMatrix4x4 viewMatrix;
476 viewMatrix.lookAt(eye, center, up);
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)));
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)));
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)));
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());
498 const QSize mapSize = m_rhiCube->pixelSize();
500 int mipmapCount = rhi->mipLevelsForSize(mapSize);
501 mipmapCount = qMin(mipmapCount, 6);
503 const float resolution = mapSize.width();
504 QVarLengthArray<QVector4D, prefilterSampleCount> sampleDirections;
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;
513 sampleDirections.clear();
514 fillPrefilterValues(roughness, resolution, sampleDirections, invTotalWeight, sampleCount);
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);
521 const float roughness = 0.0f;
522 const float lodBias = 0.0f;
523 const int distribution = 0;
524 const int sampleCount = resolution / 4;
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);
533 cb->resourceUpdate(rub);
536 for (
int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
537 if (mipLevel > 0 && m_timeSlicing == QSSGRenderReflectionProbe::ReflectionTimeSlicing::AllFacesAtOnce)
538 mipLevel = m_timeSliceFrame;
540 for (
auto face : QSSGRenderTextureCubeFaces) {
541 if (m_timeSlicing == QSSGRenderReflectionProbe::ReflectionTimeSlicing::IndividualFaces)
542 face = m_timeSliceFace;
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) {
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) }
556 cb->setShaderResources(m_prefilterSrb, 2, dynamicOffsets.constData());
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) }
566 cb->setShaderResources(m_irradianceSrb, 1, dynamicOffsets.constData());
568 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
570 QSSGRHICTX_STAT(rhiCtx, draw(36, 1));
571 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DRenderCall, 36llu | (1llu << 32), profilingId);
573 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
574 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QSSG_RENDERPASS_NAME(
"reflection_map", mipLevel, face));
576 if (m_timeSlicing == QSSGRenderReflectionProbe::ReflectionTimeSlicing::IndividualFaces)
580 if (mipLevel > 0 && m_timeSlicing == QSSGRenderReflectionProbe::ReflectionTimeSlicing::AllFacesAtOnce) {
582 if (m_timeSliceFrame >= mipmapCount)
583 m_timeSliceFrame = 1;