Open18

OpenSiv3D v0.6 debug memo

Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp> // OpenSiv3D v0.6.0
//SIV3D_SET(EngineOption::Renderer::OpenGL);

void Main()
{
	const VertexShader vs2D = HLSL{ U"example/shader/hlsl/default2d.hlsl", U"VS" }
		| GLSL{ U"example/shader/glsl/default2d.vert", {{U"VSConstants2D", 0}} };

	const PixelShader ps2DShape = HLSL{ U"example/shader/hlsl/default2d.hlsl", U"PS_Shape" }
		| GLSL{ U"example/shader/glsl/default2d_shape.frag", {{U"PSConstants2D", 0}} };

	const PixelShader ps2DTexture = HLSL{ U"example/shader/hlsl/default2d.hlsl", U"PS_Texture" }
		| GLSL{ U"example/shader/glsl/default2d_texture.frag", {{U"PSConstants2D", 0}} };

	if ((not vs2D) || (not ps2DShape) || (not ps2DTexture))
	{
		return;
	}

	const Texture texure{ U"example/windmill.png" };

	while (System::Update())
	{
		{
			const ScopedCustomShader2D shader{ vs2D, ps2DShape };
			Circle{ 600, 400, 100 }.draw(Palette::Orange);
		}

		{
			const ScopedCustomShader2D shader{ vs2D, ps2DTexture };
			texure.draw();
		}
	}
}
Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp>

void Main()
{
	const Texture windmill{ U"example/windmill.png" };
	const PixelShader ps = HLSL{ U"example/shader/hlsl/rgb_to_bgr.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/rgb_to_bgr.frag", {{U"PSConstants2D", 0}} };

	if (not ps)
	{
		throw Error{ U"Failed to load a shader file" };
	}

	while (System::Update())
	{
		{
			// R と B を入れ替えるピクセルシェーダを開始
			const ScopedCustomShader2D shader{ ps };
			windmill.draw(10, 10);
		}
	}
}
Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp>

void Main()
{
	const Texture windmill{ U"example/windmill.png" };
	const PixelShader ps = HLSL{ U"example/shader/hlsl/rgb_shift.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/rgb_shift.frag", {{U"PSConstants2D", 0}} };

	if (not ps)
	{
		throw Error{ U"Failed to load a shader file" };
	}

	while (System::Update())
	{
		{
			// RGB シフト用のシェーダを開始
			const ScopedCustomShader2D shader{ ps };
			windmill.draw(10, 10);
		}
	}
}

Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp>

void Main()
{
	const Texture windmill{ U"example/windmill.png" };
	const PixelShader ps = HLSL{ U"example/shader/hlsl/grayscale.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/grayscale.frag", {{U"PSConstants2D", 0}} };

	if (not ps)
	{
		throw Error{ U"Failed to load a shader file" };
	}

	while (System::Update())
	{
		{
			// グレースケール化するピクセルシェーダを開始
			const ScopedCustomShader2D shader{ ps };
			windmill.draw(10, 10);
		}
	}
}
Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp>

void Main()
{
	const Texture windmill{ U"example/windmill.png" };
	const PixelShader ps = HLSL{ U"example/shader/hlsl/posterize.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/posterize.frag", {{U"PSConstants2D", 0}} };

	if (not ps)
	{
		throw Error{ U"Failed to load a shader file" };
	}

	while (System::Update())
	{
		{
			// ポスタライズするピクセルシェーダを開始
			const ScopedCustomShader2D shader{ ps };
			windmill.draw(10, 10);
		}
	}
}
Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp>

// 定数バッファ (PS_1)
struct PoissonDisk
{
	// 1 ピクセルあたりの UV サイズ
	Float2 pixelSize;

	// サンプリング半径
	float diskRadius;
};

void Main()
{
	const Texture windmill{ U"example/windmill.png" };
	const PixelShader ps = HLSL{ U"example/shader/hlsl/poisson_disk.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/poisson_disk.frag", {{U"PSConstants2D", 0}, {U"PoissonDisk", 1}} };

	if (not ps)
	{
		throw Error{ U"Failed to load a shader file" };
	}

	// 定数バッファ
	ConstantBuffer<PoissonDisk> cb;
	cb->pixelSize = Float2{ 1.0, 1.0 } / windmill.size();

	// サンプリング半径
	double diskRadius = 0.0;

	while (System::Update())
	{
		// サンプリング半径をスライダーで変更
		if (SimpleGUI::Slider(U"diskRadius", diskRadius, 0.0, 8.0, Vec2{ 10, 340 }, 120, 200))
		{
			cb->diskRadius = static_cast<float>(diskRadius);
		}

		{
			// 定数バッファを、ピクセルシェーダの定数バッファインデックス 1 に設定
			Graphics2D::SetConstantBuffer(ShaderStage::Pixel, 1, cb);

			// Poisson-Disk Sampling 用のシェーダを開始
			const ScopedCustomShader2D shader{ ps };
			windmill.draw(10, 10);
		}
	}
}
Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp>

