低解像度シーンの描画とブルーム効果
https://gyazo.com/b92da47e50f3dae6836b10197173f21c
https://scrapbox.io/files/68457623b6696e523662f804.mp4
※動画中の火のピクセルアートは、次のアセットを使用しました:
キャラクターの点滅や、炎や街灯や各種エフェクトなどに使うといいかもしれない
code:cpp
namespace
{
constexpr Size SceneSize{ 320, 256 };
constexpr RectF SceneRect{ SceneSize };
void LoadAsset()
{
TextureAsset::Register(U"siv3d-kun-16", U"example/spritesheet/siv3d-kun-16.png");
}
void DrawBG()
{
const ColorF color1 = ColorF{ 0 }.lerp(Palette::Lime, 0.15);
const ColorF color2 = ColorF{ 0.1 }.lerp(Palette::Lime, 0.18);
SceneRect.draw(color1);
constexpr Size TileSize{ 32, 32 };
for (int iY = 0; iY < SceneSize.y / TileSize.y; ++iY)
{
for (int iX = 0; iX < SceneSize.x / TileSize.x; ++iX)
{
if ((iX + iY) % 2 == 0)
{
RectF{ iX * TileSize.x, iY * TileSize.y, TileSize }.draw(color2);
}
}
}
}
void DrawSiv3DKun(Arg::center_<Vec2> pos)
{
constexpr Size SpriteSize{ 20, 28 };
constexpr int AnimationFrames = 3;
constexpr int EndFrame = AnimationFrames - 1;
int currentFrame = static_cast<int>(EndFrame / 2.0 + Math::Round(Periodic::Triangle1_1(1s) * (EndFrame / 2.0)));
TextureAsset(U"siv3d-kun-16")(0 + currentFrame * SpriteSize.x, 56, SpriteSize).drawAt(*pos);
}
class BloomEffect
{
public:
BloomEffect(const Size& size)
:
rtSrc_{ size },
rt1A_{ size },
rt1B_{ size },
rt4A_{ size / 4 },
rt4B_{ size / 4 },
rt8A_{ size / 8 },
rt8B_{ size / 8 }
{
}
ScopedRenderTarget2D getRenderTarget() const
{
return ScopedRenderTarget2D{ rtSrc_ };
}
void clear()
{
rtSrc_.clear(ColorF{ 0 });
}
void update()
{
Shader::GaussianBlur(rtSrc_, rt1B_, rt1A_);
Shader::Downsample(rt1A_, rt4A_);
Shader::GaussianBlur(rt4A_, rt4B_, rt4A_);
Shader::Downsample(rt4A_, rt8A_);
Shader::GaussianBlur(rt8A_, rt8B_, rt8A_);
}
void draw(double intensity1 = 0.20, double intensity4 = 0.25, double intensity8 = 0.37) const
{
const ScopedRenderStates2D state{ BlendState::Additive, SamplerState::ClampLinear };
const auto size = rtSrc_.size();
rt1A_.draw(ColorF(intensity1));
rt4A_.resized(size).draw(ColorF(intensity4));
rt8A_.resized(size).draw(ColorF(intensity8));
}
private:
RenderTexture rtSrc_;
RenderTexture rt1A_;
RenderTexture rt1B_;
RenderTexture rt4A_;
RenderTexture rt4B_;
RenderTexture rt8A_;
RenderTexture rt8B_;
};
}
void Main()
{
Scene::SetBackground(Palette::Black);
int renderScale = 2;
// 低解像度シーンをセットアップ
Scene::SetResizeMode(ResizeMode::Virtual);
Window::Resize(SceneSize * renderScale);
Scene::SetTextureFilter(TextureFilter::Nearest);
const ScopedRenderStates2D state{ SamplerState::ClampNearest };
RenderTexture renderTexture(SceneSize, Palette::Black);
// ブルームエフェクト
BloomEffect bloom{ SceneSize };
// ブルーム調整GUI用
double bloomIntensity1 = 0.35;
double bloomIntensity4 = 0.42;
double bloomIntensity8 = 0.56;
bool enableBloom = true;
// ウィンドウサイズ調整GUI用
double renderScaleGUIValue = renderScale;
LoadAsset();
while (System::Update())
{
// ウィンドウサイズを変更
if (const int newScale = static_cast<int>(renderScaleGUIValue);
newScale != renderScale)
{
renderScale = newScale;
Window::Resize(SceneSize * newScale);
}
// 通常のシーン描画
{
const ScopedRenderTarget2D target{ renderTexture };
// 背景
DrawBG();
// キャラクター
DrawSiv3DKun(Arg::center = SceneRect.center());
}
// ブルームエフェクトの対象のみ再度描画
if (enableBloom)
{
{
auto target = bloom.getRenderTarget();
bloom.clear();
DrawSiv3DKun(Arg::center = SceneRect.center());
}
bloom.update();
}
{
const Transformer2D transform{ Mat3x2::Scale(renderScale) };
renderTexture.draw();
if (enableBloom)
{
bloom.draw(
bloomIntensity1 + 0.10 * Periodic::Sine1_1(0.05s),
bloomIntensity4 + 0.15 * Periodic::Sine1_1(0.09s),
bloomIntensity8 + 0.20 * Periodic::Sine1_1(0.11s));
}
}
{
const Transformer2D transform{ Mat3x2::Scale(0.3 * renderScale, Vec2(0, 0)), TransformCursor::Yes };
const ScopedRenderStates2D state{ SamplerState::ClampLinear };
SimpleGUI::Slider(U"{:.2f}"_fmt(bloomIntensity1), bloomIntensity1, 0.0, 2.0, Vec2(20, 20));
SimpleGUI::Slider(U"{:.2f}"_fmt(bloomIntensity4), bloomIntensity4, 0.0, 2.0, Vec2(20, 60));
SimpleGUI::Slider(U"{:.2f}"_fmt(bloomIntensity8), bloomIntensity8, 0.0, 2.0, Vec2(20, 100));
SimpleGUI::CheckBox(enableBloom, U"enable", Vec2(20, 140));
SimpleGUI::Slider(U"Scale: {}"_fmt(static_cast<int>(renderScaleGUIValue)), renderScaleGUIValue, 1, 4, Vec2(20, 180));
}
}
}