From ecfe5e7adfca4bedde148e50337352e2eef8f14d Mon Sep 17 00:00:00 2001 From: Ryo Suzuki Date: Sat, 3 Feb 2024 00:29:04 +0900 Subject: [PATCH] update --- .../glsl/default3d_forward_shadow_depth.frag | 68 ++ .../default3d_forward_shadow_shading.frag | 150 ++++ .../shader/hlsl/default3d_forward_shadow.hlsl | 166 +++++ Siv3D/include/Siv3D/Mat4x4.hpp | 3 + Siv3D/include/Siv3D/detail/Mat4x4.ipp | 3 + .../glsl/default3d_forward_shadow_depth.frag | 68 ++ .../default3d_forward_shadow_shading.frag | 150 ++++ .../shader/hlsl/default3d_forward_shadow.hlsl | 166 +++++ WindowsDesktop/Main.cpp | 703 ++++++++++++++++-- .../glsl/default3d_forward_shadow_depth.frag | 68 ++ .../default3d_forward_shadow_shading.frag | 150 ++++ .../shader/hlsl/default3d_forward_shadow.hlsl | 166 +++++ 12 files changed, 1791 insertions(+), 70 deletions(-) create mode 100644 Linux/App/example/shader/glsl/default3d_forward_shadow_depth.frag create mode 100644 Linux/App/example/shader/glsl/default3d_forward_shadow_shading.frag create mode 100644 Linux/App/example/shader/hlsl/default3d_forward_shadow.hlsl create mode 100644 WindowsDesktop/App/example/shader/glsl/default3d_forward_shadow_depth.frag create mode 100644 WindowsDesktop/App/example/shader/glsl/default3d_forward_shadow_shading.frag create mode 100644 WindowsDesktop/App/example/shader/hlsl/default3d_forward_shadow.hlsl create mode 100644 macOS/App/example/shader/glsl/default3d_forward_shadow_depth.frag create mode 100644 macOS/App/example/shader/glsl/default3d_forward_shadow_shading.frag create mode 100644 macOS/App/example/shader/hlsl/default3d_forward_shadow.hlsl diff --git a/Linux/App/example/shader/glsl/default3d_forward_shadow_depth.frag b/Linux/App/example/shader/glsl/default3d_forward_shadow_depth.frag new file mode 100644 index 000000000..b0397e2fe --- /dev/null +++ b/Linux/App/example/shader/glsl/default3d_forward_shadow_depth.frag @@ -0,0 +1,68 @@ +// Copyright (c) 2008-2023 Ryo Suzuki. +// Copyright (c) 2016-2023 OpenSiv3D Project. +// Licensed under the MIT License. + +# version 410 + +// +// Textures +// +uniform sampler2D Texture0; + +// +// PSInput +// +layout(location = 0) in vec3 WorldPosition; +layout(location = 1) in vec2 UV; +layout(location = 2) in vec3 Normal; + +// +// PSOutput +// +layout(location = 0) out vec2 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; +}; + +//////////////////////////////////////////////////////////// +// +// Depth +// + +layout(std140) uniform PSShadow // slot 4 +{ + mat4x4 g_worldToProjectedShadow; + vec3 g_sunPosition; + float g_lightBleedingReduction; +}; + +void main() +{ + float depth = length(g_sunPosition - WorldPosition); + FragColor = vec2(depth, (depth * depth)); +} + +// +//////////////////////////////////////////////////////////// diff --git a/Linux/App/example/shader/glsl/default3d_forward_shadow_shading.frag b/Linux/App/example/shader/glsl/default3d_forward_shadow_shading.frag new file mode 100644 index 000000000..09cd1ff16 --- /dev/null +++ b/Linux/App/example/shader/glsl/default3d_forward_shadow_shading.frag @@ -0,0 +1,150 @@ +// Copyright (c) 2008-2023 Ryo Suzuki. +// Copyright (c) 2016-2023 OpenSiv3D Project. +// Licensed under the MIT License. + +# version 410 + +// +// Textures +// +uniform sampler2D Texture0; + +// +// 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; +}; + +// +// 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); +} + +//////////////////////////////////////////////////////////// +// +// Shading +// + +uniform sampler2D Texture1; + +layout(std140) uniform PSShadow // slot 4 +{ + mat4x4 g_worldToProjectedShadow; + vec3 g_sunPosition; + float g_lightBleedingReduction; +}; + +float LineStep(float min, float max, float value) +{ + return clamp((value - min) / (max - min), 0.0, 1.0); +} + +float ReduceLightBleeding(float p_max, float amount) +{ + return LineStep(amount, 1.0, p_max); +} + +float CalculateShadow(vec3 worldPosition) +{ + vec4 projectedPosition = (vec4(worldPosition, 1.0) * g_worldToProjectedShadow); + + if (any(greaterThan(abs(projectedPosition.xyz - clamp(projectedPosition.xyz, 0.0, 1.0)), vec3(0.0)))) + { + return 1.0; + } + + vec2 uv = vec2(projectedPosition.x, (1.0 - projectedPosition.y)); + float depth = (length(g_sunPosition - worldPosition) - 0.03125); + vec2 moments = texture(Texture1, uv).rg; + + if (depth <= moments.x) + { + return 1.0; + } + + float variance = (moments.y - (moments.x * moments.x)); + float d = (moments.x - depth); + float lit = (variance / (variance + (d * d))); + + return ReduceLightBleeding(lit, g_lightBleedingReduction); +} + +void main() +{ + // Shadow + float lit = CalculateShadow(WorldPosition); + + vec3 lightColor = (g_sunColor * lit); + vec3 lightDirection = g_sunDirection; + + vec3 n = normalize(Normal); + vec3 l = lightDirection; + vec4 diffuseColor = GetDiffuseColor(UV); + vec3 ambientColor = (g_ambientColor * g_globalAmbientColor); + + // Diffuse + vec3 diffuseReflection = CalculateDiffuseReflection(n, l, lightColor, diffuseColor.rgb, ambientColor); + + // Specular + vec3 v = normalize(g_eyePosition - WorldPosition); + vec3 h = normalize(v + lightDirection); + vec3 specularReflection = CalculateSpecularReflection(n, h, g_shininess, dot(n, l), lightColor, g_specularColor); + + FragColor = vec4(diffuseReflection + specularReflection + g_emissionColor, diffuseColor.a); +} + +// +//////////////////////////////////////////////////////////// diff --git a/Linux/App/example/shader/hlsl/default3d_forward_shadow.hlsl b/Linux/App/example/shader/hlsl/default3d_forward_shadow.hlsl new file mode 100644 index 000000000..9bc92b6ec --- /dev/null +++ b/Linux/App/example/shader/hlsl/default3d_forward_shadow.hlsl @@ -0,0 +1,166 @@ +//----------------------------------------------- +// +// 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 Output / PS Input + // + struct PSInput + { + float4 position : SV_POSITION; + float3 worldPosition : TEXCOORD0; + float2 uv : TEXCOORD1; + float3 normal : TEXCOORD2; + }; +} + +// +// Constant Buffer +// +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; +} + +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); +} + +//////////////////////////////////////////////////////////// +// +// Depth +// +cbuffer PSShadow : register(b4) +{ + row_major float4x4 g_worldToProjectedShadow; + float3 g_sunPosition; + float g_lightBleedingReduction; +} + +float2 Depth_PS(s3d::PSInput input) : SV_TARGET +{ + const float depth = length(g_sunPosition - input.worldPosition); + return float2(depth, (depth * depth)); +} +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// +// Shading +// +Texture2D g_texture1 : register(t1); +SamplerState g_sampler1 : register(s1); + +float LineStep(float min, float max, float value) +{ + return saturate((value - min) / (max - min)); +} + +float ReduceLightBleeding(float p_max, float amount) +{ + return LineStep(amount, 1.0, p_max); +} + +float CalculateShadow(float3 worldPosition) +{ + const float4 projectedPosition = mul(float4(worldPosition, 1.0), g_worldToProjectedShadow); + + if (any(saturate(projectedPosition.xyz) != projectedPosition.xyz)) + { + return 1.0; + } + + const float2 uv = float2(projectedPosition.x, (1.0 - projectedPosition.y)); + const float depth = (length(g_sunPosition - worldPosition) - 0.03125); + const float2 moments = g_texture1.Sample(g_sampler1, uv).rg; + + if (depth <= moments.x) + { + return 1.0; + } + + const float variance = (moments.y - (moments.x * moments.x)); + const float d = (moments.x - depth); + const float lit = (variance / (variance + (d * d))); + + return ReduceLightBleeding(lit, g_lightBleedingReduction); +} + +float4 Shading_PS(s3d::PSInput input) : SV_TARGET +{ + // Shadow + const float lit = CalculateShadow(input.worldPosition); + + const float3 lightColor = (g_sunColor * lit); + const float3 lightDirection = g_sunDirection; + + const float3 n = normalize(input.normal); + const float3 l = lightDirection; + float4 diffuseColor = GetDiffuseColor(input.uv); + const float3 ambientColor = (g_ambientColor * g_globalAmbientColor); + + // Diffuse + const float3 diffuseReflection = CalculateDiffuseReflection(n, l, lightColor, diffuseColor.rgb, ambientColor); + + // 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); +} +// +//////////////////////////////////////////////////////////// diff --git a/Siv3D/include/Siv3D/Mat4x4.hpp b/Siv3D/include/Siv3D/Mat4x4.hpp index b1c07f751..fea5f0339 100644 --- a/Siv3D/include/Siv3D/Mat4x4.hpp +++ b/Siv3D/include/Siv3D/Mat4x4.hpp @@ -43,6 +43,9 @@ namespace s3d float m30, float m31, float m32, float m33 ) noexcept; + SIV3D_NODISCARD_CXX20 + explicit Mat4x4(const float* p) noexcept; + SIV3D_NODISCARD_CXX20 explicit Mat4x4(Quaternion q) noexcept; diff --git a/Siv3D/include/Siv3D/detail/Mat4x4.ipp b/Siv3D/include/Siv3D/detail/Mat4x4.ipp index 9575c6b07..02ec75de5 100644 --- a/Siv3D/include/Siv3D/detail/Mat4x4.ipp +++ b/Siv3D/include/Siv3D/detail/Mat4x4.ipp @@ -29,6 +29,9 @@ namespace s3d const float m30, const float m31, const float m32, const float m33) noexcept : value{ m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33 } {} + inline Mat4x4::Mat4x4(const float* p) noexcept + : value{ p } {} + inline Mat4x4::Mat4x4(const Quaternion q) noexcept : value{ DirectX::XMMatrixRotationQuaternion(q) } {} diff --git a/WindowsDesktop/App/example/shader/glsl/default3d_forward_shadow_depth.frag b/WindowsDesktop/App/example/shader/glsl/default3d_forward_shadow_depth.frag new file mode 100644 index 000000000..b0397e2fe --- /dev/null +++ b/WindowsDesktop/App/example/shader/glsl/default3d_forward_shadow_depth.frag @@ -0,0 +1,68 @@ +// Copyright (c) 2008-2023 Ryo Suzuki. +// Copyright (c) 2016-2023 OpenSiv3D Project. +// Licensed under the MIT License. + +# version 410 + +// +// Textures +// +uniform sampler2D Texture0; + +// +// PSInput +// +layout(location = 0) in vec3 WorldPosition; +layout(location = 1) in vec2 UV; +layout(location = 2) in vec3 Normal; + +// +// PSOutput +// +layout(location = 0) out vec2 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; +}; + +//////////////////////////////////////////////////////////// +// +// Depth +// + +layout(std140) uniform PSShadow // slot 4 +{ + mat4x4 g_worldToProjectedShadow; + vec3 g_sunPosition; + float g_lightBleedingReduction; +}; + +void main() +{ + float depth = length(g_sunPosition - WorldPosition); + FragColor = vec2(depth, (depth * depth)); +} + +// +//////////////////////////////////////////////////////////// diff --git a/WindowsDesktop/App/example/shader/glsl/default3d_forward_shadow_shading.frag b/WindowsDesktop/App/example/shader/glsl/default3d_forward_shadow_shading.frag new file mode 100644 index 000000000..09cd1ff16 --- /dev/null +++ b/WindowsDesktop/App/example/shader/glsl/default3d_forward_shadow_shading.frag @@ -0,0 +1,150 @@ +// Copyright (c) 2008-2023 Ryo Suzuki. +// Copyright (c) 2016-2023 OpenSiv3D Project. +// Licensed under the MIT License. + +# version 410 + +// +// Textures +// +uniform sampler2D Texture0; + +// +// 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; +}; + +// +// 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); +} + +//////////////////////////////////////////////////////////// +// +// Shading +// + +uniform sampler2D Texture1; + +layout(std140) uniform PSShadow // slot 4 +{ + mat4x4 g_worldToProjectedShadow; + vec3 g_sunPosition; + float g_lightBleedingReduction; +}; + +float LineStep(float min, float max, float value) +{ + return clamp((value - min) / (max - min), 0.0, 1.0); +} + +float ReduceLightBleeding(float p_max, float amount) +{ + return LineStep(amount, 1.0, p_max); +} + +float CalculateShadow(vec3 worldPosition) +{ + vec4 projectedPosition = (vec4(worldPosition, 1.0) * g_worldToProjectedShadow); + + if (any(greaterThan(abs(projectedPosition.xyz - clamp(projectedPosition.xyz, 0.0, 1.0)), vec3(0.0)))) + { + return 1.0; + } + + vec2 uv = vec2(projectedPosition.x, (1.0 - projectedPosition.y)); + float depth = (length(g_sunPosition - worldPosition) - 0.03125); + vec2 moments = texture(Texture1, uv).rg; + + if (depth <= moments.x) + { + return 1.0; + } + + float variance = (moments.y - (moments.x * moments.x)); + float d = (moments.x - depth); + float lit = (variance / (variance + (d * d))); + + return ReduceLightBleeding(lit, g_lightBleedingReduction); +} + +void main() +{ + // Shadow + float lit = CalculateShadow(WorldPosition); + + vec3 lightColor = (g_sunColor * lit); + vec3 lightDirection = g_sunDirection; + + vec3 n = normalize(Normal); + vec3 l = lightDirection; + vec4 diffuseColor = GetDiffuseColor(UV); + vec3 ambientColor = (g_ambientColor * g_globalAmbientColor); + + // Diffuse + vec3 diffuseReflection = CalculateDiffuseReflection(n, l, lightColor, diffuseColor.rgb, ambientColor); + + // Specular + vec3 v = normalize(g_eyePosition - WorldPosition); + vec3 h = normalize(v + lightDirection); + vec3 specularReflection = CalculateSpecularReflection(n, h, g_shininess, dot(n, l), lightColor, g_specularColor); + + FragColor = vec4(diffuseReflection + specularReflection + g_emissionColor, diffuseColor.a); +} + +// +//////////////////////////////////////////////////////////// diff --git a/WindowsDesktop/App/example/shader/hlsl/default3d_forward_shadow.hlsl b/WindowsDesktop/App/example/shader/hlsl/default3d_forward_shadow.hlsl new file mode 100644 index 000000000..9bc92b6ec --- /dev/null +++ b/WindowsDesktop/App/example/shader/hlsl/default3d_forward_shadow.hlsl @@ -0,0 +1,166 @@ +//----------------------------------------------- +// +// 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 Output / PS Input + // + struct PSInput + { + float4 position : SV_POSITION; + float3 worldPosition : TEXCOORD0; + float2 uv : TEXCOORD1; + float3 normal : TEXCOORD2; + }; +} + +// +// Constant Buffer +// +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; +} + +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); +} + +//////////////////////////////////////////////////////////// +// +// Depth +// +cbuffer PSShadow : register(b4) +{ + row_major float4x4 g_worldToProjectedShadow; + float3 g_sunPosition; + float g_lightBleedingReduction; +} + +float2 Depth_PS(s3d::PSInput input) : SV_TARGET +{ + const float depth = length(g_sunPosition - input.worldPosition); + return float2(depth, (depth * depth)); +} +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// +// Shading +// +Texture2D g_texture1 : register(t1); +SamplerState g_sampler1 : register(s1); + +float LineStep(float min, float max, float value) +{ + return saturate((value - min) / (max - min)); +} + +float ReduceLightBleeding(float p_max, float amount) +{ + return LineStep(amount, 1.0, p_max); +} + +float CalculateShadow(float3 worldPosition) +{ + const float4 projectedPosition = mul(float4(worldPosition, 1.0), g_worldToProjectedShadow); + + if (any(saturate(projectedPosition.xyz) != projectedPosition.xyz)) + { + return 1.0; + } + + const float2 uv = float2(projectedPosition.x, (1.0 - projectedPosition.y)); + const float depth = (length(g_sunPosition - worldPosition) - 0.03125); + const float2 moments = g_texture1.Sample(g_sampler1, uv).rg; + + if (depth <= moments.x) + { + return 1.0; + } + + const float variance = (moments.y - (moments.x * moments.x)); + const float d = (moments.x - depth); + const float lit = (variance / (variance + (d * d))); + + return ReduceLightBleeding(lit, g_lightBleedingReduction); +} + +float4 Shading_PS(s3d::PSInput input) : SV_TARGET +{ + // Shadow + const float lit = CalculateShadow(input.worldPosition); + + const float3 lightColor = (g_sunColor * lit); + const float3 lightDirection = g_sunDirection; + + const float3 n = normalize(input.normal); + const float3 l = lightDirection; + float4 diffuseColor = GetDiffuseColor(input.uv); + const float3 ambientColor = (g_ambientColor * g_globalAmbientColor); + + // Diffuse + const float3 diffuseReflection = CalculateDiffuseReflection(n, l, lightColor, diffuseColor.rgb, ambientColor); + + // 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); +} +// +//////////////////////////////////////////////////////////// diff --git a/WindowsDesktop/Main.cpp b/WindowsDesktop/Main.cpp index e173c00cc..c63ada105 100644 --- a/WindowsDesktop/Main.cpp +++ b/WindowsDesktop/Main.cpp @@ -1,105 +1,668 @@ -# include // Siv3D v0.6.13 +# include // Siv3D v0.6.14 -void Main() +/// @brief シャドウマップ +class ShadowMap { - // 背景の色を設定する | Set the background color - Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 }); +public: - // 画像ファイルからテクスチャを作成する | Create a texture from an image file - const Texture texture{ U"example/windmill.png" }; + /// @brief シャドウマップの設定 + struct Config + { + /// @brief シャドウマップの解像度 + Size resolution = Size{ 1024, 1024 }; - // 絵文字からテクスチャを作成する | Create a texture from an emoji - const Texture emoji{ U"🦖"_emoji }; + /// @brief 光の漏れを軽減する係数 [0.0, 1.0] + /// @remark 大きいと光の漏れが軽減されますが、影のソフトさが低下します。 + double lightBleedingReduction = 0.8; - // 太文字のフォントを作成する | Create a bold font with MSDF method - const Font font{ FontMethod::MSDF, 48, Typeface::Bold }; + /// @brief シャドウマップ生成に MSAA を使用するか + bool useMSAA = false; - // テキストに含まれる絵文字のためのフォントを作成し、font に追加する | Create a font for emojis in text and add it to font as a fallback - const Font emojiFont{ 48, Typeface::ColorEmoji }; - font.addFallback(emojiFont); + /// @brief フィルタオプション [0, 3] + /// @remark 0: フィルタなし, 1: 5x5, 2: 9x9, 3: 13x13 + size_t filter = 0; - // ボタンを押した回数 | Number of button presses - int32 count = 0; + /// @brief シャドウマップにミップマップを使用するか + bool useMipMap = false; + }; - // チェックボックスの状態 | Checkbox state - bool checked = false; + /// @brief シャドウマップ用の平行光源 + struct Light + { + /// @brief メイン平行光源の注視点 + Vec3 focusPos{ 0, 0, 0 }; - // プレイヤーの移動スピード | Player's movement speed - double speed = 200.0; + /// @brief メイン平行光源の位置 + Vec3 sunPos = (focusPos + Graphics3D::DefaultSunDirection * 64.0); - // プレイヤーの X 座標 | Player's X position - double playerPosX = 400; + /// @brief メイン平行光源の上方向 + Vec3 upDirection{ 0, 1, 0 }; - // プレイヤーが右を向いているか | Whether player is facing right - bool isPlayerFacingRight = true; + /// @brief シャドウマップのビューのサイズ + /// @remark 大きいと広い範囲をカバーできますが、詳細が失われます。 + double viewSize = 64.0; - while (System::Update()) + /// @brief シャドウマップの近面までの距離 + double nearZ = 1.0; + + /// @brief シャドウマップの遠面までの距離 + double farZ = 1024.0; + }; + + ShadowMap() = default; + + explicit ShadowMap(const Config& config) + : m_config{ config } + , m_initialized{ true } + { + updateTextures(); + } + + /// @brief デプスパスを開始します。 + /// @param light 平行光源の設定 + void beginDepth(const Light& light) + { + if (not m_initialized) + { + throw Error{ U"ShadowMap::beginDepth() called before initialization" }; + } + + if (m_depthPass) + { + throw Error{ U"ShadowMap::beginDepth() called twice" }; + } + + if (m_shadowPass) + { + throw Error{ U"ShadowMap::beginDepth() called while shadow pass is active" }; + } + + m_depthPass.emplace(); + m_depthPass->oldCameraMat = FromMat4x4(Graphics3D::GetCameraTransform()); + m_depthPass->oldEyePosition = Graphics3D::GetEyePosition(); + m_depthPass->renderTarget = std::make_unique(m_textures[0]->clear(ColorF{ 0.0 })); + m_depthPass->renderStates = std::make_unique(BlendState::Opaque); + + m_camera.update(light); + Graphics3D::SetCameraTransform(m_camera.viewProj, light.sunPos); + + Mat4x4 worldToShadowMap = (m_camera.viewProj * Mat4x4::Scale(Float3{ 0.5, 0.5, 1.0 }) * Mat4x4::Translate(Float3{ 0.5, 0.5, 0.0 })); + + if (System::GetRendererType() != EngineOption::Renderer::Direct3D11) + { + worldToShadowMap = worldToShadowMap.transposed(); + } + + m_cbp->worldToShadowMap = worldToShadowMap; + m_cbp->sunPosition = light.sunPos; + m_cbp->lightBleedingReduction = static_cast(m_config.lightBleedingReduction); + Graphics3D::SetPSConstantBuffer(4, m_cbp); + } + + /// @brief デプスパスを終了します。 + void endDepth() + { + if (not m_depthPass) + { + throw Error{ U"ShadowMap::endDepth() called without beginDepth()" }; + } + + if (m_shadowPass) + { + throw Error{ U"ShadowMap::endDepth() called while shadow pass is active" }; + } + + Graphics3D::SetCameraTransform(Mat4x4{ m_depthPass->oldCameraMat.data() }, m_depthPass->oldEyePosition); + + m_depthPass.reset(); + Graphics3D::Flush(); + + if (m_config.useMSAA) + { + dynamic_cast(m_textures[0].get())->resolve(); + } + + if (m_config.filter) + { + const BoxFilterSize filterSize = ToBoxFilterSize(m_config.filter); + + if (m_config.useMSAA) + { + Shader::GaussianBlur(*m_textures[0], *m_textures[1], *m_textures[2], filterSize); + } + else + { + Shader::GaussianBlur(*m_textures[0], *m_textures[1], *m_textures[0], filterSize); + } + + Graphics2D::Flush(); + } + + if (m_config.useMipMap) + { + getDepthTexture().generateMips(); + } + } + + /// @brief シャドウパスを開始します。 + void beginShadow() + { + if (not m_initialized) + { + throw Error{ U"ShadowMap::beginShadow() called before initialization" }; + } + + if (m_shadowPass) + { + throw Error{ U"ShadowMap::beginShadow() called twice" }; + } + + if (m_depthPass) + { + throw Error{ U"ShadowMap::beginShadow() called while depth pass is active" }; + } + + m_shadowPass.emplace(); + m_shadowPass->pRenderStates = std::make_unique(ScopedRenderStates3D::SamplerStateInfo{ ShaderStage::Pixel, 3, (m_config.useMipMap ? SamplerState::ClampAniso : SamplerState::ClampLinear) }); + Graphics3D::SetPSTexture(1, getDepthTexture()); + Graphics3D::SetPSConstantBuffer(4, m_cbp); + } + + /// @brief シャドウパスを終了します。 + void endShadow() { - // テクスチャを描く | Draw the texture - texture.draw(20, 20); + if (not m_shadowPass) + { + throw Error{ U"ShadowMap::endShadow() called without beginShadow()" }; + } - // テキストを描く | Draw text - font(U"Hello, Siv3D!🎮").draw(64, Vec2{ 20, 340 }, ColorF{ 0.2, 0.4, 0.8 }); + if (m_depthPass) + { + throw Error{ U"ShadowMap::endShadow() called while depth pass is active" }; + } - // 指定した範囲内にテキストを描く | Draw text within a specified area - font(U"Siv3D (シブスリーディー) は、ゲームやアプリを楽しく簡単な C++ コードで開発できるフレームワークです。") - .draw(18, Rect{ 20, 430, 480, 200 }, Palette::Black); + Graphics3D::SetPSTexture(1, none); + m_shadowPass.reset(); + } - // 長方形を描く | Draw a rectangle - Rect{ 540, 20, 80, 80 }.draw(); + /// @brief シャドウマップの設定を変更します。 + /// @param config シャドウマップの設定 + void setConfig(const Config& config) + { + if (m_depthPass || m_shadowPass) + { + throw Error{ U"ShadowMap::setConfig() called while depth or shadow pass is active" }; + } - // 角丸長方形を描く | Draw a rounded rectangle - RoundRect{ 680, 20, 80, 200, 20 }.draw(ColorF{ 0.0, 0.4, 0.6 }); + m_config = config; + updateTextures(); + } - // 円を描く | Draw a circle - Circle{ 580, 180, 40 }.draw(Palette::Seagreen); + /// @brief 深度テクスチャを取得します。 + /// @return 深度テクスチャ + [[nodiscard]] + const RenderTexture& getDepthTexture() const noexcept + { + return *m_textures[getDepthTextureIndex()]; + } + +private: - // 矢印を描く | Draw an arrow - Line{ 540, 330, 760, 260 }.drawArrow(8, SizeF{ 20, 20 }, ColorF{ 0.4 }); + struct TextureState + { + Size resolution{ 0, 0 }; + bool isRequired = false; + bool useMSAA = false; + bool useDepth = false; + bool useMipMap = false; - // 半透明の円を描く | Draw a semi-transparent circle - Circle{ Cursor::Pos(), 40 }.draw(ColorF{ 1.0, 0.0, 0.0, 0.5 }); + [[nodiscard]] + bool operator ==(const TextureState&) const = default; + }; - // ボタン | Button - if (SimpleGUI::Button(U"count: {}"_fmt(count), Vec2{ 520, 370 }, 120, (checked == false))) + struct DepthCamera + { + Mat4x4 viewProj = Mat4x4::Identity(); + + void update(const Light& light) { - // カウントを増やす | Increase the count - ++count; + const SIMD_Float4 eyePosition{ light.sunPos, 0.0f }; + const SIMD_Float4 focusPosition{ light.focusPos, 0.0f }; + const SIMD_Float4 upDirection{ light.upDirection, 0.0f }; + const Mat4x4 view = DirectX::XMMatrixLookAtLH(eyePosition, focusPosition, upDirection); + const float viewF = static_cast(light.viewSize); + const Mat4x4 proj = DirectX::XMMatrixOrthographicLH(viewF, viewF, static_cast(light.farZ), static_cast(light.nearZ)); + viewProj = (view * proj); } + }; + + struct DepthPass + { + std::array oldCameraMat = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; + Float3 oldEyePosition{ 0, 0, 0 }; + std::unique_ptr renderTarget; + std::unique_ptr renderStates; + }; - // チェックボックス | Checkbox - SimpleGUI::CheckBox(checked, U"Lock \U000F033E", Vec2{ 660, 370 }, 120); + struct ShadowPass + { + std::unique_ptr pRenderStates; + }; - // スライダー | Slider - SimpleGUI::Slider(U"speed: {:.1f}"_fmt(speed), speed, 100, 400, Vec2{ 520, 420 }, 140, 120); + struct PSShadow + { + Mat4x4 worldToShadowMap; + Float3 sunPosition{ 0,0,0 }; + float lightBleedingReduction = 0.8f; + }; - // 左キーが押されていたら | If left key is pressed - if (KeyLeft.pressed()) + static constexpr TextureFormat DepthFormat = TextureFormat::R32G32_Float; + + Config m_config; + + std::array m_currentTextureStates; + + std::array, 3> m_textures; + + DepthCamera m_camera; + + Optional m_depthPass; + + Optional m_shadowPass; + + ConstantBuffer m_cbp; + + bool m_initialized = false; + + static void CreateDepthTexture(std::unique_ptr& depthTexture, const Config& config) + { + if (depthTexture) + { + const bool currentMSAA = (dynamic_cast(depthTexture.get()) != nullptr); + const bool currentMipMap = depthTexture->hasMipMap(); + const Size currentResolution = depthTexture->size(); + + if ((config.useMSAA == currentMSAA) + && (config.useMipMap == currentMipMap) + && (config.resolution == currentResolution)) + { + return; + } + } + + depthTexture.reset(); + + if (config.useMSAA) + { + depthTexture = std::make_unique(config.resolution, DepthFormat, HasDepth::Yes, HasMipMap{ config.useMipMap }); + } + else { - // プレイヤーが左に移動する | Player moves left - playerPosX = Max((playerPosX - speed * Scene::DeltaTime()), 60.0); - isPlayerFacingRight = false; + depthTexture = std::make_unique(config.resolution, DepthFormat, HasDepth::Yes, HasMipMap{ config.useMipMap }); } + } - // 右キーが押されていたら | If right key is pressed - if (KeyRight.pressed()) + static void CreateInternalTexture(std::unique_ptr& internalTexture1, std::unique_ptr& internalTexture2, const Config& config) + { { - // プレイヤーが右に移動する | Player moves right - playerPosX = Min((playerPosX + speed * Scene::DeltaTime()), 740.0); - isPlayerFacingRight = true; + if (internalTexture1) + { + const Size currentResolution = internalTexture1->size(); + + if (config.resolution == currentResolution) + { + return; + } + } + + internalTexture1.reset(); + internalTexture1 = std::make_unique(config.resolution, DepthFormat); } - // プレイヤーを描く | Draw the player - emoji.scaled(0.75).mirrored(isPlayerFacingRight).drawAt(playerPosX, 540); + { + if (not config.useMSAA) + { + if (internalTexture2) + { + internalTexture2.reset(); + return; + } + } + + if (internalTexture2) + { + const Size currentResolution = internalTexture2->size(); + + if (config.resolution == currentResolution) + { + return; + } + } + + internalTexture2.reset(); + internalTexture2 = std::make_unique(config.resolution, DepthFormat); + } } + + [[nodiscard]] + size_t getDepthTextureIndex() const noexcept + { + if (m_config.filter) + { + return (m_config.useMSAA ? 2 : 1); + } + else + { + return 0; + } + } + + [[nodiscard]] + static std::unique_ptr CreateRenderTexture(const TextureState& textureState) + { + if (not textureState.isRequired) + { + return nullptr; + } + + if (textureState.useMSAA) + { + return std::make_unique( + textureState.resolution, + DepthFormat, + HasDepth{ textureState.useDepth }, + HasMipMap{ textureState.useMipMap }); + } + else + { + return std::make_unique( + textureState.resolution, + DepthFormat, + HasDepth{ textureState.useDepth }, + HasMipMap{ textureState.useMipMap }); + } + } + + void updateTextures() + { + if (not m_initialized) + { + return; + } + + const size_t depthTextureIndex = getDepthTextureIndex(); + + std::array requiredTextureStates; + + // [0] + { + requiredTextureStates[0] = + { + .resolution = m_config.resolution, + .isRequired = true, + .useMSAA = m_config.useMSAA, + .useDepth = true, + .useMipMap = (m_config.useMipMap && (depthTextureIndex == 0)), + }; + } + + // [1] + { + requiredTextureStates[1] = + { + .resolution = m_config.resolution, + .isRequired = static_cast(m_config.filter), + .useMSAA = false, + .useDepth = false, + .useMipMap = (m_config.useMipMap && (depthTextureIndex == 1)), + }; + } + + // [2] + { + requiredTextureStates[2] = + { + .resolution = m_config.resolution, + .isRequired = (m_config.filter && m_config.useMSAA), + .useMSAA = false, + .useDepth = false, + .useMipMap = (m_config.useMipMap && (depthTextureIndex == 2)), + }; + } + + for (size_t i = 0; i < 3; ++i) + { + if (m_currentTextureStates[i] == requiredTextureStates[i]) + { + continue; + } + + m_textures[i] = CreateRenderTexture(requiredTextureStates[i]); + m_currentTextureStates[i] = requiredTextureStates[i]; + } + } + + static std::array FromMat4x4(const Mat4x4& mat) + { + std::array result{}; + std::memcpy(result.data(), &mat, (sizeof(float) * 16)); + return result; + } + + static BoxFilterSize ToBoxFilterSize(const size_t filter) + { + if (3 <= filter) + { + return BoxFilterSize::BoxFilter13x13; + } + else if (filter == 2) + { + return BoxFilterSize::BoxFilter9x9; + } + else + { + return BoxFilterSize::BoxFilter5x5; + } + } +}; + +void DrawObjects(double time) +{ + Box::FromPoints({ -8, 0, 5 }, { 0, 0.5, 7 }).draw(); + Box::FromPoints({ -0.5, 0, 5 }, { 0, 1.8, 7 }).draw(); + Box::FromPoints({ -0.5, 1.8, 5 }, { 6, 2.0, 7 }).draw(); + + for (int32 x = -2; x <= 2; ++x) + { + Cylinder{ Vec3{ (x * 4), 0, 4 }, Vec3{ (x * 4), 4, 4 }, 0.3 }.draw(HSV{ x * 80, 0.3, 1.0 }.removeSRGBCurve()); + } + + for (int32 y = 2; y < 8; ++y) + { + Cylinder{ Vec3{ 0, y, -4 }, Vec3{ 4, y, -4 }, 0.1 }.draw(HSV{ 80, 0.3, 1.0 }.removeSRGBCurve()); + } + + Cylinder{ Vec3{ 0, 0, -4 }, Vec3{ 0, 8, -4 }, 0.1 }.draw(HSV{ 80, 0.3, 1.0 }.removeSRGBCurve()); + Cylinder{ Vec3{ 0, 0, -4 }, Vec3{ 0, 0.2, -4 }, 0.5 }.draw(HSV{ 80, 0.3, 1.0 }.removeSRGBCurve()); + Cylinder{ Vec3{ 4, 0, -4 }, Vec3{ 4, 8, -4 }, 0.1 }.draw(HSV{ 80, 0.3, 1.0 }.removeSRGBCurve()); + Cylinder{ Vec3{ 4, 0, -4 }, Vec3{ 4, 0.2, -4 }, 0.5 }.draw(HSV{ 80, 0.3, 1.0 }.removeSRGBCurve()); + Cylinder{ Vec3{ 8, 1.0, -5 }, Vec3{ 8, 9.0, -5 }, 0.1 }.draw(HSV{ 160, 0.3, 1.0 }.removeSRGBCurve()); + Cylinder{ Vec3{ 8, 1.0, -5 }, Vec3{ 8, 1.2, -5 }, 0.5 }.draw(HSV{ 160, 0.3, 1.0 }.removeSRGBCurve()); + Box::FromPoints({ -8, 0, -2 }, { -7, 8, 2 }).draw(); + Box::FromPoints({ -6, 0, -8 }, { -5.5, 6, -4 }).oriented(Quaternion::RotateY(-45_deg)).draw(Linear::Palette::Limegreen); + Box::FromPoints({ 6, 0, -6 }, { 9, 1, -3 }).oriented(Quaternion::RotateY(-45_deg)).draw(Linear::Palette::Skyblue); + + const double sphereY = (2 + Periodic::Jump0_1(1.6s, time) * 4); + Sphere{ Vec3{ 1, sphereY, 0 }, 2.0 }.draw(); + + OrientedBox{ Arg::bottomCenter(6, 0, 0), { 4, 1.5, 0.2 }, Quaternion::RotateY(time * 50_deg) }.draw(); + Cylinder{ Vec3{ 6, 0, 0 }, Vec3{ 6, 2, 0 }, 0.2 }.draw(Linear::Palette::Purple); + + OrientedBox{ Vec3{ -2, 6, -6 }, 1, 8, 1, Quaternion::RollPitchYaw(0, 45_deg, time * 60_deg) }.draw(Linear::Palette::Skyblue); + OrientedBox{ Vec3{ -2, 6, -6 }, 8, 1, 1, Quaternion::RollPitchYaw(0, 45_deg, time * 60_deg) }.draw(Linear::Palette::Skyblue); } -// -// - Debug ビルド: プログラムの最適化を減らす代わりに、エラーやクラッシュ時に詳細な情報を得られます。 -// -// - Release ビルド: 最大限の最適化でビルドします。 -// -// - [デバッグ] メニュー → [デバッグの開始] でプログラムを実行すると、[出力] ウィンドウに詳細なログが表示され、エラーの原因を探せます。 -// -// - Visual Studio を更新した直後は、プログラムのリビルド([ビルド]メニュー → [ソリューションのリビルド])が必要な場合があります。 -// +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 } }; + + // シャドウマップの設定 + ShadowMap::Config config + { + .resolution = Size{ 1024, 1024 }, + .lightBleedingReduction = 0.8, + .useMSAA = false, + .filter = 0, + .useMipMap = false, + }; + + // シャドウマップ用の平行光源設定 + ShadowMap::Light light + { + .focusPos = Vec3{ 0, 0, 0 }, + .viewSize = 22.0, + }; + + // シャドウマップ + ShadowMap shadowMap{ config }; + + // シャドウマップ用のシェーダ + const PixelShader psDepth = HLSL{ U"example/shader/hlsl/default3d_forward_shadow.hlsl", U"Depth_PS" } + | GLSL{ U"example/shader/glsl/default3d_forward_shadow_depth.frag", {{ U"PSPerFrame", 0 }, { U"PSPerView", 1 }, { U"PSPerMaterial", 3 }, { U"PSShadow", 4 }} }; + const PixelShader psShadow = HLSL{ U"example/shader/hlsl/default3d_forward_shadow.hlsl", U"Shading_PS" } + | GLSL{ U"example/shader/glsl/default3d_forward_shadow_shading.frag", {{ U"PSPerFrame", 0 }, { U"PSPerView", 1 }, { U"PSPerMaterial", 3 }, { U"PSShadow", 4 }} }; + if ((not psDepth) || (not psShadow)) + { + throw Error{ U"Failed to load shader" }; + } + + size_t resolutionOption = 4; + double time = 8.0; + bool paused = false; + bool showDepth = true; + bool showUI = true; + + while (System::Update()) + { + if (not paused) + { + time += Scene::DeltaTime(); + } + + camera.update(2.0); + Graphics3D::SetCameraTransform(camera); + + // メイン光源の位置を更新する + double halfDay0_1 = Periodic::Sawtooth0_1(60s, time); + const Quaternion q = (Quaternion::RotateY(halfDay0_1 * 180_deg) * Quaternion::RotateX(50_deg)); + const Vec3 sunDirection = (q * Vec3::Right()); + Graphics3D::SetSunDirection(sunDirection); + Graphics3D::SetGlobalAmbientColor(ColorF{ 0.25 }); + light.sunPos = (light.focusPos + sunDirection * 64.0); + + // デプスパス(シャドウマップの生成) + { + shadowMap.beginDepth(light); + { + const ScopedCustomShader3D shader{ psDepth }; + Plane{ 64 }.draw(); // デプスパスではテクスチャを使用しない + DrawObjects(time); + } + shadowMap.endDepth(); + } + + // シャドウパス(シャドウマップを使用した描画) + { + const ScopedRenderTarget3D target{ renderTexture.clear(backgroundColor) }; + shadowMap.beginShadow(); + { + const ScopedCustomShader3D shader{ psShadow }; + Plane{ 64 }.draw(uvChecker); + DrawObjects(time); + } + shadowMap.endShadow(); + } + + { + Graphics3D::Flush(); + renderTexture.resolve(); + Shader::LinearToScreen(renderTexture); + } + + // UI + if (showUI) + { + if (SimpleGUI::Slider(U"lightBleedingReduction: {:.2f}"_fmt(config.lightBleedingReduction), config.lightBleedingReduction, 0.0, 1.0, Vec2{ 860, 40 }, 280, 120)) + { + shadowMap.setConfig(config); + } + + if (SimpleGUI::RadioButtons(config.filter, { U"none", U"5x5", U"9x9", U"13x13" }, Vec2{ 936, 80 }, 160)) + { + shadowMap.setConfig(config); + } + + if (SimpleGUI::CheckBox(config.useMSAA, U"MSAA", Vec2{ 1100, 80 }, 160)) + { + shadowMap.setConfig(config); + } + + if (SimpleGUI::CheckBox(config.useMipMap, U"MipMap", Vec2{ 1100, 120 }, 160)) + { + shadowMap.setConfig(config); + } + + if (SimpleGUI::RadioButtons(resolutionOption, { U"256x256", U"256x512", U"512x512", U"512x1024", U"1024x1024", U"1024x2048", U"2048x2048", U"2048x4096", U"4096x4096" }, Vec2{ 1100, 160 }, 160)) + { + config.resolution = + (resolutionOption == 0) ? Size{ 256, 256 } + : (resolutionOption == 1) ? Size{ 256, 512 } + : (resolutionOption == 2) ? Size{ 512, 512 } + : (resolutionOption == 3) ? Size{ 512, 1024 } + : (resolutionOption == 4) ? Size{ 1024, 1024 } + : (resolutionOption == 5) ? Size{ 1024, 2048 } + : (resolutionOption == 6) ? Size{ 2048, 2048 } + : (resolutionOption == 7) ? Size{ 2048, 4096 } + : Size{ 4096, 4096 }; + shadowMap.setConfig(config); + } + + SimpleGUI::CheckBox(showDepth, U"Show depth", Vec2{ 1100, 520 }, 160); + SimpleGUI::CheckBox(paused, U"Pause", Vec2{ 1100, 560 }, 160); + + if (showDepth) + { + shadowMap.getDepthTexture().resized(512, 512).draw(0, 0, ColorF{ 1 / 100.0, 1 / 10000.0, 0.0 }); + + PutText(U"sun {:.1f}, focus {:.1f}, {}x{}"_fmt(light.sunPos, light.focusPos, + shadowMap.getDepthTexture().width(), shadowMap.getDepthTexture().height()), Arg::bottomRight(506, 510)); + + SimpleGUI::Slider(U"viewSize: {:.1f}"_fmt(light.viewSize), light.viewSize, 4.0, 100.0, Vec2{ 180, 640 }, 150.0, 240); + + if (SimpleGUI::Button(U"←", Vec2{ 40, 600 }, 40)) + { + light.focusPos.x -= 1.0; + } + + if (SimpleGUI::Button(U"→", Vec2{ 120, 600 }, 40)) + { + light.focusPos.x += 1.0; + } + + if (SimpleGUI::Button(U"↑", Vec2{ 80, 560 }, 40)) + { + light.focusPos.z += 1.0; + } + + if (SimpleGUI::Button(U"↓", Vec2{ 80, 640 }, 40)) + { + light.focusPos.z -= 1.0; + } + } + } + } +} diff --git a/macOS/App/example/shader/glsl/default3d_forward_shadow_depth.frag b/macOS/App/example/shader/glsl/default3d_forward_shadow_depth.frag new file mode 100644 index 000000000..b0397e2fe --- /dev/null +++ b/macOS/App/example/shader/glsl/default3d_forward_shadow_depth.frag @@ -0,0 +1,68 @@ +// Copyright (c) 2008-2023 Ryo Suzuki. +// Copyright (c) 2016-2023 OpenSiv3D Project. +// Licensed under the MIT License. + +# version 410 + +// +// Textures +// +uniform sampler2D Texture0; + +// +// PSInput +// +layout(location = 0) in vec3 WorldPosition; +layout(location = 1) in vec2 UV; +layout(location = 2) in vec3 Normal; + +// +// PSOutput +// +layout(location = 0) out vec2 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; +}; + +//////////////////////////////////////////////////////////// +// +// Depth +// + +layout(std140) uniform PSShadow // slot 4 +{ + mat4x4 g_worldToProjectedShadow; + vec3 g_sunPosition; + float g_lightBleedingReduction; +}; + +void main() +{ + float depth = length(g_sunPosition - WorldPosition); + FragColor = vec2(depth, (depth * depth)); +} + +// +//////////////////////////////////////////////////////////// diff --git a/macOS/App/example/shader/glsl/default3d_forward_shadow_shading.frag b/macOS/App/example/shader/glsl/default3d_forward_shadow_shading.frag new file mode 100644 index 000000000..09cd1ff16 --- /dev/null +++ b/macOS/App/example/shader/glsl/default3d_forward_shadow_shading.frag @@ -0,0 +1,150 @@ +// Copyright (c) 2008-2023 Ryo Suzuki. +// Copyright (c) 2016-2023 OpenSiv3D Project. +// Licensed under the MIT License. + +# version 410 + +// +// Textures +// +uniform sampler2D Texture0; + +// +// 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; +}; + +// +// 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); +} + +//////////////////////////////////////////////////////////// +// +// Shading +// + +uniform sampler2D Texture1; + +layout(std140) uniform PSShadow // slot 4 +{ + mat4x4 g_worldToProjectedShadow; + vec3 g_sunPosition; + float g_lightBleedingReduction; +}; + +float LineStep(float min, float max, float value) +{ + return clamp((value - min) / (max - min), 0.0, 1.0); +} + +float ReduceLightBleeding(float p_max, float amount) +{ + return LineStep(amount, 1.0, p_max); +} + +float CalculateShadow(vec3 worldPosition) +{ + vec4 projectedPosition = (vec4(worldPosition, 1.0) * g_worldToProjectedShadow); + + if (any(greaterThan(abs(projectedPosition.xyz - clamp(projectedPosition.xyz, 0.0, 1.0)), vec3(0.0)))) + { + return 1.0; + } + + vec2 uv = vec2(projectedPosition.x, (1.0 - projectedPosition.y)); + float depth = (length(g_sunPosition - worldPosition) - 0.03125); + vec2 moments = texture(Texture1, uv).rg; + + if (depth <= moments.x) + { + return 1.0; + } + + float variance = (moments.y - (moments.x * moments.x)); + float d = (moments.x - depth); + float lit = (variance / (variance + (d * d))); + + return ReduceLightBleeding(lit, g_lightBleedingReduction); +} + +void main() +{ + // Shadow + float lit = CalculateShadow(WorldPosition); + + vec3 lightColor = (g_sunColor * lit); + vec3 lightDirection = g_sunDirection; + + vec3 n = normalize(Normal); + vec3 l = lightDirection; + vec4 diffuseColor = GetDiffuseColor(UV); + vec3 ambientColor = (g_ambientColor * g_globalAmbientColor); + + // Diffuse + vec3 diffuseReflection = CalculateDiffuseReflection(n, l, lightColor, diffuseColor.rgb, ambientColor); + + // Specular + vec3 v = normalize(g_eyePosition - WorldPosition); + vec3 h = normalize(v + lightDirection); + vec3 specularReflection = CalculateSpecularReflection(n, h, g_shininess, dot(n, l), lightColor, g_specularColor); + + FragColor = vec4(diffuseReflection + specularReflection + g_emissionColor, diffuseColor.a); +} + +// +//////////////////////////////////////////////////////////// diff --git a/macOS/App/example/shader/hlsl/default3d_forward_shadow.hlsl b/macOS/App/example/shader/hlsl/default3d_forward_shadow.hlsl new file mode 100644 index 000000000..9bc92b6ec --- /dev/null +++ b/macOS/App/example/shader/hlsl/default3d_forward_shadow.hlsl @@ -0,0 +1,166 @@ +//----------------------------------------------- +// +// 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 Output / PS Input + // + struct PSInput + { + float4 position : SV_POSITION; + float3 worldPosition : TEXCOORD0; + float2 uv : TEXCOORD1; + float3 normal : TEXCOORD2; + }; +} + +// +// Constant Buffer +// +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; +} + +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); +} + +//////////////////////////////////////////////////////////// +// +// Depth +// +cbuffer PSShadow : register(b4) +{ + row_major float4x4 g_worldToProjectedShadow; + float3 g_sunPosition; + float g_lightBleedingReduction; +} + +float2 Depth_PS(s3d::PSInput input) : SV_TARGET +{ + const float depth = length(g_sunPosition - input.worldPosition); + return float2(depth, (depth * depth)); +} +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// +// Shading +// +Texture2D g_texture1 : register(t1); +SamplerState g_sampler1 : register(s1); + +float LineStep(float min, float max, float value) +{ + return saturate((value - min) / (max - min)); +} + +float ReduceLightBleeding(float p_max, float amount) +{ + return LineStep(amount, 1.0, p_max); +} + +float CalculateShadow(float3 worldPosition) +{ + const float4 projectedPosition = mul(float4(worldPosition, 1.0), g_worldToProjectedShadow); + + if (any(saturate(projectedPosition.xyz) != projectedPosition.xyz)) + { + return 1.0; + } + + const float2 uv = float2(projectedPosition.x, (1.0 - projectedPosition.y)); + const float depth = (length(g_sunPosition - worldPosition) - 0.03125); + const float2 moments = g_texture1.Sample(g_sampler1, uv).rg; + + if (depth <= moments.x) + { + return 1.0; + } + + const float variance = (moments.y - (moments.x * moments.x)); + const float d = (moments.x - depth); + const float lit = (variance / (variance + (d * d))); + + return ReduceLightBleeding(lit, g_lightBleedingReduction); +} + +float4 Shading_PS(s3d::PSInput input) : SV_TARGET +{ + // Shadow + const float lit = CalculateShadow(input.worldPosition); + + const float3 lightColor = (g_sunColor * lit); + const float3 lightDirection = g_sunDirection; + + const float3 n = normalize(input.normal); + const float3 l = lightDirection; + float4 diffuseColor = GetDiffuseColor(input.uv); + const float3 ambientColor = (g_ambientColor * g_globalAmbientColor); + + // Diffuse + const float3 diffuseReflection = CalculateDiffuseReflection(n, l, lightColor, diffuseColor.rgb, ambientColor); + + // 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); +} +// +////////////////////////////////////////////////////////////