// 定数バッファ (PS_1)
struct Swirl
{
	// 回転
	float angle;
};

void Main()
{
	const Texture windmill{ U"example/windmill.png" };
	const PixelShader ps = HLSL{ U"example/shader/hlsl/swirl.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/swirl.frag", {{U"PSConstants2D", 0}, {U"Swirl", 1}} };

	if (not ps)
	{
		throw Error{ U"Failed to load a shader file" };
	}

	// 定数バッファ
	ConstantBuffer<Swirl> cb;

	while (System::Update())
	{
		cb->angle = static_cast<float>(Math::Sin(Scene::Time()) * 720_deg);

		{
			// 定数バッファを、ピクセルシェーダの定数バッファインデックス 1 に設定
			Graphics2D::SetConstantBuffer(ShaderStage::Pixel, 1, cb);

			// 渦巻き効果のシェーダを開始
			const ScopedCustomShader2D shader{ ps };
			windmill.draw(10, 10);
		}
	}
}
Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp>

void Main()
{
	const Texture emoji{ U"🐈"_emoji };
	const Texture windmill{ U"example/windmill.png", TextureDesc::Mipped };
	const PixelShader ps = HLSL{ U"example/shader/hlsl/multi_texture_blend.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/multi_texture_blend.frag", {{U"PSConstants2D", 0}} };

	if (not ps)
	{
		throw Error{ U"Failed to load a shader file" };
	}

	while (System::Update())
	{
		// windmill をピクセルシェーダのテクスチャスロット [1] にセット 
		Graphics2D::SetPSTexture(1, windmill);
		{
			// マルチテクスチャによるブレンドのシェーダを開始
			const ScopedCustomShader2D shader{ ps };
			emoji.scaled(2).drawAt(Scene::Center());
		}
	}
}
Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp>

void Main()
{
	// ウィンドウを 960x600 にリサイズ
	Window::Resize(960, 600);

	// シーンの背景色を淡い水色に設定
	Scene::SetBackground(ColorF(0.8, 0.9, 1.0));

	const Texture emoji{ Emoji::CreateSilhouetteImage(U"🍎"), TextureDesc::Mipped };
	const Texture windmill{ U"example/windmill.png", TextureDesc::Mipped };

	const PixelShader ps = HLSL{ U"example/shader/hlsl/multi_texture_mask.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/multi_texture_mask.frag", {{U"PSConstants2D", 0}} };

	if (not ps)
	{
		throw Error{ U"Failed to load a shader file" };
	}

	// レンダーテクスチャを作成
	const RenderTexture renderTexture{ 480, 320 };

	while (System::Update())
	{
		// レンダーテクスチャをクリア
		renderTexture.clear(ColorF(0.0, 1.0));
		{
			// レンダーターゲットを renderTexture に設定
			const ScopedRenderTarget2D rt(renderTexture);
			emoji.scaled(2).rotated(Scene::Time() * 60_deg).drawAt(renderTexture.size() / 2);
		}

		// 描画された renderTexture を表示
		renderTexture.draw(0, 140);

		// renderTexture をピクセルシェーダのテクスチャスロット [1] にセット 
		Graphics2D::SetPSTexture(1, renderTexture);
		{
			// マルチテクスチャによるマスクのシェーダを開始
			const ScopedCustomShader2D shader{ ps };
			windmill.draw(480, 140);
		}
	}
}
Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp>

// 定数バッファ (PS_1)
struct GameOfLife
{
	Float2 pixelSize;
};

void Main()
{
	// ウィンドウを 1280x720 にリサイズ
	Window::Resize(1280, 720);

	// セルの数 (1280x720)
	constexpr Size FieldSize{ 1280, 720 };

	const PixelShader ps = HLSL{ U"example/shader/hlsl/game_of_life.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/game_of_life.frag", {{U"PSConstants2D", 0}, {U"GameOfLife", 1}} };

	if (not ps)
	{
		throw Error{ U"Failed to load a shader file" };
	}

	// 定数バッファ
	const ConstantBuffer<GameOfLife> cb{ { (Float2{1.0f, 1.0f} / FieldSize) } };

	// レンダーテクスチャ 0
	RenderTexture renderTexture0{ Image{FieldSize, Arg::generator = []() { return Color(RandomBool() * 255); }} };

	// レンダーテクスチャ 1
	RenderTexture renderTexture1{ FieldSize, ColorF{0.0} };

	while (System::Update())
	{
		{
			// テクスチャフィルタなし
			const ScopedRenderStates2D sampler(SamplerState::ClampNearest);

			// 現在の状態を画面に描く
			renderTexture0.draw(ColorF{ 0.0, 1.0, 0.0 });

			{
				// ライフゲーム用のシェーダ
				Graphics2D::SetConstantBuffer(ShaderStage::Pixel, 1, cb);
				const ScopedCustomShader2D shader{ ps };

				// 更新後の状態を描く rt1 に描く
				const ScopedRenderTarget2D target{ renderTexture1 };
				renderTexture0.draw();
			}
		}

		// rt0 と rt1 を入れ替える
		std::swap(renderTexture0, renderTexture1);
	}
}
Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp>

// 定数バッファ (PS_1)
struct GameOfLife
{
	Float2 pixelSize;
};

void Main()
{
	// ウィンドウを 1280x720 にリサイズ
	Window::Resize(1280, 720);

	// セルの数 (1280x720)
	constexpr Size FieldSize(1280, 720);

	const PixelShader ps = HLSL{ U"example/shader/hlsl/game_of_life.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/game_of_life.frag", {{U"PSConstants2D", 0}, {U"GameOfLife", 1}} };

	if (not ps)
	{
		throw Error{ U"Failed to load a shader file" };
	}

	// 定数バッファ
	const ConstantBuffer<GameOfLife> cb{ { (Float2{1.0f, 1.0f} / FieldSize) } };

	// レンダーテクスチャ 0
	RenderTexture renderTexture0{ Image{FieldSize, Arg::generator = []() { return Color(RandomBool() * 255); }} };

	// レンダーテクスチャ 1
	RenderTexture renderTexture1{ FieldSize, ColorF{0.0} };

	// 2D カメラ
	Camera2D camera{ Vec2{0, 0}, 4.0 };

	while (System::Update())
	{
		// 2D カメラを更新
		camera.update();
		{
			// テクスチャフィルタなし
			const ScopedRenderStates2D sampler(SamplerState::ClampNearest);

			{
				// 2D カメラの設定から Transformer2D を作成
				const auto t = camera.createTransformer();

				// 現在の状態を画面に描く
				renderTexture0.draw(ColorF{ 0.0, 1.0, 0.0 });

				// 2D カメラの UI を描画
				camera.draw(Palette::Orange);
			}

			{
				// ライフゲーム用のシェーダ
				Graphics2D::SetConstantBuffer(ShaderStage::Pixel, 1, cb);
				const ScopedCustomShader2D shader{ ps };

				// 更新後の状態を描く rt1 に描く
				const ScopedRenderTarget2D target{ renderTexture1 };
				renderTexture0.draw();
			}
		}

		// rt0 と rt1 を入れ替える
		std::swap(renderTexture0, renderTexture1);
	}
}
Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp>

// 渦巻き効果のピクセルシェーダ用の
// 定数バッファ (PS_1)
struct Swirl
{
	// 回転
	float angle;
};

void Main()
{
	// ゲームの描画用のレンダーテクスチャ
	MSRenderTexture renderTexture{ Scene::Size() };

	// グレースケール化するピクセルシェーダ
	const PixelShader psGrayscale = HLSL{ U"example/shader/hlsl/grayscale.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/grayscale.frag", {{U"PSConstants2D", 0}} };

	if (not psGrayscale)
	{
		throw Error{ U"Failed to load a shader file" };
	}

	// 渦巻き効果のピクセルシェーダ
	const PixelShader psSwirl = HLSL{ U"example/shader/hlsl/swirl.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/swirl.frag", {{U"PSConstants2D", 0}, {U"Swirl", 1}} };

	if (not psSwirl)
	{
		throw Error{ U"Failed to load a shader file" };
	}

	// 渦巻き効果のピクセルシェーダ用の定数バッファ
	ConstantBuffer<Swirl> cb;

	// ガウスぼかしの中間で使うレンダーテクスチャ
	RenderTexture rtA{ renderTexture.size() }, rtB{ renderTexture.size() };
	RenderTexture rtA4{ renderTexture.size() / 4 }, rtB4{ renderTexture.size() / 4 };
	RenderTexture rtA8{ renderTexture.size() / 8 }, rtB8{ renderTexture.size() / 8 };

	// ゲーム画面に適用するエフェクト
	size_t effectIndex = 0;

	// 背景色
	constexpr ColorF backgroundColor{ 0.3, 0.4, 0.5 };

	// ブロックのサイズ
	constexpr Size blockSize{ 40, 20 };

	// ブロックの配列
	Array<Rect> blocks;

	// 横 (Scene::Width() / blockSize.x) 個、縦 5 個のブロックを配列に追加する
	for (auto p : step(Size{ (Scene::Width() / blockSize.x), 5 }))
	{
		blocks << Rect{ (p.x * blockSize.x), (60 + p.y * blockSize.y), blockSize };
	}

	// ボールの速さ
	constexpr double speed = 480.0;

	// ボールの速度
	Vec2 ballVelocity{ 0, -speed };

	// ボール
	Circle ball{ 400, 400, 8 };

	// 自動プレイ用のパラメータ
	double paddleCenter = 400;
	double randomOffset = 0.0;

	while (System::Update())
	{
		// 自動プレイ
		paddleCenter = Math::Damp(paddleCenter, ball.x + ballVelocity.x * 1.2 + randomOffset, 0.9, Scene::DeltaTime());

		// パドル
		const RectF paddle{ Arg::center(paddleCenter, 500), 120, 10 };

		// ボールを移動
		ball.moveBy(ballVelocity * Scene::DeltaTime());

		// ブロックを順にチェック
		for (auto it = blocks.begin(); it != blocks.end(); ++it)
		{
			// ボールとブロックが交差していたら
			if (it->intersects(ball))
			{
				// ボールの向きを反転する
				(it->bottom().intersects(ball) || it->top().intersects(ball) ? ballVelocity.y : ballVelocity.x) *= -1;

				// ブロックを配列から削除(イテレータが無効になるので注意)
				blocks.erase(it);

				// これ以上チェックしない  
				break;
			}
		}

		// 天井にぶつかったらはね返る
		if (ball.y < 0 && ballVelocity.y < 0)
		{
			ballVelocity.y *= -1;
		}

		// 左右の壁にぶつかったらはね返る
		if ((ball.x < 0 && ballVelocity.x < 0) || (Scene::Width() < ball.x && ballVelocity.x > 0))
		{
			ballVelocity.x *= -1;
		}

		// パドルにあたったらはね返る
		if (ballVelocity.y > 0 && paddle.intersects(ball))
		{
			// パドルの中心からの距離に応じてはね返る向きを変える
			ballVelocity = Vec2((ball.x - paddle.center().x) * 10, -ballVelocity.y).setLength(speed);
			randomOffset = Random(-40, 40);
		}

		// レンダーテクスチャをクリア
		renderTexture.clear(backgroundColor);
		{
			// レンダーターゲットを renderTexture に設定
			const ScopedRenderTarget2D target{ renderTexture };

			for (auto y : Range(1, 5))
			{
				Line{ 0, y * 100, 800, y * 100 }.draw(1, Palette::Gray);
			}

			for (auto x : Range(1, 7))
			{
				Line{ x * 100, 0, x * 100, 600 }.draw(1, Palette::Gray);
			}

			// すべてのブロックを描画する
			for (const auto& block : blocks)
			{
				block.stretched(-1).draw(HSV{ block.y - 40 });
			}

			// ボールを描く
			ball.draw();

			// パドルを描く
			paddle.draw();
		}

		// resolve のために描画を完了させる
		Graphics2D::Flush();

		// multi-sample のテクスチャを resolve して
		// multi-sample ではない、描画可能なテクスチャを得る
		renderTexture.resolve();

		if (effectIndex == 0) // ゲーム画面をそのまま描画
		{
			renderTexture.draw();
		}
		else if (effectIndex == 1) // ゲーム画面をグレースケール化して描画
		{
			// グレースケール化するピクセルシェーダを開始
			const ScopedCustomShader2D shader(psGrayscale);
			renderTexture.draw();
		}
		else if (effectIndex == 2) // ゲーム画面を渦巻き効果で描画
		{
			cb->angle = static_cast<float>(Math::Sin(Scene::Time()) * 240_deg);

			// 定数バッファを設定
			Graphics2D::SetConstantBuffer(ShaderStage::Pixel, 1, cb);

			// 渦巻き効果のシェーダを開始
			const ScopedCustomShader2D shader(psSwirl);
			renderTexture.draw();
		}
		else if (effectIndex == 3) // ゲーム画面をガウスぼかしで描画
		{
			// [オリジナル]->[ガウスぼかし]->[1/4サイズ]->[ガウスぼかし]
			Shader::GaussianBlur(renderTexture, rtB, rtA);
			Shader::Downsample(rtA, rtA4);
			Shader::GaussianBlur(rtA4, rtB4, rtA4);
			Shader::Downsample(rtA4, rtA8);
			Shader::GaussianBlur(rtA8, rtB8, rtA8);

			rtA8.scaled(8).draw();
		}

		// エフェクトの種類の選択
		SimpleGUI::RadioButtons(effectIndex, { U"Default", U"Grayscale", U"Swirl", U"GaussianBlur" }, Vec2{ 10, 10 });
	}
}
Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp>

