Skip to content

Instantly share code, notes, and snippets.

@Reputeless
Last active October 27, 2023 16:19
Show Gist options
  • Save Reputeless/eb4830bb2efb6f8734478945008f8b42 to your computer and use it in GitHub Desktop.
Save Reputeless/eb4830bb2efb6f8734478945008f8b42 to your computer and use it in GitHub Desktop.
# include <Siv3D.hpp>
struct PSLighting
{
static constexpr uint32 MaxPointLights = 4;
struct Light
{
Float4 position{ 0, 0, 0, 0 };
Float4 diffuseColor{ 0, 0, 0, 0 };
Float4 attenuation{ 1.0f, 2.0f, 1.0f, 0 };
};
std::array<Light, MaxPointLights> pointLights;
/// @brief 点光源を設定します。
/// @param i 光源のインデックス。0 以上 MaxPointLights 未満である必要があります。
/// @param pos 光源の位置
/// @param diffuse 色
/// @param r 強さ
void setPointLight(uint32 i, const Vec3& pos, const ColorF& diffuse, double r)
{
pointLights[i].position = Float4{ pos, 1.0f };
pointLights[i].diffuseColor = diffuse.toFloat4();
pointLights[i].attenuation = Float4{ 1.0, (2.0 / r), (1.0 / (r * r)), 0.0 };
}
/// @brief 点光源を球として描画します。
/// @param i 光源のインデックス。0 以上 MaxPointLights 未満である必要があります。
/// @param r 球の半径
void drawPointLightAsEmissiveSphere(uint32 i, double r)
{
const Vec3 pos = pointLights[i].position.xyz();
const ColorF diffuse{ pointLights[i].diffuseColor };
PhongMaterial phong;
phong.ambientColor = ColorF{ 0.0 };
phong.diffuseColor = ColorF{ 0.0 };
phong.emissionColor = diffuse;
Sphere{ pos, r }.draw(phong);
}
};
void Main()
{
Window::Resize(1280, 720);
const ColorF backgroundColor = ColorF{ 0.2, 0.2, 0.2 }.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"point_light.hlsl", U"PS" };
ConstantBuffer<PSLighting> constantBuffer;
if (not ps3D)
{
return;
}
while (System::Update())
{
camera.update(2.0);
Graphics3D::SetCameraTransform(camera);
// 環境光を小さくする
Graphics3D::SetGlobalAmbientColor(ColorF{ 0.01 });
// 太陽光をオフにする
Graphics3D::SetSunColor(ColorF{ 0.0 });
// 点光源を設定する
constantBuffer->setPointLight(0, Vec3{ -8, 2, -8 }, ColorF{ 1.0, 0.2, 0.0 }, 5.0);
constantBuffer->setPointLight(1, Cylindrical{ 12, (Scene::Time() * 30_deg), (0.5 + Periodic::Sine0_1(2s) * 2) }, ColorF{ 0.2, 1.0, 0.2 }, 5.0);
constantBuffer->setPointLight(2, Vec3{ 8, 2, -8 }, ColorF{ 0.2, 0.5, 1.0 }, 5.0);
{
// カスタムシェーダを使用する
const ScopedCustomShader3D shader{ ps3D };
// ピクセルシェーダに定数バッファを渡す
Graphics3D::SetPSConstantBuffer(4, constantBuffer);
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());
constantBuffer->drawPointLightAsEmissiveSphere(0, 0.2);
constantBuffer->drawPointLightAsEmissiveSphere(1, 0.2);
constantBuffer->drawPointLightAsEmissiveSphere(2, 0.2);
}
{
Graphics3D::Flush();
renderTexture.resolve();
Shader::LinearToScreen(renderTexture);
}
}
}
//-----------------------------------------------
//
// This file is part of the Siv3D Engine.
//
// Copyright (c) 2008-2023 Ryo Suzuki
// Copyright (c) 2016-2023 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;
};
}
//
// 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;
}
struct Light
{
float4 position;
float4 diffuseColor;
float4 attenuation;
};
static const uint MaxPointLights = 4;
cbuffer PSLighting : register(b4)
{
Light g_lights[MaxPointLights];
}
float3 CalculatePointLight(uint index, float3 surfaceNormal, float3 surfacePosition)
{
const Light light = g_lights[index];
const float3 lightPosition = light.position.xyz;
float3 lightDirection = (lightPosition - surfacePosition);
const float d = length(lightDirection);
const float Kc = light.attenuation.x;
const float Kl = light.attenuation.y;
const float Kq = light.attenuation.z;
const float f_att = 1.0 / (Kc + Kl * d + Kq * d * d);
lightDirection = normalize(lightDirection);
const float diffuseInfluence = saturate(dot(lightDirection, surfaceNormal)) * f_att;
return light.diffuseColor.rgb * diffuseInfluence;
}
//
// 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);
}
float4 PS(s3d::PSInput input) : SV_TARGET
{
const float3 lightColor = g_sunColor;
const float3 lightDirection = g_sunDirection;
const float3 n = normalize(input.normal);
const float3 l = lightDirection;
const float4 diffuseColor = GetDiffuseColor(input.uv);
const float3 ambientColor = (g_ambientColor * g_globalAmbientColor);
// Diffuse
float3 diffuseReflection = CalculateDiffuseReflection(n, l, lightColor, diffuseColor.rgb, ambientColor);
// Point Light
for (uint i = 0; i < MaxPointLights; ++i)
{
diffuseReflection += (diffuseColor.rgb * CalculatePointLight(i, n, input.worldPosition));
}
// Specular
const float3 v = normalize(g_eyePosition - input.worldPosition);
const float3 h = normalize(v + lightDirection);
const float3 specularReflection = CalculateSpecularReflection(n, h, g_shininess, dot(n, l), lightColor, g_specularColor);
return float4(diffuseReflection + specularReflection + g_emissionColor, diffuseColor.a);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment