VisuTwin Canvas
C++ 3D Engine — Metal Backend
Loading...
Searching...
No Matches
renderPassSsao.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025-2026 Arnis Lektauers
3//
4//
5#include "renderPassSsao.h"
6
7#include <cmath>
8#include <random>
9
11#include "core/math/color.h"
12#include "core/math/defines.h"
16#include "scene/camera.h"
17
18namespace visutwin::canvas
19{
20
21 RenderPassSsao::RenderPassSsao(const std::shared_ptr<GraphicsDevice>& device, Texture* sourceTexture,
22 CameraComponent* cameraComponent, const bool blurEnabled)
23 : RenderPassShaderQuad(device), _sourceTexture(sourceTexture),
24 _cameraComponent(cameraComponent), _blurEnabled(blurEnabled)
25 {
26 // Create main SSAO render target (R8 format, single-channel occlusion).
27 // The texture must be kept alive by the member _ssaoTexture (RenderTarget stores raw pointer).
28 _ssaoRenderTarget = createSsaoRenderTarget("SsaoFinalTexture", _ssaoTexture);
29
30 auto options = std::make_shared<RenderPassOptions>();
31 options->resizeSource = std::shared_ptr<Texture>(_sourceTexture, [](Texture*) {});
32 init(_ssaoRenderTarget, options);
33
34 // Clear color to white (no occlusion) to avoid load op
35 const Color clearWhite(1.0f, 1.0f, 1.0f, 1.0f);
36 setClearColor(&clearWhite);
37
38 // Optional bilateral blur passes
39 if (blurEnabled) {
40 _blurTempRenderTarget = createSsaoRenderTarget("SsaoTempTexture", _blurTempTexture);
41
42 // Horizontal blur: reads from SSAO RT → writes to temp RT
43 _blurPassH = std::make_shared<RenderPassDepthAwareBlur>(
44 device, _ssaoTexture.get(), _cameraComponent, true);
45 auto blurHOptions = std::make_shared<RenderPassOptions>();
46 blurHOptions->resizeSource = _ssaoTexture;
47 _blurPassH->init(_blurTempRenderTarget, blurHOptions);
48 const Color clearBlack(0.0f, 0.0f, 0.0f, 0.0f);
49 _blurPassH->setClearColor(&clearBlack);
50 addAfterPass(_blurPassH);
51
52 // Vertical blur: reads from temp RT → writes back to SSAO RT
53 _blurPassV = std::make_shared<RenderPassDepthAwareBlur>(
54 device, _blurTempTexture.get(), _cameraComponent, false);
55 auto blurVOptions = std::make_shared<RenderPassOptions>();
56 blurVOptions->resizeSource = _ssaoTexture;
57 _blurPassV->init(_ssaoRenderTarget, blurVOptions);
58 _blurPassV->setClearColor(&clearBlack);
59 addAfterPass(_blurPassV);
60 }
61 }
62
64
65 std::shared_ptr<RenderTarget> RenderPassSsao::createSsaoRenderTarget(const std::string& name,
66 std::shared_ptr<Texture>& outTexture) const
67 {
68 TextureOptions textureOptions;
69 textureOptions.name = name;
70 textureOptions.width = 1;
71 textureOptions.height = 1;
72 textureOptions.format = PixelFormat::PIXELFORMAT_R8;
73 textureOptions.mipmaps = false;
74 textureOptions.minFilter = FilterMode::FILTER_LINEAR;
75 textureOptions.magFilter = FilterMode::FILTER_LINEAR;
76 outTexture = std::make_shared<Texture>(device().get(), textureOptions);
77 outTexture->setAddressU(AddressMode::ADDRESS_CLAMP_TO_EDGE);
78 outTexture->setAddressV(AddressMode::ADDRESS_CLAMP_TO_EDGE);
79
80 RenderTargetOptions rtOptions;
81 rtOptions.graphicsDevice = device().get();
82 rtOptions.colorBuffer = outTexture.get();
83 rtOptions.depth = false;
84 rtOptions.stencil = false;
85 rtOptions.name = name;
86 return device()->createRenderTarget(rtOptions);
87 }
88
90 {
91 const auto gd = device();
92 if (!gd || !_cameraComponent || !_cameraComponent->camera()) {
93 return;
94 }
95
96 // Get depth texture from the graphics device
97 Texture* depthTexture = gd->sceneDepthMap();
98 if (!depthTexture) {
99 return;
100 }
101
102 const auto* camera = _cameraComponent->camera();
103 const auto rt = renderTarget();
104 if (!rt || !rt->colorBuffer()) {
105 return;
106 }
107
108 const auto width = static_cast<float>(rt->colorBuffer()->width());
109 const auto height = static_cast<float>(rt->colorBuffer()->height());
110
111 if (width <= 0.0f || height <= 0.0f) {
112 return;
113 }
114
115 // Compute derived SSAO parameters (matching upstream RenderPassSsao.execute())
116 const float aspect = width / height;
117 const float spiralTurns = 10.0f;
118 const float step = (1.0f / (static_cast<float>(sampleCount) - 0.5f)) * spiralTurns * 2.0f * PI;
119 const float effectiveRadius = radius / scale();
120
121 const float bias = 0.001f;
122 const float peak = 0.1f * effectiveRadius;
123 const float computedIntensity = 2.0f * (peak * 2.0f * PI) * intensity / static_cast<float>(sampleCount);
124 const float projectionScale = 0.5f * (_sourceTexture ? static_cast<float>(_sourceTexture->height()) : height);
125
126 const float minAngleSin = std::sin(minAngle * DEG_TO_RAD);
127
128 // Blue noise for randomization (simple PRNG matching upstream BlueNoise behavior)
129 if (randomize) {
130 _blueNoiseValue = static_cast<float>(std::rand()) / static_cast<float>(RAND_MAX);
131 } else {
132 _blueNoiseValue = 0.0f;
133 }
134
135 SsaoPassParams params;
136 params.depthTexture = depthTexture;
137 params.aspect = aspect;
138 params.invResolutionX = 1.0f / width;
139 params.invResolutionY = 1.0f / height;
140 params.sampleCount = sampleCount;
141 params.spiralTurns = spiralTurns;
142 params.angleIncCos = std::cos(step);
143 params.angleIncSin = std::sin(step);
144 params.invRadiusSquared = 1.0f / (effectiveRadius * effectiveRadius);
145 params.minHorizonAngleSineSquared = minAngleSin * minAngleSin;
146 params.bias = bias;
147 params.peak2 = peak * peak;
148 params.intensity = computedIntensity;
149 params.power = power;
150 params.projectionScaleRadius = projectionScale * effectiveRadius;
151 params.randomize = _blueNoiseValue;
152 params.cameraNear = camera->nearClip();
153 params.cameraFar = camera->farClip();
154
155 gd->executeSsaoPass(params);
156 }
157
159 {
160 // The SSAO texture is now available for the compose pass
161 }
162
163 void RenderPassSsao::setScale(const float value)
164 {
165 _scale = value;
166 auto options = std::make_shared<RenderPassOptions>();
167 options->resizeSource = std::shared_ptr<Texture>(_sourceTexture, [](Texture*) {});
168 options->scaleX = value;
169 options->scaleY = value;
170 setOptions(options);
171 }
172}
virtual void init(const std::shared_ptr< RenderTarget > &renderTarget=nullptr, const std::shared_ptr< RenderPassOptions > &options=nullptr)
void setClearColor(const Color *color=nullptr)
std::shared_ptr< RenderTarget > renderTarget() const
Definition renderPass.h:98
std::shared_ptr< GraphicsDevice > device() const
Definition renderPass.h:124
void addAfterPass(const std::shared_ptr< RenderPass > &renderPass)
void setOptions(const std::shared_ptr< RenderPassOptions > &value)
RenderPassShaderQuad(const std::shared_ptr< GraphicsDevice > &device)
RenderPassSsao(const std::shared_ptr< GraphicsDevice > &device, Texture *sourceTexture, CameraComponent *cameraComponent, bool blurEnabled)
GPU texture resource supporting 2D, cubemap, volume, and array formats with mipmap management.
Definition texture.h:57
constexpr float PI
Definition defines.h:43
constexpr float DEG_TO_RAD
Definition defines.h:49
RGBA color with floating-point components in [0, 1].
Definition color.h:18