From b6132aec4de38747f9187db5de37696580ab5996 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Mon, 6 Apr 2026 14:56:43 +0800 Subject: [PATCH] Formalize camera post-process descriptors --- ...ameraPostProcess描述层正式化计划_2026-04-06.md | 127 +++++++++++++++ .../XCEngine/Components/CameraComponent.h | 12 +- .../Planning/CameraPostProcessDesc.h | 43 +++++ .../Planning/CameraPostProcessPassFactory.h | 42 +++++ engine/src/Components/CameraComponent.cpp | 149 +++++++++++++++--- .../src/Rendering/Execution/SceneRenderer.cpp | 15 +- .../test_camera_light_component.cpp | 37 ++++- .../camera_post_process_scene/main.cpp | 8 +- .../unit/test_camera_scene_renderer.cpp | 8 +- 9 files changed, 400 insertions(+), 41 deletions(-) create mode 100644 docs/plan/Renderer下一阶段_CameraPostProcess描述层正式化计划_2026-04-06.md create mode 100644 engine/include/XCEngine/Rendering/Planning/CameraPostProcessDesc.h create mode 100644 engine/include/XCEngine/Rendering/Planning/CameraPostProcessPassFactory.h diff --git a/docs/plan/Renderer下一阶段_CameraPostProcess描述层正式化计划_2026-04-06.md b/docs/plan/Renderer下一阶段_CameraPostProcess描述层正式化计划_2026-04-06.md new file mode 100644 index 00000000..49b7b467 --- /dev/null +++ b/docs/plan/Renderer下一阶段_CameraPostProcess描述层正式化计划_2026-04-06.md @@ -0,0 +1,127 @@ +# Renderer 下一阶段: Camera PostProcess 描述层正式化计划 + +日期: `2026-04-06` + +## 1. 阶段目标 + +在已经完成 runtime `SceneColor -> PostProcess -> FinalOutput` 主链收口的基础上, +把相机上的后处理配置从临时的 `ColorScale` 专用字段,提升为正式的描述层。 + +这一阶段只解决一件事: + +- `CameraComponent` 持有正式的 post-process pass 描述 +- `SceneRenderer` 通过工厂把描述翻译成 runtime pass sequence +- 旧的 `ColorScale` 接口继续兼容,旧场景序列化继续可读 + +## 2. 已完成内容 + +### 2.1 相机后处理描述层 + +已新增: + +- `CameraPostProcessPassType` +- `CameraPostProcessPassDesc` +- `CameraPostProcessStack` + +当前首个正式 builtin effect 仍然是: + +- `ColorScale` + +但它已经不再通过 `CameraComponent -> vector -> SceneRenderer if/for` 这种硬编码链路传递。 + +### 2.2 CameraComponent 正式化 + +已完成: + +- `CameraComponent` 内部存储迁移到 `CameraPostProcessStack` +- 新增通用接口: + - `GetPostProcessPasses` + - `SetPostProcessPasses` + - `AddPostProcessPass` + - `ClearPostProcessPasses` +- 旧接口继续保留: + - `SetColorScalePostProcessEnabled` + - `SetColorScalePostProcessScale` + - `SetColorScalePostProcessPasses` + - `GetColorScalePostProcessPasses` + +兼容规则: + +- 旧接口继续映射到 `ColorScale` 类型的描述 +- 新格式序列化写出正式 `postProcessPassN*` 字段 +- 反序列化同时兼容旧的 `colorScalePostProcess*` 字段 + +### 2.3 SceneRenderer 工厂翻译层 + +已完成: + +- 从 `SceneRenderer` 中移除 `ColorScale` 专用硬编码构造 +- 新增 `BuildCameraPostProcessPassSequence(...)` +- `SceneRenderer` 现在只关心: + - 读取相机的 `CameraPostProcessStack` + - 为 post-process 分配 source surface + - 调用工厂生成 `RenderPassSequence` + +这意味着后续增加新的 builtin camera post-process 时, +修改点已经收缩到描述层和工厂,而不是继续把分支堆进 `SceneRenderer`。 + +## 3. 验证结果 + +### 3.1 构建 + +已通过: + +- `cmake --build --preset debug --target components_tests rendering_unit_tests rendering_integration_camera_post_process_scene rendering_integration_post_process_scene -- /m:1` + +### 3.2 单元测试 + +已通过: + +- `components_tests --gtest_filter=CameraComponent_Test.*` +- `rendering_unit_tests --gtest_filter=SceneRenderer_Test.*:CameraRenderer_Test.*` + +覆盖点包括: + +- 新描述层 round-trip +- 旧 `ColorScale` 字段反序列化兼容 +- `SceneRenderer` 从 camera post-process stack 构建正式 request + +### 3.3 集成测试 + +已通过: + +- `rendering_integration_camera_post_process_scene` + - D3D12 + - OpenGL + - Vulkan +- `rendering_integration_post_process_scene` + - D3D12 + - OpenGL + - Vulkan + +说明: + +- `camera_post_process_scene` 验证 formal scene path +- `post_process_scene` 保留为 manual / low-level multi-pass 覆盖 + +## 4. 当前结论 + +这一阶段已经完成。 + +当前 renderer 在 camera post-process 这一层,已经从“单个效果的临时接线”升级为“正式描述 + 工厂翻译”的结构。 + +但现阶段仍然有两个边界: + +- 描述层虽然正式化了,但当前 builtin effect 仍只有 `ColorScale` +- editor / asset / material 侧还没有把 camera post-process 做成完整的资产化与配置面板体系 + +## 5. 下一步建议 + +下一步不要回头继续往 `SceneRenderer` 里塞特判,而应沿着下面的方向推进: + +1. 继续扩展 `CameraPostProcessPassDesc` + - 例如 tone mapping / exposure / gamma / final color transform +2. 让新的 builtin camera effect 继续只经过“描述层 + 工厂” +3. 等 shader/material 主线更稳定后,再决定 camera post-process 的资产化形式和 editor 配置入口 + +本阶段的核心收口标准已经满足,可以提交归档。 diff --git a/engine/include/XCEngine/Components/CameraComponent.h b/engine/include/XCEngine/Components/CameraComponent.h index 7aee6df3..e57a70ed 100644 --- a/engine/include/XCEngine/Components/CameraComponent.h +++ b/engine/include/XCEngine/Components/CameraComponent.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -91,16 +92,21 @@ public: const Math::Color& GetSkyboxBottomColor() const { return m_skyboxBottomColor; } void SetSkyboxBottomColor(const Math::Color& value) { m_skyboxBottomColor = value; } + const Rendering::CameraPostProcessStack& GetPostProcessPasses() const { return m_postProcessPasses; } + void SetPostProcessPasses(const Rendering::CameraPostProcessStack& values); + void AddPostProcessPass(const Rendering::CameraPostProcessPassDesc& value); + void ClearPostProcessPasses() { m_postProcessPasses.clear(); } + bool IsColorScalePostProcessEnabled() const; void SetColorScalePostProcessEnabled(bool value); const Math::Vector4& GetColorScalePostProcessScale() const; void SetColorScalePostProcessScale(const Math::Vector4& value); - const std::vector& GetColorScalePostProcessPasses() const { return m_colorScalePostProcessPasses; } + std::vector GetColorScalePostProcessPasses() const; void SetColorScalePostProcessPasses(const std::vector& values); void AddColorScalePostProcessPass(const Math::Vector4& value); - void ClearColorScalePostProcessPasses() { m_colorScalePostProcessPasses.clear(); } + void ClearColorScalePostProcessPasses(); void Serialize(std::ostream& os) const override; void Deserialize(std::istream& is) override; @@ -126,7 +132,7 @@ private: Math::Color m_skyboxHorizonColor = Math::Color(0.78f, 0.84f, 0.92f, 1.0f); Math::Color m_skyboxBottomColor = Math::Color(0.92f, 0.93f, 0.95f, 1.0f); Math::Vector4 m_colorScalePostProcessDefaultScale = Math::Vector4::One(); - std::vector m_colorScalePostProcessPasses; + Rendering::CameraPostProcessStack m_postProcessPasses; }; } // namespace Components diff --git a/engine/include/XCEngine/Rendering/Planning/CameraPostProcessDesc.h b/engine/include/XCEngine/Rendering/Planning/CameraPostProcessDesc.h new file mode 100644 index 00000000..4e6b1337 --- /dev/null +++ b/engine/include/XCEngine/Rendering/Planning/CameraPostProcessDesc.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include +#include + +namespace XCEngine { +namespace Rendering { + +enum class CameraPostProcessPassType : uint8_t { + ColorScale = 0 +}; + +struct CameraColorScalePostProcessDesc { + Math::Vector4 scale = Math::Vector4::One(); +}; + +struct CameraPostProcessPassDesc { + CameraPostProcessPassType type = CameraPostProcessPassType::ColorScale; + CameraColorScalePostProcessDesc colorScale = {}; + + static CameraPostProcessPassDesc MakeColorScale(const Math::Vector4& scale) { + CameraPostProcessPassDesc desc = {}; + desc.type = CameraPostProcessPassType::ColorScale; + desc.colorScale.scale = scale; + return desc; + } + + bool IsValid() const { + switch (type) { + case CameraPostProcessPassType::ColorScale: + return true; + default: + return false; + } + } +}; + +using CameraPostProcessStack = std::vector; + +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/include/XCEngine/Rendering/Planning/CameraPostProcessPassFactory.h b/engine/include/XCEngine/Rendering/Planning/CameraPostProcessPassFactory.h new file mode 100644 index 00000000..1bc44287 --- /dev/null +++ b/engine/include/XCEngine/Rendering/Planning/CameraPostProcessPassFactory.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include + +#include + +namespace XCEngine { +namespace Rendering { + +inline std::unique_ptr BuildCameraPostProcessPassSequence( + const CameraPostProcessStack& postProcessStack) { + if (postProcessStack.empty()) { + return nullptr; + } + + std::unique_ptr sequence = std::make_unique(); + for (const CameraPostProcessPassDesc& passDesc : postProcessStack) { + if (!passDesc.IsValid()) { + continue; + } + + switch (passDesc.type) { + case CameraPostProcessPassType::ColorScale: + sequence->AddPass(std::make_unique( + passDesc.colorScale.scale)); + break; + default: + break; + } + } + + if (sequence->GetPassCount() == 0u) { + return nullptr; + } + + return sequence; +} + +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/src/Components/CameraComponent.cpp b/engine/src/Components/CameraComponent.cpp index bc9d0a4f..2112694a 100644 --- a/engine/src/Components/CameraComponent.cpp +++ b/engine/src/Components/CameraComponent.cpp @@ -4,6 +4,7 @@ #include "Resources/Material/Material.h" #include +#include #include namespace XCEngine { @@ -56,49 +57,118 @@ bool TryParseVector4(const std::string& value, Math::Vector4& outValue) { return !stream.fail(); } +Rendering::CameraPostProcessStack BuildColorScalePostProcessStack( + const std::vector& values) { + Rendering::CameraPostProcessStack passes; + passes.reserve(values.size()); + for (const Math::Vector4& value : values) { + passes.push_back(Rendering::CameraPostProcessPassDesc::MakeColorScale(value)); + } + return passes; +} + +size_t FindFirstColorScalePassIndex(const Rendering::CameraPostProcessStack& passes) { + for (size_t index = 0; index < passes.size(); ++index) { + if (passes[index].type == Rendering::CameraPostProcessPassType::ColorScale) { + return index; + } + } + + return passes.size(); +} + } // namespace +void CameraComponent::SetPostProcessPasses(const Rendering::CameraPostProcessStack& values) { + m_postProcessPasses = values; + for (const Rendering::CameraPostProcessPassDesc& pass : m_postProcessPasses) { + if (pass.type == Rendering::CameraPostProcessPassType::ColorScale) { + m_colorScalePostProcessDefaultScale = pass.colorScale.scale; + break; + } + } +} + +void CameraComponent::AddPostProcessPass(const Rendering::CameraPostProcessPassDesc& value) { + if (!value.IsValid()) { + return; + } + + if (value.type == Rendering::CameraPostProcessPassType::ColorScale && + FindFirstColorScalePassIndex(m_postProcessPasses) == m_postProcessPasses.size()) { + m_colorScalePostProcessDefaultScale = value.colorScale.scale; + } + + m_postProcessPasses.push_back(value); +} + bool CameraComponent::IsColorScalePostProcessEnabled() const { - return !m_colorScalePostProcessPasses.empty(); + return FindFirstColorScalePassIndex(m_postProcessPasses) != m_postProcessPasses.size(); } void CameraComponent::SetColorScalePostProcessEnabled(bool value) { if (value) { - if (m_colorScalePostProcessPasses.empty()) { - m_colorScalePostProcessPasses.push_back(m_colorScalePostProcessDefaultScale); + if (!IsColorScalePostProcessEnabled()) { + m_postProcessPasses.push_back( + Rendering::CameraPostProcessPassDesc::MakeColorScale( + m_colorScalePostProcessDefaultScale)); } return; } - m_colorScalePostProcessPasses.clear(); + ClearColorScalePostProcessPasses(); } const Math::Vector4& CameraComponent::GetColorScalePostProcessScale() const { - return m_colorScalePostProcessPasses.empty() + const size_t firstColorScalePassIndex = FindFirstColorScalePassIndex(m_postProcessPasses); + return firstColorScalePassIndex == m_postProcessPasses.size() ? m_colorScalePostProcessDefaultScale - : m_colorScalePostProcessPasses.front(); + : m_postProcessPasses[firstColorScalePassIndex].colorScale.scale; } void CameraComponent::SetColorScalePostProcessScale(const Math::Vector4& value) { m_colorScalePostProcessDefaultScale = value; - if (!m_colorScalePostProcessPasses.empty()) { - m_colorScalePostProcessPasses.front() = value; + const size_t firstColorScalePassIndex = FindFirstColorScalePassIndex(m_postProcessPasses); + if (firstColorScalePassIndex != m_postProcessPasses.size()) { + m_postProcessPasses[firstColorScalePassIndex].colorScale.scale = value; } } +std::vector CameraComponent::GetColorScalePostProcessPasses() const { + std::vector values; + values.reserve(m_postProcessPasses.size()); + for (const Rendering::CameraPostProcessPassDesc& pass : m_postProcessPasses) { + if (pass.type == Rendering::CameraPostProcessPassType::ColorScale) { + values.push_back(pass.colorScale.scale); + } + } + return values; +} + void CameraComponent::SetColorScalePostProcessPasses(const std::vector& values) { - m_colorScalePostProcessPasses = values; - if (!m_colorScalePostProcessPasses.empty()) { - m_colorScalePostProcessDefaultScale = m_colorScalePostProcessPasses.front(); + SetPostProcessPasses(BuildColorScalePostProcessStack(values)); + if (!values.empty()) { + m_colorScalePostProcessDefaultScale = values.front(); } } void CameraComponent::AddColorScalePostProcessPass(const Math::Vector4& value) { - if (m_colorScalePostProcessPasses.empty()) { + if (!IsColorScalePostProcessEnabled()) { m_colorScalePostProcessDefaultScale = value; } - m_colorScalePostProcessPasses.push_back(value); + m_postProcessPasses.push_back(Rendering::CameraPostProcessPassDesc::MakeColorScale(value)); +} + +void CameraComponent::ClearColorScalePostProcessPasses() { + m_postProcessPasses.erase( + std::remove_if( + m_postProcessPasses.begin(), + m_postProcessPasses.end(), + [](const Rendering::CameraPostProcessPassDesc& pass) { + return pass.type == Rendering::CameraPostProcessPassType::ColorScale; + }), + m_postProcessPasses.end()); } void CameraComponent::SetFieldOfView(float value) { @@ -206,12 +276,18 @@ void CameraComponent::Serialize(std::ostream& os) const { os << "skyboxTopColor=" << m_skyboxTopColor.r << "," << m_skyboxTopColor.g << "," << m_skyboxTopColor.b << "," << m_skyboxTopColor.a << ";"; os << "skyboxHorizonColor=" << m_skyboxHorizonColor.r << "," << m_skyboxHorizonColor.g << "," << m_skyboxHorizonColor.b << "," << m_skyboxHorizonColor.a << ";"; os << "skyboxBottomColor=" << m_skyboxBottomColor.r << "," << m_skyboxBottomColor.g << "," << m_skyboxBottomColor.b << "," << m_skyboxBottomColor.a << ";"; - os << "colorScalePostProcessEnabled=" << (IsColorScalePostProcessEnabled() ? 1 : 0) << ";"; - os << "colorScalePostProcessScale=" << EncodeVector4(GetColorScalePostProcessScale()) << ";"; - os << "colorScalePostProcessPassCount=" << m_colorScalePostProcessPasses.size() << ";"; - for (size_t index = 0; index < m_colorScalePostProcessPasses.size(); ++index) { - os << "colorScalePostProcessPass" << index << "=" - << EncodeVector4(m_colorScalePostProcessPasses[index]) << ";"; + os << "postProcessPassCount=" << m_postProcessPasses.size() << ";"; + for (size_t index = 0; index < m_postProcessPasses.size(); ++index) { + const Rendering::CameraPostProcessPassDesc& pass = m_postProcessPasses[index]; + os << "postProcessPass" << index << "Type=" << static_cast(pass.type) << ";"; + switch (pass.type) { + case Rendering::CameraPostProcessPassType::ColorScale: + os << "postProcessPass" << index << "ColorScale=" + << EncodeVector4(pass.colorScale.scale) << ";"; + break; + default: + break; + } } } @@ -220,11 +296,13 @@ void CameraComponent::Deserialize(std::istream& is) { m_skyboxMaterialPath.clear(); m_skyboxMaterialRef.Reset(); m_colorScalePostProcessDefaultScale = Math::Vector4::One(); - m_colorScalePostProcessPasses.clear(); + m_postProcessPasses.clear(); std::string token; std::string pendingSkyboxMaterialPath; Resources::AssetRef pendingSkyboxMaterialRef; + size_t postProcessPassCount = 0; + Rendering::CameraPostProcessStack deserializedPostProcessPasses; bool legacyColorScaleEnabled = false; size_t colorScalePassCount = 0; std::vector deserializedColorScalePasses; @@ -289,6 +367,33 @@ void CameraComponent::Deserialize(std::istream& is) { std::replace(value.begin(), value.end(), ',', ' '); std::istringstream ss(value); ss >> m_skyboxBottomColor.r >> m_skyboxBottomColor.g >> m_skyboxBottomColor.b >> m_skyboxBottomColor.a; + } else if (key == "postProcessPassCount") { + postProcessPassCount = static_cast(std::stoul(value)); + deserializedPostProcessPasses.clear(); + deserializedPostProcessPasses.resize(postProcessPassCount); + } else if (key.rfind("postProcessPass", 0) == 0) { + const size_t prefixLength = std::string("postProcessPass").size(); + size_t propertyPos = prefixLength; + while (propertyPos < key.size() && + std::isdigit(static_cast(key[propertyPos])) != 0) { + ++propertyPos; + } + + if (propertyPos > prefixLength) { + const size_t index = static_cast( + std::stoul(key.substr(prefixLength, propertyPos - prefixLength))); + if (index >= deserializedPostProcessPasses.size()) { + deserializedPostProcessPasses.resize(index + 1); + } + + Rendering::CameraPostProcessPassDesc& pass = deserializedPostProcessPasses[index]; + const std::string property = key.substr(propertyPos); + if (property == "Type") { + pass.type = static_cast(std::stoi(value)); + } else if (property == "ColorScale") { + TryParseVector4(value, pass.colorScale.scale); + } + } } else if (key == "colorScalePostProcessEnabled") { legacyColorScaleEnabled = (std::stoi(value) != 0); } else if (key == "colorScalePostProcessScale") { @@ -331,7 +436,9 @@ void CameraComponent::Deserialize(std::istream& is) { m_skyboxMaterialRef = pendingSkyboxMaterialRef; } - if (!deserializedColorScalePasses.empty()) { + if (!deserializedPostProcessPasses.empty()) { + SetPostProcessPasses(deserializedPostProcessPasses); + } else if (!deserializedColorScalePasses.empty()) { SetColorScalePostProcessPasses(deserializedColorScalePasses); } else if (legacyColorScaleEnabled) { SetColorScalePostProcessEnabled(true); diff --git a/engine/src/Rendering/Execution/SceneRenderer.cpp b/engine/src/Rendering/Execution/SceneRenderer.cpp index c0fe48c2..885803e8 100644 --- a/engine/src/Rendering/Execution/SceneRenderer.cpp +++ b/engine/src/Rendering/Execution/SceneRenderer.cpp @@ -2,7 +2,7 @@ #include "Components/CameraComponent.h" #include "Rendering/Caches/FullscreenPassSurfaceCache.h" -#include "Rendering/Passes/BuiltinColorScalePostProcessPass.h" +#include "Rendering/Planning/CameraPostProcessPassFactory.h" #include "Rendering/Planning/SceneRenderRequestUtils.h" namespace XCEngine { @@ -104,8 +104,8 @@ void SceneRenderer::AttachCameraPostProcessRequests( continue; } - const std::vector& colorScalePasses = request.camera->GetColorScalePostProcessPasses(); - if (colorScalePasses.empty()) { + const CameraPostProcessStack& postProcessPasses = request.camera->GetPostProcessPasses(); + if (postProcessPasses.empty()) { continue; } @@ -131,12 +131,11 @@ void SceneRenderer::AttachCameraPostProcessRequests( continue; } - std::unique_ptr postProcessSequence = std::make_unique(); - for (const Math::Vector4& colorScale : colorScalePasses) { - postProcessSequence->AddPass( - std::make_unique(colorScale)); + std::unique_ptr postProcessSequence = + BuildCameraPostProcessPassSequence(postProcessPasses); + if (postProcessSequence == nullptr || postProcessSequence->GetPassCount() == 0u) { + continue; } - RenderSurface sourceSurface = sourceEntry->surface; sourceSurface.SetDepthAttachment(request.surface.GetDepthAttachment()); sourceSurface.SetColorStateBefore(RHI::ResourceStates::Common); diff --git a/tests/Components/test_camera_light_component.cpp b/tests/Components/test_camera_light_component.cpp index 2356d390..f7a6fc3b 100644 --- a/tests/Components/test_camera_light_component.cpp +++ b/tests/Components/test_camera_light_component.cpp @@ -32,6 +32,7 @@ TEST(CameraComponent_Test, DefaultValues) { EXPECT_FLOAT_EQ(camera.GetSkyboxTopColor().r, 0.18f); EXPECT_FLOAT_EQ(camera.GetSkyboxHorizonColor().g, 0.84f); EXPECT_FLOAT_EQ(camera.GetSkyboxBottomColor().b, 0.95f); + EXPECT_TRUE(camera.GetPostProcessPasses().empty()); EXPECT_FALSE(camera.IsColorScalePostProcessEnabled()); EXPECT_FLOAT_EQ(camera.GetColorScalePostProcessScale().x, 1.0f); EXPECT_FLOAT_EQ(camera.GetColorScalePostProcessScale().y, 1.0f); @@ -75,9 +76,11 @@ TEST(CameraComponent_Test, SerializeRoundTripPreservesViewportAndClearState) { source.SetSkyboxTopColor(XCEngine::Math::Color(0.12f, 0.21f, 0.64f, 1.0f)); source.SetSkyboxHorizonColor(XCEngine::Math::Color(0.71f, 0.76f, 0.88f, 1.0f)); source.SetSkyboxBottomColor(XCEngine::Math::Color(0.92f, 0.82f, 0.58f, 1.0f)); - source.SetColorScalePostProcessPasses({ - XCEngine::Math::Vector4(0.55f, 0.8f, 1.2f, 1.0f), - XCEngine::Math::Vector4(1.0f, 0.95f, 0.75f, 1.0f) + source.SetPostProcessPasses({ + XCEngine::Rendering::CameraPostProcessPassDesc::MakeColorScale( + XCEngine::Math::Vector4(0.55f, 0.8f, 1.2f, 1.0f)), + XCEngine::Rendering::CameraPostProcessPassDesc::MakeColorScale( + XCEngine::Math::Vector4(1.0f, 0.95f, 0.75f, 1.0f)) }); std::stringstream stream; @@ -98,6 +101,13 @@ TEST(CameraComponent_Test, SerializeRoundTripPreservesViewportAndClearState) { EXPECT_FLOAT_EQ(target.GetSkyboxTopColor().b, 0.64f); EXPECT_FLOAT_EQ(target.GetSkyboxHorizonColor().g, 0.76f); EXPECT_FLOAT_EQ(target.GetSkyboxBottomColor().r, 0.92f); + ASSERT_EQ(target.GetPostProcessPasses().size(), 2u); + EXPECT_EQ( + target.GetPostProcessPasses()[0].type, + XCEngine::Rendering::CameraPostProcessPassType::ColorScale); + EXPECT_EQ( + target.GetPostProcessPasses()[1].type, + XCEngine::Rendering::CameraPostProcessPassType::ColorScale); EXPECT_TRUE(target.IsColorScalePostProcessEnabled()); EXPECT_FLOAT_EQ(target.GetColorScalePostProcessScale().x, 0.55f); EXPECT_FLOAT_EQ(target.GetColorScalePostProcessScale().y, 0.8f); @@ -110,6 +120,27 @@ TEST(CameraComponent_Test, SerializeRoundTripPreservesViewportAndClearState) { EXPECT_FLOAT_EQ(target.GetColorScalePostProcessPasses()[1].w, 1.0f); } +TEST(CameraComponent_Test, DeserializeLegacyColorScalePostProcessFields) { + std::stringstream stream; + stream + << "colorScalePostProcessEnabled=1;" + << "colorScalePostProcessScale=0.55,0.8,1.2,1;" + << "colorScalePostProcessPassCount=2;" + << "colorScalePostProcessPass0=0.55,0.8,1.2,1;" + << "colorScalePostProcessPass1=1,0.95,0.75,1;"; + + CameraComponent camera; + camera.Deserialize(stream); + + ASSERT_EQ(camera.GetPostProcessPasses().size(), 2u); + EXPECT_EQ( + camera.GetPostProcessPasses()[0].type, + XCEngine::Rendering::CameraPostProcessPassType::ColorScale); + EXPECT_TRUE(camera.IsColorScalePostProcessEnabled()); + EXPECT_FLOAT_EQ(camera.GetColorScalePostProcessPasses()[0].x, 0.55f); + EXPECT_FLOAT_EQ(camera.GetColorScalePostProcessPasses()[1].y, 0.95f); +} + TEST(CameraComponent_Test, LegacyColorScaleAccessorsDriveFirstPass) { CameraComponent camera; diff --git a/tests/Rendering/integration/camera_post_process_scene/main.cpp b/tests/Rendering/integration/camera_post_process_scene/main.cpp index 91bb0609..be65a88d 100644 --- a/tests/Rendering/integration/camera_post_process_scene/main.cpp +++ b/tests/Rendering/integration/camera_post_process_scene/main.cpp @@ -243,9 +243,11 @@ void CameraPostProcessSceneTest::BuildScene() { mCamera->SetNearClipPlane(0.1f); mCamera->SetFarClipPlane(20.0f); mCamera->SetClearColor(XCEngine::Math::Color(0.06f, 0.08f, 0.12f, 1.0f)); - mCamera->SetColorScalePostProcessPasses({ - Vector4(1.0f, 0.75f, 0.75f, 1.0f), - Vector4(0.55f, 0.95f, 1.0f, 1.0f) + mCamera->SetPostProcessPasses({ + XCEngine::Rendering::CameraPostProcessPassDesc::MakeColorScale( + Vector4(1.0f, 0.75f, 0.75f, 1.0f)), + XCEngine::Rendering::CameraPostProcessPassDesc::MakeColorScale( + Vector4(0.55f, 0.95f, 1.0f, 1.0f)) }); GameObject* quadObject = mScene->CreateGameObject("Quad"); diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index 627dd8b1..9c53d978 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -1778,9 +1778,11 @@ TEST(SceneRenderer_Test, BuildsCameraColorScalePostProcessRequestFromCameraPassS camera->SetPrimary(true); camera->SetDepth(2.0f); camera->SetViewportRect(XCEngine::Math::Rect(0.25f, 0.125f, 0.5f, 0.625f)); - camera->SetColorScalePostProcessPasses({ - XCEngine::Math::Vector4(1.0f, 0.75f, 0.75f, 1.0f), - XCEngine::Math::Vector4(0.55f, 0.95f, 1.1f, 1.0f) + camera->SetPostProcessPasses({ + XCEngine::Rendering::CameraPostProcessPassDesc::MakeColorScale( + XCEngine::Math::Vector4(1.0f, 0.75f, 0.75f, 1.0f)), + XCEngine::Rendering::CameraPostProcessPassDesc::MakeColorScale( + XCEngine::Math::Vector4(0.55f, 0.95f, 1.1f, 1.0f)) }); auto allocationState = std::make_shared();