diff --git a/CMakeLists.txt b/CMakeLists.txt index 122a66f8..4ed762df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,6 +151,7 @@ set(LIBRARY_SOURCES Src/ModelLoadSDKMESH.cpp Src/ModelLoadVBO.cpp Src/NormalMapEffect.cpp + Src/NPREffect.cpp Src/PBREffect.cpp Src/PBREffectFactory.cpp Src/pch.h @@ -172,6 +173,7 @@ set(SHADER_SOURCES Src/Shaders/EnvironmentMapEffect.fx Src/Shaders/GenerateMips.hlsl Src/Shaders/NormalMapEffect.fx + Src/Shaders/NPREffect.fx Src/Shaders/PBREffect.fx Src/Shaders/PostProcess.fx Src/Shaders/RootSig.fxh diff --git a/DirectXTK_Desktop_2022_Win10.vcxproj b/DirectXTK_Desktop_2022_Win10.vcxproj index 89db95de..4965e855 100644 --- a/DirectXTK_Desktop_2022_Win10.vcxproj +++ b/DirectXTK_Desktop_2022_Win10.vcxproj @@ -111,6 +111,7 @@ + @@ -193,6 +194,11 @@ Document + + + Document + + {3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0} Win32Proj diff --git a/DirectXTK_Desktop_2022_Win10.vcxproj.filters b/DirectXTK_Desktop_2022_Win10.vcxproj.filters index 1ec33539..5116af65 100644 --- a/DirectXTK_Desktop_2022_Win10.vcxproj.filters +++ b/DirectXTK_Desktop_2022_Win10.vcxproj.filters @@ -281,6 +281,9 @@ Src + + Src + Src @@ -376,6 +379,9 @@ Src\Shaders + + Src\Shaders + Src\Shaders\Shared diff --git a/DirectXTK_Desktop_2026.vcxproj b/DirectXTK_Desktop_2026.vcxproj index fd498f0d..5704fd65 100644 --- a/DirectXTK_Desktop_2026.vcxproj +++ b/DirectXTK_Desktop_2026.vcxproj @@ -111,6 +111,7 @@ + @@ -193,6 +194,11 @@ Document + + + Document + + {3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0} Win32Proj diff --git a/DirectXTK_Desktop_2026.vcxproj.filters b/DirectXTK_Desktop_2026.vcxproj.filters index 1ec33539..5116af65 100644 --- a/DirectXTK_Desktop_2026.vcxproj.filters +++ b/DirectXTK_Desktop_2026.vcxproj.filters @@ -281,6 +281,9 @@ Src + + Src + Src @@ -376,6 +379,9 @@ Src\Shaders + + Src\Shaders + Src\Shaders\Shared diff --git a/DirectXTK_GDKW_2022.vcxproj b/DirectXTK_GDKW_2022.vcxproj index bf83e1a1..d83bf563 100644 --- a/DirectXTK_GDKW_2022.vcxproj +++ b/DirectXTK_GDKW_2022.vcxproj @@ -438,6 +438,7 @@ + @@ -511,6 +512,11 @@ Document + + + Document + + diff --git a/DirectXTK_GDKW_2022.vcxproj.filters b/DirectXTK_GDKW_2022.vcxproj.filters index cb5d5384..8a878fbe 100644 --- a/DirectXTK_GDKW_2022.vcxproj.filters +++ b/DirectXTK_GDKW_2022.vcxproj.filters @@ -260,6 +260,9 @@ Src + + Src + Src @@ -346,6 +349,9 @@ Src\Shaders + + Src\Shaders + Src\Shaders diff --git a/DirectXTK_GDKX_2022.vcxproj b/DirectXTK_GDKX_2022.vcxproj index fef34f47..60aec220 100644 --- a/DirectXTK_GDKX_2022.vcxproj +++ b/DirectXTK_GDKX_2022.vcxproj @@ -612,6 +612,7 @@ + @@ -693,6 +694,11 @@ Document + + + Document + + diff --git a/DirectXTK_GDKX_2022.vcxproj.filters b/DirectXTK_GDKX_2022.vcxproj.filters index b3e20206..efb30072 100644 --- a/DirectXTK_GDKX_2022.vcxproj.filters +++ b/DirectXTK_GDKX_2022.vcxproj.filters @@ -266,6 +266,9 @@ Src + + Src + Src @@ -355,6 +358,9 @@ Src\Shaders + + Src\Shaders + Src\Shaders diff --git a/DirectXTK_GDKX_2026.vcxproj b/DirectXTK_GDKX_2026.vcxproj index 60e1b528..9f85832f 100644 --- a/DirectXTK_GDKX_2026.vcxproj +++ b/DirectXTK_GDKX_2026.vcxproj @@ -612,6 +612,7 @@ + @@ -693,6 +694,11 @@ Document + + + Document + + diff --git a/DirectXTK_GDKX_2026.vcxproj.filters b/DirectXTK_GDKX_2026.vcxproj.filters index b3e20206..efb30072 100644 --- a/DirectXTK_GDKX_2026.vcxproj.filters +++ b/DirectXTK_GDKX_2026.vcxproj.filters @@ -266,6 +266,9 @@ Src + + Src + Src @@ -355,6 +358,9 @@ Src\Shaders + + Src\Shaders + Src\Shaders diff --git a/DirectXTK_GDK_2022.vcxproj b/DirectXTK_GDK_2022.vcxproj index 1542f005..fdbd09bf 100644 --- a/DirectXTK_GDK_2022.vcxproj +++ b/DirectXTK_GDK_2022.vcxproj @@ -597,6 +597,7 @@ + @@ -678,6 +679,11 @@ Document + + + Document + + diff --git a/DirectXTK_GDK_2022.vcxproj.filters b/DirectXTK_GDK_2022.vcxproj.filters index b3e20206..efb30072 100644 --- a/DirectXTK_GDK_2022.vcxproj.filters +++ b/DirectXTK_GDK_2022.vcxproj.filters @@ -266,6 +266,9 @@ Src + + Src + Src @@ -355,6 +358,9 @@ Src\Shaders + + Src\Shaders + Src\Shaders diff --git a/DirectXTK_Windows10_2022.vcxproj b/DirectXTK_Windows10_2022.vcxproj index 473be188..6a19fdd2 100644 --- a/DirectXTK_Windows10_2022.vcxproj +++ b/DirectXTK_Windows10_2022.vcxproj @@ -124,6 +124,7 @@ + @@ -195,6 +196,11 @@ Document + + + Document + + {945B8F0E-AE5F-447C-933A-9D069532D3E4} StaticLibrary diff --git a/DirectXTK_Windows10_2022.vcxproj.filters b/DirectXTK_Windows10_2022.vcxproj.filters index 8bfddbfc..9f01fe06 100644 --- a/DirectXTK_Windows10_2022.vcxproj.filters +++ b/DirectXTK_Windows10_2022.vcxproj.filters @@ -221,6 +221,9 @@ Src\Shaders + + Src\Shaders + Src\Shaders\Shared @@ -350,6 +353,9 @@ Src + + Src + Src diff --git a/Inc/Effects.h b/Inc/Effects.h index aa0343d5..1493f870 100644 --- a/Inc/Effects.h +++ b/Inc/Effects.h @@ -751,6 +751,78 @@ namespace DirectX }; + //------------------------------------------------------------------------------ + // Built-in shader for non-photorealistic rendering (cel shading, Gooch shading). + class NPREffect : public IEffect, public IEffectMatrices, public IEffectLights + { + public: + enum Mode : uint32_t + { + Mode_Cel = 0, // Cel (toon) shading + Mode_Gooch, // Gooch shading + }; + + DIRECTX_TOOLKIT_API NPREffect( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + Mode nprMode = Mode_Cel); + + DIRECTX_TOOLKIT_API NPREffect(NPREffect&&) noexcept; + DIRECTX_TOOLKIT_API NPREffect& operator= (NPREffect&&) noexcept; + + NPREffect(NPREffect const&) = delete; + NPREffect& operator= (NPREffect const&) = delete; + + DIRECTX_TOOLKIT_API ~NPREffect() override; + + // IEffect methods. + DIRECTX_TOOLKIT_API void __cdecl Apply(_In_ ID3D12GraphicsCommandList* commandList) override; + + // Camera settings. + DIRECTX_TOOLKIT_API void XM_CALLCONV SetWorld(FXMMATRIX value) override; + DIRECTX_TOOLKIT_API void XM_CALLCONV SetView(FXMMATRIX value) override; + DIRECTX_TOOLKIT_API void XM_CALLCONV SetProjection(FXMMATRIX value) override; + DIRECTX_TOOLKIT_API void XM_CALLCONV SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) override; + + // Material settings. + DIRECTX_TOOLKIT_API void XM_CALLCONV SetDiffuseColor(FXMVECTOR value); + DIRECTX_TOOLKIT_API void XM_CALLCONV SetSpecularColor(FXMVECTOR value); + DIRECTX_TOOLKIT_API void __cdecl SetSpecularPower(float value); + DIRECTX_TOOLKIT_API void __cdecl DisableSpecular(); + DIRECTX_TOOLKIT_API void __cdecl SetAlpha(float value); + DIRECTX_TOOLKIT_API void XM_CALLCONV SetColorAndAlpha(FXMVECTOR value); + + // Light settings. + void XM_CALLCONV SetLightDirection(int whichLight, FXMVECTOR value) override; + DIRECTX_TOOLKIT_API void __cdecl EnableDefaultLighting() override; + + static constexpr int MaxDirectionalLights = 1; + + // Texture settings. + // TODO: Implement texture settings. + + // Cel shading settings. + DIRECTX_TOOLKIT_API void __cdecl SetCelShaderBands(int bands); + + // Gooch shading settings. + DIRECTX_TOOLKIT_API void XM_CALLCONV SetGoochCoolColor(FXMVECTOR value, float alpha = 0.25f); + DIRECTX_TOOLKIT_API void XM_CALLCONV SetGoochWarmColor(FXMVECTOR value, float beta = 0.25f); + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + + // Unsupported interface methods. + DIRECTX_TOOLKIT_API void XM_CALLCONV SetAmbientLightColor(FXMVECTOR value) override; + DIRECTX_TOOLKIT_API void __cdecl SetLightEnabled(int whichLight, bool value) override; + DIRECTX_TOOLKIT_API void XM_CALLCONV SetLightDiffuseColor(int whichLight, FXMVECTOR value) override; + DIRECTX_TOOLKIT_API void XM_CALLCONV SetLightSpecularColor(int whichLight, FXMVECTOR value) override; + }; + + //------------------------------------------------------------------------------ // Abstract interface to factory texture resources class DIRECTX_TOOLKIT_API IEffectTextureFactory diff --git a/Src/NPREffect.cpp b/Src/NPREffect.cpp new file mode 100644 index 00000000..104b79ed --- /dev/null +++ b/Src/NPREffect.cpp @@ -0,0 +1,567 @@ +//-------------------------------------------------------------------------------------- +// File: NPREffect.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// https://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "EffectCommon.h" + +using namespace DirectX; + +namespace +{ + // Constant buffer layout. Must match the shader! + struct NPREffectConstants + { + XMVECTOR lightDirectionAndCelBands; + XMVECTOR diffuseColorAndAlpha; + XMVECTOR specularColorAndSpecularPower; + XMVECTOR goochCoolColorAndAlpha; + XMVECTOR goochWarmColorAndBeta; + XMVECTOR eyePosition; + XMMATRIX world; + XMVECTOR worldInverseTranspose[3]; + XMMATRIX worldViewProj; + }; + + static_assert((sizeof(NPREffectConstants) % 16) == 0, "CB size not padded correctly"); + + + // Traits type describes our characteristics to the EffectBase template. + struct NPREffectTraits + { + using ConstantBufferType = NPREffectConstants; + + static constexpr int VertexShaderCount = 8; + static constexpr int PixelShaderCount = 2; + static constexpr int ShaderPermutationCount = 16; + static constexpr int RootSignatureCount = 1; + }; + + + // Default values + constexpr XMVECTORF32 s_defaultLightDir = { { { 0.f, -1.f, 0.f, 4.f } } }; + constexpr XMVECTORF32 s_defaultDiffuse = { { { 1.f, 1.f, 1.f, 1.f } } }; + constexpr XMVECTORF32 s_defaultSpecular = { { { 1.f, 1.f, 1.f, 32.f } } }; + constexpr XMVECTORF32 s_defaultCool = { { { 0.f, 0.f, 0.55f, 0.25f } } }; + constexpr XMVECTORF32 s_defaultWarm = { { { 0.3f, 0.3f, 0.f, 0.25f } } }; +} + + +// Internal NPREffect implementation class. +class NPREffect::Impl : public EffectBase +{ +public: + Impl(_In_ ID3D12Device* device, uint32_t effectFlags, const EffectPipelineStateDescription& pipelineDescription, + NPREffect::Mode nprMode); + + Impl(const Impl&) = delete; + Impl& operator=(const Impl&) = delete; + + Impl(Impl&&) = default; + Impl& operator=(Impl&&) = default; + + enum RootParameterIndex + { + ConstantBuffer, + RootParameterCount + }; + + int GetPipelineStatePermutation(NPREffect::Mode nprMode, uint32_t effectFlags) const noexcept; + + void Apply(_In_ ID3D12GraphicsCommandList* commandList); +}; + + +#pragma region Shaders +// Include the precompiled shader code. +namespace +{ +#ifdef _GAMING_XBOX_SCARLETT +#include "XboxGamingScarlettNPREffect_VSNPREffect.inc" +#include "XboxGamingScarlettNPREffect_VSNPREffectInst.inc" + +#include "XboxGamingScarlettNPREffect_VSNPREffectVc.inc" +#include "XboxGamingScarlettNPREffect_VSNPREffectVcInst.inc" + +#include "XboxGamingScarlettNPREffect_VSNPREffectBn.inc" +#include "XboxGamingScarlettNPREffect_VSNPREffectBnInst.inc" + +#include "XboxGamingScarlettNPREffect_VSNPREffectVcBn.inc" +#include "XboxGamingScarlettNPREffect_VSNPREffectVcBnInst.inc" + +#include "XboxGamingScarlettNPREffect_PSCelShading.inc" +#include "XboxGamingScarlettNPREffect_PSGoochShading.inc" +#elif defined(_GAMING_XBOX) +#include "XboxGamingXboxOneNPREffect_VSNPREffect.inc" +#include "XboxGamingXboxOneNPREffect_VSNPREffectInst.inc" + +#include "XboxGamingXboxOneNPREffect_VSNPREffectVc.inc" +#include "XboxGamingXboxOneNPREffect_VSNPREffectVcInst.inc" + +#include "XboxGamingXboxOneNPREffect_VSNPREffectBn.inc" +#include "XboxGamingXboxOneNPREffect_VSNPREffectBnInst.inc" + +#include "XboxGamingXboxOneNPREffect_VSNPREffectVcBn.inc" +#include "XboxGamingXboxOneNPREffect_VSNPREffectVcBnInst.inc" + +#include "XboxGamingXboxOneNPREffect_PSCelShading.inc" +#include "XboxGamingXboxOneNPREffect_PSGoochShading.inc" +#elif defined(_XBOX_ONE) && defined(_TITLE) +#include "XboxOneNPREffect_VSNPREffect.inc" +#include "XboxOneNPREffect_VSNPREffectInst.inc" + +#include "XboxOneNPREffect_VSNPREffectVc.inc" +#include "XboxOneNPREffect_VSNPREffectVcInst.inc" + +#include "XboxOneNPREffect_VSNPREffectBn.inc" +#include "XboxOneNPREffect_VSNPREffectBnInst.inc" + +#include "XboxOneNPREffect_VSNPREffectVcBn.inc" +#include "XboxOneNPREffect_VSNPREffectVcBnInst.inc" + +#include "XboxOneNPREffect_PSCelShading.inc" +#include "XboxOneNPREffect_PSGoochShading.inc" +#else +#include "NPREffect_VSNPREffect.inc" +#include "NPREffect_VSNPREffectInst.inc" + +#include "NPREffect_VSNPREffectVc.inc" +#include "NPREffect_VSNPREffectVcInst.inc" + +#include "NPREffect_VSNPREffectBn.inc" +#include "NPREffect_VSNPREffectBnInst.inc" + +#include "NPREffect_VSNPREffectVcBn.inc" +#include "NPREffect_VSNPREffectVcBnInst.inc" + +#include "NPREffect_PSCelShading.inc" +#include "NPREffect_PSGoochShading.inc" +#endif +} + + +template<> +const D3D12_SHADER_BYTECODE EffectBase::VertexShaderBytecode[] = +{ + { NPREffect_VSNPREffect, sizeof(NPREffect_VSNPREffect) }, + { NPREffect_VSNPREffectVc, sizeof(NPREffect_VSNPREffectVc) }, + { NPREffect_VSNPREffectBn, sizeof(NPREffect_VSNPREffectBn) }, + { NPREffect_VSNPREffectVcBn, sizeof(NPREffect_VSNPREffectVcBn) }, + { NPREffect_VSNPREffectInst, sizeof(NPREffect_VSNPREffectInst) }, + { NPREffect_VSNPREffectVcInst, sizeof(NPREffect_VSNPREffectVcInst) }, + { NPREffect_VSNPREffectBnInst, sizeof(NPREffect_VSNPREffectBnInst) }, + { NPREffect_VSNPREffectVcBnInst, sizeof(NPREffect_VSNPREffectVcBnInst) }, +}; + + +template<> +const int EffectBase::VertexShaderIndices[] = +{ + 0, // cel shading + 0, // gooch shading + + 1, // vertex color + cel shading + 1, // vertex color + gooch shading + + 2, // biased normals + cel shading + 2, // biased normals + gooch shading + + 3, // vertex color + biased normals + cel shading + 3, // vertex color + biased normals + gooch shading + + 4, // instancing + cel shading + 4, // instancing + gooch shading + + 5, // instancing + vertex color + cel shading + 5, // instancing + vertex color + gooch shading + + 6, // instancing + biased normals + cel shading + 6, // instancing + biased normals + gooch shading + + 7, // instancing + vertex color + biased normals + cel shading + 7, // instancing + vertex color + biased normals + gooch shading +}; + +template<> +const D3D12_SHADER_BYTECODE EffectBase::PixelShaderBytecode[] = +{ + { NPREffect_PSCelShading, sizeof(NPREffect_PSCelShading) }, + { NPREffect_PSGoochShading, sizeof(NPREffect_PSGoochShading) }, +}; + + +template<> +const int EffectBase::PixelShaderIndices[] = +{ + 0, // cel shading + 1, // gooch shading + + 0, // vertex color + cel shading + 1, // vertex color + gooch shading + + 0, // biased normals + cel shading + 1, // biased normals + gooch shading + + 0, // vertex color + biased normals + cel shading + 1, // vertex color + biased normals + gooch shading + + 0, // instancing + cel shading + 1, // instancing + gooch shading + + 0, // instancing + vertex color + cel shading + 1, // instancing + vertex color + gooch shading + + 0, // instancing + biased normals + cel shading + 1, // instancing + biased normals + gooch shading + + 0, // instancing + vertex color + biased normals + cel shading + 1, // instancing + vertex color + biased normals + gooch shading +}; +#pragma endregion + +// Global pool of per-device NPREffect resources. +template<> +SharedResourcePool::DeviceResources> EffectBase::deviceResourcesPool = {}; + + +// Constructor. +NPREffect::Impl::Impl( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + NPREffect::Mode nprMode) + : EffectBase(device) +{ + static_assert(static_cast(std::size(EffectBase::VertexShaderIndices)) == NPREffectTraits::ShaderPermutationCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::VertexShaderBytecode)) == NPREffectTraits::VertexShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderBytecode)) == NPREffectTraits::PixelShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderIndices)) == NPREffectTraits::ShaderPermutationCount, "array/max mismatch"); + + constants.lightDirectionAndCelBands = s_defaultLightDir; + constants.diffuseColorAndAlpha = s_defaultDiffuse; + constants.specularColorAndSpecularPower = s_defaultSpecular; + constants.goochCoolColorAndAlpha = s_defaultCool; + constants.goochWarmColorAndBeta = s_defaultWarm; + constants.eyePosition = g_XMZero; + + switch (nprMode) + { + case Mode_Cel: + case Mode_Gooch: + break; + + default: + throw std::invalid_argument("Invalid nprMode"); + } + + // Create root signature. + { + ENUM_FLAGS_CONSTEXPR D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags = + D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS + #ifdef _GAMING_XBOX_SCARLETT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS + #endif + ; + + CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount] = {}; + rootParameters[RootParameterIndex::ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_ALL); + + CD3DX12_ROOT_SIGNATURE_DESC rsigDesc = {}; + rsigDesc.Init(1, rootParameters, 0, nullptr, rootSignatureFlags); + + mRootSignature = GetRootSignature(0, rsigDesc); + } + + assert(mRootSignature != nullptr); + + // Create pipeline state. + const int sp = GetPipelineStatePermutation(nprMode, effectFlags); + assert(sp >= 0 && sp < NPREffectTraits::ShaderPermutationCount); + _Analysis_assume_(sp >= 0 && sp < NPREffectTraits::ShaderPermutationCount); + + const int vi = EffectBase::VertexShaderIndices[sp]; + assert(vi >= 0 && vi < NPREffectTraits::VertexShaderCount); + _Analysis_assume_(vi >= 0 && vi < NPREffectTraits::VertexShaderCount); + const int pi = EffectBase::PixelShaderIndices[sp]; + assert(pi >= 0 && pi < NPREffectTraits::PixelShaderCount); + _Analysis_assume_(pi >= 0 && pi < NPREffectTraits::PixelShaderCount); + + pipelineDescription.CreatePipelineState( + device, + mRootSignature, + EffectBase::VertexShaderBytecode[vi], + EffectBase::PixelShaderBytecode[pi], + mPipelineState.GetAddressOf()); + + SetDebugObjectName(mPipelineState.Get(), L"NPREffect"); +} + + +int NPREffect::Impl::GetPipelineStatePermutation(NPREffect::Mode nprMode, uint32_t effectFlags) const noexcept +{ + int permutation = static_cast(nprMode); + + // Support vertex coloring? + if (effectFlags & EffectFlags::VertexColor) + { + permutation += 2; + } + + if (effectFlags & EffectFlags::BiasedVertexNormals) + { + // Compressed normals need to be scaled and biased in the vertex shader. + permutation += 4; + } + + if (effectFlags & EffectFlags::Instancing) + { + // Vertex shader needs to use vertex matrix transform. + permutation += 8; + } + + return permutation; +} + + +// Sets our state onto the D3D device. +void NPREffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + // Compute derived parameter values. + matrices.SetConstants(dirtyFlags, constants.worldViewProj); + + // World inverse transpose matrix. + if (dirtyFlags & EffectDirtyFlags::WorldInverseTranspose) + { + constants.world = XMMatrixTranspose(matrices.world); + + const XMMATRIX worldInverse = XMMatrixInverse(nullptr, matrices.world); + + constants.worldInverseTranspose[0] = worldInverse.r[0]; + constants.worldInverseTranspose[1] = worldInverse.r[1]; + constants.worldInverseTranspose[2] = worldInverse.r[2]; + + dirtyFlags &= ~EffectDirtyFlags::WorldInverseTranspose; + dirtyFlags |= EffectDirtyFlags::ConstantBuffer; + } + + // Eye position. + if (dirtyFlags & EffectDirtyFlags::EyePosition) + { + const XMMATRIX viewInverse = XMMatrixInverse(nullptr, matrices.view); + constants.eyePosition = viewInverse.r[3]; + + dirtyFlags &= ~EffectDirtyFlags::EyePosition; + dirtyFlags |= EffectDirtyFlags::ConstantBuffer; + } + + UpdateConstants(); + + // Set the root signature + commandList->SetGraphicsRootSignature(mRootSignature); + + // Set constants + commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBuffer, GetConstantBufferGpuAddress()); + + // Set the pipeline state + commandList->SetPipelineState(EffectBase::mPipelineState.Get()); +} + + +// Public constructor. +NPREffect::NPREffect( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + Mode nprMode) + : pImpl(std::make_unique(device, effectFlags, pipelineDescription, nprMode)) +{} + + +NPREffect::NPREffect(NPREffect&&) noexcept = default; +NPREffect& NPREffect::operator= (NPREffect&&) noexcept = default; +NPREffect::~NPREffect() = default; + + +// IEffect methods. +void NPREffect::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + pImpl->Apply(commandList); +} + + +// Camera settings. +void NPREffect::SetWorld(FXMMATRIX value) +{ + pImpl->matrices.world = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose; +} + + +void NPREffect::SetView(FXMMATRIX value) +{ + pImpl->matrices.view = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::EyePosition; +} + + +void NPREffect::SetProjection(FXMMATRIX value) +{ + pImpl->matrices.projection = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj; +} + + +void NPREffect::SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) +{ + pImpl->matrices.world = world; + pImpl->matrices.view = view; + pImpl->matrices.projection = projection; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose | EffectDirtyFlags::EyePosition; +} + + +// Light settings. +void NPREffect::SetAmbientLightColor(FXMVECTOR) +{ + // Unsupported interface. +} + + +void NPREffect::SetLightEnabled(int, bool) +{ + // Unsupported interface. +} + + +void NPREffect::SetLightDirection(int whichLight, FXMVECTOR value) +{ + if (whichLight != 0) + { + // Only support one light + return; + } + + // Set xyz to new value, but preserve existing w (cel bands). + pImpl->constants.lightDirectionAndCelBands = XMVectorSelect(pImpl->constants.lightDirectionAndCelBands, value, g_XMSelect1110); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void NPREffect::SetLightDiffuseColor(int, FXMVECTOR) +{ + // Unsupported interface. +} + + +void NPREffect::SetLightSpecularColor(int, FXMVECTOR) +{ + // Unsupported interface. +} + + +void NPREffect::EnableDefaultLighting() +{ + // Set xyz to new value, but preserve existing w (cel bands). + pImpl->constants.lightDirectionAndCelBands = XMVectorSelect(pImpl->constants.lightDirectionAndCelBands, s_defaultLightDir, g_XMSelect1110); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +// Material settings. +void NPREffect::SetDiffuseColor(FXMVECTOR value) +{ + // Set xyz, preserve w (alpha). + pImpl->constants.diffuseColorAndAlpha = XMVectorSelect(pImpl->constants.diffuseColorAndAlpha, value, g_XMSelect1110); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void NPREffect::SetSpecularColor(FXMVECTOR value) +{ + // Set xyz, preserve w (specular power). + pImpl->constants.specularColorAndSpecularPower = XMVectorSelect(pImpl->constants.specularColorAndSpecularPower, value, g_XMSelect1110); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void NPREffect::SetSpecularPower(float value) +{ + // Set w of specularColorAndSpecularPower. + pImpl->constants.specularColorAndSpecularPower = XMVectorSetW(pImpl->constants.specularColorAndSpecularPower, value); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void NPREffect::DisableSpecular() +{ + // Set w of specularColorAndSpecularPower to 0. + pImpl->constants.specularColorAndSpecularPower = XMVectorSetW(pImpl->constants.specularColorAndSpecularPower, 0.0f); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void NPREffect::SetAlpha(float value) +{ + // Set w of diffuseColorAndAlpha. + pImpl->constants.diffuseColorAndAlpha = XMVectorSetW(pImpl->constants.diffuseColorAndAlpha, value); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void NPREffect::SetColorAndAlpha(FXMVECTOR value) +{ + pImpl->constants.diffuseColorAndAlpha = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +// Texture settings. +// TODO: Implement texture settings. + + +// Cel shading settings. +void NPREffect::SetCelShaderBands(int bands) +{ + // Set w of lightDirectionAndCelBands. + pImpl->constants.lightDirectionAndCelBands = XMVectorSetW(pImpl->constants.lightDirectionAndCelBands, static_cast(bands)); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +// Gooch shading settings. +void NPREffect::SetGoochCoolColor(FXMVECTOR value, float alpha) +{ + pImpl->constants.goochCoolColorAndAlpha = XMVectorSetW(value, alpha); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void NPREffect::SetGoochWarmColor(FXMVECTOR value, float beta) +{ + pImpl->constants.goochWarmColorAndBeta = XMVectorSetW(value, beta); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} diff --git a/Src/Shaders/CompileShaders.cmd b/Src/Shaders/CompileShaders.cmd index f1f2317a..edaa0f78 100644 --- a/Src/Shaders/CompileShaders.cmd +++ b/Src/Shaders/CompileShaders.cmd @@ -258,6 +258,19 @@ call :CompileShader%1 DebugEffect ps PSRGBNormals call :CompileShader%1 DebugEffect ps PSRGBTangents call :CompileShader%1 DebugEffect ps PSRGBBiTangents +call :CompileShader%1 NPREffect vs VSNPREffect +call :CompileShader%1 NPREffect vs VSNPREffectBn +call :CompileShader%1 NPREffect vs VSNPREffectVc +call :CompileShader%1 NPREffect vs VSNPREffectVcBn + +call :CompileShader%1 NPREffect vs VSNPREffectInst +call :CompileShader%1 NPREffect vs VSNPREffectBnInst +call :CompileShader%1 NPREffect vs VSNPREffectVcInst +call :CompileShader%1 NPREffect vs VSNPREffectVcBnInst + +call :CompileShader%1 NPREffect ps PSCelShading +call :CompileShader%1 NPREffect ps PSGoochShading + call :CompileShader%1 SpriteEffect vs SpriteVertexShader call :CompileShader%1 SpriteEffect ps SpritePixelShader diff --git a/Src/Shaders/NPREffect.fx b/Src/Shaders/NPREffect.fx new file mode 100644 index 00000000..548aec3e --- /dev/null +++ b/Src/Shaders/NPREffect.fx @@ -0,0 +1,232 @@ +//-------------------------------------------------------------------------------------- +// File: NPREffect.fx +// +// Non-photorealistic rendering effects (cel shading and Gooch shading) +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// https://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + + +cbuffer Parameters : register(b0) +{ + float3 LightDirection : packoffset(c0); + float CelBands : packoffset(c0.w); + + float3 DiffuseColor : packoffset(c1); + float Alpha : packoffset(c1.w); + + float3 SpecularColor : packoffset(c2); + float SpecularPower : packoffset(c2.w); + + float3 GoochCoolColor : packoffset(c3); + float GoochAlpha : packoffset(c3.w); + + float3 GoochWarmColor : packoffset(c4); + float GoochBeta : packoffset(c4.w); + + float3 EyePosition : packoffset(c5); + + float4x4 World : packoffset(c6); + float3x3 WorldInverseTranspose : packoffset(c10); + float4x4 WorldViewProj : packoffset(c13); +}; + + +#include "RootSig.fxh" +#include "Structures.fxh" +#include "Utilities.fxh" + +// Vertex shader: basic +[RootSignature(NPREffectRS)] +VSOutputPixelLightingTx VSNPREffect(VSInputNmTx vin) +{ + VSOutputPixelLightingTx vout; + + vout.PositionPS = mul(vin.Position, WorldViewProj); + vout.PositionWS = float4(mul(vin.Position, World).xyz, 1); + vout.NormalWS = normalize(mul(vin.Normal, WorldInverseTranspose)); + vout.Diffuse = float4(DiffuseColor, Alpha); + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(NPREffectRS)] +VSOutputPixelLightingTx VSNPREffectBn(VSInputNmTx vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + vout.PositionPS = mul(vin.Position, WorldViewProj); + vout.PositionWS = float4(mul(vin.Position, World).xyz, 1); + vout.NormalWS = normalize(mul(normal, WorldInverseTranspose)); + vout.Diffuse = float4(DiffuseColor, Alpha); + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: vertex color +[RootSignature(NPREffectRS)] +VSOutputPixelLightingTx VSNPREffectVc(VSInputNmTxVc vin) +{ + VSOutputPixelLightingTx vout; + + vout.PositionPS = mul(vin.Position, WorldViewProj); + vout.PositionWS = float4(mul(vin.Position, World).xyz, 1); + vout.NormalWS = normalize(mul(vin.Normal, WorldInverseTranspose)); + vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor; + vout.Diffuse.a = vin.Color.a * Alpha; + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(NPREffectRS)] +VSOutputPixelLightingTx VSNPREffectVcBn(VSInputNmTxVc vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + vout.PositionPS = mul(vin.Position, WorldViewProj); + vout.PositionWS = float4(mul(vin.Position, World).xyz, 1); + vout.NormalWS = normalize(mul(normal, WorldInverseTranspose)); + vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor; + vout.Diffuse.a = vin.Color.a * Alpha; + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: instancing +[RootSignature(NPREffectRS)] +VSOutputPixelLightingTx VSNPREffectInst(VSInputNmTxInst vin) +{ + VSOutputPixelLightingTx vout; + + CommonInstancing inst = ComputeCommonInstancing(vin.Position, vin.Normal, vin.Transform); + + vout.PositionPS = mul(inst.Position, WorldViewProj); + vout.PositionWS = float4(mul(inst.Position, World).xyz, 1); + vout.NormalWS = normalize(mul(inst.Normal, WorldInverseTranspose)); + vout.Diffuse = float4(DiffuseColor, Alpha); + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(NPREffectRS)] +VSOutputPixelLightingTx VSNPREffectBnInst(VSInputNmTxInst vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + CommonInstancing inst = ComputeCommonInstancing(vin.Position, normal, vin.Transform); + + vout.PositionPS = mul(inst.Position, WorldViewProj); + vout.PositionWS = float4(mul(inst.Position, World).xyz, 1); + vout.NormalWS = normalize(mul(inst.Normal, WorldInverseTranspose)); + vout.Diffuse = float4(DiffuseColor, Alpha); + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: vertex color + instancing +[RootSignature(NPREffectRS)] +VSOutputPixelLightingTx VSNPREffectVcInst(VSInputNmTxVcInst vin) +{ + VSOutputPixelLightingTx vout; + + CommonInstancing inst = ComputeCommonInstancing(vin.Position, vin.Normal, vin.Transform); + + vout.PositionPS = mul(inst.Position, WorldViewProj); + vout.PositionWS = float4(mul(inst.Position, World).xyz, 1); + vout.NormalWS = normalize(mul(inst.Normal, WorldInverseTranspose)); + vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor; + vout.Diffuse.a = vin.Color.a * Alpha; + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(NPREffectRS)] +VSOutputPixelLightingTx VSNPREffectVcBnInst(VSInputNmTxVcInst vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + CommonInstancing inst = ComputeCommonInstancing(vin.Position, normal, vin.Transform); + + vout.PositionPS = mul(inst.Position, WorldViewProj); + vout.PositionWS = float4(mul(inst.Position, World).xyz, 1); + vout.NormalWS = normalize(mul(inst.Normal, WorldInverseTranspose)); + vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor; + vout.Diffuse.a = vin.Color.a * Alpha; + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Pixel shader: cel shading +[RootSignature(NPREffectRS)] +float4 PSCelShading(PSInputPixelLightingTx pin) : SV_Target0 +{ + float3 normal = normalize(pin.NormalWS); + float3 lightDir = normalize(-LightDirection); + + // Quantize the diffuse lighting into discrete bands + float NdotL = dot(normal, lightDir); + float intensity = max(0, NdotL); + float quantized = floor(intensity * CelBands) / CelBands; + + float3 color = pin.Diffuse.rgb * quantized; + + // Specular highlight (hard edge) + float3 viewDir = normalize(EyePosition - pin.PositionWS.xyz); + float3 halfVec = normalize(lightDir + viewDir); + float NdotH = max(0, dot(normal, halfVec)); + float specular = step(0.95, pow(NdotH, SpecularPower)); + + color += SpecularColor * specular; + + return float4(color, pin.Diffuse.a); +} + + +// Pixel shader: Gooch shading +[RootSignature(NPREffectRS)] +float4 PSGoochShading(PSInputPixelLightingTx pin) : SV_Target0 +{ + float3 normal = normalize(pin.NormalWS); + float3 lightDir = normalize(-LightDirection); + + // Gooch diffuse term: blend between cool and warm based on NdotL + float NdotL = dot(normal, lightDir); + float t = (1.0 + NdotL) * 0.5; + + float3 coolContrib = GoochCoolColor + GoochAlpha * pin.Diffuse.rgb; + float3 warmContrib = GoochWarmColor + GoochBeta * pin.Diffuse.rgb; + + float3 color = lerp(coolContrib, warmContrib, t); + + // Specular highlight + float3 viewDir = normalize(EyePosition - pin.PositionWS.xyz); + float3 reflectDir = reflect(LightDirection, normal); + float spec = pow(max(0, dot(viewDir, reflectDir)), SpecularPower); + + color += SpecularColor * spec; + + return float4(color, pin.Diffuse.a); +} diff --git a/Src/Shaders/RootSig.fxh b/Src/Shaders/RootSig.fxh index 45e3198a..83d891cb 100644 --- a/Src/Shaders/RootSig.fxh +++ b/Src/Shaders/RootSig.fxh @@ -202,6 +202,15 @@ " DENY_MESH_SHADER_ROOT_ACCESS )," \ "CBV(b0)" +#define NPREffectRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_MESH_SHADER_ROOT_ACCESS )," \ +"CBV(b0)" + #else // !__XBOX_SCARLETT #define NoTextureRS \ @@ -371,4 +380,11 @@ " DENY_HULL_SHADER_ROOT_ACCESS )," \ "CBV(b0)" +#define NPREffectRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS )," \ +"CBV(b0)" + #endif