Skip to content

Instantly share code, notes, and snippets.

@Reputeless
Created May 4, 2025 16:59
Show Gist options
  • Select an option

  • Save Reputeless/9064220b9fc72caf1e3c07acf3d09da6 to your computer and use it in GitHub Desktop.

Select an option

Save Reputeless/9064220b9fc72caf1e3c07acf3d09da6 to your computer and use it in GitHub Desktop.
# include <Siv3D.hpp>
struct SpotLight
{
Float3 position; // ライト位置
float cutoff; // cos(カットオフ角)
Float3 direction; // スポット軸方向
float exponent; // フォールオフ指数
Float3 color; // ライト色
float _unused = 0.0f;
};
struct PSSpotLight
{
SpotLight spotLights[2];
};
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 PixelShader ps3D = HLSL{ U"spotlight.hlsl", U"PS" }
| GLSL{ U"spotlight.frag", {{ U"PSPerFrame", 0 }, { U"PSPerView", 1 }, { U"PSPerMaterial", 3 }, { U"PSSpotLight", 4 }} };
if (not ps3D)
{
return;
}
ConstantBuffer<PSSpotLight> cbSpotLight;
Graphics3D::SetSunColor(ColorF{ 0.2 }.removeSRGBCurve());
Graphics3D::SetGlobalAmbientColor(ColorF{ 0.1 }.removeSRGBCurve());
while (System::Update())
{
camera.update(2.0);
Graphics3D::SetCameraTransform(camera);
{
auto& spotLight0 = cbSpotLight->spotLights[0];
spotLight0.position = Float3{ 0, 4, -4 };
spotLight0.cutoff = Math::Cos(30_deg);
spotLight0.direction = Float3{ 0, -1, 0 }.normalized();
spotLight0.exponent = 40.0f;
spotLight0.color = ColorF{ 1, 0, 0 }.removeSRGBCurve().rgb();
}
{
auto& spotLight1 = cbSpotLight->spotLights[1];
spotLight1.position = Float3{ 12, 6, -8 };
spotLight1.cutoff = Math::Cos(30_deg);
spotLight1.direction = Float3{ -0.5, -1, 0.5 }.normalized();
spotLight1.exponent = 10.0f;
spotLight1.color = ColorF{ 0, 0, 1 }.removeSRGBCurve().rgb();
}
// 3D 描画
{
// カスタムシェーダ使用
const ScopedCustomShader3D shader{ ps3D };
Graphics3D::SetPSConstantBuffer(4, cbSpotLight);
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());
}
// 3D シーンを 2D シーンに描画
{
Graphics3D::Flush();
renderTexture.resolve();
Shader::LinearToScreen(renderTexture);
}
}
}
// Copyright (c) 2008-2025 Ryo Suzuki.
// Copyright (c) 2016-2025 OpenSiv3D Project.
// Licensed under the MIT License.
# version 410
//
// Textures
//
uniform sampler2D Texture0;
struct Spotlight
{
vec3 position;
float cutoff;
vec3 direction;
float exponent;
vec3 color;
float _unused;
};
//
// PSInput
//
layout(location = 0) in vec3 WorldPosition;
layout(location = 1) in vec2 UV;
layout(location = 2) in vec3 Normal;
//
// PSOutput
//
layout(location = 0) out vec4 FragColor;
//
// Constant Buffer
//
layout(std140) uniform PSPerFrame // slot 0
{
vec3 g_globalAmbientColor;
vec3 g_sunColor;
vec3 g_sunDirection;
};
layout(std140) uniform PSPerView // slot 1
{
vec3 g_eyePosition;
};
layout(std140) uniform PSPerMaterial // slot 3
{
vec3 g_ambientColor;
uint g_hasTexture;
vec4 g_diffuseColor;
vec3 g_specularColor;
float g_shininess;
vec3 g_emissionColor;
};
layout(std140) uniform PSSpotLight // slot 4
{
Spotlight g_spotLights[2];
};
//
// Functions
//
vec4 GetDiffuseColor(vec2 uv)
{
vec4 diffuseColor = g_diffuseColor;
if (g_hasTexture == 1)
{
diffuseColor *= texture(Texture0, uv);
}
return diffuseColor;
}
vec3 CalculateDiffuseReflection(vec3 n, vec3 l, vec3 lightColor, vec3 diffuseColor, vec3 ambientColor)
{
vec3 directColor = lightColor * max(dot(n, l), 0.0f);
return ((ambientColor + directColor) * diffuseColor);
}
vec3 CalculateSpecularReflection(vec3 n, vec3 h, float shininess, float nl, vec3 lightColor, vec3 specularColor)
{
float highlight = pow(max(dot(n, h), 0.0f), shininess) * float(0.0f < nl);
return (lightColor * specularColor * highlight);
}
vec3 CalculateSpotlightReflection(
Spotlight spot,
vec3 n,
vec3 worldPos,
vec3 viewDir,
vec4 diffCol,
vec3 ambCol)
{
// ライト方向とスポット減衰
vec3 L = normalize(spot.position - worldPos);
float cosAngle = dot(-L, normalize(spot.direction));
float spotAtten = (cosAngle > spot.cutoff)
? pow(cosAngle, spot.exponent)
: 0.0;
// 拡散
vec3 diff = CalculateDiffuseReflection(
n, L,
spot.color,
diffCol.rgb,
ambCol
) * spotAtten;
// 鏡面
vec3 H = normalize(viewDir + L);
float NdotL = max(dot(n, L), 0.0);
vec3 spec = CalculateSpecularReflection(
n, H,
g_shininess,
NdotL,
spot.color,
g_specularColor
) * spotAtten;
return diff + spec;
}
void main()
{
vec3 n = normalize(Normal);
vec3 viewDir = normalize(g_eyePosition - WorldPosition);
vec4 diffuseColor = GetDiffuseColor(UV);
vec3 ambCol = g_ambientColor * g_globalAmbientColor;
vec3 sunDiff = CalculateDiffuseReflection(
n, normalize(g_sunDirection),
g_sunColor,
diffuseColor.rgb,
ambCol
);
vec3 H_sun = normalize(viewDir + normalize(g_sunDirection));
vec3 sunSpec = CalculateSpecularReflection(
n, H_sun,
g_shininess,
max(dot(n, normalize(g_sunDirection)), 0.0),
g_sunColor,
g_specularColor
);
vec3 spotAccum = vec3(0.0);
for (int i = 0; i < 2; ++i)
{
spotAccum += CalculateSpotlightReflection(
g_spotLights[i],
n,
WorldPosition,
viewDir,
diffuseColor,
ambCol
);
}
vec3 finalColor = sunDiff + sunSpec + spotAccum + g_emissionColor;
FragColor = vec4(finalColor, diffuseColor.a);
}
//-----------------------------------------------
//
// This file is part of the Siv3D Engine.
//
// Copyright (c) 2008-2025 Ryo Suzuki
// Copyright (c) 2016-2025 OpenSiv3D Project
//
// Licensed under the MIT License.
//
//-----------------------------------------------
//
// Textures
//
Texture2D g_texture0 : register(t0);
SamplerState g_sampler0 : register(s0);
namespace s3d
{
//
// VS Input
//
struct VSInput
{
float4 position : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
//
// VS Output / PS Input
//
struct PSInput
{
float4 position : SV_POSITION;
float3 worldPosition : TEXCOORD0;
float2 uv : TEXCOORD1;
float3 normal : TEXCOORD2;
};
}
struct Spotlight
{
float3 position;
float cutoff;
float3 direction;
float exponent;
float3 color;
float _unused;
};
//
// Constant Buffer
//
cbuffer VSPerView : register(b1)
{
row_major float4x4 g_worldToProjected;
}
cbuffer VSPerObject : register(b2)
{
row_major float4x4 g_localToWorld;
}
cbuffer VSPerMaterial : register(b3)
{
float4 g_uvTransform;
}
cbuffer PSPerFrame : register(b0)
{
float3 g_globalAmbientColor;
float3 g_sunColor;
float3 g_sunDirection;
}
cbuffer PSPerView : register(b1)
{
float3 g_eyePosition;
}
cbuffer PSPerMaterial : register(b3)
{
float3 g_ambientColor;
uint g_hasTexture;
float4 g_diffuseColor;
float3 g_specularColor;
float g_shininess;
float3 g_emissionColor;
}
cbuffer PSSpotLight : register(b4)
{
Spotlight g_spotLights[2];
}
//
// Functions
//
s3d::PSInput VS(s3d::VSInput input)
{
s3d::PSInput result;
const float4 worldPosition = mul(input.position, g_localToWorld);
result.position = mul(worldPosition, g_worldToProjected);
result.worldPosition = worldPosition.xyz;
result.uv = (input.uv * g_uvTransform.xy + g_uvTransform.zw);
result.normal = mul(input.normal, (float3x3)g_localToWorld);
return result;
}
float4 GetDiffuseColor(float2 uv)
{
float4 diffuseColor = g_diffuseColor;
if (g_hasTexture)
{
diffuseColor *= g_texture0.Sample(g_sampler0, uv);
}
return diffuseColor;
}
float3 CalculateDiffuseReflection(float3 n, float3 l, float3 lightColor, float3 diffuseColor, float3 ambientColor)
{
const float3 directColor = lightColor * saturate(dot(n, l));
return ((ambientColor + directColor) * diffuseColor);
}
float3 CalculateSpecularReflection(float3 n, float3 h, float shininess, float nl, float3 lightColor, float3 specularColor)
{
const float highlight = pow(saturate(dot(n, h)), shininess) * float(0.0 < nl);
return (lightColor * specularColor * highlight);
}
// スポットライト(拡散+鏡面反射)
float3 CalculateSpotlightReflection(
float3 n,
float3 worldPos,
float3 viewDir,
float4 diffCol,
float3 ambCol,
float shininess,
float3 specCol,
Spotlight spot)
{
float3 L = normalize(spot.position - worldPos);
float cosAngle = dot(-L, normalize(spot.direction));
float spotAtten = (cosAngle > spot.cutoff) ? pow(abs(cosAngle), spot.exponent) : 0.0;
float3 diff = CalculateDiffuseReflection(n, L, spot.color, diffCol.rgb, ambCol) * spotAtten;
float3 H = normalize(viewDir + L);
float NdotL = dot(n, L);
float3 spec = CalculateSpecularReflection(n, H, shininess, NdotL, spot.color, specCol) * spotAtten;
return diff + spec;
}
float4 PS(s3d::PSInput input) : SV_TARGET
{
float3 n = normalize(input.normal);
float3 eyeDir = normalize(g_eyePosition - input.worldPosition);
float4 diffuseColor = GetDiffuseColor(input.uv);
float3 ambCol = g_ambientColor * g_globalAmbientColor;
float3 sunDiff = CalculateDiffuseReflection(n, g_sunDirection, g_sunColor, diffuseColor.rgb, ambCol);
float3 H_sun = normalize(eyeDir + g_sunDirection);
float3 sunSpec = CalculateSpecularReflection(n, H_sun, g_shininess, dot(n, g_sunDirection), g_sunColor, g_specularColor);
float3 spotAccum = float3(0, 0, 0);
[unroll]
for (int i = 0; i < 2; ++i)
{
spotAccum += CalculateSpotlightReflection(
n, input.worldPosition, eyeDir,
diffuseColor, ambCol,
g_shininess, g_specularColor,
g_spotLights[i]);
}
const float3 finalColor = sunDiff + sunSpec + spotAccum + g_emissionColor;
return float4(finalColor, diffuseColor.a);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment