64static const float cube[] = {
66 -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
69 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f,
72 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f,
75 -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f,
78 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f,
81 -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
84 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
87 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
90 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
93 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
96 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
99 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
104 qDebug() <<
"Using RHI backend" << name;
107 QFile ktxOutputFile(outPath);
108 if (!ktxOutputFile.open(QIODevice::WriteOnly)) {
109 return QStringLiteral(
"Could not open file: %1").arg(outPath);
112 QScopedPointer<QRhi> rhi(QRhi::create(impl, initParams, QRhi::Flags(),
nullptr));
114 return QStringLiteral(
"Failed to initialize QRhi");
116 qDebug() << rhi->driverInfo();
118 QRhiCommandBuffer *cb;
119 rhi->beginOffscreenFrame(&cb);
121 const auto rhiContext = std::make_unique<QSSGRhiContext>(rhi.get());
122 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiContext.get());
123 rhiCtxD->setCommandBuffer(cb);
125 QScopedPointer<QSSGLoadedTexture> inImage(QSSGLoadedTexture::loadHdrImage(QSSGInputUtil::getStreamForFile(inPath), FORMAT));
127 return QStringLiteral(
"Failed to load hdr file");
129 auto shaderCache = std::make_unique<QSSGShaderCache>(*rhiContext);
157 const int suggestedSize = qMax(512.f, inImage->height * 0.5f);
158 const QSize environmentMapSize(suggestedSize, suggestedSize);
159 const bool isRGBE = inImage->format.format == QSSGRenderTextureFormat::Format::RGBE8;
160 const int colorSpace = inImage->isSRGB ? 1 : 0;
163 QRhiTexture *envCubeMap = rhi->newTexture(QRhiTexture::RGBA16F,
166 QRhiTexture::RenderTarget | QRhiTexture::CubeMap | QRhiTexture::MipMapped
167 | QRhiTexture::UsedWithGenerateMips);
168 if (!envCubeMap->create()) {
169 return QStringLiteral(
"Failed to create Environment Cube Map");
171 envCubeMap->deleteLater();
174 QRhiRenderBuffer *envMapRenderBuffer = rhi->newRenderBuffer(QRhiRenderBuffer::Color, environmentMapSize);
175 if (!envMapRenderBuffer->create()) {
176 return QStringLiteral(
"Failed to create Environment Map Render Buffer");
178 envMapRenderBuffer->deleteLater();
181 QVarLengthArray<QRhiTextureRenderTarget *, 6> renderTargets;
182 QRhiRenderPassDescriptor *renderPassDesc =
nullptr;
183 for (
int face = 0; face < 6; ++face) {
184 QRhiColorAttachment att(envCubeMap);
186 QRhiTextureRenderTargetDescription rtDesc;
187 rtDesc.setColorAttachments({ att });
188 auto renderTarget = rhi->newTextureRenderTarget(rtDesc);
189 renderTarget->setDescription(rtDesc);
191 renderPassDesc = renderTarget->newCompatibleRenderPassDescriptor();
192 renderTarget->setRenderPassDescriptor(renderPassDesc);
193 if (!renderTarget->create()) {
194 return QStringLiteral(
"Failed to build env map render target");
196 renderTarget->deleteLater();
197 renderTargets << renderTarget;
199 renderPassDesc->deleteLater();
202 QSize size(inImage->width, inImage->height);
203 auto *sourceTexture = rhi->newTexture(QRhiTexture::RGBA16F, size, 1);
204 if (!sourceTexture->create()) {
205 return QStringLiteral(
"Failed to create source env map texture");
207 sourceTexture->deleteLater();
210 QRhiTextureUploadDescription desc;
211 if (inImage->textureFileData.isValid()) {
214 { inImage->textureFileData.data().constData() + inImage->textureFileData.dataOffset(0),
215 quint32(inImage->textureFileData.dataLength(0)) } } };
217 desc = { { 0, 0, { inImage->data, inImage->dataSizeInBytes } } };
219 auto *rub = rhi->nextResourceUpdateBatch();
220 rub->uploadTexture(sourceTexture, desc);
222 const QSSGRhiSamplerDescription samplerDesc {
223 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None, QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat
225 QRhiSampler *sampler = rhiContext->sampler(samplerDesc);
228 const auto &envMapShaderStages = shaderCache->getBuiltInRhiShaders().getRhiEnvironmentmapShader();
231 QRhiBuffer *vertexBuffer = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer,
sizeof(cube));
232 vertexBuffer->create();
233 vertexBuffer->deleteLater();
234 rub->uploadStaticBuffer(vertexBuffer,
cube);
237 int ubufElementSize = rhi->ubufAligned(128);
238 QRhiBuffer *uBuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufElementSize * 6);
242 int ubufEnvMapElementSize = rhi->ubufAligned(4);
243 QRhiBuffer *uBufEnvMap = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufEnvMapElementSize * 6);
244 uBufEnvMap->create();
245 uBufEnvMap->deleteLater();
248 QRhiShaderResourceBindings *envMapSrb = rhi->newShaderResourceBindings();
249 envMapSrb->setBindings(
250 { QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, uBuf, 128),
251 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, uBufEnvMap, ubufEnvMapElementSize),
252 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, sourceTexture, sampler) });
254 envMapSrb->deleteLater();
257 QRhiGraphicsPipeline *envMapPipeline = rhi->newGraphicsPipeline();
258 envMapPipeline->setCullMode(QRhiGraphicsPipeline::Front);
259 envMapPipeline->setFrontFace(QRhiGraphicsPipeline::CCW);
260 envMapPipeline->setShaderStages({ *envMapShaderStages->vertexStage(), *envMapShaderStages->fragmentStage() });
262 QRhiVertexInputLayout inputLayout;
263 inputLayout.setBindings({ { 3 *
sizeof(
float) } });
264 inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float3, 0 } });
266 envMapPipeline->setVertexInputLayout(inputLayout);
267 envMapPipeline->setShaderResourceBindings(envMapSrb);
268 envMapPipeline->setRenderPassDescriptor(renderPassDesc);
269 if (!envMapPipeline->create()) {
270 delete envMapPipeline;
272 return QStringLiteral(
"Failed to create source env map pipeline state");
274 envMapPipeline->deleteLater();
277 cb->debugMarkBegin(
"Environment Cubemap Generation");
278 const QRhiCommandBuffer::VertexInput vbufBinding(vertexBuffer, 0);
281 QMatrix4x4 mvp = rhi->clipSpaceCorrMatrix();
282 mvp.perspective(90.0f, 1.0f, 0.1f, 10.0f);
284 auto lookAt = [](
const QVector3D &eye,
const QVector3D ¢er,
const QVector3D &up) {
285 QMatrix4x4 viewMatrix;
286 viewMatrix.lookAt(eye, center, up);
289 QVarLengthArray<QMatrix4x4, 6> views;
290 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(1.0, 0.0, 0.0), QVector3D(0.0f, -1.0f, 0.0f)));
291 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(-1.0, 0.0, 0.0), QVector3D(0.0f, -1.0f, 0.0f)));
292 if (rhi->isYUpInFramebuffer()) {
293 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 1.0, 0.0), QVector3D(0.0f, 0.0f, 1.0f)));
294 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, -1.0, 0.0), QVector3D(0.0f, 0.0f, -1.0f)));
296 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, -1.0, 0.0), QVector3D(0.0f, 0.0f, -1.0f)));
297 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 1.0, 0.0), QVector3D(0.0f, 0.0f, 1.0f)));
299 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 0.0, 1.0), QVector3D(0.0f, -1.0f, 0.0f)));
300 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 0.0, -1.0), QVector3D(0.0f, -1.0f, 0.0f)));
301 for (
int face = 0; face < 6; ++face) {
302 rub->updateDynamicBuffer(uBuf, face * ubufElementSize, 64, mvp.constData());
303 rub->updateDynamicBuffer(uBuf, face * ubufElementSize + 64, 64, views[face].constData());
304 rub->updateDynamicBuffer(uBufEnvMap, face * ubufEnvMapElementSize, 4, &colorSpace);
306 cb->resourceUpdate(rub);
308 for (
int face = 0; face < 6; ++face) {
309 cb->beginPass(renderTargets[face], QColor(0, 0, 0, 1), { 1.0f, 0 },
nullptr, rhiContext->commonPassFlags());
312 cb->setGraphicsPipeline(envMapPipeline);
313 cb->setVertexInput(0, 1, &vbufBinding);
314 cb->setViewport(QRhiViewport(0, 0, environmentMapSize.width(), environmentMapSize.height()));
315 QVector<QPair<
int, quint32>> dynamicOffset = {
316 { 0, quint32(ubufElementSize * face) },
317 { 2, quint32(ubufEnvMapElementSize * face )}
319 cb->setShaderResources(envMapSrb, 2, dynamicOffset.constData());
328 rub = rhi->nextResourceUpdateBatch();
329 rub->generateMips(envCubeMap);
330 cb->resourceUpdate(rub);
334 cb->debugMarkBegin(
"Pre-filtered Environment Cubemap Generation");
335 QRhiTexture *preFilteredEnvCubeMap = rhi->newTexture(QRhiTexture::RGBA16F,
338 QRhiTexture::RenderTarget | QRhiTexture::CubeMap | QRhiTexture::MipMapped);
339 if (!preFilteredEnvCubeMap->create())
340 qWarning(
"Failed to create Pre-filtered Environment Cube Map");
341 int mipmapCount = rhi->mipLevelsForSize(environmentMapSize);
342 mipmapCount = qMin(mipmapCount, 6);
343 QMap<
int, QSize> mipLevelSizes;
344 QMap<
int, QVarLengthArray<QRhiTextureRenderTarget *, 6>> renderTargetsMap;
345 QRhiRenderPassDescriptor *renderPassDescriptorPhase2 =
nullptr;
348 for (
int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
349 const QSize levelSize = QSize(environmentMapSize.width() * std::pow(0.5, mipLevel),
350 environmentMapSize.height() * std::pow(0.5, mipLevel));
351 mipLevelSizes.insert(mipLevel, levelSize);
353 QVarLengthArray<QRhiTextureRenderTarget *, 6> renderTargets;
354 for (
int face = 0; face < 6; ++face) {
355 QRhiColorAttachment att(preFilteredEnvCubeMap);
357 att.setLevel(mipLevel);
358 QRhiTextureRenderTargetDescription rtDesc;
359 rtDesc.setColorAttachments({ att });
360 auto renderTarget = rhi->newTextureRenderTarget(rtDesc);
361 renderTarget->setDescription(rtDesc);
362 if (!renderPassDescriptorPhase2)
363 renderPassDescriptorPhase2 = renderTarget->newCompatibleRenderPassDescriptor();
364 renderTarget->setRenderPassDescriptor(renderPassDescriptorPhase2);
365 if (!renderTarget->create())
366 qWarning(
"Failed to build prefilter env map render target");
367 renderTarget->deleteLater();
368 renderTargets << renderTarget;
370 renderTargetsMap.insert(mipLevel, renderTargets);
371 renderPassDescriptorPhase2->deleteLater();
375 const auto &prefilterShaderStages = shaderCache->getBuiltInRhiShaders().getRhienvironmentmapPreFilterShader(isRGBE);
378 const QSSGRhiSamplerDescription samplerMipMapDesc {
379 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat
382 QRhiSampler *envMapCubeSampler =
nullptr;
385 envMapCubeSampler = rhiContext->sampler(samplerMipMapDesc);
387 envMapCubeSampler = sampler;
399 int ubufPrefilterElementSize = rhi->ubufAligned(20);
400 QRhiBuffer *uBufPrefilter = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufPrefilterElementSize * mipmapCount);
401 uBufPrefilter->create();
402 uBufPrefilter->deleteLater();
405 QRhiShaderResourceBindings *preFilterSrb = rhi->newShaderResourceBindings();
406 preFilterSrb->setBindings({
407 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, uBuf, 128),
408 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, uBufPrefilter, 20),
409 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, envCubeMap, envMapCubeSampler)
411 preFilterSrb->create();
412 preFilterSrb->deleteLater();
415 QRhiGraphicsPipeline *prefilterPipeline = rhi->newGraphicsPipeline();
416 prefilterPipeline->setCullMode(QRhiGraphicsPipeline::Front);
417 prefilterPipeline->setFrontFace(QRhiGraphicsPipeline::CCW);
418 prefilterPipeline->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
419 prefilterPipeline->setShaderStages({
420 *prefilterShaderStages->vertexStage(),
421 *prefilterShaderStages->fragmentStage()
424 prefilterPipeline->setVertexInputLayout(inputLayout);
425 prefilterPipeline->setShaderResourceBindings(preFilterSrb);
426 prefilterPipeline->setRenderPassDescriptor(renderPassDescriptorPhase2);
427 if (!prefilterPipeline->create())
428 return QStringLiteral(
"Failed to create pre-filter env map pipeline state");
429 prefilterPipeline->deleteLater();
433 rub = rhi->nextResourceUpdateBatch();
434 const float resolution = environmentMapSize.width();
435 const float lodBias = 0.0f;
436 const int sampleCount = 1024;
437 for (
int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
438 Q_ASSERT(mipmapCount - 2);
439 const float roughness =
float(mipLevel) /
float(mipmapCount - 2);
440 const int distribution = mipLevel == (mipmapCount - 1) ? 0 : 1;
441 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize, 4, &roughness);
442 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4, 4, &resolution);
443 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4 + 4, 4, &lodBias);
444 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4 + 4 + 4, 4, &sampleCount);
445 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4 + 4 + 4 + 4, 4, &distribution);
448 cb->resourceUpdate(rub);
451 for (
int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
452 for (
int face = 0; face < 6; ++face) {
453 cb->beginPass(renderTargetsMap[mipLevel][face], QColor(0, 0, 0, 1), { 1.0f, 0 },
nullptr, rhiContext->commonPassFlags());
454 cb->setGraphicsPipeline(prefilterPipeline);
455 cb->setVertexInput(0, 1, &vbufBinding);
456 cb->setViewport(QRhiViewport(0, 0, mipLevelSizes[mipLevel].width(), mipLevelSizes[mipLevel].height()));
457 QVector<QPair<
int, quint32>> dynamicOffsets = {
458 { 0, quint32(ubufElementSize * face) },
459 { 2, quint32(ubufPrefilterElementSize * mipLevel) }
461 cb->setShaderResources(preFilterSrb, 2, dynamicOffsets.constData());
470 const quint32 numberOfMipmapLevels = renderTargetsMap.size();
471 const quint32 numberOfFaces = 6;
473 constexpr size_t KTX_IDENTIFIER_LENGTH = 12;
474 constexpr char ktxIdentifier[KTX_IDENTIFIER_LENGTH] = {
'\xAB',
'K',
'T',
'X',
' ',
'1',
475 '1',
'\xBB',
'\r',
'\n',
'\x1A',
'\n' };
476 constexpr quint32 platformEndianIdentifier = 0x04030201;
477 QVector<
char> keyValueData;
482 static const char key[] =
"QT_IBL_BAKER_VERSION";
483 static const char value[] =
"1";
485 constexpr size_t keyAndValueByteSize =
sizeof(key) +
sizeof(value);
486 appendBinaryVector(keyValueData, keyAndValueByteSize);
487 appendBinaryVector(keyValueData, key);
488 appendBinaryVector(keyValueData, value);
491 const size_t padding = 3 - ((keyAndValueByteSize + 3) % 4);
492 keyValueData.resize(keyValueData.size() + padding);
498 ktxOutputFile.write(ktxIdentifier, KTX_IDENTIFIER_LENGTH);
501 writeUInt32(ktxOutputFile, quint32(platformEndianIdentifier));
507 writeUInt32(ktxOutputFile, quint32(FORMAT.getSizeofFormat()) / quint32(FORMAT.getNumberOfComponent()));
510 writeUInt32(ktxOutputFile, quint32(
GL_RGBA));
513 writeUInt32(ktxOutputFile, quint32(
GL_RGBA16F));
516 writeUInt32(ktxOutputFile, quint32(
GL_RGBA));
519 writeUInt32(ktxOutputFile, quint32(environmentMapSize.width()));
522 writeUInt32(ktxOutputFile, quint32(environmentMapSize.height()));
525 writeUInt32(ktxOutputFile, quint32(0));
528 writeUInt32(ktxOutputFile, quint32(0));
531 writeUInt32(ktxOutputFile, quint32(numberOfFaces));
534 writeUInt32(ktxOutputFile, quint32(numberOfMipmapLevels));
537 writeUInt32(ktxOutputFile, quint32(keyValueData.size()));
540 ktxOutputFile.write(keyValueData.data(), keyValueData.size());
543 for (quint32 mipmap_level = 0; mipmap_level < numberOfMipmapLevels; mipmap_level++) {
544 quint32 imageSize = 0;
545 for (size_t face = 0; face < numberOfFaces; face++) {
546 QRhiTextureRenderTarget *renderTarget = renderTargetsMap[mipmap_level][face];
549 Q_ASSERT(rhi->isRecordingFrame());
551 const auto descr = renderTarget->description();
552 const auto texture = descr.cbeginColorAttachments()->texture();
554 QRhiReadbackResult result;
555 QRhiReadbackDescription readbackDesc(texture);
556 readbackDesc.setLayer(
int(face));
557 readbackDesc.setLevel(mipmap_level);
559 QRhiResourceUpdateBatch *resourceUpdates = rhi->nextResourceUpdateBatch();
560 resourceUpdates->readBackTexture(readbackDesc, &result);
562 cb->resourceUpdate(resourceUpdates);
566 if (imageSize == 0) {
567 imageSize = result.data.size();
568 writeUInt32(ktxOutputFile, quint32(imageSize));
571 ktxOutputFile.write(result.data);
575 ktxOutputFile.close();
577 preFilteredEnvCubeMap->deleteLater();
579 rhi->endOffscreenFrame();