62static const float cube[] = {
64 -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,
67 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,
70 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,
73 -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,
76 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,
79 -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,
82 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
85 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
88 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
91 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
94 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
97 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
105 QFile ktxOutputFile(outPath);
110 QScopedPointer<QRhi> rhi(
QRhi::create(impl, initParams, QRhi::Flags(),
nullptr));
114 qDebug() << rhi->driverInfo();
117 rhi->beginOffscreenFrame(&
cb);
119 const auto rhiContext = std::make_unique<QSSGRhiContext>(rhi.get());
121 rhiCtxD->setCommandBuffer(
cb);
127 auto shaderCache = std::make_unique<QSSGShaderCache>(*rhiContext);
155 const int suggestedSize =
qMax(512.f, inImage->height * 0.5f);
156 const QSize environmentMapSize(suggestedSize, suggestedSize);
158 const int colorSpace = inImage->isSRGB ? 1 : 0;
166 if (!envCubeMap->create()) {
169 envCubeMap->deleteLater();
173 if (!envMapRenderBuffer->create()) {
174 return QStringLiteral(
"Failed to create Environment Map Render Buffer");
176 envMapRenderBuffer->deleteLater();
179 QVarLengthArray<QRhiTextureRenderTarget *, 6> renderTargets;
186 auto renderTarget = rhi->newTextureRenderTarget(rtDesc);
187 renderTarget->setDescription(rtDesc);
189 renderPassDesc = renderTarget->newCompatibleRenderPassDescriptor();
190 renderTarget->setRenderPassDescriptor(renderPassDesc);
191 if (!renderTarget->create()) {
194 renderTarget->deleteLater();
195 renderTargets << renderTarget;
197 renderPassDesc->deleteLater();
200 QSize size(inImage->width, inImage->height);
209 if (inImage->textureFileData.isValid()) {
212 { inImage->textureFileData.data().constData() + inImage->textureFileData.dataOffset(0),
213 quint32(inImage->textureFileData.dataLength(0)) } } };
215 desc = { { 0, 0, { inImage->data, inImage->dataSizeInBytes } } };
217 auto *rub = rhi->nextResourceUpdateBatch();
226 const auto &envMapShaderStages = shaderCache->getBuiltInRhiShaders().getRhiEnvironmentmapShader();
232 rub->uploadStaticBuffer(vertexBuffer,
cube);
235 int ubufElementSize = rhi->ubufAligned(128);
240 int ubufEnvMapElementSize = rhi->ubufAligned(4);
242 uBufEnvMap->create();
243 uBufEnvMap->deleteLater();
247 envMapSrb->setBindings(
252 envMapSrb->deleteLater();
258 envMapPipeline->setShaderStages({ *envMapShaderStages->vertexStage(), *envMapShaderStages->fragmentStage() });
261 inputLayout.
setBindings({ { 3 *
sizeof(float) } });
264 envMapPipeline->setVertexInputLayout(inputLayout);
265 envMapPipeline->setShaderResourceBindings(envMapSrb);
266 envMapPipeline->setRenderPassDescriptor(renderPassDesc);
267 if (!envMapPipeline->create()) {
268 return QStringLiteral(
"Failed to create source env map pipeline state");
270 envMapPipeline->deleteLater();
273 cb->debugMarkBegin(
"Environment Cubemap Generation");
278 mvp.perspective(90.0f, 1.0f, 0.1f, 10.0f);
282 viewMatrix.
lookAt(eye, center, up);
285 QVarLengthArray<QMatrix4x4, 6> views;
288 if (rhi->isYUpInFramebuffer()) {
298 rub->updateDynamicBuffer(uBuf,
face * ubufElementSize, 64, mvp.constData());
299 rub->updateDynamicBuffer(uBuf,
face * ubufElementSize + 64, 64, views[
face].constData());
300 rub->updateDynamicBuffer(uBufEnvMap,
face * ubufEnvMapElementSize, 4, &colorSpace);
302 cb->resourceUpdate(rub);
305 cb->beginPass(renderTargets[
face],
QColor(0, 0, 0, 1), { 1.0f, 0 },
nullptr, rhiContext->commonPassFlags());
308 cb->setGraphicsPipeline(envMapPipeline);
309 cb->setVertexInput(0, 1, &vbufBinding);
310 cb->setViewport(
QRhiViewport(0, 0, environmentMapSize.width(), environmentMapSize.height()));
311 QVector<QPair<int, quint32>> dynamicOffset = {
315 cb->setShaderResources(envMapSrb, 2, dynamicOffset.constData());
324 rub = rhi->nextResourceUpdateBatch();
325 rub->generateMips(envCubeMap);
326 cb->resourceUpdate(rub);
330 cb->debugMarkBegin(
"Pre-filtered Environment Cubemap Generation");
335 if (!preFilteredEnvCubeMap->create())
336 qWarning(
"Failed to create Pre-filtered Environment Cube Map");
337 int mipmapCount = rhi->mipLevelsForSize(environmentMapSize);
338 mipmapCount =
qMin(mipmapCount, 6);
339 QMap<int, QSize> mipLevelSizes;
340 QMap<int, QVarLengthArray<QRhiTextureRenderTarget *, 6>> renderTargetsMap;
344 for (
int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
345 const QSize levelSize =
QSize(environmentMapSize.width() * std::pow(0.5, mipLevel),
346 environmentMapSize.height() * std::pow(0.5, mipLevel));
347 mipLevelSizes.insert(mipLevel, levelSize);
349 QVarLengthArray<QRhiTextureRenderTarget *, 6> renderTargets;
356 auto renderTarget = rhi->newTextureRenderTarget(rtDesc);
357 renderTarget->setDescription(rtDesc);
358 if (!renderPassDescriptorPhase2)
359 renderPassDescriptorPhase2 = renderTarget->newCompatibleRenderPassDescriptor();
360 renderTarget->setRenderPassDescriptor(renderPassDescriptorPhase2);
361 if (!renderTarget->create())
362 qWarning(
"Failed to build prefilter env map render target");
363 renderTarget->deleteLater();
364 renderTargets << renderTarget;
366 renderTargetsMap.insert(mipLevel, renderTargets);
367 renderPassDescriptorPhase2->deleteLater();
371 const auto &prefilterShaderStages = shaderCache->getBuiltInRhiShaders().getRhienvironmentmapPreFilterShader(isRGBE);
381 envMapCubeSampler = rhiContext->sampler(samplerMipMapDesc);
395 int ubufPrefilterElementSize = rhi->ubufAligned(20);
397 uBufPrefilter->create();
398 uBufPrefilter->deleteLater();
402 preFilterSrb->setBindings({
407 preFilterSrb->create();
408 preFilterSrb->deleteLater();
415 prefilterPipeline->setShaderStages({
416 *prefilterShaderStages->vertexStage(),
417 *prefilterShaderStages->fragmentStage()
420 prefilterPipeline->setVertexInputLayout(inputLayout);
421 prefilterPipeline->setShaderResourceBindings(preFilterSrb);
422 prefilterPipeline->setRenderPassDescriptor(renderPassDescriptorPhase2);
423 if (!prefilterPipeline->create())
424 return QStringLiteral(
"Failed to create pre-filter env map pipeline state");
425 prefilterPipeline->deleteLater();
429 rub = rhi->nextResourceUpdateBatch();
430 const float resolution = environmentMapSize.width();
431 const float lodBias = 0.0f;
432 const int sampleCount = 1024;
433 for (
int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
435 const float roughness = float(mipLevel) / float(mipmapCount - 2);
436 const int distribution = mipLevel == (mipmapCount - 1) ? 0 : 1;
437 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize, 4, &roughness);
438 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4, 4, &resolution);
439 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4 + 4, 4, &lodBias);
440 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4 + 4 + 4, 4, &sampleCount);
441 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4 + 4 + 4 + 4, 4, &distribution);
444 cb->resourceUpdate(rub);
447 for (
int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
449 cb->beginPass(renderTargetsMap[mipLevel][
face],
QColor(0, 0, 0, 1), { 1.0f, 0 },
nullptr, rhiContext->commonPassFlags());
450 cb->setGraphicsPipeline(prefilterPipeline);
451 cb->setVertexInput(0, 1, &vbufBinding);
453 QVector<QPair<int, quint32>> dynamicOffsets = {
455 { 2,
quint32(ubufPrefilterElementSize * mipLevel) }
457 cb->setShaderResources(preFilterSrb, 2, dynamicOffsets.constData());
466 const quint32 numberOfMipmapLevels = renderTargetsMap.size();
467 const quint32 numberOfFaces = 6;
471 '1',
'\xBB',
'\r',
'\n',
'\x1A',
'\n' };
473 QVector<char> keyValueData;
478 static const char key[] =
"QT_IBL_BAKER_VERSION";
479 static const char value[] =
"1";
481 constexpr size_t keyAndValueByteSize =
sizeof(
key) +
sizeof(
value);
482 appendBinaryVector(keyValueData, keyAndValueByteSize);
483 appendBinaryVector(keyValueData,
key);
484 appendBinaryVector(keyValueData,
value);
487 const size_t padding = 3 - ((keyAndValueByteSize + 3) % 4);
488 keyValueData.resize(keyValueData.size() + padding);
515 writeUInt32(ktxOutputFile,
quint32(environmentMapSize.width()));
518 writeUInt32(ktxOutputFile,
quint32(environmentMapSize.height()));
521 writeUInt32(ktxOutputFile,
quint32(0));
524 writeUInt32(ktxOutputFile,
quint32(0));
527 writeUInt32(ktxOutputFile,
quint32(numberOfFaces));
530 writeUInt32(ktxOutputFile,
quint32(numberOfMipmapLevels));
533 writeUInt32(ktxOutputFile,
quint32(keyValueData.size()));
536 ktxOutputFile.write(keyValueData.data(), keyValueData.size());
539 for (
quint32 mipmap_level = 0; mipmap_level < numberOfMipmapLevels; mipmap_level++) {
551 readbackDesc.setLayer(
int(
face));
552 readbackDesc.setLevel(mipmap_level);
555 resourceUpdates->readBackTexture(readbackDesc, &
result);
557 cb->resourceUpdate(resourceUpdates);
566 ktxOutputFile.write(
result.data);
570 ktxOutputFile.close();
572 preFilteredEnvCubeMap->deleteLater();
574 rhi->endOffscreenFrame();