-
-
Save Reputeless/61c4948459409f548a5622e7863b6a40 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # include <Siv3D.hpp> // Siv3D v0.6.13 | |
| class ShadowMap | |
| { | |
| public: | |
| struct Config | |
| { | |
| Size resolution = Size{ 1024, 1024 }; | |
| double lightBleedingReduction = 0.8; | |
| bool useMSAA = false; | |
| bool blur = false; | |
| bool useMipMap = false; | |
| }; | |
| struct Light | |
| { | |
| Vec3 focusPos{ 0, 0, 0 }; | |
| Vec3 sunPos = (focusPos + Graphics3D::DefaultSunDirection * 64.0); | |
| Vec3 upDirection{ 0, 1, 0 }; | |
| double viewSize = 64.0; | |
| double nearZ = 1.0; | |
| double farZ = 1024.0; | |
| }; | |
| ShadowMap() = default; | |
| explicit ShadowMap(const Config& config) | |
| : m_config{ config } | |
| , m_initialized{ true } | |
| { | |
| updateTextures(); | |
| } | |
| 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 = Graphics3D::GetCameraTransform(); | |
| m_depthPass->oldEyePosition = Graphics3D::GetEyePosition(); | |
| m_depthPass->renderTarget = std::make_unique<ScopedRenderTarget3D>(m_textures[0]->clear(ColorF{ 0.0 })); | |
| m_depthPass->renderStates = std::make_unique<ScopedRenderStates3D>(BlendState::Opaque); | |
| m_camera.update(light); | |
| Graphics3D::SetCameraTransform(m_camera.viewProj, light.sunPos); | |
| m_cbp->worldToShadowMap = (m_camera.viewProj * Mat4x4::Scale(Float3{ 0.5, 0.5, 1.0 }) * Mat4x4::Translate(Float3{ 0.5, 0.5, 0.0 })); | |
| m_cbp->sunPosition = light.sunPos; | |
| m_cbp->lightBleedingReduction = static_cast<float>(m_config.lightBleedingReduction); | |
| Graphics3D::SetPSConstantBuffer(4, m_cbp); | |
| } | |
| 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(m_depthPass->oldCameraMat, m_depthPass->oldEyePosition); | |
| m_depthPass.reset(); | |
| Graphics3D::Flush(); | |
| if (m_config.useMSAA) | |
| { | |
| dynamic_cast<MSRenderTexture*>(m_textures[0].get())->resolve(); | |
| } | |
| if (m_config.blur) | |
| { | |
| if (m_config.useMSAA) | |
| { | |
| Shader::GaussianBlur(*m_textures[0], *m_textures[1], *m_textures[2]); | |
| } | |
| else | |
| { | |
| Shader::GaussianBlur(*m_textures[0], *m_textures[1], *m_textures[0]); | |
| } | |
| Graphics2D::Flush(); | |
| } | |
| if (m_config.useMipMap) | |
| { | |
| getDepthTexture().generateMips(); | |
| } | |
| } | |
| void beginShadow(const Light& light) | |
| { | |
| 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>(ScopedRenderStates3D::SamplerStateInfo{ ShaderStage::Pixel, 3, (m_config.useMipMap ? SamplerState::ClampAniso : SamplerState::ClampLinear) }); | |
| Graphics3D::SetPSTexture(3, getDepthTexture()); | |
| Graphics3D::SetPSConstantBuffer(4, m_cbp); | |
| } | |
| void endShadow() | |
| { | |
| if (not m_shadowPass) | |
| { | |
| throw Error{ U"ShadowMap::endShadow() called without beginShadow()" }; | |
| } | |
| if (m_depthPass) | |
| { | |
| throw Error{ U"ShadowMap::endShadow() called while depth pass is active" }; | |
| } | |
| Graphics3D::SetPSTexture(3, none); | |
| m_shadowPass.reset(); | |
| } | |
| void setConfig(const Config& config) | |
| { | |
| if (m_depthPass || m_shadowPass) | |
| { | |
| throw Error{ U"ShadowMap::setConfig() called while depth or shadow pass is active" }; | |
| } | |
| m_config = config; | |
| updateTextures(); | |
| } | |
| [[nodiscard]] | |
| const RenderTexture& getDepthTexture() const noexcept | |
| { | |
| return *m_textures[getDepthTextureIndex()]; | |
| } | |
| private: | |
| struct TextureState | |
| { | |
| Size resolution{ 0, 0 }; | |
| bool isRequired = false; | |
| bool useMSAA = false; | |
| bool useDepth = false; | |
| bool useMipMap = false; | |
| [[nodiscard]] | |
| bool operator ==(const TextureState&) const = default; | |
| }; | |
| struct DepthCamera | |
| { | |
| Mat4x4 viewProj = Mat4x4::Identity(); | |
| void update(const Light& light) | |
| { | |
| 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<float>(light.viewSize); | |
| const Mat4x4 proj = DirectX::XMMatrixOrthographicLH(viewF, viewF, static_cast<float>(light.farZ), static_cast<float>(light.nearZ)); | |
| viewProj = (view * proj); | |
| } | |
| }; | |
| struct alignas(16) DepthPass | |
| { | |
| Mat4x4 oldCameraMat = Mat4x4::Identity(); | |
| Float3 oldEyePosition{ 0, 0, 0 }; | |
| std::unique_ptr<ScopedRenderTarget3D> renderTarget; | |
| std::unique_ptr<ScopedRenderStates3D> renderStates; | |
| }; | |
| struct ShadowPass | |
| { | |
| std::unique_ptr<ScopedRenderStates3D> pRenderStates; | |
| }; | |
| struct PSShadow | |
| { | |
| Mat4x4 worldToShadowMap; | |
| Float3 sunPosition{ 0,0,0 }; | |
| float lightBleedingReduction = 0.8f; | |
| }; | |
| static constexpr TextureFormat DepthFormat = TextureFormat::R32G32_Float; | |
| Config m_config; | |
| std::array<TextureState, 3> m_currentTextureStates; | |
| std::array<std::unique_ptr<RenderTexture>, 3> m_textures; | |
| DepthCamera m_camera; | |
| Optional<DepthPass> m_depthPass; | |
| Optional<ShadowPass> m_shadowPass; | |
| ConstantBuffer<PSShadow> m_cbp; | |
| bool m_initialized = false; | |
| static void CreateDepthTexture(std::unique_ptr<RenderTexture>& depthTexture, const Config& config) | |
| { | |
| if (depthTexture) | |
| { | |
| const bool currentMSAA = (dynamic_cast<const MSRenderTexture*>(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<MSRenderTexture>(config.resolution, DepthFormat, HasDepth::Yes, HasMipMap{config.useMipMap}); | |
| } | |
| else | |
| { | |
| depthTexture = std::make_unique<RenderTexture>(config.resolution, DepthFormat, HasDepth::Yes, HasMipMap{ config.useMipMap }); | |
| } | |
| } | |
| static void CreateInternalTexture(std::unique_ptr<RenderTexture>& internalTexture1, std::unique_ptr<RenderTexture>& internalTexture2, const Config& config) | |
| { | |
| { | |
| if (internalTexture1) | |
| { | |
| const Size currentResolution = internalTexture1->size(); | |
| if (config.resolution == currentResolution) | |
| { | |
| return; | |
| } | |
| } | |
| internalTexture1.reset(); | |
| internalTexture1 = std::make_unique<RenderTexture>(config.resolution, DepthFormat); | |
| } | |
| { | |
| 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<RenderTexture>(config.resolution, DepthFormat); | |
| } | |
| } | |
| [[nodiscard]] | |
| size_t getDepthTextureIndex() const noexcept | |
| { | |
| if (m_config.blur) | |
| { | |
| return (m_config.useMSAA ? 2 : 1); | |
| } | |
| else | |
| { | |
| return 0; | |
| } | |
| } | |
| [[nodiscard]] | |
| static std::unique_ptr<RenderTexture> CreateRenderTexture(const TextureState& textureState) | |
| { | |
| if (not textureState.isRequired) | |
| { | |
| return nullptr; | |
| } | |
| if (textureState.useMSAA) | |
| { | |
| return std::make_unique<MSRenderTexture>( | |
| textureState.resolution, | |
| DepthFormat, | |
| HasDepth{ textureState.useDepth }, | |
| HasMipMap{ textureState.useMipMap }); | |
| } | |
| else | |
| { | |
| return std::make_unique<RenderTexture>( | |
| textureState.resolution, | |
| DepthFormat, | |
| HasDepth{ textureState.useDepth }, | |
| HasMipMap{ textureState.useMipMap }); | |
| } | |
| } | |
| void updateTextures() | |
| { | |
| if (not m_initialized) | |
| { | |
| return; | |
| } | |
| const size_t depthTextureIndex = getDepthTextureIndex(); | |
| std::array<TextureState, 3> 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 = m_config.blur, | |
| .useMSAA = false, | |
| .useDepth = false, | |
| .useMipMap = (m_config.useMipMap && (depthTextureIndex == 1)), | |
| }; | |
| } | |
| // [2] | |
| { | |
| requiredTextureStates[2] = | |
| { | |
| .resolution = m_config.resolution, | |
| .isRequired = (m_config.blur && 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]; | |
| } | |
| } | |
| }; | |
| 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); | |
| } | |
| 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 } }; | |
| size_t resolutionOption = 4; | |
| ShadowMap::Config config | |
| { | |
| .resolution = Size{ 1024, 1024 }, | |
| .lightBleedingReduction = 0.8, | |
| .useMSAA = false, | |
| .blur = false, | |
| .useMipMap = false, | |
| }; | |
| ShadowMap::Light light | |
| { | |
| .focusPos = Vec3{ 0, 0, 0 }, | |
| .viewSize = 22.0, | |
| }; | |
| ShadowMap shadowMap{ config }; | |
| const PixelShader psDepth = HLSL{ U"shadow.hlsl", U"Depth_PS" }; | |
| const PixelShader psShadow = HLSL{ U"shadow.hlsl", U"Shading_PS" }; | |
| double time = 8.0; | |
| bool paused = true; | |
| 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(); | |
| } | |
| // 3D scene | |
| { | |
| const ScopedRenderTarget3D target{ renderTexture.clear(backgroundColor) }; | |
| shadowMap.beginShadow(light); | |
| { | |
| const ScopedCustomShader3D shader{ psShadow }; | |
| Plane{ 64 }.draw(uvChecker); | |
| DrawObjects(time); | |
| } | |
| shadowMap.endShadow(); | |
| } | |
| { | |
| Graphics3D::Flush(); | |
| renderTexture.resolve(); | |
| Shader::LinearToScreen(renderTexture); | |
| } | |
| if (showUI) | |
| { | |
| //Print << U"shadowCenter: {:.1f}"_fmt(shadowParameter.shadowCenter); | |
| 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::CheckBox(config.useMSAA, U"MSAA", Vec2{ 1100, 80 }, 160)) | |
| { | |
| shadowMap.setConfig(config); | |
| } | |
| if (SimpleGUI::CheckBox(config.blur, U"blur", Vec2{ 1100, 120 }, 160)) | |
| { | |
| shadowMap.setConfig(config); | |
| } | |
| if (SimpleGUI::CheckBox(config.useMipMap, U"MipMap", Vec2{ 1100, 160 }, 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, 200 }, 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, 560 }, 160); | |
| SimpleGUI::CheckBox(paused, U"Pause", Vec2{ 1100, 600 }, 160); | |
| if (showDepth) | |
| { | |
| shadowMap.getDepthTexture().resized(512, 512).draw(0, 0, ColorF{ 1 / 100.0, 1 / 10000.0, 0.0 }); | |
| PutText(U"{}x{}"_fmt(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; | |
| } | |
| } | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //----------------------------------------------- | |
| // | |
| // 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_texture3 : register(t3); | |
| SamplerState g_sampler3 : register(s3); | |
| 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_texture3.Sample(g_sampler3, 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); | |
| } | |
| // | |
| //////////////////////////////////////////////////////////// |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment