feat: add camera viewport rect render areas

This commit is contained in:
2026-04-01 13:01:11 +08:00
parent 0fe02fd1b4
commit f80fb9860e
9 changed files with 219 additions and 12 deletions

View File

@@ -25,6 +25,16 @@ void CameraComponent::SetFarClipPlane(float value) {
m_farClipPlane = std::max(m_nearClipPlane + 0.001f, value);
}
void CameraComponent::SetViewportRect(const Math::Rect& value) {
const float x = std::clamp(value.x, 0.0f, 1.0f);
const float y = std::clamp(value.y, 0.0f, 1.0f);
const float width = std::clamp(value.width, 0.0f, 1.0f);
const float height = std::clamp(value.height, 0.0f, 1.0f);
const float right = std::min(1.0f, x + width);
const float bottom = std::min(1.0f, y + height);
m_viewportRect = Math::Rect(x, y, right - x, bottom - y);
}
void CameraComponent::Serialize(std::ostream& os) const {
os << "projection=" << static_cast<int>(m_projectionType) << ";";
os << "fov=" << m_fieldOfView << ";";
@@ -35,6 +45,7 @@ void CameraComponent::Serialize(std::ostream& os) const {
os << "primary=" << (m_primary ? 1 : 0) << ";";
os << "clearMode=" << static_cast<int>(m_clearMode) << ";";
os << "cullingMask=" << m_cullingMask << ";";
os << "viewportRect=" << m_viewportRect.x << "," << m_viewportRect.y << "," << m_viewportRect.width << "," << m_viewportRect.height << ";";
os << "clearColor=" << m_clearColor.r << "," << m_clearColor.g << "," << m_clearColor.b << "," << m_clearColor.a << ";";
}
@@ -71,6 +82,12 @@ void CameraComponent::Deserialize(std::istream& is) {
m_clearMode = static_cast<CameraClearMode>(std::stoi(value));
} else if (key == "cullingMask") {
m_cullingMask = static_cast<uint32_t>(std::stoul(value));
} else if (key == "viewportRect") {
std::replace(value.begin(), value.end(), ',', ' ');
std::istringstream ss(value);
Math::Rect viewportRect;
ss >> viewportRect.x >> viewportRect.y >> viewportRect.width >> viewportRect.height;
SetViewportRect(viewportRect);
} else if (key == "clearColor") {
std::replace(value.begin(), value.end(), ',', ' ');
std::istringstream ss(value);

View File

@@ -101,11 +101,16 @@ bool CameraRenderer::Render(
return false;
}
if (request.surface.GetRenderAreaWidth() == 0 ||
request.surface.GetRenderAreaHeight() == 0) {
return false;
}
RenderSceneData sceneData = m_sceneExtractor.ExtractForCamera(
*request.scene,
*request.camera,
request.surface.GetWidth(),
request.surface.GetHeight());
request.surface.GetRenderAreaWidth(),
request.surface.GetRenderAreaHeight());
if (!sceneData.HasCamera()) {
return false;
}

View File

@@ -324,19 +324,24 @@ bool BuiltinForwardPipeline::ExecuteForwardOpaquePass(const RenderPassContext& p
renderTargets.data(),
surface.GetDepthAttachment());
const Math::RectInt renderArea = surface.GetRenderArea();
if (renderArea.width <= 0 || renderArea.height <= 0) {
return false;
}
const RHI::Viewport viewport = {
0.0f,
0.0f,
static_cast<float>(surface.GetWidth()),
static_cast<float>(surface.GetHeight()),
static_cast<float>(renderArea.x),
static_cast<float>(renderArea.y),
static_cast<float>(renderArea.width),
static_cast<float>(renderArea.height),
0.0f,
1.0f
};
const RHI::Rect scissorRect = {
0,
0,
static_cast<int32_t>(surface.GetWidth()),
static_cast<int32_t>(surface.GetHeight())
renderArea.x,
renderArea.y,
renderArea.x + renderArea.width,
renderArea.y + renderArea.height
};
commandList->SetViewport(viewport);
commandList->SetScissorRect(scissorRect);

View File

@@ -1,8 +1,39 @@
#include "Rendering/RenderSurface.h"
#include <algorithm>
#include <cstdint>
namespace XCEngine {
namespace Rendering {
namespace {
Math::RectInt ClampRenderArea(
const Math::RectInt& renderArea,
uint32_t surfaceWidth,
uint32_t surfaceHeight) {
const int32_t maxWidth = static_cast<int32_t>(surfaceWidth);
const int32_t maxHeight = static_cast<int32_t>(surfaceHeight);
const int32_t left = std::clamp(renderArea.x, 0, maxWidth);
const int32_t top = std::clamp(renderArea.y, 0, maxHeight);
const int64_t unclampedRight = static_cast<int64_t>(renderArea.x) +
static_cast<int64_t>(std::max(renderArea.width, 0));
const int64_t unclampedBottom = static_cast<int64_t>(renderArea.y) +
static_cast<int64_t>(std::max(renderArea.height, 0));
const int32_t right = std::clamp(
static_cast<int32_t>(std::min<int64_t>(unclampedRight, maxWidth)),
left,
maxWidth);
const int32_t bottom = std::clamp(
static_cast<int32_t>(std::min<int64_t>(unclampedBottom, maxHeight)),
top,
maxHeight);
return Math::RectInt(left, top, right - left, bottom - top);
}
} // namespace
RenderSurface::RenderSurface(uint32_t width, uint32_t height)
: m_width(width)
, m_height(height) {
@@ -11,6 +42,9 @@ RenderSurface::RenderSurface(uint32_t width, uint32_t height)
void RenderSurface::SetSize(uint32_t width, uint32_t height) {
m_width = width;
m_height = height;
if (m_hasCustomRenderArea) {
m_renderArea = ClampRenderArea(m_renderArea, m_width, m_height);
}
}
void RenderSurface::SetColorAttachment(RHI::RHIResourceView* colorAttachment) {
@@ -24,6 +58,32 @@ void RenderSurface::SetColorAttachments(const std::vector<RHI::RHIResourceView*>
m_colorAttachments = colorAttachments;
}
void RenderSurface::SetRenderArea(const Math::RectInt& renderArea) {
m_renderArea = ClampRenderArea(renderArea, m_width, m_height);
m_hasCustomRenderArea = true;
}
void RenderSurface::ResetRenderArea() {
m_hasCustomRenderArea = false;
m_renderArea = {};
}
Math::RectInt RenderSurface::GetRenderArea() const {
if (!m_hasCustomRenderArea) {
return Math::RectInt(0, 0, static_cast<int32_t>(m_width), static_cast<int32_t>(m_height));
}
return ClampRenderArea(m_renderArea, m_width, m_height);
}
uint32_t RenderSurface::GetRenderAreaWidth() const {
return static_cast<uint32_t>(std::max(GetRenderArea().width, 0));
}
uint32_t RenderSurface::GetRenderAreaHeight() const {
return static_cast<uint32_t>(std::max(GetRenderArea().height, 0));
}
void RenderSurface::SetClearColorOverride(const Math::Color& clearColor) {
m_hasClearColorOverride = true;
m_clearColorOverride = clearColor;

View File

@@ -5,6 +5,7 @@
#include "Scene/Scene.h"
#include <algorithm>
#include <cmath>
namespace XCEngine {
namespace Rendering {
@@ -40,6 +41,20 @@ RenderClearFlags ResolveClearFlags(const Components::CameraComponent& camera, si
}
}
Math::RectInt ResolveCameraRenderArea(
const Components::CameraComponent& camera,
const RenderSurface& surface) {
const Math::Rect viewportRect = camera.GetViewportRect();
const float surfaceWidth = static_cast<float>(surface.GetWidth());
const float surfaceHeight = static_cast<float>(surface.GetHeight());
const int32_t left = static_cast<int32_t>(std::floor(viewportRect.x * surfaceWidth));
const int32_t top = static_cast<int32_t>(std::floor(viewportRect.y * surfaceHeight));
const int32_t right = static_cast<int32_t>(std::ceil((viewportRect.x + viewportRect.width) * surfaceWidth));
const int32_t bottom = static_cast<int32_t>(std::ceil((viewportRect.y + viewportRect.height) * surfaceHeight));
return Math::RectInt(left, top, right - left, bottom - top);
}
} // namespace
SceneRenderer::SceneRenderer() = default;
@@ -101,9 +116,13 @@ std::vector<CameraRenderRequest> SceneRenderer::BuildRenderRequests(
request.camera = camera;
request.context = context;
request.surface = surface;
request.surface.SetRenderArea(ResolveCameraRenderArea(*camera, surface));
request.cameraDepth = camera->GetDepth();
request.clearFlags = ResolveClearFlags(*camera, cameraIndex);
requests.push_back(request);
if (request.surface.GetRenderAreaWidth() > 0 &&
request.surface.GetRenderAreaHeight() > 0) {
requests.push_back(request);
}
}
return requests;