241 srb.reset(rhi->newShaderResourceBindings());
246 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
uniformBuffer.get(),kUniformBlockSize)
252 auto getShader = [](
const QString &name) {
254 return f.open(QIODevice::ReadOnly) ? QShader::fromSerialized(f.readAll()) : QShader();
261 QString vert = (mode ==
Holographic || mode ==
XRay) ?
":/holographic.vert.qsb" :
262 (mode ==
Anatomical) ?
":/anatomical.vert.qsb" :
263 (mode ==
Dipole) ?
":/dipole.vert.qsb" :
264 (mode ==
ShowNormals) ?
":/shownormals.vert.qsb" :
":/standard.vert.qsb";
266 QString frag = (mode ==
Holographic || mode ==
XRay) ?
":/holographic.frag.qsb" :
267 (mode ==
Anatomical) ?
":/anatomical.frag.qsb" :
268 (mode ==
Dipole) ?
":/dipole.frag.qsb" :
269 (mode ==
ShowNormals) ?
":/shownormals.frag.qsb" :
":/standard.frag.qsb";
271 QShader vS = getShader(vert);
272 QShader fS = getShader(frag);
274 if (!vS.isValid() || !fS.isValid()) {
275 qWarning() <<
"BrainRenderer: Could not load shaders for mode" << mode << vert << frag;
280 auto pipeline = std::unique_ptr<QRhiGraphicsPipeline>(rhi->newGraphicsPipeline());
282 QRhiGraphicsPipeline::TargetBlend blend;
285 blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
286 blend.dstColor = QRhiGraphicsPipeline::One;
287 blend.srcAlpha = QRhiGraphicsPipeline::SrcAlpha;
288 blend.dstAlpha = QRhiGraphicsPipeline::One;
289 }
else if (mode ==
Dipole) {
291 blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
292 blend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
293 blend.srcAlpha = QRhiGraphicsPipeline::SrcAlpha;
294 blend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
297 auto setup = [&](QRhiGraphicsPipeline* p, QRhiGraphicsPipeline::CullMode cull) {
298 p->setShaderStages({{ QRhiShaderStage::Vertex, vS }, { QRhiShaderStage::Fragment, fS }});
300 QRhiVertexInputLayout il;
304 { 6 *
sizeof(float) },
305 { 21 *
sizeof(float), QRhiVertexInputBinding::PerInstance }
310 { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
311 { 0, 1, QRhiVertexInputAttribute::Float3, 3 *
sizeof(float) },
315 { 1, 2, QRhiVertexInputAttribute::Float4, 0 },
316 { 1, 3, QRhiVertexInputAttribute::Float4, 4 *
sizeof(float) },
317 { 1, 4, QRhiVertexInputAttribute::Float4, 8 *
sizeof(float) },
318 { 1, 5, QRhiVertexInputAttribute::Float4, 12 *
sizeof(float) },
320 { 1, 6, QRhiVertexInputAttribute::Float4, 16 *
sizeof(float) },
322 { 1, 7, QRhiVertexInputAttribute::Float, 20 *
sizeof(float) }
325 il.setBindings({{ 36 }});
326 il.setAttributes({{ 0, 0, QRhiVertexInputAttribute::Float3, 0 },
327 { 0, 1, QRhiVertexInputAttribute::Float3, 12 },
328 { 0, 2, QRhiVertexInputAttribute::UNormByte4, 24 },
329 { 0, 3, QRhiVertexInputAttribute::UNormByte4, 28 },
330 { 0, 4, QRhiVertexInputAttribute::Float, 32 }});
333 p->setVertexInputLayout(il);
334 p->setShaderResourceBindings(
srb.get());
335 p->setRenderPassDescriptor(rp);
336 p->setSampleCount(sampleCount);
337 p->setCullMode(cull);
339 p->setTargetBlends({blend});
340 p->setDepthTest(
true);
341 p->setDepthWrite(
false);
342 }
else if (mode ==
XRay) {
343 p->setTargetBlends({blend});
344 p->setDepthTest(
false);
345 p->setDepthWrite(
false);
346 }
else if (mode ==
Dipole) {
347 p->setTargetBlends({blend});
348 p->setCullMode(QRhiGraphicsPipeline::None);
349 p->setDepthTest(
true);
350 p->setDepthWrite(
false);
352 p->setDepthTest(
true);
353 p->setDepthWrite(
true);
355 p->setFlags(QRhiGraphicsPipeline::UsesScissor);
360 auto pipelineBack = std::unique_ptr<QRhiGraphicsPipeline>(rhi->newGraphicsPipeline());
361 setup(pipelineBack.get(), QRhiGraphicsPipeline::Front);
363 setup(pipeline.get(), QRhiGraphicsPipeline::Back);
366 setup(pipeline.get(), QRhiGraphicsPipeline::None);
469 QRhiResourceUpdateBatch *u,
472 if (!overlay || !overlay->
isEnabled())
return;
474 if (!rhi || !u)
return;
476 QImage frame = tightlyPackedRgba(overlay->
frame());
477 if (frame.isNull())
return;
479 auto &k = d->videoOverlay;
480 k.currentUniformOffset = 0;
483 if (!k.initialized) {
485 QFile vFile(QStringLiteral(
":/video_overlay.vert.qsb"));
486 QFile fFile(QStringLiteral(
":/video_overlay.frag.qsb"));
487 if (!vFile.open(QIODevice::ReadOnly) || !fFile.open(QIODevice::ReadOnly)) {
488 qWarning() <<
"BrainRenderer: failed to open video overlay shaders";
491 QShader vShader = QShader::fromSerialized(vFile.readAll());
492 QShader fShader = QShader::fromSerialized(fFile.readAll());
493 if (!vShader.isValid() || !fShader.isValid()) {
494 qWarning() <<
"BrainRenderer: invalid video overlay shaders";
499 constexpr int kUbSize = 256;
500 k.uniformBufferOffsetAlignment = rhi->ubufAlignment();
501 k.uniformBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic,
502 QRhiBuffer::UniformBuffer,
503 64 * k.uniformBufferOffsetAlignment));
504 k.uniformBuffer->create();
507 constexpr int kVbSize = 4 * 5 *
sizeof(float);
508 k.vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic,
509 QRhiBuffer::VertexBuffer, kVbSize));
510 k.vertexBuffer->create();
513 constexpr int kIbSize = 6 *
sizeof(quint32);
514 k.indexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable,
515 QRhiBuffer::IndexBuffer, kIbSize));
516 k.indexBuffer->create();
519 k.sampler.reset(rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear,
521 QRhiSampler::ClampToEdge,
522 QRhiSampler::ClampToEdge));
526 k.texture.reset(rhi->newTexture(QRhiTexture::RGBA8, frame.size()));
528 k.textureSize = frame.size();
531 k.srb.reset(rhi->newShaderResourceBindings());
533 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
534 QRhiShaderResourceBinding::VertexStage |
535 QRhiShaderResourceBinding::FragmentStage,
536 k.uniformBuffer.get(), kUniformBlockSize),
537 QRhiShaderResourceBinding::sampledTexture(1,
538 QRhiShaderResourceBinding::FragmentStage,
539 k.texture.get(), k.sampler.get())
544 QRhiGraphicsPipeline::TargetBlend blend;
546 blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
547 blend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
548 blend.srcAlpha = QRhiGraphicsPipeline::One;
549 blend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
551 k.pipeline.reset(rhi->newGraphicsPipeline());
552 k.pipeline->setShaderStages({
553 { QRhiShaderStage::Vertex, vShader },
554 { QRhiShaderStage::Fragment, fShader }
556 QRhiVertexInputLayout il;
557 il.setBindings({{ 5 *
sizeof(float) }});
559 { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
560 { 0, 1, QRhiVertexInputAttribute::Float2, 3 *
sizeof(float) }
562 k.pipeline->setVertexInputLayout(il);
563 k.pipeline->setShaderResourceBindings(k.srb.get());
564 k.pipeline->setRenderPassDescriptor(d->rtClear->renderPassDescriptor());
565 k.pipeline->setSampleCount(d->rtClear->sampleCount());
566 k.pipeline->setCullMode(QRhiGraphicsPipeline::None);
567 k.pipeline->setTargetBlends({blend});
568 k.pipeline->setDepthTest(
false);
569 k.pipeline->setDepthWrite(
false);
570 k.pipeline->setFlags(QRhiGraphicsPipeline::UsesScissor);
571 k.pipeline->create();
573 QFile dvFile(QStringLiteral(
":/video_decal.vert.qsb"));
574 QFile dfFile(QStringLiteral(
":/video_decal.frag.qsb"));
575 if (!dvFile.open(QIODevice::ReadOnly) || !dfFile.open(QIODevice::ReadOnly)) {
576 qWarning() <<
"BrainRenderer: failed to open video decal shaders";
579 QShader dvShader = QShader::fromSerialized(dvFile.readAll());
580 QShader dfShader = QShader::fromSerialized(dfFile.readAll());
581 if (!dvShader.isValid() || !dfShader.isValid()) {
582 qWarning() <<
"BrainRenderer: invalid video decal shaders";
586 QRhiGraphicsPipeline::TargetBlend decalBlend;
587 decalBlend.enable =
true;
588 decalBlend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
589 decalBlend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
590 decalBlend.srcAlpha = QRhiGraphicsPipeline::One;
591 decalBlend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
593 k.surfacePipeline.reset(rhi->newGraphicsPipeline());
594 k.surfacePipeline->setShaderStages({
595 { QRhiShaderStage::Vertex, dvShader },
596 { QRhiShaderStage::Fragment, dfShader }
598 QRhiVertexInputLayout dil;
599 dil.setBindings({{ 36 }});
601 { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
602 { 0, 1, QRhiVertexInputAttribute::Float3, 12 },
603 { 0, 2, QRhiVertexInputAttribute::UNormByte4, 24 },
604 { 0, 3, QRhiVertexInputAttribute::UNormByte4, 28 },
605 { 0, 4, QRhiVertexInputAttribute::Float, 32 }
607 k.surfacePipeline->setVertexInputLayout(dil);
608 k.surfacePipeline->setShaderResourceBindings(k.srb.get());
609 k.surfacePipeline->setRenderPassDescriptor(d->rtClear->renderPassDescriptor());
610 k.surfacePipeline->setSampleCount(d->rtClear->sampleCount());
611 k.surfacePipeline->setCullMode(QRhiGraphicsPipeline::None);
612 k.surfacePipeline->setTargetBlends({decalBlend});
613 k.surfacePipeline->setDepthTest(
true);
614 k.surfacePipeline->setDepthWrite(
false);
615 k.surfacePipeline->setFlags(QRhiGraphicsPipeline::UsesScissor);
616 k.surfacePipeline->create();
618 k.initialized =
true;
622 if (overlay->
isDepthEnabled() && !k.depthInitialized && k.initialized) {
623 QFile ddvFile(QStringLiteral(
":/video_decal_depth.vert.qsb"));
624 QFile ddfFile(QStringLiteral(
":/video_decal_depth.frag.qsb"));
625 if (ddvFile.open(QIODevice::ReadOnly) && ddfFile.open(QIODevice::ReadOnly)) {
626 QShader ddvShader = QShader::fromSerialized(ddvFile.readAll());
627 QShader ddfShader = QShader::fromSerialized(ddfFile.readAll());
628 if (ddvShader.isValid() && ddfShader.isValid()) {
631 k.depthSampler.reset(rhi->newSampler(
632 QRhiSampler::Linear, QRhiSampler::Linear,
634 QRhiSampler::ClampToEdge,
635 QRhiSampler::ClampToEdge));
636 k.depthSampler->create();
639 k.depthTexture.reset(rhi->newTexture(
640 QRhiTexture::RGBA8, QSize(1, 1), 1, QRhiTexture::MipMapped));
641 k.depthTexture->create();
642 k.depthTextureSize = QSize(1, 1);
645 k.srbDepth.reset(rhi->newShaderResourceBindings());
646 k.srbDepth->setBindings({
647 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
648 QRhiShaderResourceBinding::VertexStage |
649 QRhiShaderResourceBinding::FragmentStage,
650 k.uniformBuffer.get(), kUniformBlockSize),
651 QRhiShaderResourceBinding::sampledTexture(1,
652 QRhiShaderResourceBinding::FragmentStage,
653 k.texture.get(), k.sampler.get()),
654 QRhiShaderResourceBinding::sampledTexture(2,
655 QRhiShaderResourceBinding::VertexStage |
656 QRhiShaderResourceBinding::FragmentStage,
657 k.depthTexture.get(), k.depthSampler.get())
659 k.srbDepth->create();
661 QRhiGraphicsPipeline::TargetBlend depthDecalBlend;
662 depthDecalBlend.enable =
true;
663 depthDecalBlend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
664 depthDecalBlend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
665 depthDecalBlend.srcAlpha = QRhiGraphicsPipeline::One;
666 depthDecalBlend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
668 k.surfaceDepthPipeline.reset(rhi->newGraphicsPipeline());
669 k.surfaceDepthPipeline->setShaderStages({
670 { QRhiShaderStage::Vertex, ddvShader },
671 { QRhiShaderStage::Fragment, ddfShader }
673 QRhiVertexInputLayout ddil;
674 ddil.setBindings({{ 36 }});
676 { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
677 { 0, 1, QRhiVertexInputAttribute::Float3, 12 },
678 { 0, 2, QRhiVertexInputAttribute::UNormByte4, 24 },
679 { 0, 3, QRhiVertexInputAttribute::UNormByte4, 28 },
680 { 0, 4, QRhiVertexInputAttribute::Float, 32 }
682 k.surfaceDepthPipeline->setVertexInputLayout(ddil);
683 k.surfaceDepthPipeline->setShaderResourceBindings(k.srbDepth.get());
684 k.surfaceDepthPipeline->setRenderPassDescriptor(d->rtClear->renderPassDescriptor());
685 k.surfaceDepthPipeline->setSampleCount(d->rtClear->sampleCount());
686 k.surfaceDepthPipeline->setCullMode(QRhiGraphicsPipeline::None);
687 k.surfaceDepthPipeline->setTargetBlends({depthDecalBlend});
688 k.surfaceDepthPipeline->setDepthTest(
false);
689 k.surfaceDepthPipeline->setDepthWrite(
false);
690 k.surfaceDepthPipeline->setFlags(QRhiGraphicsPipeline::UsesScissor);
691 k.surfaceDepthPipeline->create();
692 k.depthInitialized =
true;
698 if (!k.indexUploaded) {
699 const quint32 idx[6] = { 0, 1, 2, 2, 1, 3 };
700 u->uploadStaticBuffer(k.indexBuffer.get(), idx);
701 k.indexUploaded =
true;
705 if (frame.size() != k.textureSize) {
706 k.texture.reset(rhi->newTexture(QRhiTexture::RGBA8, frame.size()));
708 k.textureSize = frame.size();
711 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
712 QRhiShaderResourceBinding::VertexStage |
713 QRhiShaderResourceBinding::FragmentStage,
714 k.uniformBuffer.get(), kUniformBlockSize),
715 QRhiShaderResourceBinding::sampledTexture(1,
716 QRhiShaderResourceBinding::FragmentStage,
717 k.texture.get(), k.sampler.get())
721 if (k.srbDepth && k.depthTexture) {
722 k.srbDepth->setBindings({
723 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
724 QRhiShaderResourceBinding::VertexStage |
725 QRhiShaderResourceBinding::FragmentStage,
726 k.uniformBuffer.get(), kUniformBlockSize),
727 QRhiShaderResourceBinding::sampledTexture(1,
728 QRhiShaderResourceBinding::FragmentStage,
729 k.texture.get(), k.sampler.get()),
730 QRhiShaderResourceBinding::sampledTexture(2,
731 QRhiShaderResourceBinding::VertexStage |
732 QRhiShaderResourceBinding::FragmentStage,
733 k.depthTexture.get(), k.depthSampler.get())
735 k.srbDepth->create();
737 k.uploadedFrameGen = std::numeric_limits<quint64>::max();
740 QRhiTextureSubresourceUploadDescription sub(frame);
741 QRhiTextureUploadDescription desc({ 0, 0, sub });
742 u->uploadTexture(k.texture.get(), desc);
748 QImage depthFrame = tightlyPackedRgba(overlay->
depthFrame());
749 if (!depthFrame.isNull()) {
750 if (depthFrame.size() != k.depthTextureSize) {
751 k.depthTexture.reset(rhi->newTexture(
752 QRhiTexture::RGBA8, depthFrame.size(), 1, QRhiTexture::MipMapped));
753 k.depthTexture->create();
754 k.depthTextureSize = depthFrame.size();
756 k.srbDepth->setBindings({
757 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
758 QRhiShaderResourceBinding::VertexStage |
759 QRhiShaderResourceBinding::FragmentStage,
760 k.uniformBuffer.get(), kUniformBlockSize),
761 QRhiShaderResourceBinding::sampledTexture(1,
762 QRhiShaderResourceBinding::FragmentStage,
763 k.texture.get(), k.sampler.get()),
764 QRhiShaderResourceBinding::sampledTexture(2,
765 QRhiShaderResourceBinding::VertexStage |
766 QRhiShaderResourceBinding::FragmentStage,
767 k.depthTexture.get(), k.depthSampler.get())
769 k.srbDepth->create();
770 k.uploadedDepthFrameGen = std::numeric_limits<quint64>::max();
773 QRhiTextureSubresourceUploadDescription depthSub(depthFrame);
774 QRhiTextureUploadDescription depthDesc({ 0, 0, depthSub });
775 u->uploadTexture(k.depthTexture.get(), depthDesc);
776 u->generateMips(k.depthTexture.get());
787 if (!overlay || !overlay->
isEnabled())
return;
790 auto &k = d->videoOverlay;
791 if (!k.initialized)
return;
792 if (k.uniformBufferOffsetAlignment <= 0)
return;
794 QRhiResourceUpdateBatch *u = rhi->nextResourceUpdateBatch();
795 const int uniformOffset = k.currentUniformOffset;
796 k.currentUniformOffset += k.uniformBufferOffsetAlignment;
797 if (uniformOffset + kUniformBlockSize > k.uniformBuffer->size())
return;
805 const QImage &frm = overlay->
frame();
806 const float aspect = (frm.height() > 0)
807 ?
static_cast<float>(frm.width()) / frm.height()
809 const float halfW = 0.5f * sizeM;
810 const float halfH = (aspect > 0.0f) ? halfW / aspect : halfW;
812 QVector3D viewDir = centre - data.
cameraPos;
813 if (viewDir.lengthSquared() < 1e-12f) viewDir = QVector3D(0, 0, -1);
818 const QVector3D hint = overlay->
upHint();
820 if (hint.lengthSquared() > 1e-8f) {
822 up = (hint - QVector3D::dotProduct(hint, viewDir) * viewDir).normalized();
823 if (up.lengthSquared() < 1e-8f)
824 up = QVector3D(0.0f, 0.0f, 1.0f);
826 QVector3D worldUp(0.0f, 0.0f, 1.0f);
827 if (std::abs(QVector3D::dotProduct(viewDir, worldUp)) > 0.95f)
828 worldUp = QVector3D(0.0f, 1.0f, 0.0f);
829 up = QVector3D::crossProduct(
830 QVector3D::crossProduct(viewDir, worldUp), viewDir).normalized();
832 QVector3D right = QVector3D::crossProduct(viewDir, up).normalized();
834 const QVector3D c00 = centre - right * halfW - up * halfH;
835 const QVector3D c10 = centre + right * halfW - up * halfH;
836 const QVector3D c01 = centre - right * halfW + up * halfH;
837 const QVector3D c11 = centre + right * halfW + up * halfH;
839 const float verts[4 * 5] = {
840 c00.x(), c00.y(), c00.z(), 0.0f, 1.0f,
841 c10.x(), c10.y(), c10.z(), 1.0f, 1.0f,
842 c01.x(), c01.y(), c01.z(), 0.0f, 0.0f,
843 c11.x(), c11.y(), c11.z(), 1.0f, 0.0f
845 u->updateDynamicBuffer(k.vertexBuffer.get(), 0,
sizeof(verts), verts);
850 float borderColor[4];
852 float pad0, pad1, pad2;
854 memcpy(ub.mvp, data.
mvp.constData(), 64);
855 ub.borderColor[0] = 0.0f;
856 ub.borderColor[1] = 0.85f;
857 ub.borderColor[2] = 1.0f;
858 ub.borderColor[3] = 1.0f;
859 ub.opacity = overlay->
opacity();
860 ub.pad0 = ub.pad1 = ub.pad2 = 0.0f;
861 u->updateDynamicBuffer(k.uniformBuffer.get(), uniformOffset,
sizeof(ub), &ub);
863 cb->resourceUpdate(u);
866 cb->setViewport(toViewport(data));
867 cb->setScissor(toScissor(data));
868 cb->setGraphicsPipeline(k.pipeline.get());
869 const QRhiCommandBuffer::DynamicOffset srbOffset = { 0, uint32_t(uniformOffset) };
870 cb->setShaderResources(k.srb.get(), 1, &srbOffset);
871 const QRhiCommandBuffer::VertexInput vbuf(k.vertexBuffer.get(), 0);
872 cb->setVertexInput(0, 1, &vbuf, k.indexBuffer.get(), 0, QRhiCommandBuffer::IndexUInt32);
883 if (!surface)
return;
885 auto &k = d->videoOverlay;
886 if (!k.initialized || !k.surfacePipeline)
return;
887 if (k.uniformBufferOffsetAlignment <= 0)
return;
889 QRhiResourceUpdateBatch *u = rhi->nextResourceUpdateBatch();
890 const int uniformOffset = k.currentUniformOffset;
891 k.currentUniformOffset += k.uniformBufferOffsetAlignment;
892 if (uniformOffset + kUniformBlockSize > k.uniformBuffer->size())
return;
896 if (localNormal.lengthSquared() < 1e-12f) {
897 localNormal = QVector3D(0.0f, 0.0f, 1.0f);
899 localNormal.normalize();
904 QVector3D referenceUp = overlay->
upHint();
905 if (referenceUp.lengthSquared() < 1e-6f) {
907 referenceUp = QVector3D(0.0f, 0.0f, 1.0f);
908 if (std::abs(QVector3D::dotProduct(localNormal, referenceUp)) > 0.95f) {
909 referenceUp = QVector3D(1.0f, 0.0f, 0.0f);
913 referenceUp = (referenceUp - QVector3D::dotProduct(referenceUp, localNormal) * localNormal);
914 if (referenceUp.lengthSquared() < 1e-12f) {
915 referenceUp = QVector3D(0.0f, 0.0f, 1.0f);
917 referenceUp.normalize();
919 const QVector3D axisV = referenceUp;
920 const QVector3D axisU = QVector3D::crossProduct(axisV, localNormal).normalized();
924 && k.depthInitialized
925 && k.surfaceDepthPipeline;
932 float focusAndSize[4];
933 float axisUAndOpacity[4];
934 float axisVAndOffset[4];
935 float axisNAndDepth[4];
936 float cameraPosAndFacing[4];
937 float borderColor[4];
938 float depthParams[4];
940 memcpy(ub.mvp, data.
mvp.constData(), 64);
945 ub.axisUAndOpacity[0] = axisU.x();
946 ub.axisUAndOpacity[1] = axisU.y();
947 ub.axisUAndOpacity[2] = axisU.z();
948 ub.axisUAndOpacity[3] = overlay->
opacity();
949 ub.axisVAndOffset[0] = axisV.x();
950 ub.axisVAndOffset[1] = axisV.y();
951 ub.axisVAndOffset[2] = axisV.z();
952 ub.axisVAndOffset[3] = 0.00045f;
953 ub.axisNAndDepth[0] = localNormal.x();
954 ub.axisNAndDepth[1] = localNormal.y();
955 ub.axisNAndDepth[2] = localNormal.z();
956 ub.axisNAndDepth[3] = std::max(0.015f, overlay->
sizeMeters() * 0.45f);
957 ub.cameraPosAndFacing[0] = data.
cameraPos.x();
958 ub.cameraPosAndFacing[1] = data.
cameraPos.y();
959 ub.cameraPosAndFacing[2] = data.
cameraPos.z();
960 const QImage &decalFrame = overlay->
frame();
961 ub.cameraPosAndFacing[3] = (decalFrame.height() > 0)
962 ?
static_cast<float>(decalFrame.width()) / decalFrame.height()
964 ub.borderColor[0] = 0.0f;
965 ub.borderColor[1] = 0.85f;
966 ub.borderColor[2] = 1.0f;
967 ub.borderColor[3] = 1.0f;
968 ub.depthParams[0] = useDepth ? overlay->
depthScale() : 0.0f;
969 ub.depthParams[1] = useDepth ?
static_cast<float>(overlay->
depthSteps()) : 0.0f;
970 ub.depthParams[2] = useDepth ? 1.0f : 0.0f;
971 ub.depthParams[3] = 0.0f;
972 u->updateDynamicBuffer(k.uniformBuffer.get(), uniformOffset,
sizeof(ub), &ub);
973 cb->resourceUpdate(u);
975 cb->setViewport(toViewport(data));
976 cb->setScissor(toScissor(data));
978 cb->setGraphicsPipeline(k.surfaceDepthPipeline.get());
979 const QRhiCommandBuffer::DynamicOffset srbOffset = { 0, uint32_t(uniformOffset) };
980 cb->setShaderResources(k.srbDepth.get(), 1, &srbOffset);
982 cb->setGraphicsPipeline(k.surfacePipeline.get());
983 const QRhiCommandBuffer::DynamicOffset srbOffset = { 0, uint32_t(uniformOffset) };
984 cb->setShaderResources(k.srb.get(), 1, &srbOffset);
986 const QRhiCommandBuffer::VertexInput vbuf(surface->
vertexBuffer(), 0);
987 cb->setVertexInput(0, 1, &vbuf, surface->
indexBuffer(), 0, QRhiCommandBuffer::IndexUInt32);
996 QRhiResourceUpdateBatch *u,
1000 if (!rhi || !u)
return;
1003 auto &k = d->sliceRes;
1004 auto &sliceSlot = k.sliceSlots[slotIndex];
1007 sliceSlot.visible =
false;
1011 const QImage &img = slice->
image();
1013 sliceSlot.visible =
false;
1018 if (!k.initialized) {
1019 QFile vFile(QStringLiteral(
":/slice.vert.qsb"));
1020 QFile fFile(QStringLiteral(
":/slice.frag.qsb"));
1021 if (!vFile.open(QIODevice::ReadOnly) || !fFile.open(QIODevice::ReadOnly)) {
1022 qWarning() <<
"BrainRenderer: failed to open MRI slice shaders";
1025 QShader vShader = QShader::fromSerialized(vFile.readAll());
1026 QShader fShader = QShader::fromSerialized(fFile.readAll());
1027 if (!vShader.isValid() || !fShader.isValid()) {
1028 qWarning() <<
"BrainRenderer: invalid MRI slice shaders";
1032 k.uniformBufferOffsetAlignment = rhi->ubufAlignment();
1033 k.uniformBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic,
1034 QRhiBuffer::UniformBuffer,
1035 64 * k.uniformBufferOffsetAlignment));
1036 k.uniformBuffer->create();
1039 k.indexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable,
1040 QRhiBuffer::IndexBuffer,
1041 6 *
sizeof(quint32)));
1042 k.indexBuffer->create();
1044 k.sampler.reset(rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear,
1046 QRhiSampler::ClampToEdge,
1047 QRhiSampler::ClampToEdge));
1048 k.sampler->create();
1052 constexpr int kVbSize = 4 * 5 *
sizeof(float);
1053 k.vertexBuffer[i].reset(rhi->newBuffer(QRhiBuffer::Dynamic,
1054 QRhiBuffer::VertexBuffer, kVbSize));
1055 k.vertexBuffer[i]->create();
1058 k.sliceSlots[i].texture.reset(rhi->newTexture(QRhiTexture::R8, QSize(1, 1)));
1059 k.sliceSlots[i].texture->create();
1060 k.sliceSlots[i].textureSize = QSize(1, 1);
1062 k.srb[i].reset(rhi->newShaderResourceBindings());
1063 k.srb[i]->setBindings({
1064 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
1065 QRhiShaderResourceBinding::VertexStage |
1066 QRhiShaderResourceBinding::FragmentStage,
1067 k.uniformBuffer.get(), kUniformBlockSize),
1068 QRhiShaderResourceBinding::sampledTexture(1,
1069 QRhiShaderResourceBinding::FragmentStage,
1070 k.sliceSlots[i].texture.get(), k.sampler.get())
1076 QRhiGraphicsPipeline::TargetBlend blend;
1077 blend.enable =
true;
1078 blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
1079 blend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
1080 blend.srcAlpha = QRhiGraphicsPipeline::One;
1081 blend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
1083 k.pipeline.reset(rhi->newGraphicsPipeline());
1084 k.pipeline->setShaderStages({
1085 { QRhiShaderStage::Vertex, vShader },
1086 { QRhiShaderStage::Fragment, fShader }
1088 QRhiVertexInputLayout il;
1089 il.setBindings({{ 5 *
sizeof(float) }});
1091 { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
1092 { 0, 1, QRhiVertexInputAttribute::Float2, 3 *
sizeof(float) }
1094 k.pipeline->setVertexInputLayout(il);
1095 k.pipeline->setShaderResourceBindings(k.srb[0].get());
1096 k.pipeline->setRenderPassDescriptor(d->rtClear->renderPassDescriptor());
1097 k.pipeline->setSampleCount(d->rtClear->sampleCount());
1098 k.pipeline->setCullMode(QRhiGraphicsPipeline::None);
1099 k.pipeline->setTargetBlends({blend});
1100 k.pipeline->setDepthTest(
true);
1101 k.pipeline->setDepthWrite(
false);
1102 k.pipeline->setFlags(QRhiGraphicsPipeline::UsesScissor);
1103 k.pipeline->create();
1105 k.initialized =
true;
1109 if (!k.indexUploaded) {
1110 const quint32 idx[6] = { 0, 1, 2, 2, 1, 3 };
1111 u->uploadStaticBuffer(k.indexBuffer.get(), idx);
1112 k.indexUploaded =
true;
1117 QImage gray = img.convertToFormat(QImage::Format_Grayscale8);
1118 const QSize imgSize = gray.size();
1120 if (imgSize != sliceSlot.textureSize) {
1121 sliceSlot.texture.reset(rhi->newTexture(QRhiTexture::R8, imgSize));
1122 sliceSlot.texture->create();
1123 sliceSlot.textureSize = imgSize;
1125 k.srb[slotIndex]->setBindings({
1126 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0,
1127 QRhiShaderResourceBinding::VertexStage |
1128 QRhiShaderResourceBinding::FragmentStage,
1129 k.uniformBuffer.get(), kUniformBlockSize),
1130 QRhiShaderResourceBinding::sampledTexture(1,
1131 QRhiShaderResourceBinding::FragmentStage,
1132 sliceSlot.texture.get(), k.sampler.get())
1134 k.srb[slotIndex]->create();
1139 const qsizetype tightStride = qsizetype(gray.width());
1141 texData.resize(gray.height() * tightStride);
1142 for (
int y = 0; y < gray.height(); ++y) {
1143 memcpy(texData.data() + y * tightStride,
1144 gray.constScanLine(y),
1145 size_t(tightStride));
1147 QRhiTextureSubresourceUploadDescription sub(texData.constData(), texData.size());
1148 sub.setSourceSize(imgSize);
1149 QRhiTextureUploadDescription desc({ 0, 0, sub });
1150 u->uploadTexture(sliceSlot.texture.get(), desc);
1155 u->updateDynamicBuffer(k.vertexBuffer[slotIndex].get(), 0,
1156 sliceSlot.vertices.size() *
sizeof(
float),
1157 sliceSlot.vertices.constData());
1159 sliceSlot.opacity = slice->
opacity();
1162 sliceSlot.visible =
true;
1543 const QVector<BrainSurface*> &surfaces,
1544 const QString &groupName)
1546 auto &group = d->mergedGroups[groupName];
1550 if (group.surfaces.size() != surfaces.size()) {
1553 for (
int i = 0; i < surfaces.size(); ++i) {
1554 if (group.surfaces[i] != surfaces[i]) {
1564 if (!group.dirty && group.indexCount > 0) {
1566 bool anyChanged =
false;
1567 if (group.surfaceGenerations.size() != surfaces.size()) {
1570 for (
int i = 0; i < surfaces.size(); ++i) {
1571 if (surfaces[i] && surfaces[i]->vertexGeneration() != group.surfaceGenerations[i]) {
1587 for (
int si = 0; si < surfaces.size(); ++si)
1588 if (surfaces[si]) totalVerts += surfaces[si]->vertexDataRef().size();
1589 if (totalVerts != group.totalVertexCount) {
1593 float brainId = 0.0f;
1594 float nonBrainId = 100.0f;
1595 group.surfaceGenerations.resize(surfaces.size());
1597 for (
int si = 0; si < surfaces.size(); ++si) {
1599 if (!surf) { brainId += 1.0f; nonBrainId += 1.0f;
continue; }
1601 const float id = isBrain ? brainId : nonBrainId;
1603 const int n = srcVerts.size();
1604 memcpy(dst, srcVerts.constData(), n *
sizeof(
VertexData));
1605 for (
int j = 0; j < n; ++j)
1606 dst[j].surfaceId =
id;
1612 group.gpuVertexDirty =
true;
1618 group.surfaces = surfaces;
1619 group.indexCount = 0;
1620 group.totalVertexCount = 0;
1621 group.dirty =
false;
1626 int totalIndices = 0;
1627 for (
int si = 0; si < surfaces.size(); ++si) {
1628 if (!surfaces[si])
continue;
1629 totalVerts += surfaces[si]->vertexDataRef().size();
1630 totalIndices += surfaces[si]->indexDataRef().size();
1633 group.indexCount = totalIndices;
1634 if (group.indexCount == 0)
return;
1636 const quint32 vbufSize = totalVerts *
sizeof(
VertexData);
1637 const quint32 ibufSize = totalIndices *
sizeof(uint32_t);
1640 if (!group.vertexBuffer || group.vertexBuffer->size() < vbufSize) {
1641 group.vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic,
1642 QRhiBuffer::VertexBuffer, vbufSize));
1643 group.vertexBuffer->create();
1645 if (!group.indexBuffer || group.indexBuffer->size() < ibufSize) {
1646 group.indexBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic,
1647 QRhiBuffer::IndexBuffer, ibufSize));
1648 group.indexBuffer->create();
1652 group.vertexRaw.resize(vbufSize);
1653 group.indexRaw.resize(ibufSize);
1654 group.surfaceGenerations.resize(surfaces.size());
1657 uint32_t *iDst =
reinterpret_cast<uint32_t*
>(group.indexRaw.data());
1658 float brainId = 0.0f;
1659 float nonBrainId = 100.0f;
1660 uint32_t vertexOffset = 0;
1661 for (
int si = 0; si < surfaces.size(); ++si) {
1663 if (!surf) { brainId += 1.0f; nonBrainId += 1.0f;
continue; }
1666 const float id = isBrain ? brainId : nonBrainId;
1669 const int nv = srcVerts.size();
1670 const int ni = srcIdx.size();
1673 memcpy(vDst, srcVerts.constData(), nv *
sizeof(
VertexData));
1674 for (
int j = 0; j < nv; ++j)
1675 vDst[j].surfaceId =
id;
1679 const uint32_t *srcI = srcIdx.constData();
1680 for (
int j = 0; j < ni; ++j)
1681 iDst[j] = srcI[j] + vertexOffset;
1689 group.totalVertexCount = totalVerts;
1690 group.gpuVertexDirty =
true;
1691 group.gpuIndexDirty =
true;