void Main()
{
	Window::Resize(1280, 720);

	const VertexShader vs3D = HLSL{ U"example/shader/hlsl/default3d_forward.hlsl", U"VS" }
		| GLSL{ U"example/shader/glsl/default3d_forward.vert", {{ U"VSPerView", 1 }, { U"VSPerObject", 2 }} };

	const PixelShader ps3D = HLSL{ U"example/shader/hlsl/default3d_forward.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/default3d_forward.frag", {{ U"PSPerFrame", 0 }, { U"PSPerView", 1 }, { U"PSPerMaterial", 3 }} };

	if ((not vs3D) || (not ps3D))
	{
		return;
	}

	const ColorF backgroundColor = ColorF{ 0.4, 0.6, 0.8 }.removeSRGBCurve();
	const Texture uvChecker{ U"example/texture/uv.png", TextureDesc::MippedSRGB };
	const MSRenderTexture renderTexture{ Scene::Size(), TextureFormat::R8G8B8A8_Unorm_SRGB, HasDepth::Yes };
	DebugCamera3D camera{ renderTexture.size(), 30_deg, Vec3{ 10, 16, -32 } };

	while (System::Update())
	{
		camera.update(2.0);

		// 3D
		{
			Graphics3D::SetCameraTransform(camera);

			const ScopedCustomShader3D shader{ vs3D, ps3D };
			const ScopedRenderTarget3D target{ renderTexture.clear(backgroundColor) };

			Plane{ 64 }.draw(uvChecker);
			Box{ -8,2,0,4 }.draw(ColorF{ 0.8, 0.6, 0.4 }.removeSRGBCurve());
			Sphere{ 0,2,0,2 }.draw(ColorF{ 0.4, 0.8, 0.6 }.removeSRGBCurve());
			Cylinder{ 8, 2, 0, 2, 4 }.draw(ColorF{ 0.6, 0.4, 0.8 }.removeSRGBCurve());
		}

		// RenderTexture を 2D シーンに描画
		{
			Graphics3D::Flush();
			renderTexture.resolve();
			Shader::LinearToScreen(renderTexture);
		}
	}
}
Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp>

struct PSFog
{
	Float3 fogColor;
	float fogCoefficient;
};

void Main()
{
	Window::Resize(1280, 720);

	const PixelShader ps = HLSL{ U"example/shader/hlsl/forward_fog.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/forward_fog.frag", {{ U"PSPerFrame", 0 }, { U"PSPerView", 1 }, { U"PSPerMaterial", 3 }, { U"PSFog", 4 }} };

	if (not ps)
	{
		return;
	}

	const ColorF backgroundColor = ColorF{ 0.4, 0.6, 0.8 }.removeSRGBCurve();
	const Texture uvChecker{ U"example/texture/uv.png", TextureDesc::MippedSRGB };
	const MSRenderTexture renderTexture{ Scene::Size(), TextureFormat::R8G8B8A8_Unorm_SRGB, HasDepth::Yes };
	DebugCamera3D camera{ renderTexture.size(), 30_deg, Vec3{ 10, 16, -32 } };

	double fogParam = 0.6;
	ConstantBuffer<PSFog> cb{ { backgroundColor.rgb(), 0.0f } };
	
	while (System::Update())
	{
		camera.update(2.0);

		const double fogCoefficient = Math::Eerp(0.001, 0.25, fogParam);
		cb->fogCoefficient = static_cast<float>(fogCoefficient);

		// 3D
		{
			Graphics3D::SetCameraTransform(camera);
			Graphics3D::SetPSConstantBuffer(4, cb);

			const ScopedCustomShader3D shader{ ps };
			const ScopedRenderTarget3D target{ renderTexture.clear(backgroundColor) };

			Plane{ 64 }.draw(uvChecker);

			for (auto x : Range(-4, 4))
			{
				for (auto z : Range(-4, 4))
				{
					Box{ {x * 8, 4, z * 8} , {2, 8, 2} }.draw();
				}
			}
		}

		// RenderTexture を 2D シーンに描画
		{
			Graphics3D::Flush();
			renderTexture.resolve();
			Shader::LinearToScreen(renderTexture);
		}

		SimpleGUI::Slider(U"fogCoefficient: {:.3f}"_fmt(fogCoefficient), fogParam, Vec2{ 20, 20 }, 200, 160);
	}
}
Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp>

void Main()
{
	Window::Resize(1280, 720);

	const PixelShader ps3D = HLSL{ U"example/shader/hlsl/forward_triplanar.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/forward_triplanar.frag", {{ U"PSPerFrame", 0 }, { U"PSPerView", 1 }, { U"PSPerMaterial", 3 }} };

	if (not ps3D)
	{
		return;
	}

	const ColorF backgroundColor = ColorF{ 0.4, 0.6, 0.8 }.removeSRGBCurve();
	const Texture uvChecker{ U"example/texture/uv.png", TextureDesc::MippedSRGB };
	const Texture woodTexture{ U"example/texture/wood.jpg", TextureDesc::MippedSRGB };
	const MSRenderTexture renderTexture{ Scene::Size(), TextureFormat::R8G8B8A8_Unorm_SRGB, HasDepth::Yes };
	DebugCamera3D camera{ renderTexture.size(), 30_deg, Vec3{ 10, 16, -32 } };

	const Mesh mesh0{ MeshData::Octahedron(2) };
	const Mesh mesh1{ MeshData::Dodecahedron(2) };
	const Mesh mesh2{ MeshData::Icosahedron(2) };

	bool triplanar = true;

	while (System::Update())
	{
		camera.update(2.0);

		// 3D
		{
			Graphics3D::SetCameraTransform(camera);

			const ScopedRenderTarget3D target{ renderTexture.clear(backgroundColor) };

			Plane{ 64 }.draw(uvChecker);

			const auto shader =
				triplanar ? ScopedCustomShader3D{ ps3D } : ScopedCustomShader3D{};

			Box{ -8,2,0,4 }.draw(woodTexture);
			Sphere{ 0,2,0,2 }.draw(woodTexture);
			Cylinder{ 8, 2, 0, 2, 4 }.draw(woodTexture);
			mesh0.draw({ -8, 2, 8 }, woodTexture);
			mesh1.draw({ 0, 2, 8 }, woodTexture);
			mesh2.draw({ 8, 2, 8 }, woodTexture);
		}

		// RenderTexture を 2D シーンに描画
		{
			Graphics3D::Flush();
			renderTexture.resolve();
			Shader::LinearToScreen(renderTexture);
		}

		SimpleGUI::CheckBox(triplanar, U"triplanar", Vec2{ 20,20 });
	}
}
Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp>

void Main()
{
	Window::Resize(1280, 720);

	const VertexShader vsTerrain = HLSL{ U"example/shader/hlsl/terrain_forward.hlsl", U"VS" }
		| GLSL{ U"example/shader/glsl/terrain_forward.vert", {{ U"VSPerView", 1 }, { U"VSPerObject", 2 }} };

	const PixelShader psTerrain = HLSL{ U"example/shader/hlsl/terrain_forward.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/terrain_forward.frag", {{ U"PSPerFrame", 0 }, { U"PSPerView", 1 }, { U"PSPerMaterial", 3 }} };

	const PixelShader psNormal = HLSL{ U"example/shader/hlsl/terrain_normal.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/terrain_normal.frag", {{U"PSConstants2D", 0}} };

	if ((not vsTerrain) || (not psTerrain) || (not psNormal))
	{
		return;
	}

	const ColorF backgroundColor = ColorF{ 0.4, 0.6, 0.8 }.removeSRGBCurve();
	const Texture uvChecker{ U"example/texture/uv.png", TextureDesc::MippedSRGB };
	const Texture terrainTexture{ U"example/texture/grass.jpg", TextureDesc::MippedSRGB };
	const Texture rockTexture{ U"example/texture/rock.jpg", TextureDesc::MippedSRGB };
	const Texture brushTexture{ U"example/particle.png" };
	const MSRenderTexture renderTexture{ Scene::Size(), TextureFormat::R8G8B8A8_Unorm_SRGB, HasDepth::Yes };
	const Mesh gridMesh{ MeshData::Grid({128, 128}, 128, 128) };
	DebugCamera3D camera{ renderTexture.size(), 30_deg, Vec3{ 10, 16, -32 } };
	RenderTexture heightmap{ Size{ 256, 256 }, ColorF{0.0}, TextureFormat::R32_Float };
	RenderTexture normalmap{ Size{ 256, 256 }, ColorF{0.0, 0.0, 0.0}, TextureFormat::R16G16_Float };

	while (System::Update())
	{
		camera.update(2.0);

		// 3D
		{
			Graphics3D::SetCameraTransform(camera);

			const ScopedCustomShader3D shader{ vsTerrain, psTerrain };
			const ScopedRenderTarget3D target{ renderTexture.clear(backgroundColor) };

			const ScopedRenderStates3D ss{ { ShaderStage::Vertex, 0, SamplerState::ClampLinear} };
			Graphics3D::SetVSTexture(0, heightmap);
			Graphics3D::SetPSTexture(1, normalmap);
			Graphics3D::SetPSTexture(2, rockTexture);

			gridMesh.draw(terrainTexture);
		}

		// RenderTexture を 2D シーンに描画
		{
			Graphics3D::Flush();
			renderTexture.resolve();
			Shader::LinearToScreen(renderTexture);
		}

		if (const bool gen = SimpleGUI::Button(U"Random", Vec2{270, 10});
			(gen || (MouseL | MouseR).pressed())) // 地形を編集
		{
			// heightmap を編集
			if (gen)
			{
				const PerlinNoiseF perlin{ RandomUint64() };
				Grid<float> grid(256, 256);
				for (auto p : step(grid.size()))
				{
					grid[p] = perlin.octave2D0_1(p / 256.0f, 5) * 16.0f;
				}
				const RenderTexture noise{ grid };

				const ScopedRenderTarget2D target{ heightmap };
				noise.draw();
			}
			else
			{
				const ScopedRenderTarget2D target{ heightmap };
				const ScopedRenderStates2D blend{ BlendState::Additive };
				brushTexture.scaled(1.0 + MouseL.pressed()).drawAt(Cursor::PosF(), ColorF{ Scene::DeltaTime() * 15.0 });
			}

			// normal map を更新
			{
				const ScopedRenderTarget2D target{ normalmap };
				const ScopedCustomShader2D shader{ psNormal };
				const ScopedRenderStates2D blend{ BlendState::Opaque, SamplerState::ClampLinear };
				heightmap.draw();
			}
		}

		heightmap.draw(ColorF{ 0.1 });
		normalmap.draw(0, 260);
	}
}
Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp>

