From d5dcd8142dc9daada4bad7b35e6d8aafddaa7bab Mon Sep 17 00:00:00 2001 From: italink <657959053@qq.com> Date: Sun, 3 Sep 2023 20:02:36 +0800 Subject: [PATCH] =?UTF-8?q?[feature]=20=E5=A2=9E=E5=8A=A0=E6=8F=8F?= =?UTF-8?q?=E8=BE=B9=E7=A4=BA=E4=BE=8B=EF=BC=8C=E6=93=8D=E4=BD=9C=E6=8F=90?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Private/RenderDocPlugin.cpp | 3 + .../Private/Render/Pass/QBlurRenderPass.cpp | 2 +- .../Render/Pass/QMotionBlurRenderPass.cpp | 176 ++++++++++++++++++ .../Render/Pass/QOutliningRenderPass.cpp | 127 +++++++++++++ .../Private/Render/Pass/QSsaoRenderPass.cpp | 2 +- .../Render/Pass/QMotionBlurRenderPass.h | 52 ++++++ .../Public/Render/Pass/QOutliningRenderPass.h | 75 ++++++++ Source/Core/Source/Public/Utils/QCamera.h | 2 +- Source/Editor/Resources.qrc | 1 + Source/Editor/Resources/tips.png | Bin 0 -> 36583 bytes .../Private/Render/Painter/DebugUiPainter.cpp | 28 ++- .../Private/Render/Painter/FrameGraphView.cpp | 1 + .../Private/Render/Painter/FrameGraphView.h | 3 +- 13 files changed, 466 insertions(+), 6 deletions(-) create mode 100644 Source/Core/Source/Private/Render/Pass/QMotionBlurRenderPass.cpp create mode 100644 Source/Core/Source/Private/Render/Pass/QOutliningRenderPass.cpp create mode 100644 Source/Core/Source/Public/Render/Pass/QMotionBlurRenderPass.h create mode 100644 Source/Core/Source/Public/Render/Pass/QOutliningRenderPass.h create mode 100644 Source/Editor/Resources/tips.png diff --git a/Plugins/RenderDocPlugin/Source/Private/RenderDocPlugin.cpp b/Plugins/RenderDocPlugin/Source/Private/RenderDocPlugin.cpp index 36cb52df..3895cba6 100644 --- a/Plugins/RenderDocPlugin/Source/Private/RenderDocPlugin.cpp +++ b/Plugins/RenderDocPlugin/Source/Private/RenderDocPlugin.cpp @@ -25,6 +25,9 @@ void RenderDocPlugin::startup() { } }); } + else { + qWarning() << "Render Doc Load Failed: " << renderdoc.errorString(); + } } void RenderDocPlugin::shutdown() { diff --git a/Source/Core/Source/Private/Render/Pass/QBlurRenderPass.cpp b/Source/Core/Source/Private/Render/Pass/QBlurRenderPass.cpp index ce5a34d0..f196c2e7 100644 --- a/Source/Core/Source/Private/Render/Pass/QBlurRenderPass.cpp +++ b/Source/Core/Source/Private/Render/Pass/QBlurRenderPass.cpp @@ -36,7 +36,7 @@ void QBlurRenderPass::setDownSample(int val) { void QBlurRenderPass::resizeAndLinkNode(const QSize& size) { mSrcTexture = getTextureIn_Src(); for (int i = 0; i < 2; i++) { - mBlurRT[i].colorAttachment.reset(mRhi->newTexture(QRhiTexture::RGBA32F, mSrcTexture->pixelSize() / mDownSampleCount, 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource)); + mBlurRT[i].colorAttachment.reset(mRhi->newTexture(mSrcTexture->format(), mSrcTexture->pixelSize() / mDownSampleCount, 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource)); mBlurRT[i].colorAttachment->create(); mBlurRT[i].renderTarget.reset(mRhi->newTextureRenderTarget({ mBlurRT[i].colorAttachment.get() })); } diff --git a/Source/Core/Source/Private/Render/Pass/QMotionBlurRenderPass.cpp b/Source/Core/Source/Private/Render/Pass/QMotionBlurRenderPass.cpp new file mode 100644 index 00000000..d5c62ba3 --- /dev/null +++ b/Source/Core/Source/Private/Render/Pass/QMotionBlurRenderPass.cpp @@ -0,0 +1,176 @@ +#include "Render/Pass/QMotionBlurRenderPass.h" + +QMotionBlurRenderPass::QMotionBlurRenderPass() { + setMotionBlurSize(2); + setMotionBlurSeparation(1.0f); +} + +void QMotionBlurRenderPass::setMotionBlurSize(int size) { + if (size <= 0 || size == mParams.size) + return; + mParams.size = size; + sigUpdateParams.request(); +} + +void QMotionBlurRenderPass::setMotionBlurSeparation(float val) +{ + mParams.separation = val; + sigUpdateParams.request(); +} + + +void QMotionBlurRenderPass::resizeAndLinkNode(const QSize& size) { + auto baseuBaseColor = getTextureIn_BaseColor(); + auto uPosition = getTextureIn_Position(); + mMotionBlurRT.colorAttachment.reset(mRhi->newTexture(QRhiTexture::RGBA32F, baseuBaseColor->pixelSize() , 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource)); + mMotionBlurRT.colorAttachment->create(); + mMotionBlurRT.renderTarget.reset(mRhi->newTextureRenderTarget({ mMotionBlurRT.colorAttachment.get() })); + + renderPassDesc.reset(mMotionBlurRT.renderTarget->newCompatibleRenderPassDescriptor()); + + mMotionBlurRT.renderTarget->setRenderPassDescriptor(renderPassDesc.get()); + mMotionBlurRT.renderTarget->create(); + + mSampler.reset(mRhi->newSampler(QRhiSampler::Linear, + QRhiSampler::Linear, + QRhiSampler::None, + QRhiSampler::ClampToEdge, + QRhiSampler::ClampToEdge) + ); + mSampler->create(); + + mUniformBuffer.reset(mRhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, sizeof(Params))); + mUniformBuffer->create(); + + mBindings.reset(mRhi->newShaderResourceBindings()); + mBindings->setBindings({ + QRhiShaderResourceBinding::sampledTexture(0,QRhiShaderResourceBinding::FragmentStage, baseuBaseColor, mSampler.get()), + QRhiShaderResourceBinding::sampledTexture(1,QRhiShaderResourceBinding::FragmentStage, uPosition, mSampler.get()), + QRhiShaderResourceBinding::uniformBuffer(2,QRhiShaderResourceBinding::FragmentStage,mUniformBuffer.get()), + }); + mBindings->create(); + + registerTextureOut_Result(mMotionBlurRT.colorAttachment.get()); + + sigUpdateParams.request(); +} + +void QMotionBlurRenderPass::compile() { + mPipeline.reset(mRhi->newGraphicsPipeline()); + QRhiGraphicsPipeline::TargetBlend blendState; + blendState.enable = false; + mPipeline->setTargetBlends({ blendState }); + mPipeline->setSampleCount(mMotionBlurRT.renderTarget->sampleCount()); + + QShader vs = mRhi->newShaderFromCode(QShader::VertexStage, R"(#version 450 + layout (location = 0) out vec2 vUV; + out gl_PerVertex{ + vec4 gl_Position; + }; + void main() { + vUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + gl_Position = vec4(vUV * 2.0f - 1.0f, 0.0f, 1.0f); +#if Y_UP_IN_NDC + gl_Position.y = - gl_Position.y; +#endif + })" + , QShaderDefinitions() + .addDefinition("Y_UP_IN_NDC", mRhi->isYUpInNDC()) + ); + + QShader fs = mRhi->newShaderFromCode(QShader::FragmentStage, R"(#version 450 + layout (location = 0) in vec2 vUV; + layout (location = 0) out vec4 outFragColor; + + layout (binding = 0) uniform sampler2D uBaseColor; + layout (binding = 1) uniform sampler2D uPosition; + layout (binding = 2) uniform MontionBlurParams{ + mat4 previousViewToWorld; + mat4 worldToView; + mat4 projection; + int size; + float separation; + }params; + + void main(){ + vec2 texSize = textureSize(uBaseColor, 0).xy; + vec2 vUV = gl_FragCoord.xy / texSize; + + outFragColor = texture(uBaseColor, vUV); + + vec4 position1 = texture(uPosition, vUV); + + if (params.size <= 0 || params.separation <= 0.0) { return; } + + vec4 position0 = params.worldToView * params.previousViewToWorld * position1; + + position0 = params.projection * position0; + position0.xyz /= position0.w; + position0.xy = position0.xy * 0.5 + 0.5; + + position1 = params.projection * position1; + position1.xyz /= position1.w; + position1.xy = position1.xy * 0.5 + 0.5; + + vec2 direction = position1.xy - position0.xy; + + direction.xy *= params.separation; + + vec2 forward = vUV; + vec2 backward = vUV; + + float count = 1.0; + + for (int i = 0; i < params.size; ++i) { + forward += direction; + backward -= direction; + + outFragColor += + texture + ( uBaseColor + , forward + ); + outFragColor += + texture + ( uBaseColor + , backward + ); + + count += 2.0; + } + outFragColor /= count; + } + )"); + mPipeline->setShaderStages({ + { QRhiShaderStage::Vertex, vs }, + { QRhiShaderStage::Fragment, fs } + }); + QRhiVertexInputLayout inputLayout; + mPipeline->setVertexInputLayout(inputLayout); + mPipeline->setShaderResourceBindings(mBindings.get()); + mPipeline->setRenderPassDescriptor(mMotionBlurRT.renderTarget->renderPassDescriptor()); + mPipeline->create(); + + QMatrix4x4 view = mRenderer->getCamera()->getViewMatrix(); + mParams.worldToView = view.toGenericMatrix<4, 4>(); + mParams.previousViewToWorld = view.inverted().toGenericMatrix<4, 4>(); +} + +void QMotionBlurRenderPass::render(QRhiCommandBuffer* cmdBuffer) { + mParams.projection = mRenderer->getCamera()->getProjectionMatrixWithCorr(mRhi).toGenericMatrix<4, 4>(); + QMatrix4x4 view = mRenderer->getCamera()->getViewMatrix(); + mParams.worldToView = view.toGenericMatrix<4, 4>(); + + QRhiResourceUpdateBatch* batch = mRhi->nextResourceUpdateBatch(); + batch->updateDynamicBuffer(mUniformBuffer.get(), 0, sizeof(Params), &mParams); + + cmdBuffer->resourceUpdate(batch); + cmdBuffer->beginPass(mMotionBlurRT.renderTarget.get(), QColor::fromRgbF(0.0f, 0.0f, 0.0f, 0.0f), { 1.0f, 0 }); + cmdBuffer->setGraphicsPipeline(mPipeline.get()); + cmdBuffer->setShaderResources(mBindings.get()); + cmdBuffer->setViewport(QRhiViewport(0, 0, mMotionBlurRT.renderTarget->pixelSize().width(), mMotionBlurRT.renderTarget->pixelSize().height())); + cmdBuffer->draw(4); + cmdBuffer->endPass(); + + mParams.previousViewToWorld = view.inverted().toGenericMatrix<4, 4>(); +} diff --git a/Source/Core/Source/Private/Render/Pass/QOutliningRenderPass.cpp b/Source/Core/Source/Private/Render/Pass/QOutliningRenderPass.cpp new file mode 100644 index 00000000..9ceb76be --- /dev/null +++ b/Source/Core/Source/Private/Render/Pass/QOutliningRenderPass.cpp @@ -0,0 +1,127 @@ +#include "Render/Pass/QOutliningRenderPass.h" + +QOutliningRenderPass::QOutliningRenderPass() { +} + +void QOutliningRenderPass::resizeAndLinkNode(const QSize& size) { + auto baseColorTexture = getTextureIn_BaseColor(); + auto positionTexture = getTextureIn_Position(); + mOutliningRT.colorAttachment.reset(mRhi->newTexture(baseColorTexture->format(), baseColorTexture->pixelSize(), 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource)); + mOutliningRT.colorAttachment->create(); + mOutliningRT.renderTarget.reset(mRhi->newTextureRenderTarget({ mOutliningRT.colorAttachment.get() })); + renderPassDesc.reset(mOutliningRT.renderTarget->newCompatibleRenderPassDescriptor()); + + mOutliningRT.renderTarget->setRenderPassDescriptor(renderPassDesc.get()); + mOutliningRT.renderTarget->create(); + + mSampler.reset(mRhi->newSampler(QRhiSampler::Linear, + QRhiSampler::Linear, + QRhiSampler::None, + QRhiSampler::ClampToEdge, + QRhiSampler::ClampToEdge) + ); + mSampler->create(); + + mUniformBuffer.reset(mRhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, sizeof(Params))); + mUniformBuffer->create(); + + mBindings.reset(mRhi->newShaderResourceBindings()); + mBindings->setBindings({ + QRhiShaderResourceBinding::sampledTexture(0,QRhiShaderResourceBinding::FragmentStage,baseColorTexture,mSampler.get()), + QRhiShaderResourceBinding::sampledTexture(1,QRhiShaderResourceBinding::FragmentStage,positionTexture,mSampler.get()), + QRhiShaderResourceBinding::uniformBuffer(2,QRhiShaderResourceBinding::FragmentStage,mUniformBuffer.get()) + }); + mBindings->create(); + + registerTextureOut_Result(mOutliningRT.colorAttachment.get()); + + sigUpdateParams.request(); +} + +void QOutliningRenderPass::compile() { + mPipeline.reset(mRhi->newGraphicsPipeline()); + QRhiGraphicsPipeline::TargetBlend blendState; + blendState.enable = false; + mPipeline->setTargetBlends({ blendState }); + mPipeline->setSampleCount(1); + mPipeline->setDepthTest(false); + mPipeline->setDepthWrite(false); + + QShader vs = mRhi->newShaderFromCode(QShader::VertexStage, R"(#version 450 + layout (location = 0) out vec2 vUV; + out gl_PerVertex{ + vec4 gl_Position; + }; + void main() { + vUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + gl_Position = vec4(vUV * 2.0f - 1.0f, 0.0f, 1.0f); +#if Y_UP_IN_NDC + gl_Position.y = - gl_Position.y; +#endif + })" + , QShaderDefinitions() + .addDefinition("Y_UP_IN_NDC", mRhi->isYUpInNDC()) + ); + + QShader fs = mRhi->newShaderFromCode(QShader::FragmentStage, R"(#version 450 + layout (location = 0) in vec2 vUV; + layout (location = 0) out vec4 outFragColor; + layout (binding = 0) uniform sampler2D uBaseColor; + layout (binding = 1) uniform sampler2D uPosition; + layout (binding = 2) uniform Params{ + mat4 VP; + float MinSeparation; + float MaxSeparation; + float MinDistance; + float MaxDistance; + float FarNear; + int Radius; + vec4 ColorModifier; + }params; + + void main(){ + vec2 texOffset = 1.0 / textureSize(uBaseColor, 0); + vec4 baseColor = texture(uBaseColor, vUV); + vec4 screenPosition = params.VP * texture(uPosition, vUV); + float depth = screenPosition.z/screenPosition.w; + vec2 separation = mix(params.MaxSeparation, params.MinSeparation, depth) * texOffset; + float mx = 0.0f; + for (int i = -params.Radius; i <= params.Radius; ++i) { + for (int j = -params.Radius; j <= params.Radius; ++j) { + vec2 currUV = vUV + vec2(i,j) * separation; + vec4 currScreenPosition = params.VP * texture(uPosition, currUV); + float currDepth = currScreenPosition.z/currScreenPosition.w; + mx = max(mx, abs(depth - currDepth)); + } + } + float diff = smoothstep(params.MinDistance, params.MaxDistance, mx * params.FarNear); + vec3 lineColor = baseColor.rgb * params.ColorModifier.rgb; + outFragColor = vec4(mix(baseColor.rgb, lineColor, diff ),baseColor.a); + } + )"); + mPipeline->setShaderStages({ + { QRhiShaderStage::Vertex, vs }, + { QRhiShaderStage::Fragment, fs } + }); + QRhiVertexInputLayout inputLayout; + mPipeline->setVertexInputLayout(inputLayout); + mPipeline->setShaderResourceBindings(mBindings.get()); + mPipeline->setRenderPassDescriptor(renderPassDesc.get()); + mPipeline->create(); +} + +void QOutliningRenderPass::render(QRhiCommandBuffer* cmdBuffer) { + QMatrix4x4 VP = mRenderer->getCamera()->getProjectionMatrixWithCorr(mRhi) * mRenderer->getCamera()->getViewMatrix(); + mParams.VP = VP.toGenericMatrix<4, 4>(); + mParams.FarNear = mRenderer->getCamera()->getFarPlane() / mRenderer->getCamera()->getNearPlane(); + QRhiResourceUpdateBatch* batch = mRhi->nextResourceUpdateBatch(); + batch->updateDynamicBuffer(mUniformBuffer.get(), 0, sizeof(Params), &mParams); + cmdBuffer->resourceUpdate(batch); + + cmdBuffer->beginPass(mOutliningRT.renderTarget.get(), QColor::fromRgbF(0.0f, 0.0f, 0.0f, 0.0f), { 1.0f, 0 }); + cmdBuffer->setGraphicsPipeline(mPipeline.get()); + cmdBuffer->setShaderResources(mBindings.get()); + cmdBuffer->setViewport(QRhiViewport(0, 0, mOutliningRT.renderTarget->pixelSize().width(), mOutliningRT.renderTarget->pixelSize().height())); + cmdBuffer->draw(4); + cmdBuffer->endPass(); +} diff --git a/Source/Core/Source/Private/Render/Pass/QSsaoRenderPass.cpp b/Source/Core/Source/Private/Render/Pass/QSsaoRenderPass.cpp index 22d2d4cb..2e9ef05b 100644 --- a/Source/Core/Source/Private/Render/Pass/QSsaoRenderPass.cpp +++ b/Source/Core/Source/Private/Render/Pass/QSsaoRenderPass.cpp @@ -54,7 +54,7 @@ QSsaoRenderPass* QSsaoRenderPass::setupBias(float var) { void QSsaoRenderPass::resizeAndLinkNode(const QSize& size) { auto positionTexture = getTextureIn_Position(); auto normalTexture = getTextureIn_Normal(); - mRT.colorAttachment.reset(mRhi->newTexture(QRhiTexture::R16F, positionTexture->pixelSize() , 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource)); + mRT.colorAttachment.reset(mRhi->newTexture(QRhiTexture::R16, positionTexture->pixelSize() , 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource)); mRT.colorAttachment->create(); mRT.renderTarget.reset(mRhi->newTextureRenderTarget({ mRT.colorAttachment.get() })); renderPassDesc.reset(mRT.renderTarget->newCompatibleRenderPassDescriptor()); diff --git a/Source/Core/Source/Public/Render/Pass/QMotionBlurRenderPass.h b/Source/Core/Source/Public/Render/Pass/QMotionBlurRenderPass.h new file mode 100644 index 00000000..76268713 --- /dev/null +++ b/Source/Core/Source/Public/Render/Pass/QMotionBlurRenderPass.h @@ -0,0 +1,52 @@ +#ifndef QMotionBlurRenderPass_h__ +#define QMotionBlurRenderPass_h__ + +#include "Render/IRenderPass.h" + +class QENGINECORE_API QMotionBlurRenderPass: public IRenderPass { + Q_OBJECT + Q_PROPERTY(int MotionBlurSize READ getMotionBlurSize WRITE setMotionBlurSize) + Q_PROPERTY(float MotionBlurSeparation READ getMotionBlurSeparation WRITE setMotionBlurSeparation) + + //Q_CLASSINFO("MotionBlurIterations", "Min=0,Max=10") + //Q_CLASSINFO("MotionBlurSize", "Min=1,Max=40") + //Q_CLASSINFO("DownSampleCount", "Min=1,Max=16") + + Q_BUILDER_BEGIN_RENDER_PASS(QMotionBlurRenderPass,BaseColor,Position) + Q_BUILDER_ATTRIBUTE(int, MotionBlurSize) + Q_BUILDER_ATTRIBUTE(float, MotionBlurSeparation) + Q_BUILDER_END_RENDER_PASS(Result) +public: + QMotionBlurRenderPass(); + + void setMotionBlurSize(int size); + void setMotionBlurSeparation(float val); + + int getMotionBlurSize() const { return mParams.size; } + float getMotionBlurSeparation() const { return mParams.separation; } + + void resizeAndLinkNode(const QSize& size) override; + void compile() override; + void render(QRhiCommandBuffer* cmdBuffer) override; +private: + QScopedPointer mSampler; + QScopedPointer mUniformBuffer; + struct MotionBlurRT { + QScopedPointer colorAttachment; + QScopedPointer renderTarget; + }; + MotionBlurRT mMotionBlurRT; + QScopedPointer renderPassDesc; + QScopedPointer mPipeline; + QScopedPointer mBindings; + struct Params { + QGenericMatrix<4, 4, float> previousViewToWorld; + QGenericMatrix<4, 4, float> worldToView; + QGenericMatrix<4, 4, float> projection; + uint32_t size = 0; + float separation; + }mParams; + QRhiEx::Signal sigUpdateParams; +}; + +#endif // QMotionBlurRenderPass_h__ diff --git a/Source/Core/Source/Public/Render/Pass/QOutliningRenderPass.h b/Source/Core/Source/Public/Render/Pass/QOutliningRenderPass.h new file mode 100644 index 00000000..a73c5e6e --- /dev/null +++ b/Source/Core/Source/Public/Render/Pass/QOutliningRenderPass.h @@ -0,0 +1,75 @@ +#ifndef QOutliningRenderPass_h__ +#define QOutliningRenderPass_h__ + +#include "Render/IRenderPass.h" +#include "Type/QColor4D.h" + +class QENGINECORE_API QOutliningRenderPass: public IRenderPass { + Q_OBJECT + Q_PROPERTY(float MinSeparation READ getMinSeparation WRITE setMinSeparation) + Q_PROPERTY(float MaxSeparation READ getMaxSeparation WRITE setMaxSeparation) + Q_PROPERTY(float MinDistance READ getMinDistance WRITE setMinDistance) + Q_PROPERTY(float MaxDistance READ getMaxDistance WRITE setMaxDistance) + Q_PROPERTY(int Radius READ getRadius WRITE setRadius) + Q_PROPERTY(QColor4D ColorModifier READ getColorModifier WRITE setColorModifier) + + Q_CLASSINFO("MinSeparation", "Min=0,Max=10") + Q_CLASSINFO("MaxSeparation", "Min=1,Max=10") + Q_CLASSINFO("MinDistance", "Min=0,Max=10") + Q_CLASSINFO("MaxDistance", "Min=0,Max=10") + Q_CLASSINFO("Radius", "Min=1,Max=8") + + Q_BUILDER_BEGIN_RENDER_PASS(QOutliningRenderPass,BaseColor,Position) + Q_BUILDER_ATTRIBUTE(float, MinSeparation) + Q_BUILDER_ATTRIBUTE(float, MaxSeparation) + Q_BUILDER_ATTRIBUTE(float, MinDistance) + Q_BUILDER_ATTRIBUTE(float, MaxDistance) + Q_BUILDER_ATTRIBUTE(int, Radius) + Q_BUILDER_ATTRIBUTE(QColor4D, ColorModifier) + Q_BUILDER_END_RENDER_PASS(Result) +public: + QOutliningRenderPass(); + + void setMinSeparation(float val) { mParams.MinSeparation = val; } + void setMaxSeparation(float val) { mParams.MaxSeparation = val; } + void setMinDistance(float val) { mParams.MinDistance = val; } + void setMaxDistance(float val) { mParams.MaxDistance = val; } + void setRadius(int val) { mParams.Radius = val; } + void setColorModifier(QColor4D val) { mParams.ColorModifier = val; } + + float getMinSeparation() const { return mParams.MinSeparation; } + float getMaxSeparation() const { return mParams.MaxSeparation; } + float getMinDistance() const { return mParams.MinDistance; } + float getMaxDistance() const { return mParams.MaxDistance; } + int getRadius() const { return mParams.Radius; } + QColor4D getColorModifier() const { return mParams.ColorModifier; } + + void resizeAndLinkNode(const QSize& size) override; + void compile() override; + void render(QRhiCommandBuffer* cmdBuffer) override; +private: + QScopedPointer mSampler; + QScopedPointer mUniformBuffer; + struct OutliningRT { + QScopedPointer colorAttachment; + QScopedPointer renderTarget; + }; + OutliningRT mOutliningRT; + QScopedPointer renderPassDesc; + QScopedPointer mPipeline; + QScopedPointer mBindings; + + struct Params { + QGenericMatrix<4, 4, float> VP; + float MinSeparation = 1.0f; + float MaxSeparation = 3.0f; + float MinDistance = 0.5f; + float MaxDistance = 2.0f ; + float FarNear; + int Radius = 2; + alignas(16) QColor4D ColorModifier = QColor4D(0.324f,0.063f,0.099f,1.0f); + }mParams; + QRhiEx::Signal sigUpdateParams; +}; + +#endif // QOutliningRenderPass_h__ diff --git a/Source/Core/Source/Public/Utils/QCamera.h b/Source/Core/Source/Public/Utils/QCamera.h index fe390c3d..d6848d77 100644 --- a/Source/Core/Source/Public/Utils/QCamera.h +++ b/Source/Core/Source/Public/Utils/QCamera.h @@ -72,7 +72,7 @@ class QENGINECORE_API QCamera :public QObject { float mFov = 45.0f; float mAspectRatio = 1.0; float mNearPlane = 0.1f; - float mFarPlane = 10000.0f; + float mFarPlane = 3000.0f; QVector3D mCameraDirection; QVector3D mCameraUp; diff --git a/Source/Editor/Resources.qrc b/Source/Editor/Resources.qrc index 32761299..ffc71499 100644 --- a/Source/Editor/Resources.qrc +++ b/Source/Editor/Resources.qrc @@ -31,5 +31,6 @@ Resources/replace.png Resources/text-format.png Resources/close.png + Resources/tips.png diff --git a/Source/Editor/Resources/tips.png b/Source/Editor/Resources/tips.png new file mode 100644 index 0000000000000000000000000000000000000000..d1e747a5a6114b5df20593a957bb778d3ee4adb8 GIT binary patch literal 36583 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 literal 0 HcmV?d00001 diff --git a/Source/Engine/Source/Private/Render/Painter/DebugUiPainter.cpp b/Source/Engine/Source/Private/Render/Painter/DebugUiPainter.cpp index 603a3cc5..e07f7cf9 100644 --- a/Source/Engine/Source/Private/Render/Painter/DebugUiPainter.cpp +++ b/Source/Engine/Source/Private/Render/Painter/DebugUiPainter.cpp @@ -5,7 +5,8 @@ #include "Render/RHI/QRhiWindow.h" #include "Utils/ImGuiWidgets.h" #include "QEngineEditorStyleManager.h" -#include "QFile" +#include +#include void QDebugUIPainter::setupDebugIdTexture(QRhiTexture* texture) { mDebugIdTexture = texture; @@ -125,6 +126,29 @@ QDebugUIPainter::QDebugUIPainter(QWindowRenderer* inRenderer) } ImGui::PopStyleVar(); ImGui::End(); + + static int FrameCounter = 1000; + if (FrameCounter >= 0) { + ImGui::SetNextWindowBgAlpha(0); + ImVec2 Size(700, 170); + ImVec2 Pos(viewport->WorkSize.x - Size.x, viewport->WorkSize.y - Size.y); + ImGui::SetNextWindowPos(Pos); + ImGui::SetNextWindowSize(Size); + ImGui::Begin("Tips", NULL, + ImGuiWindowFlags_NoTitleBar + | ImGuiWindowFlags_NoScrollbar + | ImGuiWindowFlags_NoMove + | ImGuiWindowFlags_NoResize + | ImGuiWindowFlags_NoCollapse + | ImGuiWindowFlags_NoNav + | ImGuiWindowFlags_NoBackground + | ImGuiWindowFlags_NoBringToFrontOnFocus + | ImGuiWindowFlags_UnsavedDocument); + ImGui::Image(getImageId("tips"), Size,ImVec2(0,0),ImVec2(1,1),ImVec4(1,1,1,FrameCounter/500.0f)); + ImGui::End(); + FrameCounter--; + } + if (bUseLineMode) QRhiGraphicsPipelineBuilder::setPolygonModeOverride(QRhiGraphicsPipeline::Line); else @@ -143,7 +167,6 @@ QDebugUIPainter::QDebugUIPainter(QWindowRenderer* inRenderer) ImGui::TextColored(ImColor(0, 255, 0), "GPU Time\t%.2f ms", mRenderer->getRhiWindow()->getGpuFrameTime()); ImGui::End(); } - } }); setupRhi(mRenderer->getRhi()); @@ -188,6 +211,7 @@ void QDebugUIPainter::compile() { registerImage("polygon", QImage(":/Resources/polygon.png")); registerImage("camera", QImage(":/Resources/camera.png")); registerImage("graph", QImage(":/Resources/graph.png")); + registerImage("tips", QImage(":/Resources/tips.png")); ImGuiPainter::compile(); if (mDebugIdTexture == nullptr) return; diff --git a/Source/Engine/Source/Private/Render/Painter/FrameGraphView.cpp b/Source/Engine/Source/Private/Render/Painter/FrameGraphView.cpp index 4fc9eb42..5a7b4048 100644 --- a/Source/Engine/Source/Private/Render/Painter/FrameGraphView.cpp +++ b/Source/Engine/Source/Private/Render/Painter/FrameGraphView.cpp @@ -168,6 +168,7 @@ void FrameGraphView::ShowFrameComparer(float thickness, float leftMinWidth, floa auto dpr = qApp->devicePixelRatio(); ImGui::SetNextWindowPos(ImVec2(viewport->WorkSize.x - 60 * dpr , viewport->WorkSize.y - 40 * dpr)); ImGui::SetNextWindowSize(ImVec2(100 * dpr, 40 * dpr)); + ImGui::Begin("ExitButton", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar diff --git a/Source/Engine/Source/Private/Render/Painter/FrameGraphView.h b/Source/Engine/Source/Private/Render/Painter/FrameGraphView.h index 1749bcfc..63e65127 100644 --- a/Source/Engine/Source/Private/Render/Painter/FrameGraphView.h +++ b/Source/Engine/Source/Private/Render/Painter/FrameGraphView.h @@ -6,7 +6,8 @@ #include "GraphEditor.h" #include "Render/Renderer/QWindowRenderer.h" -struct FrameGraphView : public GraphEditor::Delegate { +class FrameGraphView : public GraphEditor::Delegate { +public: FrameGraphView(); void Rebuild(QFrameGraph* frameGraph); void Show();