void Main()
{
	Window::Resize(1280, 720);
	Scene::SetResizeMode(ResizeMode::Keep);

	const PixelShader psBright = HLSL{ U"example/shader/hlsl/extract_bright_linear.hlsl", U"PS" }
		| GLSL{ U"example/shader/glsl/extract_bright_linear.frag", {{U"PSConstants2D", 0}} };

	if (not psBright)
	{
		return;
	}

	const ColorF backgroundColor = ColorF{ 0.02 }.removeSRGBCurve();
	const MSRenderTexture renderTexture{ Scene::Size(), TextureFormat::R16G16B16A16_Float, HasDepth::Yes };
	RenderTexture rtA4{ renderTexture.size() / 4 }, rtB4{ renderTexture.size() / 4 };
	RenderTexture rtA8{ renderTexture.size() / 8 }, rtB8{ renderTexture.size() / 8 };
	RenderTexture rtA16{ renderTexture.size() / 16 }, rtB16{ renderTexture.size() / 16 };
	DebugCamera3D camera{ renderTexture.size(), 30_deg, Vec3{ 10, 16, -32 } };

	bool glowEffect = true;

	while (System::Update())
	{
		camera.update(2.0);

		// 3D
		{
			Graphics3D::SetCameraTransform(camera);

			const ScopedRenderTarget3D target{ renderTexture.clear(backgroundColor) };

			PhongMaterial phong;
			phong.amibientColor = ColorF{ 1.0 };
			phong.diffuseColor = ColorF{ 0.0 };

			for (auto i : Range(0, 10))
			{
				phong.emissionColor = ColorF{ 1.0, 0.4, 0.4 }.removeSRGBCurve() * (i / 5.0);
				Box{ {-20 + i * 4, 2, 8}, 2 }
					.draw(phong);

				phong.emissionColor = ColorF{ 0.4, 1.0, 0.4 }.removeSRGBCurve() * (i / 5.0);
				Sphere{ {-20 + i * 4, 2, 0}, 1 }
					.draw(phong);

				phong.emissionColor = ColorF{ 0.4, 0.4, 1.0 }.removeSRGBCurve() * (i / 5.0);
				Cylinder{ {-20 + i * 4, 2, -8}, 1, 2 }
					.draw(phong);
			}
		}

		// RenderTexture を 2D シーンに描画
		{
			Graphics3D::Flush();
			renderTexture.resolve();
			Shader::LinearToScreen(renderTexture);
		}

		if (glowEffect)
		{
			// 高輝度部分を抽出
			{
				const ScopedCustomShader2D shader{ psBright };
				const ScopedRenderTarget2D target{ rtA4.clear(ColorF{0.0}) };
				renderTexture.scaled(0.25).draw();
			}

			// 高輝度部分のぼかしテクスチャの生成
			{
				Shader::GaussianBlur(rtA4, rtB4, rtA4);
				Shader::Downsample(rtA4, rtA8);
				Shader::GaussianBlur(rtA8, rtB8, rtA8);
				Shader::GaussianBlur(rtA8, rtB8, rtA8);
				Shader::Downsample(rtA8, rtA16);
				Shader::GaussianBlur(rtA16, rtB16, rtA16);
				Shader::GaussianBlur(rtA16, rtB16, rtA16);
				Shader::GaussianBlur(rtA16, rtB16, rtA16);
			}

			// Glow エフェクト
			{
				const ScopedRenderStates2D blend{ BlendState::AdditiveRGB };

				{
					const ScopedRenderTarget2D target{ rtA8 };
					rtA16.scaled(2.0).draw(ColorF{ 3.0 });
				}

				{
					const ScopedRenderTarget2D target{ rtA4 };
					rtA8.scaled(2.0).draw(ColorF{ 1.0 });
				}

				rtA4.resized(Scene::Size()).draw(ColorF{ 1.0 });
			}
		}

		SimpleGUI::CheckBox(glowEffect, U"Glow", Vec2{ 20,20 });
	}
}
Ryo SuzukiRyo Suzuki
# include <Siv3D.hpp> // OpenSiv3D v0.6

void Main()
{
	Window::Resize(1280, 720);

	const ColorF backgroundColor = ColorF{ 0.4, 0.6, 0.8 }.removeSRGBCurve();
	const Texture uvChecker{ U"example/texture/uv.png", TextureDesc::MippedSRGB };
	const MSRenderTexture renderTexture{ Scene::Size(), TextureFormat::R8G8B8A8_Unorm_SRGB, HasDepth::Yes };
	DebugCamera3D camera{ renderTexture.size(), 30_deg, Vec3{ 10, 16, -32 } };
	const Mesh billboard{ MeshData::Billboard() };
	const Mesh wideBillboard{ MeshData::Billboard({2, 1}) };

	while (System::Update())
	{
		camera.update(2.0);
		Graphics3D::SetCameraTransform(camera);

		// 3D
		{
			const ScopedRenderTarget3D target{ renderTexture.clear(backgroundColor) };

			Plane{ 64 }.draw(uvChecker);
			Box{ -8,2,0,4 }.draw(ColorF{ 0.8, 0.6, 0.4 }.removeSRGBCurve());
			Sphere{ 0,2,0,2 }.draw(ColorF{ 0.4, 0.8, 0.6 }.removeSRGBCurve());
			Cylinder{ 8, 2, 0, 2, 4 }.draw(ColorF{ 0.6, 0.4, 0.8 }.removeSRGBCurve());

			billboard.draw(camera.billboard(Vec3{ -8, 4, -4 }, 1), uvChecker);
			billboard.draw(camera.billboard(Vec3{ 0, 4, -4 }, 2), uvChecker);
			billboard.draw(camera.billboard(Vec3{ 8, 4, -4 }, 4), uvChecker);

			wideBillboard.draw(camera.billboard(Vec3{ -8, 4, 4 }, { 2, 1 }), uvChecker);
			wideBillboard.draw(camera.billboard(Vec3{ 0, 4, 4 }, { 4, 2 }), uvChecker);
			wideBillboard.draw(camera.billboard(Vec3{ 8, 4, 4 }, { 8, 4 }), uvChecker);
		}

		// RenderTexture を 2D シーンに描画
		{
			Graphics3D::Flush();
			renderTexture.resolve();
			Shader::LinearToScreen(renderTexture);
		}
	}
}