From 7e81784212354d5be76f924bbd8c964d8c342f1a Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Mon, 8 Jun 2026 10:42:08 -0700 Subject: [PATCH 1/9] NPREffect to support cel shading, gooch, etc. --- CMakeLists.txt | 2 + DirectXTK_Desktop_2022_Win10.vcxproj | 6 + DirectXTK_Desktop_2022_Win10.vcxproj.filters | 6 + DirectXTK_Desktop_2026.vcxproj | 6 + DirectXTK_Desktop_2026.vcxproj.filters | 6 + DirectXTK_GDKW_2022.vcxproj | 6 + DirectXTK_GDKW_2022.vcxproj.filters | 6 + DirectXTK_GDKX_2022.vcxproj | 6 + DirectXTK_GDKX_2022.vcxproj.filters | 6 + DirectXTK_GDKX_2026.vcxproj | 6 + DirectXTK_GDKX_2026.vcxproj.filters | 6 + DirectXTK_GDK_2022.vcxproj | 6 + DirectXTK_GDK_2022.vcxproj.filters | 6 + DirectXTK_Windows10_2022.vcxproj | 6 + DirectXTK_Windows10_2022.vcxproj.filters | 6 + Inc/Effects.h | 72 +++ Src/NPREffect.cpp | 567 +++++++++++++++++++ Src/Shaders/CompileShaders.cmd | 13 + Src/Shaders/NPREffect.fx | 232 ++++++++ Src/Shaders/RootSig.fxh | 16 + 20 files changed, 986 insertions(+) create mode 100644 Src/NPREffect.cpp create mode 100644 Src/Shaders/NPREffect.fx diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ff1e161..2d783008 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 From f72180afb6abb629d8ebcaf4a89b3864ca0c8dad Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Tue, 14 Jul 2026 11:01:39 -0700 Subject: [PATCH 2/9] Fix specular --- Src/NPREffect.cpp | 6 ++++-- Src/Shaders/NPREffect.fx | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Src/NPREffect.cpp b/Src/NPREffect.cpp index 104b79ed..3584dec8 100644 --- a/Src/NPREffect.cpp +++ b/Src/NPREffect.cpp @@ -512,8 +512,10 @@ void NPREffect::SetSpecularPower(float value) void NPREffect::DisableSpecular() { - // Set w of specularColorAndSpecularPower to 0. - pImpl->constants.specularColorAndSpecularPower = XMVectorSetW(pImpl->constants.specularColorAndSpecularPower, 0.0f); + // Set specular color to black, power to 1 + // Note: Don't use a power of 0 or the shader will generate strange highlights on non-specular materials + + pImpl->constants.specularColorAndSpecularPower = g_XMIdentityR3; pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; } diff --git a/Src/Shaders/NPREffect.fx b/Src/Shaders/NPREffect.fx index 548aec3e..ebef55d2 100644 --- a/Src/Shaders/NPREffect.fx +++ b/Src/Shaders/NPREffect.fx @@ -224,9 +224,9 @@ float4 PSGoochShading(PSInputPixelLightingTx pin) : SV_Target0 // Specular highlight float3 viewDir = normalize(EyePosition - pin.PositionWS.xyz); float3 reflectDir = reflect(LightDirection, normal); - float spec = pow(max(0, dot(viewDir, reflectDir)), SpecularPower); + float specular = pow(max(0, dot(viewDir, reflectDir)), SpecularPower); - color += SpecularColor * spec; + color += SpecularColor * specular; return float4(color, pin.Diffuse.a); } From a192516ab6536a910b3234b3d632a86748a0bbd4 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Tue, 14 Jul 2026 16:03:45 -0700 Subject: [PATCH 3/9] Add texture support to NPREffect --- Inc/Effects.h | 6 +- Src/NPREffect.cpp | 245 ++++++++++++++++++++++++++----- Src/Shaders/CompileShaders.cmd | 13 ++ Src/Shaders/NPREffect.fx | 255 +++++++++++++++++++++++++++++---- Src/Shaders/RootSig.fxh | 16 --- Src/Shaders/Structures.fxh | 15 ++ 6 files changed, 472 insertions(+), 78 deletions(-) diff --git a/Inc/Effects.h b/Inc/Effects.h index 1493f870..5f208d35 100644 --- a/Inc/Effects.h +++ b/Inc/Effects.h @@ -556,7 +556,7 @@ namespace DirectX DIRECTX_TOOLKIT_API void __cdecl SetFogEnd(float value) override; DIRECTX_TOOLKIT_API void XM_CALLCONV SetFogColor(FXMVECTOR value) override; - // Texture setting - albedo, normal and specular intensity + // Texture settings - albedo, normal and specular intensity. DIRECTX_TOOLKIT_API void __cdecl SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor); DIRECTX_TOOLKIT_API void __cdecl SetNormalTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor); DIRECTX_TOOLKIT_API void __cdecl SetSpecularTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor); @@ -799,8 +799,8 @@ namespace DirectX static constexpr int MaxDirectionalLights = 1; - // Texture settings. - // TODO: Implement texture settings. + // Texture setting. + DIRECTX_TOOLKIT_API void __cdecl SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor); // Cel shading settings. DIRECTX_TOOLKIT_API void __cdecl SetCelShaderBands(int bands); diff --git a/Src/NPREffect.cpp b/Src/NPREffect.cpp index 3584dec8..5f84ad56 100644 --- a/Src/NPREffect.cpp +++ b/Src/NPREffect.cpp @@ -36,10 +36,10 @@ namespace { using ConstantBufferType = NPREffectConstants; - static constexpr int VertexShaderCount = 8; - static constexpr int PixelShaderCount = 2; - static constexpr int ShaderPermutationCount = 16; - static constexpr int RootSignatureCount = 1; + static constexpr int VertexShaderCount = 16; + static constexpr int PixelShaderCount = 4; + static constexpr int ShaderPermutationCount = 32; + static constexpr int RootSignatureCount = 2; }; @@ -68,9 +68,16 @@ class NPREffect::Impl : public EffectBase enum RootParameterIndex { ConstantBuffer, + TextureSRV, + TextureSampler, RootParameterCount }; + bool textureEnabled; + + D3D12_GPU_DESCRIPTOR_HANDLE texture; + D3D12_GPU_DESCRIPTOR_HANDLE sampler; + int GetPipelineStatePermutation(NPREffect::Mode nprMode, uint32_t effectFlags) const noexcept; void Apply(_In_ ID3D12GraphicsCommandList* commandList); @@ -94,8 +101,23 @@ namespace #include "XboxGamingScarlettNPREffect_VSNPREffectVcBn.inc" #include "XboxGamingScarlettNPREffect_VSNPREffectVcBnInst.inc" +#include "XboxGamingScarlettNPREffect_VSNPREffectTx.inc" +#include "XboxGamingScarlettNPREffect_VSNPREffectInstTx.inc" + +#include "XboxGamingScarlettNPREffect_VSNPREffectVcTx.inc" +#include "XboxGamingScarlettNPREffect_VSNPREffectVcInstTx.inc" + +#include "XboxGamingScarlettNPREffect_VSNPREffectBnTx.inc" +#include "XboxGamingScarlettNPREffect_VSNPREffectBnInstTx.inc" + +#include "XboxGamingScarlettNPREffect_VSNPREffectVcBnTx.inc" +#include "XboxGamingScarlettNPREffect_VSNPREffectVcBnInstTx.inc" + #include "XboxGamingScarlettNPREffect_PSCelShading.inc" +#include "XboxGamingScarlettNPREffect_PSCelShadingTx.inc" + #include "XboxGamingScarlettNPREffect_PSGoochShading.inc" +#include "XboxGamingScarlettNPREffect_PSGoochShadingTx.inc" #elif defined(_GAMING_XBOX) #include "XboxGamingXboxOneNPREffect_VSNPREffect.inc" #include "XboxGamingXboxOneNPREffect_VSNPREffectInst.inc" @@ -109,8 +131,23 @@ namespace #include "XboxGamingXboxOneNPREffect_VSNPREffectVcBn.inc" #include "XboxGamingXboxOneNPREffect_VSNPREffectVcBnInst.inc" +#include "XboxGamingXboxOneNPREffect_VSNPREffectTx.inc" +#include "XboxGamingXboxOneNPREffect_VSNPREffectInstTx.inc" + +#include "XboxGamingXboxOneNPREffect_VSNPREffectVcTx.inc" +#include "XboxGamingXboxOneNPREffect_VSNPREffectVcInstTx.inc" + +#include "XboxGamingXboxOneNPREffect_VSNPREffectBnTx.inc" +#include "XboxGamingXboxOneNPREffect_VSNPREffectBnInstTx.inc" + +#include "XboxGamingXboxOneNPREffect_VSNPREffectVcBnTx.inc" +#include "XboxGamingXboxOneNPREffect_VSNPREffectVcBnInstTx.inc" + #include "XboxGamingXboxOneNPREffect_PSCelShading.inc" +#include "XboxGamingXboxOneNPREffect_PSCelShadingTx.inc" + #include "XboxGamingXboxOneNPREffect_PSGoochShading.inc" +#include "XboxGamingXboxOneNPREffect_PSGoochShadingTx.inc" #elif defined(_XBOX_ONE) && defined(_TITLE) #include "XboxOneNPREffect_VSNPREffect.inc" #include "XboxOneNPREffect_VSNPREffectInst.inc" @@ -124,8 +161,23 @@ namespace #include "XboxOneNPREffect_VSNPREffectVcBn.inc" #include "XboxOneNPREffect_VSNPREffectVcBnInst.inc" +#include "XboxOneNPREffect_VSNPREffectTx.inc" +#include "XboxOneNPREffect_VSNPREffectInstTx.inc" + +#include "XboxOneNPREffect_VSNPREffectVcTx.inc" +#include "XboxOneNPREffect_VSNPREffectVcInstTx.inc" + +#include "XboxOneNPREffect_VSNPREffectBnTx.inc" +#include "XboxOneNPREffect_VSNPREffectBnInstTx.inc" + +#include "XboxOneNPREffect_VSNPREffectVcBnTx.inc" +#include "XboxOneNPREffect_VSNPREffectVcBnInstTx.inc" + #include "XboxOneNPREffect_PSCelShading.inc" +#include "XboxOneNPREffect_PSCelShadingTx.inc" + #include "XboxOneNPREffect_PSGoochShading.inc" +#include "XboxOneNPREffect_PSGoochShadingTx.inc" #else #include "NPREffect_VSNPREffect.inc" #include "NPREffect_VSNPREffectInst.inc" @@ -139,8 +191,23 @@ namespace #include "NPREffect_VSNPREffectVcBn.inc" #include "NPREffect_VSNPREffectVcBnInst.inc" +#include "NPREffect_VSNPREffectTx.inc" +#include "NPREffect_VSNPREffectInstTx.inc" + +#include "NPREffect_VSNPREffectVcTx.inc" +#include "NPREffect_VSNPREffectVcInstTx.inc" + +#include "NPREffect_VSNPREffectBnTx.inc" +#include "NPREffect_VSNPREffectBnInstTx.inc" + +#include "NPREffect_VSNPREffectVcBnTx.inc" +#include "NPREffect_VSNPREffectVcBnInstTx.inc" + #include "NPREffect_PSCelShading.inc" +#include "NPREffect_PSCelShadingTx.inc" + #include "NPREffect_PSGoochShading.inc" +#include "NPREffect_PSGoochShadingTx.inc" #endif } @@ -148,14 +215,22 @@ namespace 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) }, + { 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) }, + { NPREffect_VSNPREffectTx, sizeof(NPREffect_VSNPREffectTx) }, + { NPREffect_VSNPREffectVcTx, sizeof(NPREffect_VSNPREffectVcTx) }, + { NPREffect_VSNPREffectBnTx, sizeof(NPREffect_VSNPREffectBnTx) }, + { NPREffect_VSNPREffectVcBnTx, sizeof(NPREffect_VSNPREffectVcBnTx) }, + { NPREffect_VSNPREffectInstTx, sizeof(NPREffect_VSNPREffectInstTx) }, + { NPREffect_VSNPREffectVcInstTx, sizeof(NPREffect_VSNPREffectVcInstTx) }, + { NPREffect_VSNPREffectBnInstTx, sizeof(NPREffect_VSNPREffectBnInstTx) }, + { NPREffect_VSNPREffectVcBnInstTx, sizeof(NPREffect_VSNPREffectVcBnInstTx) }, }; @@ -168,11 +243,11 @@ const int EffectBase::VertexShaderIndices[] = 1, // vertex color + cel shading 1, // vertex color + gooch shading - 2, // biased normals + cel shading - 2, // biased normals + gooch shading + 2, // cel shading (biased vertex normal) + 2, // gooch shading (biased vertex normal) - 3, // vertex color + biased normals + cel shading - 3, // vertex color + biased normals + gooch shading + 3, // vertex color (biased vertex normal) + cel shading + 3, // vertex color (biased vertex normal) + gooch shading 4, // instancing + cel shading 4, // instancing + gooch shading @@ -180,18 +255,45 @@ const int EffectBase::VertexShaderIndices[] = 5, // instancing + vertex color + cel shading 5, // instancing + vertex color + gooch shading - 6, // instancing + biased normals + cel shading - 6, // instancing + biased normals + gooch shading + 6, // instancing (biased vertex normal) + cel shading + 6, // instancing (biased vertex normal) + gooch shading + + 7, // instancing + vertex color (biased vertex normal) + cel shading + 7, // instancing + vertex color (biased vertex normal) + gooch shading + + 8, // cel shading + texture + 8, // gooch shading + texture + + 9, // vertex color + cel shading + texture + 9, // vertex color + gooch shading + texture + + 10, // cel shading (biased vertex normal) + texture + 10, // gooch shading (biased vertex normal) + texture + + 11, // vertex color (biased vertex normal) + cel shading + texture + 11, // vertex color (biased vertex normal) + gooch shading + texture - 7, // instancing + vertex color + biased normals + cel shading - 7, // instancing + vertex color + biased normals + gooch shading + 12, // instancing + cel shading + texture + 12, // instancing + gooch shading + texture + + 13, // instancing + vertex color + cel shading + texture + 13, // instancing + vertex color + gooch shading + texture + + 14, // instancing (biased vertex normal) + cel shading + texture + 14, // instancing (biased vertex normal) + gooch shading + texture + + 15, // instancing + vertex color (biased vertex normal) + cel shading + texture + 15, // instancing + vertex color (biased vertex normal) + gooch shading + texture }; + template<> const D3D12_SHADER_BYTECODE EffectBase::PixelShaderBytecode[] = { - { NPREffect_PSCelShading, sizeof(NPREffect_PSCelShading) }, - { NPREffect_PSGoochShading, sizeof(NPREffect_PSGoochShading) }, + { NPREffect_PSCelShading, sizeof(NPREffect_PSCelShading) }, + { NPREffect_PSGoochShading, sizeof(NPREffect_PSGoochShading) }, + { NPREffect_PSCelShadingTx, sizeof(NPREffect_PSCelShadingTx) }, + { NPREffect_PSGoochShadingTx, sizeof(NPREffect_PSGoochShadingTx) }, }; @@ -204,11 +306,11 @@ const int EffectBase::PixelShaderIndices[] = 0, // vertex color + cel shading 1, // vertex color + gooch shading - 0, // biased normals + cel shading - 1, // biased normals + gooch shading + 0, // cel shading (biased vertex normal) + 1, // gooch shading (biased vertex normal) - 0, // vertex color + biased normals + cel shading - 1, // vertex color + biased normals + gooch shading + 0, // vertex color (biased vertex normal) + cel shading + 1, // vertex color (biased vertex normal) + gooch shading 0, // instancing + cel shading 1, // instancing + gooch shading @@ -216,11 +318,35 @@ const int EffectBase::PixelShaderIndices[] = 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 (biased vertex normal) + cel shading + 1, // instancing (biased vertex normal) + gooch shading + + 0, // instancing + vertex color (biased vertex normal) + cel shading + 1, // instancing + vertex color (biased vertex normal) + gooch shading + + 2, // cel shading + texture + 3, // gooch shading + texture + + 2, // vertex color + cel shading + texture + 3, // vertex color + gooch shading + texture - 0, // instancing + vertex color + biased normals + cel shading - 1, // instancing + vertex color + biased normals + gooch shading + 2, // cel shading (biased vertex normal) + texture + 3, // gooch shading (biased vertex normal) + texture + + 2, // vertex color (biased vertex normal) + cel shading + texture + 3, // vertex color (biased vertex normal) + gooch shading + texture + + 2, // instancing + cel shading + texture + 3, // instancing + gooch shading + texture + + 2, // instancing + vertex color + cel shading + texture + 3, // instancing + vertex color + gooch shading + texture + + 2, // instancing (biased vertex normal) + cel shading + texture + 3, // instancing (biased vertex normal) + gooch shading + texture + + 2, // instancing + vertex color (biased vertex normal) + cel shading + texture + 3, // instancing + vertex color (biased vertex normal) + gooch shading + texture }; #pragma endregion @@ -235,7 +361,9 @@ NPREffect::Impl::Impl( uint32_t effectFlags, const EffectPipelineStateDescription& pipelineDescription, NPREffect::Mode nprMode) - : EffectBase(device) + : EffectBase(device), + texture{}, + sampler{} { 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"); @@ -259,6 +387,8 @@ NPREffect::Impl::Impl( throw std::invalid_argument("Invalid nprMode"); } + textureEnabled = (effectFlags & EffectFlags::Texture) != 0; + // Create root signature. { ENUM_FLAGS_CONSTEXPR D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags = @@ -272,13 +402,34 @@ NPREffect::Impl::Impl( #endif ; + // Create root parameters and initialize first (constants) CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount] = {}; rootParameters[RootParameterIndex::ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_ALL); + // Root parameter descriptor - conditionally initialized CD3DX12_ROOT_SIGNATURE_DESC rsigDesc = {}; - rsigDesc.Init(1, rootParameters, 0, nullptr, rootSignatureFlags); - mRootSignature = GetRootSignature(0, rsigDesc); + if (textureEnabled) + { + // Include texture and srv + const CD3DX12_DESCRIPTOR_RANGE textureSRV(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); + const CD3DX12_DESCRIPTOR_RANGE textureSampler(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0); + + rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &textureSRV, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[RootParameterIndex::TextureSampler].InitAsDescriptorTable(1, &textureSampler, D3D12_SHADER_VISIBILITY_PIXEL); + + // use all parameters + rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 0, nullptr, rootSignatureFlags); + + mRootSignature = GetRootSignature(1, rsigDesc); + } + else + { + // only use constant + rsigDesc.Init(1, rootParameters, 0, nullptr, rootSignatureFlags); + + mRootSignature = GetRootSignature(0, rsigDesc); + } } assert(mRootSignature != nullptr); @@ -328,6 +479,12 @@ int NPREffect::Impl::GetPipelineStatePermutation(NPREffect::Mode nprMode, uint32 permutation += 8; } + // Use shaders without texture coordinates? + if (textureEnabled) + { + permutation += 16; + } + return permutation; } @@ -368,6 +525,20 @@ void NPREffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList) // Set the root signature commandList->SetGraphicsRootSignature(mRootSignature); + // Set the texture + if (textureEnabled) + { + if (!texture.ptr || !sampler.ptr) + { + DebugTrace("ERROR: Missing texture or sampler for NPREffect (texture %llu, sampler %llu)\n", texture.ptr, sampler.ptr); + throw std::runtime_error("NPREffect"); + } + + // **NOTE** If D3D asserts or crashes here, you probably need to call commandList->SetDescriptorHeaps() with the required descriptor heaps. + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSRV, texture); + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSampler, sampler); + } + // Set constants commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBuffer, GetConstantBufferGpuAddress()); @@ -567,3 +738,11 @@ void NPREffect::SetGoochWarmColor(FXMVECTOR value, float beta) pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; } + + +// Texture setting. +void NPREffect::SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor) +{ + pImpl->texture = srvDescriptor; + pImpl->sampler = samplerDescriptor; +} diff --git a/Src/Shaders/CompileShaders.cmd b/Src/Shaders/CompileShaders.cmd index edaa0f78..c8acb80b 100644 --- a/Src/Shaders/CompileShaders.cmd +++ b/Src/Shaders/CompileShaders.cmd @@ -268,8 +268,21 @@ call :CompileShader%1 NPREffect vs VSNPREffectBnInst call :CompileShader%1 NPREffect vs VSNPREffectVcInst call :CompileShader%1 NPREffect vs VSNPREffectVcBnInst +call :CompileShader%1 NPREffect vs VSNPREffectTx +call :CompileShader%1 NPREffect vs VSNPREffectBnTx +call :CompileShader%1 NPREffect vs VSNPREffectVcTx +call :CompileShader%1 NPREffect vs VSNPREffectVcBnTx + +call :CompileShader%1 NPREffect vs VSNPREffectInstTx +call :CompileShader%1 NPREffect vs VSNPREffectBnInstTx +call :CompileShader%1 NPREffect vs VSNPREffectVcInstTx +call :CompileShader%1 NPREffect vs VSNPREffectVcBnInstTx + call :CompileShader%1 NPREffect ps PSCelShading +call :CompileShader%1 NPREffect ps PSCelShadingTx + call :CompileShader%1 NPREffect ps PSGoochShading +call :CompileShader%1 NPREffect ps PSGoochShadingTx call :CompileShader%1 SpriteEffect vs SpriteVertexShader call :CompileShader%1 SpriteEffect ps SpritePixelShader diff --git a/Src/Shaders/NPREffect.fx b/Src/Shaders/NPREffect.fx index ebef55d2..6e8d0904 100644 --- a/Src/Shaders/NPREffect.fx +++ b/Src/Shaders/NPREffect.fx @@ -10,6 +10,10 @@ //-------------------------------------------------------------------------------------- +Texture2D Texture : register(t0); +SamplerState Sampler : register(s0); + + cbuffer Parameters : register(b0) { float3 LightDirection : packoffset(c0); @@ -39,9 +43,141 @@ cbuffer Parameters : register(b0) #include "Structures.fxh" #include "Utilities.fxh" -// Vertex shader: basic -[RootSignature(NPREffectRS)] -VSOutputPixelLightingTx VSNPREffect(VSInputNmTx vin) +// Vertex shader: basic. +[RootSignature(NoTextureRS)] +VSOutputPixelLighting VSNPREffect(VSInputNm vin) +{ + VSOutputPixelLighting 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); + + return vout; +} + +[RootSignature(NoTextureRS)] +VSOutputPixelLighting VSNPREffectBn(VSInputNm vin) +{ + VSOutputPixelLighting 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); + + return vout; +} + + +// Vertex shader: vertex color. +[RootSignature(NoTextureRS)] +VSOutputPixelLighting VSNPREffectVc(VSInputNmVc vin) +{ + VSOutputPixelLighting 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; + + return vout; +} + +[RootSignature(NoTextureRS)] +VSOutputPixelLighting VSNPREffectVcBn(VSInputNmVc vin) +{ + VSOutputPixelLighting 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; + + return vout; +} + + +// Vertex shader: instancing. +[RootSignature(NoTextureRS)] +VSOutputPixelLighting VSNPREffectInst(VSInputNmInst vin) +{ + VSOutputPixelLighting 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); + + return vout; +} + +[RootSignature(NoTextureRS)] +VSOutputPixelLighting VSNPREffectBnInst(VSInputNmInst vin) +{ + VSOutputPixelLighting 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); + + return vout; +} + + +// Vertex shader: vertex color + instancing. +[RootSignature(NoTextureRS)] +VSOutputPixelLighting VSNPREffectVcInst(VSInputNmVcInst vin) +{ + VSOutputPixelLighting 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; + + return vout; +} + +[RootSignature(NoTextureRS)] +VSOutputPixelLighting VSNPREffectVcBnInst(VSInputNmVcInst vin) +{ + VSOutputPixelLighting 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; + + return vout; +} + + +// Vertex shader: texture. +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSNPREffectTx(VSInputNmTx vin) { VSOutputPixelLightingTx vout; @@ -54,8 +190,8 @@ VSOutputPixelLightingTx VSNPREffect(VSInputNmTx vin) return vout; } -[RootSignature(NPREffectRS)] -VSOutputPixelLightingTx VSNPREffectBn(VSInputNmTx vin) +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSNPREffectBnTx(VSInputNmTx vin) { VSOutputPixelLightingTx vout; @@ -71,9 +207,9 @@ VSOutputPixelLightingTx VSNPREffectBn(VSInputNmTx vin) } -// Vertex shader: vertex color -[RootSignature(NPREffectRS)] -VSOutputPixelLightingTx VSNPREffectVc(VSInputNmTxVc vin) +// Vertex shader: texture + vertex color. +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSNPREffectVcTx(VSInputNmTxVc vin) { VSOutputPixelLightingTx vout; @@ -87,8 +223,8 @@ VSOutputPixelLightingTx VSNPREffectVc(VSInputNmTxVc vin) return vout; } -[RootSignature(NPREffectRS)] -VSOutputPixelLightingTx VSNPREffectVcBn(VSInputNmTxVc vin) +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSNPREffectVcBnTx(VSInputNmTxVc vin) { VSOutputPixelLightingTx vout; @@ -105,9 +241,9 @@ VSOutputPixelLightingTx VSNPREffectVcBn(VSInputNmTxVc vin) } -// Vertex shader: instancing -[RootSignature(NPREffectRS)] -VSOutputPixelLightingTx VSNPREffectInst(VSInputNmTxInst vin) +// Vertex shader: texture + instancing. +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSNPREffectInstTx(VSInputNmTxInst vin) { VSOutputPixelLightingTx vout; @@ -122,8 +258,8 @@ VSOutputPixelLightingTx VSNPREffectInst(VSInputNmTxInst vin) return vout; } -[RootSignature(NPREffectRS)] -VSOutputPixelLightingTx VSNPREffectBnInst(VSInputNmTxInst vin) +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSNPREffectBnInstTx(VSInputNmTxInst vin) { VSOutputPixelLightingTx vout; @@ -141,9 +277,9 @@ VSOutputPixelLightingTx VSNPREffectBnInst(VSInputNmTxInst vin) } -// Vertex shader: vertex color + instancing -[RootSignature(NPREffectRS)] -VSOutputPixelLightingTx VSNPREffectVcInst(VSInputNmTxVcInst vin) +// Vertex shader: texture + vertex color + instancing. +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSNPREffectVcInstTx(VSInputNmTxVcInst vin) { VSOutputPixelLightingTx vout; @@ -159,8 +295,8 @@ VSOutputPixelLightingTx VSNPREffectVcInst(VSInputNmTxVcInst vin) return vout; } -[RootSignature(NPREffectRS)] -VSOutputPixelLightingTx VSNPREffectVcBnInst(VSInputNmTxVcInst vin) +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSNPREffectVcBnInstTx(VSInputNmTxVcInst vin) { VSOutputPixelLightingTx vout; @@ -179,9 +315,9 @@ VSOutputPixelLightingTx VSNPREffectVcBnInst(VSInputNmTxVcInst vin) } -// Pixel shader: cel shading -[RootSignature(NPREffectRS)] -float4 PSCelShading(PSInputPixelLightingTx pin) : SV_Target0 +// Pixel shader: cel shading. +[RootSignature(NoTextureRS)] +float4 PSCelShading(PSInputPixelLighting pin) : SV_Target0 { float3 normal = normalize(pin.NormalWS); float3 lightDir = normalize(-LightDirection); @@ -197,6 +333,8 @@ float4 PSCelShading(PSInputPixelLightingTx pin) : SV_Target0 float3 viewDir = normalize(EyePosition - pin.PositionWS.xyz); float3 halfVec = normalize(lightDir + viewDir); float NdotH = max(0, dot(normal, halfVec)); + + //TODO: Need to revisit for cel float specular = step(0.95, pow(NdotH, SpecularPower)); color += SpecularColor * specular; @@ -205,9 +343,40 @@ float4 PSCelShading(PSInputPixelLightingTx pin) : SV_Target0 } -// Pixel shader: Gooch shading -[RootSignature(NPREffectRS)] -float4 PSGoochShading(PSInputPixelLightingTx pin) : SV_Target0 +// Pixel shader: cel shading + texture. +[RootSignature(MainRS)] +float4 PSCelShadingTx(PSInputPixelLightingTx pin) : SV_Target0 +{ + float3 normal = normalize(pin.NormalWS); + float3 lightDir = normalize(-LightDirection); + + // Sample base texture + float4 texColor = Texture.Sample(Sampler, pin.TexCoord); + + // 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 * texColor.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)); + + //TODO: Need to revisit for cel + float specular = step(0.95, pow(NdotH, SpecularPower)); + + color += SpecularColor * specular; + + return float4(color, pin.Diffuse.a * texColor.a); +} + + +// Pixel shader: Gooch shading. +[RootSignature(NoTextureRS)] +float4 PSGoochShading(PSInputPixelLighting pin) : SV_Target0 { float3 normal = normalize(pin.NormalWS); float3 lightDir = normalize(-LightDirection); @@ -224,9 +393,43 @@ float4 PSGoochShading(PSInputPixelLightingTx pin) : SV_Target0 // Specular highlight float3 viewDir = normalize(EyePosition - pin.PositionWS.xyz); float3 reflectDir = reflect(LightDirection, normal); + + //TODO: Need to revisit for Gooch float specular = pow(max(0, dot(viewDir, reflectDir)), SpecularPower); color += SpecularColor * specular; return float4(color, pin.Diffuse.a); } + + +// Pixel shader: Gooch shading + texture. +[RootSignature(MainRS)] +float4 PSGoochShadingTx(PSInputPixelLightingTx pin) : SV_Target0 +{ + float3 normal = normalize(pin.NormalWS); + float3 lightDir = normalize(-LightDirection); + + // Sample base texture + float4 texColor = Texture.Sample(Sampler, pin.TexCoord); + + // 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 * texColor.rgb; + float3 warmContrib = GoochWarmColor + GoochBeta * pin.Diffuse.rgb * texColor.rgb; + + float3 color = lerp(coolContrib, warmContrib, t); + + // Specular highlight + float3 viewDir = normalize(EyePosition - pin.PositionWS.xyz); + float3 reflectDir = reflect(LightDirection, normal); + + //TODO: Need to revisit for Gooch + float specular = pow(max(0, dot(viewDir, reflectDir)), SpecularPower); + + color += SpecularColor * specular; + + return float4(color, pin.Diffuse.a * texColor.a); +} diff --git a/Src/Shaders/RootSig.fxh b/Src/Shaders/RootSig.fxh index 83d891cb..45e3198a 100644 --- a/Src/Shaders/RootSig.fxh +++ b/Src/Shaders/RootSig.fxh @@ -202,15 +202,6 @@ " 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 \ @@ -380,11 +371,4 @@ " 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 diff --git a/Src/Shaders/Structures.fxh b/Src/Shaders/Structures.fxh index 0b49c2c3..cf0cc067 100644 --- a/Src/Shaders/Structures.fxh +++ b/Src/Shaders/Structures.fxh @@ -60,6 +60,21 @@ struct VSInputNmTxVc float4 Color : COLOR; }; +struct VSInputNmInst +{ + float4 Position : SV_Position; + float3 Normal : NORMAL; + float4x3 Transform : InstMatrix; +}; + +struct VSInputNmVcInst +{ + float4 Position : SV_Position; + float3 Normal : NORMAL; + float4 Color : COLOR; + float4x3 Transform : InstMatrix; +}; + struct VSInputNmTxInst { float4 Position : SV_Position; From 5e8950cea1402232b1c154652b2e91b7d656cb9c Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Tue, 14 Jul 2026 16:21:12 -0700 Subject: [PATCH 4/9] Code review --- Src/NPREffect.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Src/NPREffect.cpp b/Src/NPREffect.cpp index 5f84ad56..e9e73c33 100644 --- a/Src/NPREffect.cpp +++ b/Src/NPREffect.cpp @@ -665,6 +665,7 @@ void NPREffect::SetDiffuseColor(FXMVECTOR value) void NPREffect::SetSpecularColor(FXMVECTOR value) { + // TODO: specular power needs to be specular threshold! // Set xyz, preserve w (specular power). pImpl->constants.specularColorAndSpecularPower = XMVectorSelect(pImpl->constants.specularColorAndSpecularPower, value, g_XMSelect1110); @@ -709,8 +710,12 @@ void NPREffect::SetColorAndAlpha(FXMVECTOR value) } -// Texture settings. -// TODO: Implement texture settings. +// Texture setting. +void NPREffect::SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor) +{ + pImpl->texture = srvDescriptor; + pImpl->sampler = samplerDescriptor; +} // Cel shading settings. @@ -738,11 +743,3 @@ void NPREffect::SetGoochWarmColor(FXMVECTOR value, float beta) pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; } - - -// Texture setting. -void NPREffect::SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor) -{ - pImpl->texture = srvDescriptor; - pImpl->sampler = samplerDescriptor; -} From ac7e5989d243e1ecac1a0308fa0dadac7caa77d0 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Thu, 16 Jul 2026 16:38:31 -0700 Subject: [PATCH 5/9] Specular smoothing and rim lighting --- Inc/Effects.h | 11 +- Src/NPREffect.cpp | 100 +++++++++++-- Src/Shaders/NPREffect.fx | 305 +++++++++++++++++---------------------- 3 files changed, 229 insertions(+), 187 deletions(-) diff --git a/Inc/Effects.h b/Inc/Effects.h index 5f208d35..83b7c050 100644 --- a/Inc/Effects.h +++ b/Inc/Effects.h @@ -788,7 +788,7 @@ namespace DirectX // 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 SetSpecularThreshold(float threshold, float smoothing); DIRECTX_TOOLKIT_API void __cdecl DisableSpecular(); DIRECTX_TOOLKIT_API void __cdecl SetAlpha(float value); DIRECTX_TOOLKIT_API void XM_CALLCONV SetColorAndAlpha(FXMVECTOR value); @@ -802,13 +802,20 @@ namespace DirectX // Texture setting. DIRECTX_TOOLKIT_API void __cdecl SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor); - // Cel shading settings. + // Cel shading setting. 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); + // Rim lighting settings. + DIRECTX_TOOLKIT_API void XM_CALLCONV SetRimLightingColor(FXMVECTOR value); + DIRECTX_TOOLKIT_API void __cdecl SetRimLightingPower(float power); + DIRECTX_TOOLKIT_API void __cdecl SetRimLightingIntensity(float strength); + DIRECTX_TOOLKIT_API void __cdecl SetRimLightingRange(float start, float end); + DIRECTX_TOOLKIT_API void __cdecl DisableRimLighting(); + private: // Private implementation. class Impl; diff --git a/Src/NPREffect.cpp b/Src/NPREffect.cpp index e9e73c33..83733abf 100644 --- a/Src/NPREffect.cpp +++ b/Src/NPREffect.cpp @@ -19,7 +19,9 @@ namespace { XMVECTOR lightDirectionAndCelBands; XMVECTOR diffuseColorAndAlpha; - XMVECTOR specularColorAndSpecularPower; + XMVECTOR specularColorAndSpecularThreshold; + XMVECTOR rimColorAndPower; + XMVECTOR extraSettings; // .x = SpecularSmoothing, .y = RimStrength, .z = RimStart, .w = RimEnd XMVECTOR goochCoolColorAndAlpha; XMVECTOR goochWarmColorAndBeta; XMVECTOR eyePosition; @@ -46,7 +48,9 @@ namespace // 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_defaultSpecular = { { { 1.f, 1.f, 1.f, 0.95f } } }; + constexpr XMVECTORF32 s_defaultRim = { { { 0.f, 0.f, 0.f, 4.f } } }; + constexpr XMVECTORF32 s_defaultExtraSettings = { { { 0.004f, 0.75f, 0.2f, 0.6f } } }; constexpr XMVECTORF32 s_defaultCool = { { { 0.f, 0.f, 0.55f, 0.25f } } }; constexpr XMVECTORF32 s_defaultWarm = { { { 0.3f, 0.3f, 0.f, 0.25f } } }; } @@ -372,7 +376,9 @@ NPREffect::Impl::Impl( constants.lightDirectionAndCelBands = s_defaultLightDir; constants.diffuseColorAndAlpha = s_defaultDiffuse; - constants.specularColorAndSpecularPower = s_defaultSpecular; + constants.specularColorAndSpecularThreshold = s_defaultSpecular; + constants.rimColorAndPower = s_defaultRim; + constants.extraSettings = s_defaultExtraSettings; constants.goochCoolColorAndAlpha = s_defaultCool; constants.goochWarmColorAndBeta = s_defaultWarm; constants.eyePosition = g_XMZero; @@ -665,18 +671,30 @@ void NPREffect::SetDiffuseColor(FXMVECTOR value) void NPREffect::SetSpecularColor(FXMVECTOR value) { - // TODO: specular power needs to be specular threshold! - // Set xyz, preserve w (specular power). - pImpl->constants.specularColorAndSpecularPower = XMVectorSelect(pImpl->constants.specularColorAndSpecularPower, value, g_XMSelect1110); + // Set xyz, preserve w (specular threshold). + pImpl->constants.specularColorAndSpecularThreshold = XMVectorSelect(pImpl->constants.specularColorAndSpecularThreshold, value, g_XMSelect1110); pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; } -void NPREffect::SetSpecularPower(float value) +void NPREffect::SetSpecularThreshold(float threshold, float smoothing) { - // Set w of specularColorAndSpecularPower. - pImpl->constants.specularColorAndSpecularPower = XMVectorSetW(pImpl->constants.specularColorAndSpecularPower, value); + if (threshold < 0.f || threshold > 1.f) + { + throw std::invalid_argument("Specular threshold must be between 0 and 1"); + } + + if (smoothing < 0.f || smoothing > 1.f) + { + throw std::invalid_argument("Specular smoothing must be between 0 and 1"); + } + + // Set w of specularColorAndSpecularThreshold. + pImpl->constants.specularColorAndSpecularThreshold = XMVectorSetW(pImpl->constants.specularColorAndSpecularThreshold, threshold); + + // Set x of extraSettings. + pImpl->constants.extraSettings = XMVectorSetX(pImpl->constants.extraSettings, smoothing); pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; } @@ -684,10 +702,8 @@ void NPREffect::SetSpecularPower(float value) void NPREffect::DisableSpecular() { - // Set specular color to black, power to 1 - // Note: Don't use a power of 0 or the shader will generate strange highlights on non-specular materials - - pImpl->constants.specularColorAndSpecularPower = g_XMIdentityR3; + // Set specular color to black, threshold to 1 + pImpl->constants.specularColorAndSpecularThreshold = g_XMIdentityR3; pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; } @@ -718,7 +734,7 @@ void NPREffect::SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_ } -// Cel shading settings. +// Cel shading setting. void NPREffect::SetCelShaderBands(int bands) { // Set w of lightDirectionAndCelBands. @@ -743,3 +759,59 @@ void NPREffect::SetGoochWarmColor(FXMVECTOR value, float beta) pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; } + + +// Rim lighting settings. +void NPREffect::SetRimLightingColor(FXMVECTOR value) +{ + // Set xyz, preserve w (rim power). + pImpl->constants.rimColorAndPower = XMVectorSelect(pImpl->constants.rimColorAndPower, value, g_XMSelect1110); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void NPREffect::SetRimLightingPower(float power) +{ + // Set w of rimColorAndPower. + pImpl->constants.rimColorAndPower = XMVectorSetW(pImpl->constants.rimColorAndPower, power); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void NPREffect::SetRimLightingIntensity(float strength) +{ + if (strength < 0.f || strength > 1.f) + { + throw std::invalid_argument("Rim lighting strength must be between 0 and 1"); + } + + // Set y of extraSettings. + pImpl->constants.extraSettings = XMVectorSetY(pImpl->constants.extraSettings, strength); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void NPREffect::SetRimLightingRange(float start, float end) +{ + if (start < 0.f || end > 1.f || start > end) + { + throw std::invalid_argument("Rim lighting start/end must be between 0 and 1"); + } + + // Set zw of extraSettings. + XMVECTORF32 range = { { { 0.f, 0.f, start, end } } }; + pImpl->constants.extraSettings = XMVectorSelect(range, pImpl->constants.extraSettings, g_XMSelect1100); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void NPREffect::DisableRimLighting() +{ + pImpl->constants.rimColorAndPower = g_XMIdentityR3; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} diff --git a/Src/Shaders/NPREffect.fx b/Src/Shaders/NPREffect.fx index 6e8d0904..e27f835f 100644 --- a/Src/Shaders/NPREffect.fx +++ b/Src/Shaders/NPREffect.fx @@ -1,13 +1,7 @@ -//-------------------------------------------------------------------------------------- -// 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 -//-------------------------------------------------------------------------------------- Texture2D Texture : register(t0); @@ -23,19 +17,27 @@ cbuffer Parameters : register(b0) float Alpha : packoffset(c1.w); float3 SpecularColor : packoffset(c2); - float SpecularPower : packoffset(c2.w); + float SpecularThreshold : packoffset(c2.w); + + float3 RimColor : packoffset(c3); + float RimPower : packoffset(c3.w); - float3 GoochCoolColor : packoffset(c3); - float GoochAlpha : packoffset(c3.w); + float SpecularSmoothing : packoffset(c4.x); + float RimStrength : packoffset(c4.y); + float RimStart : packoffset(c4.z); + float RimEnd : packoffset(c4.w); - float3 GoochWarmColor : packoffset(c4); - float GoochBeta : packoffset(c4.w); + float3 GoochCoolColor : packoffset(c5); + float GoochAlpha : packoffset(c5.w); - float3 EyePosition : packoffset(c5); + float3 GoochWarmColor : packoffset(c6); + float GoochBeta : packoffset(c6.w); - float4x4 World : packoffset(c6); - float3x3 WorldInverseTranspose : packoffset(c10); - float4x4 WorldViewProj : packoffset(c13); + float3 EyePosition : packoffset(c7); + + float4x4 World : packoffset(c8); + float3x3 WorldInverseTranspose : packoffset(c12); + float4x4 WorldViewProj : packoffset(c15); }; @@ -43,32 +45,41 @@ cbuffer Parameters : register(b0) #include "Structures.fxh" #include "Utilities.fxh" +VSOutputPixelLighting ComputeCommonVS(float4 position, float3 normal) +{ + VSOutputPixelLighting vout; + vout.PositionPS = mul(position, WorldViewProj); + vout.PositionWS = float4(mul(position, World).xyz, 1); + vout.NormalWS = normalize(mul(normal, WorldInverseTranspose)); + return vout; +} + +VSOutputPixelLightingTx ComputeCommonVSTx(float4 position, float3 normal, float2 texcoord) +{ + VSOutputPixelLightingTx vout; + vout.TexCoord = texcoord; + vout.PositionPS = mul(position, WorldViewProj); + vout.PositionWS = float4(mul(position, World).xyz, 1); + vout.NormalWS = normalize(mul(normal, WorldInverseTranspose)); + return vout; +} + // Vertex shader: basic. [RootSignature(NoTextureRS)] VSOutputPixelLighting VSNPREffect(VSInputNm vin) { - VSOutputPixelLighting vout; - - vout.PositionPS = mul(vin.Position, WorldViewProj); - vout.PositionWS = float4(mul(vin.Position, World).xyz, 1); - vout.NormalWS = normalize(mul(vin.Normal, WorldInverseTranspose)); + VSOutputPixelLighting vout = ComputeCommonVS(vin.Position, vin.Normal); vout.Diffuse = float4(DiffuseColor, Alpha); - return vout; } [RootSignature(NoTextureRS)] VSOutputPixelLighting VSNPREffectBn(VSInputNm vin) { - VSOutputPixelLighting 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)); + VSOutputPixelLighting vout = ComputeCommonVS(vin.Position, normal); vout.Diffuse = float4(DiffuseColor, Alpha); - return vout; } @@ -77,30 +88,20 @@ VSOutputPixelLighting VSNPREffectBn(VSInputNm vin) [RootSignature(NoTextureRS)] VSOutputPixelLighting VSNPREffectVc(VSInputNmVc vin) { - VSOutputPixelLighting vout; - - vout.PositionPS = mul(vin.Position, WorldViewProj); - vout.PositionWS = float4(mul(vin.Position, World).xyz, 1); - vout.NormalWS = normalize(mul(vin.Normal, WorldInverseTranspose)); + VSOutputPixelLighting vout = ComputeCommonVS(vin.Position, vin.Normal); vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor; vout.Diffuse.a = vin.Color.a * Alpha; - return vout; } [RootSignature(NoTextureRS)] VSOutputPixelLighting VSNPREffectVcBn(VSInputNmVc vin) { - VSOutputPixelLighting 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)); + VSOutputPixelLighting vout = ComputeCommonVS(vin.Position, normal); vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor; vout.Diffuse.a = vin.Color.a * Alpha; - return vout; } @@ -109,32 +110,20 @@ VSOutputPixelLighting VSNPREffectVcBn(VSInputNmVc vin) [RootSignature(NoTextureRS)] VSOutputPixelLighting VSNPREffectInst(VSInputNmInst vin) { - VSOutputPixelLighting 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)); + VSOutputPixelLighting vout = ComputeCommonVS(inst.Position, inst.Normal); vout.Diffuse = float4(DiffuseColor, Alpha); - return vout; } [RootSignature(NoTextureRS)] VSOutputPixelLighting VSNPREffectBnInst(VSInputNmInst vin) { - VSOutputPixelLighting 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)); + VSOutputPixelLighting vout = ComputeCommonVS(inst.Position, inst.Normal); vout.Diffuse = float4(DiffuseColor, Alpha); - return vout; } @@ -143,34 +132,22 @@ VSOutputPixelLighting VSNPREffectBnInst(VSInputNmInst vin) [RootSignature(NoTextureRS)] VSOutputPixelLighting VSNPREffectVcInst(VSInputNmVcInst vin) { - VSOutputPixelLighting 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)); + VSOutputPixelLighting vout = ComputeCommonVS(inst.Position, inst.Normal); vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor; vout.Diffuse.a = vin.Color.a * Alpha; - return vout; } [RootSignature(NoTextureRS)] VSOutputPixelLighting VSNPREffectVcBnInst(VSInputNmVcInst vin) { - VSOutputPixelLighting 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)); + VSOutputPixelLighting vout = ComputeCommonVS(inst.Position, inst.Normal); vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor; vout.Diffuse.a = vin.Color.a * Alpha; - return vout; } @@ -179,30 +156,18 @@ VSOutputPixelLighting VSNPREffectVcBnInst(VSInputNmVcInst vin) [RootSignature(MainRS)] VSOutputPixelLightingTx VSNPREffectTx(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)); + VSOutputPixelLightingTx vout = ComputeCommonVSTx(vin.Position, vin.Normal, vin.TexCoord); vout.Diffuse = float4(DiffuseColor, Alpha); - vout.TexCoord = vin.TexCoord; - return vout; } [RootSignature(MainRS)] VSOutputPixelLightingTx VSNPREffectBnTx(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)); + VSOutputPixelLightingTx vout = ComputeCommonVSTx(vin.Position, normal, vin.TexCoord); vout.Diffuse = float4(DiffuseColor, Alpha); - vout.TexCoord = vin.TexCoord; - return vout; } @@ -211,32 +176,20 @@ VSOutputPixelLightingTx VSNPREffectBnTx(VSInputNmTx vin) [RootSignature(MainRS)] VSOutputPixelLightingTx VSNPREffectVcTx(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)); + VSOutputPixelLightingTx vout = ComputeCommonVSTx(vin.Position, vin.Normal, vin.TexCoord); vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor; vout.Diffuse.a = vin.Color.a * Alpha; - vout.TexCoord = vin.TexCoord; - return vout; } [RootSignature(MainRS)] VSOutputPixelLightingTx VSNPREffectVcBnTx(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)); + VSOutputPixelLightingTx vout = ComputeCommonVSTx(vin.Position, normal, vin.TexCoord); vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor; vout.Diffuse.a = vin.Color.a * Alpha; - vout.TexCoord = vin.TexCoord; - return vout; } @@ -245,34 +198,20 @@ VSOutputPixelLightingTx VSNPREffectVcBnTx(VSInputNmTxVc vin) [RootSignature(MainRS)] VSOutputPixelLightingTx VSNPREffectInstTx(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)); + VSOutputPixelLightingTx vout = ComputeCommonVSTx(inst.Position, inst.Normal, vin.TexCoord); vout.Diffuse = float4(DiffuseColor, Alpha); - vout.TexCoord = vin.TexCoord; - return vout; } [RootSignature(MainRS)] VSOutputPixelLightingTx VSNPREffectBnInstTx(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)); + VSOutputPixelLightingTx vout = ComputeCommonVSTx(inst.Position, inst.Normal, vin.TexCoord); vout.Diffuse = float4(DiffuseColor, Alpha); - vout.TexCoord = vin.TexCoord; - return vout; } @@ -281,62 +220,70 @@ VSOutputPixelLightingTx VSNPREffectBnInstTx(VSInputNmTxInst vin) [RootSignature(MainRS)] VSOutputPixelLightingTx VSNPREffectVcInstTx(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)); + VSOutputPixelLightingTx vout = ComputeCommonVSTx(inst.Position, inst.Normal, vin.TexCoord); vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor; vout.Diffuse.a = vin.Color.a * Alpha; - vout.TexCoord = vin.TexCoord; - return vout; } [RootSignature(MainRS)] VSOutputPixelLightingTx VSNPREffectVcBnInstTx(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)); + VSOutputPixelLightingTx vout = ComputeCommonVSTx(inst.Position, inst.Normal, vin.TexCoord); vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor; vout.Diffuse.a = vin.Color.a * Alpha; - vout.TexCoord = vin.TexCoord; - return vout; } +//--- Cel shading --- +float Quantize(float intensity, float bands) +{ + intensity = max(0, intensity); + return floor(intensity * bands) / bands; +} + +float HardEdgeSpecular(float dot1) +{ + return smoothstep(SpecularThreshold - SpecularSmoothing, SpecularThreshold + SpecularSmoothing, dot1); +} + +float RimLighting(float dot1) +{ + float fresnel = pow(1.0 - saturate(dot1), RimPower); + return smoothstep(RimStart, RimEnd, fresnel) * RimStrength; +} + + // Pixel shader: cel shading. [RootSignature(NoTextureRS)] float4 PSCelShading(PSInputPixelLighting pin) : SV_Target0 { float3 normal = normalize(pin.NormalWS); - float3 lightDir = normalize(-LightDirection); - // Quantize the diffuse lighting into discrete bands + float3 lightDir = normalize(-LightDirection); 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); + float NdotV = max(0, dot(normal, viewDir)); + float3 halfVec = normalize(lightDir + viewDir); float NdotH = max(0, dot(normal, halfVec)); - //TODO: Need to revisit for cel - float specular = step(0.95, pow(NdotH, SpecularPower)); + // Quantize the diffuse lighting into discrete bands + float quantized = Quantize(NdotL, CelBands); + float3 color = pin.Diffuse.rgb * quantized; + + // Rim lighting + float outline = RimLighting(NdotV); + color = lerp(color, RimColor, outline); + // Specular highlight (hard edge) + float specular = HardEdgeSpecular(NdotH); color += SpecularColor * specular; return float4(color, pin.Diffuse.a); @@ -348,55 +295,71 @@ float4 PSCelShading(PSInputPixelLighting pin) : SV_Target0 float4 PSCelShadingTx(PSInputPixelLightingTx pin) : SV_Target0 { float3 normal = normalize(pin.NormalWS); + float3 lightDir = normalize(-LightDirection); + float NdotL = dot(normal, lightDir); + + float3 viewDir = normalize(EyePosition - pin.PositionWS.xyz); + float NdotV = max(0, dot(normal, viewDir)); + float VdotL = max(0, dot(viewDir, lightDir)); + + float3 halfVec = normalize(lightDir + viewDir); + float NdotH = max(0, dot(normal, halfVec)); // Sample base texture float4 texColor = Texture.Sample(Sampler, pin.TexCoord); // Quantize the diffuse lighting into discrete bands - float NdotL = dot(normal, lightDir); - float intensity = max(0, NdotL); - float quantized = floor(intensity * CelBands) / CelBands; - + float quantized = Quantize(NdotL, CelBands); float3 color = pin.Diffuse.rgb * texColor.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)); - - //TODO: Need to revisit for cel - float specular = step(0.95, pow(NdotH, SpecularPower)); + // Rim lighting + float outline = RimLighting(NdotV); + color = lerp(color, RimColor, outline); + // Specular highlight (hard edge) + float specular = HardEdgeSpecular(NdotH); color += SpecularColor * specular; return float4(color, pin.Diffuse.a * texColor.a); } +//--- Gooch shading --- +float3 GoochShading(float dot1, float3 color) +{ + float t = (1.0 + dot1) * 0.5; + + float3 coolContrib = GoochCoolColor + GoochAlpha * color; + float3 warmContrib = GoochWarmColor + GoochBeta * color; + + return lerp(coolContrib, warmContrib, t); +} + + // Pixel shader: Gooch shading. [RootSignature(NoTextureRS)] float4 PSGoochShading(PSInputPixelLighting 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); + float NdotV = max(0, dot(normal, viewDir)); + float3 reflectDir = reflect(LightDirection, normal); + float RdotV = max(0, dot(viewDir, reflectDir)); + + // Gooch diffuse term: blend between cool and warm based + float3 color = GoochShading(NdotL, pin.Diffuse.rgb); - //TODO: Need to revisit for Gooch - float specular = pow(max(0, dot(viewDir, reflectDir)), SpecularPower); + // Rim lighting + float outline = RimLighting(NdotV); + color += lerp(color, RimColor, outline); + // Specular highlight (hard edge) + float specular = HardEdgeSpecular(RdotV); color += SpecularColor * specular; return float4(color, pin.Diffuse.a); @@ -409,26 +372,26 @@ float4 PSGoochShadingTx(PSInputPixelLightingTx pin) : SV_Target0 { float3 normal = normalize(pin.NormalWS); float3 lightDir = normalize(-LightDirection); + float NdotL = dot(normal, lightDir); + + float3 viewDir = normalize(EyePosition - pin.PositionWS.xyz); + float NdotV = max(0, dot(normal, viewDir)); + + float3 reflectDir = reflect(LightDirection, normal); + float RdotV = max(0, dot(viewDir, reflectDir)); // Sample base texture float4 texColor = Texture.Sample(Sampler, pin.TexCoord); - // 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 * texColor.rgb; - float3 warmContrib = GoochWarmColor + GoochBeta * pin.Diffuse.rgb * texColor.rgb; + // Gooch diffuse term: blend between cool and warm based + float3 color = GoochShading(NdotL, pin.Diffuse.rgb * texColor.rgb); - float3 color = lerp(coolContrib, warmContrib, t); + // Rim lighting + float outline = RimLighting(NdotV); + color += lerp(color, RimColor, outline); // Specular highlight - float3 viewDir = normalize(EyePosition - pin.PositionWS.xyz); - float3 reflectDir = reflect(LightDirection, normal); - - //TODO: Need to revisit for Gooch - float specular = pow(max(0, dot(viewDir, reflectDir)), SpecularPower); - + float specular = HardEdgeSpecular(RdotV); color += SpecularColor * specular; return float4(color, pin.Diffuse.a * texColor.a); From 7cd339392d3fc414df745741fd5a10f05727c945 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Thu, 16 Jul 2026 16:50:40 -0700 Subject: [PATCH 6/9] Code review --- Src/NPREffect.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Src/NPREffect.cpp b/Src/NPREffect.cpp index 83733abf..cdca6a35 100644 --- a/Src/NPREffect.cpp +++ b/Src/NPREffect.cpp @@ -737,6 +737,11 @@ void NPREffect::SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_ // Cel shading setting. void NPREffect::SetCelShaderBands(int bands) { + if (bands < 1) + { + throw std::invalid_argument("Cel shading bands must be greater than 0"); + } + // Set w of lightDirectionAndCelBands. pImpl->constants.lightDirectionAndCelBands = XMVectorSetW(pImpl->constants.lightDirectionAndCelBands, static_cast(bands)); From 342be45695230321b549b211822568115eb792f7 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Thu, 16 Jul 2026 17:47:58 -0700 Subject: [PATCH 7/9] Update copilot instr --- .github/copilot-instructions.md | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index f98a6054..7ba730e0 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -335,11 +335,37 @@ When reviewing documentation, do the following: - Don't fix pre-existing issues unrelated to your task. However, if you discover bugs directly caused by or tightly coupled to the code you're changing, fix those too. - Update documentation if it is directly related to the changes you are making. - Always validate that your changes don't break existing behavior. -- Only run linters, builds and tests that already exist. Do not add new linting, building or testing tools unless necessary. -- Use the smallest targeted test, build, or lint command that covers the changed behavior. + +### Linting, Building, Testing + +- Only run linters, builds and tests that already exist. Do not add new linting, building or testing tools unless necessary for the task. +- Use the smallest targeted test, build, or lint command that covers the changed behavior. When related targeted selectors use the same runner, include them in one invocation; escalate to full-suite or baseline runs only when targeted validation shows they are needed. +- Documentation changes do not need to be linted, built or tested unless there are specific tests for documentation. + +### Using Ecosystem Tools + - Prefer ecosystem tools (package managers, scaffolding, refactoring tools, linters) over manual changes. -- Only comment code that needs clarification. Do not comment otherwise. +- Install packages only when changing dependencies or after a missing-dependency failure. + +### Code Commenting Style + +- Only comment code that needs a bit of clarification. Do not comment otherwise. ## Release Process The release process is documented in the [release skill](.github/skills/release/SKILL.md). Invoke the `release` skill for step-by-step guidance when performing a release. + +## CoPilot Skills + +The project includes published CoPilot skills for developers: + +- **Release-Process**: Guide for performing the DirectX Tool Kit release process. Invoked when asked to help with releasing a new version, publishing packages, or updating ports. See [Release Process skill](.github/skills/release/SKILL.md) for details. +- **directxtk12-usage**: Provides usage guidance for the DirectX Tool Kit for DX12 library. Located in the `skills/directxtk12-usage/` directory. +- **directxtk12-tutorial**: Provides an interactive tutorial which can create a project, add the toolkit, and implement features in a step-by-step fashion. Located in the `skills/directxtk12-tutorial` directory. + +To use these skills in the Copilot CLI: + +```bash +/skills list +/skills search directxtk12 +``` From 21974fedb609d334fda449d0a46f86474e1ff854 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Fri, 17 Jul 2026 01:46:14 -0700 Subject: [PATCH 8/9] Add MatCap shading --- Inc/Effects.h | 7 +- Src/NPREffect.cpp | 315 ++++++++++++++++++++++++++++----- Src/Shaders/CompileShaders.cmd | 23 +++ Src/Shaders/NPREffect.fx | 146 +++++++++++++++ Src/Shaders/RootSig.fxh | 22 +++ 5 files changed, 463 insertions(+), 50 deletions(-) diff --git a/Inc/Effects.h b/Inc/Effects.h index 83b7c050..3fd7a300 100644 --- a/Inc/Effects.h +++ b/Inc/Effects.h @@ -752,7 +752,7 @@ namespace DirectX //------------------------------------------------------------------------------ - // Built-in shader for non-photorealistic rendering (cel shading, Gooch shading). + // Built-in shader for non-photorealistic rendering class NPREffect : public IEffect, public IEffectMatrices, public IEffectLights { public: @@ -760,6 +760,7 @@ namespace DirectX { Mode_Cel = 0, // Cel (toon) shading Mode_Gooch, // Gooch shading + Mode_MatCap, // Material Capture shading }; DIRECTX_TOOLKIT_API NPREffect( @@ -809,6 +810,10 @@ namespace DirectX 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); + // MatCap shading settings. + DIRECTX_TOOLKIT_API void __cdecl SetMatCap(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor); + DIRECTX_TOOLKIT_API void __cdecl SetMatCap(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor); + // Rim lighting settings. DIRECTX_TOOLKIT_API void XM_CALLCONV SetRimLightingColor(FXMVECTOR value); DIRECTX_TOOLKIT_API void __cdecl SetRimLightingPower(float power); diff --git a/Src/NPREffect.cpp b/Src/NPREffect.cpp index cdca6a35..a493644d 100644 --- a/Src/NPREffect.cpp +++ b/Src/NPREffect.cpp @@ -38,10 +38,10 @@ namespace { using ConstantBufferType = NPREffectConstants; - static constexpr int VertexShaderCount = 16; - static constexpr int PixelShaderCount = 4; - static constexpr int ShaderPermutationCount = 32; - static constexpr int RootSignatureCount = 2; + static constexpr int VertexShaderCount = 32; + static constexpr int PixelShaderCount = 6; + static constexpr int ShaderPermutationCount = 48; + static constexpr int RootSignatureCount = 3; }; @@ -74,13 +74,16 @@ class NPREffect::Impl : public EffectBase ConstantBuffer, TextureSRV, TextureSampler, + Texture2SRV, RootParameterCount }; + bool matCapEnabled; bool textureEnabled; D3D12_GPU_DESCRIPTOR_HANDLE texture; D3D12_GPU_DESCRIPTOR_HANDLE sampler; + D3D12_GPU_DESCRIPTOR_HANDLE matcap; int GetPipelineStatePermutation(NPREffect::Mode nprMode, uint32_t effectFlags) const noexcept; @@ -122,6 +125,33 @@ namespace #include "XboxGamingScarlettNPREffect_PSGoochShading.inc" #include "XboxGamingScarlettNPREffect_PSGoochShadingTx.inc" + +#include "XboxGamingScarlettNPREffect_VSNPREffectMC.inc" +#include "XboxGamingScarlettNPREffect_VSNPREffectInstMC.inc" + +#include "XboxGamingScarlettNPREffect_VSNPREffectVcMC.inc" +#include "XboxGamingScarlettNPREffect_VSNPREffectVcInstMC.inc" + +#include "XboxGamingScarlettNPREffect_VSNPREffectBnMC.inc" +#include "XboxGamingScarlettNPREffect_VSNPREffectBnInstMC.inc" + +#include "XboxGamingScarlettNPREffect_VSNPREffectVcBnMC.inc" +#include "XboxGamingScarlettNPREffect_VSNPREffectVcBnInstMC.inc" + +#include "XboxGamingScarlettNPREffect_VSNPREffectTxMC.inc" +#include "XboxGamingScarlettNPREffect_VSNPREffectInstTxMC.inc" + +#include "XboxGamingScarlettNPREffect_VSNPREffectVcTxMC.inc" +#include "XboxGamingScarlettNPREffect_VSNPREffectVcInstTxMC.inc" + +#include "XboxGamingScarlettNPREffect_VSNPREffectBnTxMC.inc" +#include "XboxGamingScarlettNPREffect_VSNPREffectBnInstTxMC.inc" + +#include "XboxGamingScarlettNPREffect_VSNPREffectVcBnTxMC.inc" +#include "XboxGamingScarlettNPREffect_VSNPREffectVcBnInstTxMC.inc" + +#include "XboxGamingScarlettNPREffect_PSMatCapShading.inc" +#include "XboxGamingScarlettNPREffect_PSMatCapShadingTx.inc" #elif defined(_GAMING_XBOX) #include "XboxGamingXboxOneNPREffect_VSNPREffect.inc" #include "XboxGamingXboxOneNPREffect_VSNPREffectInst.inc" @@ -152,6 +182,33 @@ namespace #include "XboxGamingXboxOneNPREffect_PSGoochShading.inc" #include "XboxGamingXboxOneNPREffect_PSGoochShadingTx.inc" + +#include "XboxGamingXboxOneNPREffect_VSNPREffectMC.inc" +#include "XboxGamingXboxOneNPREffect_VSNPREffectInstMC.inc" + +#include "XboxGamingXboxOneNPREffect_VSNPREffectVcMC.inc" +#include "XboxGamingXboxOneNPREffect_VSNPREffectVcInstMC.inc" + +#include "XboxGamingXboxOneNPREffect_VSNPREffectBnMC.inc" +#include "XboxGamingXboxOneNPREffect_VSNPREffectBnInstMC.inc" + +#include "XboxGamingXboxOneNPREffect_VSNPREffectVcBnMC.inc" +#include "XboxGamingXboxOneNPREffect_VSNPREffectVcBnInstMC.inc" + +#include "XboxGamingXboxOneNPREffect_VSNPREffectTxMC.inc" +#include "XboxGamingXboxOneNPREffect_VSNPREffectInstTxMC.inc" + +#include "XboxGamingXboxOneNPREffect_VSNPREffectVcTxMC.inc" +#include "XboxGamingXboxOneNPREffect_VSNPREffectVcInstTxMC.inc" + +#include "XboxGamingXboxOneNPREffect_VSNPREffectBnTxMC.inc" +#include "XboxGamingXboxOneNPREffect_VSNPREffectBnInstTxMC.inc" + +#include "XboxGamingXboxOneNPREffect_VSNPREffectVcBnTxMC.inc" +#include "XboxGamingXboxOneNPREffect_VSNPREffectVcBnInstTxMC.inc" + +#include "XboxGamingXboxOneNPREffect_PSMatCapShading.inc" +#include "XboxGamingXboxOneNPREffect_PSMatCapShadingTx.inc" #elif defined(_XBOX_ONE) && defined(_TITLE) #include "XboxOneNPREffect_VSNPREffect.inc" #include "XboxOneNPREffect_VSNPREffectInst.inc" @@ -182,6 +239,33 @@ namespace #include "XboxOneNPREffect_PSGoochShading.inc" #include "XboxOneNPREffect_PSGoochShadingTx.inc" + +#include "XboxOneNPREffect_VSNPREffectMC.inc" +#include "XboxOneNPREffect_VSNPREffectInstMC.inc" + +#include "XboxOneNPREffect_VSNPREffectVcMC.inc" +#include "XboxOneNPREffect_VSNPREffectVcInstMC.inc" + +#include "XboxOneNPREffect_VSNPREffectBnMC.inc" +#include "XboxOneNPREffect_VSNPREffectBnInstMC.inc" + +#include "XboxOneNPREffect_VSNPREffectVcBnMC.inc" +#include "XboxOneNPREffect_VSNPREffectVcBnInstMC.inc" + +#include "XboxOneNPREffect_VSNPREffectTxMC.inc" +#include "XboxOneNPREffect_VSNPREffectInstTxMC.inc" + +#include "XboxOneNPREffect_VSNPREffectVcTxMC.inc" +#include "XboxOneNPREffect_VSNPREffectVcInstTxMC.inc" + +#include "XboxOneNPREffect_VSNPREffectBnTxMC.inc" +#include "XboxOneNPREffect_VSNPREffectBnInstTxMC.inc" + +#include "XboxOneNPREffect_VSNPREffectVcBnTxMC.inc" +#include "XboxOneNPREffect_VSNPREffectVcBnInstTxMC.inc" + +#include "XboxOneNPREffect_PSMatCapShading.inc" +#include "XboxOneNPREffect_PSMatCapShadingTx.inc" #else #include "NPREffect_VSNPREffect.inc" #include "NPREffect_VSNPREffectInst.inc" @@ -212,6 +296,33 @@ namespace #include "NPREffect_PSGoochShading.inc" #include "NPREffect_PSGoochShadingTx.inc" + +#include "NPREffect_VSNPREffectMC.inc" +#include "NPREffect_VSNPREffectInstMC.inc" + +#include "NPREffect_VSNPREffectVcMC.inc" +#include "NPREffect_VSNPREffectVcInstMC.inc" + +#include "NPREffect_VSNPREffectBnMC.inc" +#include "NPREffect_VSNPREffectBnInstMC.inc" + +#include "NPREffect_VSNPREffectVcBnMC.inc" +#include "NPREffect_VSNPREffectVcBnInstMC.inc" + +#include "NPREffect_VSNPREffectTxMC.inc" +#include "NPREffect_VSNPREffectInstTxMC.inc" + +#include "NPREffect_VSNPREffectVcTxMC.inc" +#include "NPREffect_VSNPREffectVcInstTxMC.inc" + +#include "NPREffect_VSNPREffectBnTxMC.inc" +#include "NPREffect_VSNPREffectBnInstTxMC.inc" + +#include "NPREffect_VSNPREffectVcBnTxMC.inc" +#include "NPREffect_VSNPREffectVcBnInstTxMC.inc" + +#include "NPREffect_PSMatCapShading.inc" +#include "NPREffect_PSMatCapShadingTx.inc" #endif } @@ -219,22 +330,38 @@ namespace 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) }, - { NPREffect_VSNPREffectTx, sizeof(NPREffect_VSNPREffectTx) }, - { NPREffect_VSNPREffectVcTx, sizeof(NPREffect_VSNPREffectVcTx) }, - { NPREffect_VSNPREffectBnTx, sizeof(NPREffect_VSNPREffectBnTx) }, - { NPREffect_VSNPREffectVcBnTx, sizeof(NPREffect_VSNPREffectVcBnTx) }, - { NPREffect_VSNPREffectInstTx, sizeof(NPREffect_VSNPREffectInstTx) }, - { NPREffect_VSNPREffectVcInstTx, sizeof(NPREffect_VSNPREffectVcInstTx) }, - { NPREffect_VSNPREffectBnInstTx, sizeof(NPREffect_VSNPREffectBnInstTx) }, - { NPREffect_VSNPREffectVcBnInstTx, sizeof(NPREffect_VSNPREffectVcBnInstTx) }, + { 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) }, + { NPREffect_VSNPREffectTx, sizeof(NPREffect_VSNPREffectTx) }, + { NPREffect_VSNPREffectVcTx, sizeof(NPREffect_VSNPREffectVcTx) }, + { NPREffect_VSNPREffectBnTx, sizeof(NPREffect_VSNPREffectBnTx) }, + { NPREffect_VSNPREffectVcBnTx, sizeof(NPREffect_VSNPREffectVcBnTx) }, + { NPREffect_VSNPREffectInstTx, sizeof(NPREffect_VSNPREffectInstTx) }, + { NPREffect_VSNPREffectVcInstTx, sizeof(NPREffect_VSNPREffectVcInstTx) }, + { NPREffect_VSNPREffectBnInstTx, sizeof(NPREffect_VSNPREffectBnInstTx) }, + { NPREffect_VSNPREffectVcBnInstTx, sizeof(NPREffect_VSNPREffectVcBnInstTx) }, + { NPREffect_VSNPREffectMC, sizeof(NPREffect_VSNPREffectMC) }, // Different rootsig + { NPREffect_VSNPREffectVcMC, sizeof(NPREffect_VSNPREffectVcMC) }, + { NPREffect_VSNPREffectBnMC, sizeof(NPREffect_VSNPREffectBnMC) }, + { NPREffect_VSNPREffectVcBnMC, sizeof(NPREffect_VSNPREffectVcBnMC) }, + { NPREffect_VSNPREffectInstMC, sizeof(NPREffect_VSNPREffectInstMC) }, + { NPREffect_VSNPREffectVcInstMC, sizeof(NPREffect_VSNPREffectVcInstMC) }, + { NPREffect_VSNPREffectBnInstMC, sizeof(NPREffect_VSNPREffectBnInstMC) }, + { NPREffect_VSNPREffectVcBnInstMC, sizeof(NPREffect_VSNPREffectVcBnInstMC) }, + { NPREffect_VSNPREffectTxMC, sizeof(NPREffect_VSNPREffectTxMC) }, + { NPREffect_VSNPREffectVcTxMC, sizeof(NPREffect_VSNPREffectVcTxMC) }, + { NPREffect_VSNPREffectBnTxMC, sizeof(NPREffect_VSNPREffectBnTxMC) }, + { NPREffect_VSNPREffectVcBnTxMC, sizeof(NPREffect_VSNPREffectVcBnTxMC) }, + { NPREffect_VSNPREffectInstTxMC, sizeof(NPREffect_VSNPREffectInstTxMC) }, + { NPREffect_VSNPREffectVcInstTxMC, sizeof(NPREffect_VSNPREffectVcInstTxMC) }, + { NPREffect_VSNPREffectBnInstTxMC, sizeof(NPREffect_VSNPREffectBnInstTxMC) }, + { NPREffect_VSNPREffectVcBnInstTxMC, sizeof(NPREffect_VSNPREffectVcBnInstTxMC) }, }; @@ -243,61 +370,79 @@ const int EffectBase::VertexShaderIndices[] = { 0, // cel shading 0, // gooch shading + 16, // matcap shading 1, // vertex color + cel shading 1, // vertex color + gooch shading + 17, // vertex color + matcap shading 2, // cel shading (biased vertex normal) 2, // gooch shading (biased vertex normal) + 18, // matcap shading (biased vertex normal) 3, // vertex color (biased vertex normal) + cel shading 3, // vertex color (biased vertex normal) + gooch shading + 19, // vertex color (biased vertex normal) + matcap shading 4, // instancing + cel shading 4, // instancing + gooch shading + 20, // instancing + matcap shading 5, // instancing + vertex color + cel shading 5, // instancing + vertex color + gooch shading + 21, // instancing + vertex color + matcap shading 6, // instancing (biased vertex normal) + cel shading 6, // instancing (biased vertex normal) + gooch shading + 22, // instancing (biased vertex normal) + matcap shading 7, // instancing + vertex color (biased vertex normal) + cel shading 7, // instancing + vertex color (biased vertex normal) + gooch shading + 23, // instancing + vertex color (biased vertex normal) + matcap shading 8, // cel shading + texture 8, // gooch shading + texture + 24, // matcap shading + texture 9, // vertex color + cel shading + texture 9, // vertex color + gooch shading + texture + 25, // vertex color + matcap shading + texture 10, // cel shading (biased vertex normal) + texture 10, // gooch shading (biased vertex normal) + texture + 26, // matcap shading (biased vertex normal) + texture 11, // vertex color (biased vertex normal) + cel shading + texture 11, // vertex color (biased vertex normal) + gooch shading + texture + 27, // vertex color (biased vertex normal) + matcap shading + texture 12, // instancing + cel shading + texture 12, // instancing + gooch shading + texture + 28, // instancing + matcap shading + texture 13, // instancing + vertex color + cel shading + texture 13, // instancing + vertex color + gooch shading + texture + 29, // instancing + vertex color + matcap shading + texture 14, // instancing (biased vertex normal) + cel shading + texture 14, // instancing (biased vertex normal) + gooch shading + texture + 30, // instancing (biased vertex normal) + matcap shading + texture 15, // instancing + vertex color (biased vertex normal) + cel shading + texture 15, // instancing + vertex color (biased vertex normal) + gooch shading + texture + 31, // instancing + vertex color (biased vertex normal) + matcap shading + texture }; template<> const D3D12_SHADER_BYTECODE EffectBase::PixelShaderBytecode[] = { - { NPREffect_PSCelShading, sizeof(NPREffect_PSCelShading) }, - { NPREffect_PSGoochShading, sizeof(NPREffect_PSGoochShading) }, - { NPREffect_PSCelShadingTx, sizeof(NPREffect_PSCelShadingTx) }, - { NPREffect_PSGoochShadingTx, sizeof(NPREffect_PSGoochShadingTx) }, + { NPREffect_PSCelShading, sizeof(NPREffect_PSCelShading) }, + { NPREffect_PSGoochShading, sizeof(NPREffect_PSGoochShading) }, + { NPREffect_PSMatCapShading, sizeof(NPREffect_PSMatCapShading) }, + { NPREffect_PSCelShadingTx, sizeof(NPREffect_PSCelShadingTx) }, + { NPREffect_PSGoochShadingTx, sizeof(NPREffect_PSGoochShadingTx) }, + { NPREffect_PSMatCapShadingTx, sizeof(NPREffect_PSMatCapShadingTx) }, }; @@ -306,51 +451,67 @@ const int EffectBase::PixelShaderIndices[] = { 0, // cel shading 1, // gooch shading + 2, // matcap shading 0, // vertex color + cel shading 1, // vertex color + gooch shading + 2, // vertex color + matcap shading 0, // cel shading (biased vertex normal) 1, // gooch shading (biased vertex normal) + 2, // matcap shading (biased vertex normal) 0, // vertex color (biased vertex normal) + cel shading 1, // vertex color (biased vertex normal) + gooch shading + 2, // vertex color (biased vertex normal) + matcap shading 0, // instancing + cel shading 1, // instancing + gooch shading + 2, // instancing + matcap shading 0, // instancing + vertex color + cel shading 1, // instancing + vertex color + gooch shading + 2, // instancing + vertex color + matcap shading 0, // instancing (biased vertex normal) + cel shading 1, // instancing (biased vertex normal) + gooch shading + 2, // instancing (biased vertex normal) + matcap shading 0, // instancing + vertex color (biased vertex normal) + cel shading 1, // instancing + vertex color (biased vertex normal) + gooch shading + 2, // instancing + vertex color (biased vertex normal) + matcap shading - 2, // cel shading + texture - 3, // gooch shading + texture + 3, // cel shading + texture + 4, // gooch shading + texture + 5, // matcap shading + texture - 2, // vertex color + cel shading + texture - 3, // vertex color + gooch shading + texture + 3, // vertex color + cel shading + texture + 4, // vertex color + gooch shading + texture + 5, // vertex color + matcap shading + texture - 2, // cel shading (biased vertex normal) + texture - 3, // gooch shading (biased vertex normal) + texture + 3, // cel shading (biased vertex normal) + texture + 4, // gooch shading (biased vertex normal) + texture + 5, // matcap shading (biased vertex normal) + texture - 2, // vertex color (biased vertex normal) + cel shading + texture - 3, // vertex color (biased vertex normal) + gooch shading + texture + 3, // vertex color (biased vertex normal) + cel shading + texture + 4, // vertex color (biased vertex normal) + gooch shading + texture + 5, // vertex color (biased vertex normal) + matcap shading + texture - 2, // instancing + cel shading + texture - 3, // instancing + gooch shading + texture + 3, // instancing + cel shading + texture + 4, // instancing + gooch shading + texture + 5, // instancing + matcap shading + texture - 2, // instancing + vertex color + cel shading + texture - 3, // instancing + vertex color + gooch shading + texture + 3, // instancing + vertex color + cel shading + texture + 4, // instancing + vertex color + gooch shading + texture + 5, // instancing + vertex color + matcap shading + texture - 2, // instancing (biased vertex normal) + cel shading + texture - 3, // instancing (biased vertex normal) + gooch shading + texture + 3, // instancing (biased vertex normal) + cel shading + texture + 4, // instancing (biased vertex normal) + gooch shading + texture + 5, // instancing (biased vertex normal) + matcap shading + texture - 2, // instancing + vertex color (biased vertex normal) + cel shading + texture - 3, // instancing + vertex color (biased vertex normal) + gooch shading + texture + 3, // instancing + vertex color (biased vertex normal) + cel shading + texture + 4, // instancing + vertex color (biased vertex normal) + gooch shading + texture + 5, // instancing + vertex color (biased vertex normal) + matcap shading + texture }; #pragma endregion @@ -367,7 +528,8 @@ NPREffect::Impl::Impl( NPREffect::Mode nprMode) : EffectBase(device), texture{}, - sampler{} + sampler{}, + matcap{} { 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"); @@ -387,6 +549,11 @@ NPREffect::Impl::Impl( { case Mode_Cel: case Mode_Gooch: + matCapEnabled = false; + break; + + case Mode_MatCap: + matCapEnabled = true; break; default: @@ -415,18 +582,35 @@ NPREffect::Impl::Impl( // Root parameter descriptor - conditionally initialized CD3DX12_ROOT_SIGNATURE_DESC rsigDesc = {}; - if (textureEnabled) + if (textureEnabled && matCapEnabled) { - // Include texture and srv + // Include two textures and one sampler const CD3DX12_DESCRIPTOR_RANGE textureSRV(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); const CD3DX12_DESCRIPTOR_RANGE textureSampler(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0); rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &textureSRV, D3D12_SHADER_VISIBILITY_PIXEL); rootParameters[RootParameterIndex::TextureSampler].InitAsDescriptorTable(1, &textureSampler, D3D12_SHADER_VISIBILITY_PIXEL); + const CD3DX12_DESCRIPTOR_RANGE texture2Range(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1); + rootParameters[RootParameterIndex::Texture2SRV].InitAsDescriptorTable(1, &texture2Range, D3D12_SHADER_VISIBILITY_PIXEL); + // use all parameters rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 0, nullptr, rootSignatureFlags); + mRootSignature = GetRootSignature(2, rsigDesc); + } + else if (textureEnabled || matCapEnabled) + { + // Include texture and sampler + const CD3DX12_DESCRIPTOR_RANGE textureSRV(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); + const CD3DX12_DESCRIPTOR_RANGE textureSampler(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0); + + rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &textureSRV, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[RootParameterIndex::TextureSampler].InitAsDescriptorTable(1, &textureSampler, D3D12_SHADER_VISIBILITY_PIXEL); + + // use all but last parameter + rsigDesc.Init(static_cast(std::size(rootParameters) - 1), rootParameters, 0, nullptr, rootSignatureFlags); + mRootSignature = GetRootSignature(1, rsigDesc); } else @@ -470,25 +654,25 @@ int NPREffect::Impl::GetPipelineStatePermutation(NPREffect::Mode nprMode, uint32 // Support vertex coloring? if (effectFlags & EffectFlags::VertexColor) { - permutation += 2; + permutation += 3; } if (effectFlags & EffectFlags::BiasedVertexNormals) { // Compressed normals need to be scaled and biased in the vertex shader. - permutation += 4; + permutation += 6; } if (effectFlags & EffectFlags::Instancing) { // Vertex shader needs to use vertex matrix transform. - permutation += 8; + permutation += 12; } // Use shaders without texture coordinates? if (textureEnabled) { - permutation += 16; + permutation += 24; } return permutation; @@ -536,15 +720,34 @@ void NPREffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList) { if (!texture.ptr || !sampler.ptr) { - DebugTrace("ERROR: Missing texture or sampler for NPREffect (texture %llu, sampler %llu)\n", texture.ptr, sampler.ptr); + DebugTrace("ERROR: Missing texture or sampler for NPREffect (texture %llu, sampler %llu)\n", + texture.ptr, sampler.ptr); throw std::runtime_error("NPREffect"); } - // **NOTE** If D3D asserts or crashes here, you probably need to call commandList->SetDescriptorHeaps() with the required descriptor heaps. + // **NOTE** If D3D asserts or crashes here, you probably need to call commandList->SetDescriptorHeaps() + // with the required descriptor heaps. commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSRV, texture); commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSampler, sampler); } + if (matCapEnabled) + { + if (!matcap.ptr || !sampler.ptr) + { + DebugTrace("ERROR: Missing matcap texture or sampler for NPREffect (texture %llu, sampler %llu)\n", + matcap.ptr, sampler.ptr); + throw std::runtime_error("NPREffect"); + } + + // **NOTE** If D3D asserts or crashes here, you probably need to call commandList->SetDescriptorHeaps() + // with the required descriptor heaps. + commandList->SetGraphicsRootDescriptorTable( + textureEnabled ? RootParameterIndex::Texture2SRV : RootParameterIndex::TextureSRV, + matcap); + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSampler, sampler); + } + // Set constants commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBuffer, GetConstantBufferGpuAddress()); @@ -766,6 +969,20 @@ void NPREffect::SetGoochWarmColor(FXMVECTOR value, float beta) } +// MatCap shading setting. +void NPREffect::SetMatCap(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor) +{ + pImpl->matcap = srvDescriptor; +} + + +void NPREffect::SetMatCap(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor) +{ + pImpl->matcap = srvDescriptor; + pImpl->sampler = samplerDescriptor; +} + + // Rim lighting settings. void NPREffect::SetRimLightingColor(FXMVECTOR value) { diff --git a/Src/Shaders/CompileShaders.cmd b/Src/Shaders/CompileShaders.cmd index c8acb80b..3a817f0b 100644 --- a/Src/Shaders/CompileShaders.cmd +++ b/Src/Shaders/CompileShaders.cmd @@ -278,12 +278,35 @@ call :CompileShader%1 NPREffect vs VSNPREffectBnInstTx call :CompileShader%1 NPREffect vs VSNPREffectVcInstTx call :CompileShader%1 NPREffect vs VSNPREffectVcBnInstTx +call :CompileShader%1 NPREffect vs VSNPREffectMC +call :CompileShader%1 NPREffect vs VSNPREffectBnMC +call :CompileShader%1 NPREffect vs VSNPREffectVcMC +call :CompileShader%1 NPREffect vs VSNPREffectVcBnMC + +call :CompileShader%1 NPREffect vs VSNPREffectInstMC +call :CompileShader%1 NPREffect vs VSNPREffectBnInstMC +call :CompileShader%1 NPREffect vs VSNPREffectVcInstMC +call :CompileShader%1 NPREffect vs VSNPREffectVcBnInstMC + +call :CompileShader%1 NPREffect vs VSNPREffectTxMC +call :CompileShader%1 NPREffect vs VSNPREffectBnTxMC +call :CompileShader%1 NPREffect vs VSNPREffectVcTxMC +call :CompileShader%1 NPREffect vs VSNPREffectVcBnTxMC + +call :CompileShader%1 NPREffect vs VSNPREffectInstTxMC +call :CompileShader%1 NPREffect vs VSNPREffectBnInstTxMC +call :CompileShader%1 NPREffect vs VSNPREffectVcInstTxMC +call :CompileShader%1 NPREffect vs VSNPREffectVcBnInstTxMC + call :CompileShader%1 NPREffect ps PSCelShading call :CompileShader%1 NPREffect ps PSCelShadingTx call :CompileShader%1 NPREffect ps PSGoochShading call :CompileShader%1 NPREffect ps PSGoochShadingTx +call :CompileShader%1 NPREffect ps PSMatCapShading +call :CompileShader%1 NPREffect ps PSMatCapShadingTx + call :CompileShader%1 SpriteEffect vs SpriteVertexShader call :CompileShader%1 SpriteEffect ps SpritePixelShader diff --git a/Src/Shaders/NPREffect.fx b/Src/Shaders/NPREffect.fx index e27f835f..b33d495c 100644 --- a/Src/Shaders/NPREffect.fx +++ b/Src/Shaders/NPREffect.fx @@ -6,6 +6,7 @@ Texture2D Texture : register(t0); SamplerState Sampler : register(s0); +Texture2D Texture2 : register(t1); cbuffer Parameters : register(b0) @@ -73,6 +74,12 @@ VSOutputPixelLighting VSNPREffect(VSInputNm vin) return vout; } +[RootSignature(MainRS)] +VSOutputPixelLighting VSNPREffectMC(VSInputNm vin) +{ + return VSNPREffect(vin); +} + [RootSignature(NoTextureRS)] VSOutputPixelLighting VSNPREffectBn(VSInputNm vin) { @@ -83,6 +90,12 @@ VSOutputPixelLighting VSNPREffectBn(VSInputNm vin) return vout; } +[RootSignature(MainRS)] +VSOutputPixelLighting VSNPREffectBnMC(VSInputNm vin) +{ + return VSNPREffectBn(vin); +} + // Vertex shader: vertex color. [RootSignature(NoTextureRS)] @@ -94,6 +107,12 @@ VSOutputPixelLighting VSNPREffectVc(VSInputNmVc vin) return vout; } +[RootSignature(MainRS)] +VSOutputPixelLighting VSNPREffectVcMC(VSInputNmVc vin) +{ + return VSNPREffectVc(vin); +} + [RootSignature(NoTextureRS)] VSOutputPixelLighting VSNPREffectVcBn(VSInputNmVc vin) { @@ -105,6 +124,13 @@ VSOutputPixelLighting VSNPREffectVcBn(VSInputNmVc vin) return vout; } +[RootSignature(MainRS)] +VSOutputPixelLighting VSNPREffectVcBnMC(VSInputNmVc vin) +{ + return VSNPREffectVcBn(vin); + +} + // Vertex shader: instancing. [RootSignature(NoTextureRS)] @@ -116,6 +142,12 @@ VSOutputPixelLighting VSNPREffectInst(VSInputNmInst vin) return vout; } +[RootSignature(MainRS)] +VSOutputPixelLighting VSNPREffectInstMC(VSInputNmInst vin) +{ + return VSNPREffectInst(vin); +} + [RootSignature(NoTextureRS)] VSOutputPixelLighting VSNPREffectBnInst(VSInputNmInst vin) { @@ -127,6 +159,12 @@ VSOutputPixelLighting VSNPREffectBnInst(VSInputNmInst vin) return vout; } +[RootSignature(MainRS)] +VSOutputPixelLighting VSNPREffectBnInstMC(VSInputNmInst vin) +{ + return VSNPREffectBnInst(vin); +} + // Vertex shader: vertex color + instancing. [RootSignature(NoTextureRS)] @@ -139,6 +177,12 @@ VSOutputPixelLighting VSNPREffectVcInst(VSInputNmVcInst vin) return vout; } +[RootSignature(MainRS)] +VSOutputPixelLighting VSNPREffectVcInstMC(VSInputNmVcInst vin) +{ + return VSNPREffectVcInst(vin); +} + [RootSignature(NoTextureRS)] VSOutputPixelLighting VSNPREffectVcBnInst(VSInputNmVcInst vin) { @@ -151,6 +195,13 @@ VSOutputPixelLighting VSNPREffectVcBnInst(VSInputNmVcInst vin) return vout; } +[RootSignature(MainRS)] +VSOutputPixelLighting VSNPREffectVcBnInstMC(VSInputNmVcInst vin) +{ + return VSNPREffectVcBnInst(vin); + +} + // Vertex shader: texture. [RootSignature(MainRS)] @@ -161,6 +212,12 @@ VSOutputPixelLightingTx VSNPREffectTx(VSInputNmTx vin) return vout; } +[RootSignature(DualTextureOneSamplerRS)] +VSOutputPixelLightingTx VSNPREffectTxMC(VSInputNmTx vin) +{ + return VSNPREffectTx(vin); +} + [RootSignature(MainRS)] VSOutputPixelLightingTx VSNPREffectBnTx(VSInputNmTx vin) { @@ -171,6 +228,13 @@ VSOutputPixelLightingTx VSNPREffectBnTx(VSInputNmTx vin) return vout; } +[RootSignature(DualTextureOneSamplerRS)] +VSOutputPixelLightingTx VSNPREffectBnTxMC(VSInputNmTx vin) +{ + return VSNPREffectBnTx(vin); + +} + // Vertex shader: texture + vertex color. [RootSignature(MainRS)] @@ -182,6 +246,12 @@ VSOutputPixelLightingTx VSNPREffectVcTx(VSInputNmTxVc vin) return vout; } +[RootSignature(DualTextureOneSamplerRS)] +VSOutputPixelLightingTx VSNPREffectVcTxMC(VSInputNmTxVc vin) +{ + return VSNPREffectVcTx(vin); +} + [RootSignature(MainRS)] VSOutputPixelLightingTx VSNPREffectVcBnTx(VSInputNmTxVc vin) { @@ -193,6 +263,13 @@ VSOutputPixelLightingTx VSNPREffectVcBnTx(VSInputNmTxVc vin) return vout; } +[RootSignature(DualTextureOneSamplerRS)] +VSOutputPixelLightingTx VSNPREffectVcBnTxMC(VSInputNmTxVc vin) +{ + return VSNPREffectVcBnTx(vin); + +} + // Vertex shader: texture + instancing. [RootSignature(MainRS)] @@ -204,6 +281,12 @@ VSOutputPixelLightingTx VSNPREffectInstTx(VSInputNmTxInst vin) return vout; } +[RootSignature(DualTextureOneSamplerRS)] +VSOutputPixelLightingTx VSNPREffectInstTxMC(VSInputNmTxInst vin) +{ + return VSNPREffectInstTx(vin); +} + [RootSignature(MainRS)] VSOutputPixelLightingTx VSNPREffectBnInstTx(VSInputNmTxInst vin) { @@ -215,6 +298,13 @@ VSOutputPixelLightingTx VSNPREffectBnInstTx(VSInputNmTxInst vin) return vout; } +[RootSignature(DualTextureOneSamplerRS)] +VSOutputPixelLightingTx VSNPREffectBnInstTxMC(VSInputNmTxInst vin) +{ + return VSNPREffectBnInstTx(vin); + +} + // Vertex shader: texture + vertex color + instancing. [RootSignature(MainRS)] @@ -227,6 +317,12 @@ VSOutputPixelLightingTx VSNPREffectVcInstTx(VSInputNmTxVcInst vin) return vout; } +[RootSignature(DualTextureOneSamplerRS)] +VSOutputPixelLightingTx VSNPREffectVcInstTxMC(VSInputNmTxVcInst vin) +{ + return VSNPREffectVcInstTx(vin); +} + [RootSignature(MainRS)] VSOutputPixelLightingTx VSNPREffectVcBnInstTx(VSInputNmTxVcInst vin) { @@ -239,6 +335,13 @@ VSOutputPixelLightingTx VSNPREffectVcBnInstTx(VSInputNmTxVcInst vin) return vout; } +[RootSignature(DualTextureOneSamplerRS)] +VSOutputPixelLightingTx VSNPREffectVcBnInstTxMC(VSInputNmTxVcInst vin) +{ + return VSNPREffectVcBnInstTx(vin); + +} + //--- Cel shading --- float Quantize(float intensity, float bands) @@ -396,3 +499,46 @@ float4 PSGoochShadingTx(PSInputPixelLightingTx pin) : SV_Target0 return float4(color, pin.Diffuse.a * texColor.a); } + + +//--- MatCap shading --- + +// Pixel shader: MatCap shading. +[RootSignature(MainRS)] +float4 PSMatCapShading(PSInputPixelLighting pin) : SV_Target0 +{ + float3 normal = normalize(pin.NormalWS); + float3 viewDir = normalize(EyePosition - pin.PositionWS.xyz); + float3 reflectDir = reflect(viewDir, normal); + + float2 uv = reflectDir.xy * 0.5 + 0.5; + + // Sample matcap texture + float4 matcap = Texture.Sample(Sampler, uv); + + float3 color = pin.Diffuse.rgb * matcap.rgb; + + return float4(color, pin.Diffuse.a); +} + + +// Pixel shader: MatCap shading + texture. +[RootSignature(DualTextureOneSamplerRS)] +float4 PSMatCapShadingTx(PSInputPixelLightingTx pin) : SV_Target0 +{ + float3 normal = normalize(pin.NormalWS); + float3 viewDir = normalize(EyePosition - pin.PositionWS.xyz); + float3 reflectDir = reflect(viewDir, normal); + + float2 uv = reflectDir.xy * 0.5 + 0.5; + + // Sample base texture + float4 texColor = Texture.Sample(Sampler, pin.TexCoord); + + // Sample matcap texture + float4 matcap = Texture2.Sample(Sampler, uv); + + float3 color = pin.Diffuse.rgb * texColor.rgb * matcap.rgb; + + return float4(color, pin.Diffuse.a * texColor.a); +} diff --git a/Src/Shaders/RootSig.fxh b/Src/Shaders/RootSig.fxh index 45e3198a..416d2509 100644 --- a/Src/Shaders/RootSig.fxh +++ b/Src/Shaders/RootSig.fxh @@ -202,6 +202,18 @@ " DENY_MESH_SHADER_ROOT_ACCESS )," \ "CBV(b0)" +#define DualTextureOneSamplerRS \ +"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),"\ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL )" + #else // !__XBOX_SCARLETT #define NoTextureRS \ @@ -371,4 +383,14 @@ " DENY_HULL_SHADER_ROOT_ACCESS )," \ "CBV(b0)" +#define DualTextureOneSamplerRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS )," \ +"CBV(b0),"\ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL )" + #endif From c9d6ce9c854543ddb3c3f37a7c7ea71ef95c18ff Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Sun, 19 Jul 2026 12:22:24 -0700 Subject: [PATCH 9/9] Code review --- Src/DebugEffect.cpp | 23 +++++--------- Src/EffectCommon.cpp | 75 +++++++++++++++++++++++++++++++++++++++++--- Src/EffectCommon.h | 47 ++++++++++++++++++++++----- Src/NPREffect.cpp | 32 ++++--------------- Src/PBREffect.cpp | 34 +++++--------------- 5 files changed, 131 insertions(+), 80 deletions(-) diff --git a/Src/DebugEffect.cpp b/Src/DebugEffect.cpp index 25d1c939..02329903 100644 --- a/Src/DebugEffect.cpp +++ b/Src/DebugEffect.cpp @@ -369,22 +369,13 @@ int DebugEffect::Impl::GetPipelineStatePermutation(DebugEffect::Mode debugMode, void DebugEffect::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; - } + XMVECTOR eyePosition{}; + matrices.SetConstants( + dirtyFlags, + constants.world, + constants.worldInverseTranspose, + constants.worldViewProj, + eyePosition); UpdateConstants(); diff --git a/Src/EffectCommon.cpp b/Src/EffectCommon.cpp index a662ad3d..584b0816 100644 --- a/Src/EffectCommon.cpp +++ b/Src/EffectCommon.cpp @@ -37,7 +37,8 @@ EffectMatrices::EffectMatrices() noexcept // Lazily recomputes the combined world+view+projection matrix. -_Use_decl_annotations_ void EffectMatrices::SetConstants(int& dirtyFlags, XMMATRIX& worldViewProjConstant) +_Use_decl_annotations_ +void EffectMatrices::SetConstants(int& dirtyFlags, XMMATRIX& worldViewProjConstant) { if (dirtyFlags & EffectDirtyFlags::WorldViewProj) { @@ -51,6 +52,54 @@ _Use_decl_annotations_ void EffectMatrices::SetConstants(int& dirtyFlags, XMMATR } +// Lazily recomputes the combined world+view+projection matrix, inverse, eyePosition, etc. +// This version is used for effects that do not use the EffectLights helper. +_Use_decl_annotations_ +void EffectMatrices::SetConstants( + int& dirtyFlags, + XMMATRIX& worldConstant, + XMVECTOR worldInverseTransposeConstant[3], + XMMATRIX& worldViewProjConstant, + XMVECTOR& eyePositionConstant) +{ + // Combined world+view+projection matrix. + if (dirtyFlags & EffectDirtyFlags::WorldViewProj) + { + worldView = XMMatrixMultiply(world, view); + + worldViewProjConstant = XMMatrixTranspose(XMMatrixMultiply(worldView, projection)); + + dirtyFlags &= ~EffectDirtyFlags::WorldViewProj; + dirtyFlags |= EffectDirtyFlags::ConstantBuffer; + } + + // World inverse transpose matrix. + if (dirtyFlags & EffectDirtyFlags::WorldInverseTranspose) + { + worldConstant = XMMatrixTranspose(world); + + const XMMATRIX worldInverse = XMMatrixInverse(nullptr, world); + + worldInverseTransposeConstant[0] = worldInverse.r[0]; + worldInverseTransposeConstant[1] = worldInverse.r[1]; + worldInverseTransposeConstant[2] = worldInverse.r[2]; + + dirtyFlags &= ~EffectDirtyFlags::WorldInverseTranspose; + dirtyFlags |= EffectDirtyFlags::ConstantBuffer; + } + + // Eye position. + if (dirtyFlags & EffectDirtyFlags::EyePosition) + { + const XMMATRIX viewInverse = XMMatrixInverse(nullptr, view); + eyePositionConstant = viewInverse.r[3]; + + dirtyFlags &= ~EffectDirtyFlags::EyePosition; + dirtyFlags |= EffectDirtyFlags::ConstantBuffer; + } +} + + // Constructor initializes default fog settings. EffectFog::EffectFog() noexcept : enabled(false), @@ -156,7 +205,12 @@ EffectLights::EffectLights() noexcept : #endif // Initializes constant buffer fields to match the current lighting state. -_Use_decl_annotations_ void EffectLights::InitializeConstants(XMVECTOR& specularColorAndPowerConstant, XMVECTOR* lightDirectionConstant, XMVECTOR* lightDiffuseConstant, XMVECTOR* lightSpecularConstant) const +_Use_decl_annotations_ +void EffectLights::InitializeConstants( + XMVECTOR& specularColorAndPowerConstant, + XMVECTOR* lightDirectionConstant, + XMVECTOR* lightDiffuseConstant, + XMVECTOR* lightSpecularConstant) const { static const XMVECTORF32 defaultSpecular = { { { 1, 1, 1, 16 } } }; static const XMVECTORF32 defaultLightDirection = { { { 0, -1, 0, 0 } } }; @@ -179,7 +233,15 @@ _Use_decl_annotations_ void EffectLights::InitializeConstants(XMVECTOR& specular // Lazily recomputes derived parameter values used by shader lighting calculations. _Use_decl_annotations_ -void EffectLights::SetConstants(int& dirtyFlags, EffectMatrices const& matrices, XMMATRIX& worldConstant, XMVECTOR worldInverseTransposeConstant[3], XMVECTOR& eyePositionConstant, XMVECTOR& diffuseColorConstant, XMVECTOR& emissiveColorConstant, bool lightingEnabled) +void EffectLights::SetConstants( + int& dirtyFlags, + EffectMatrices const& matrices, + XMMATRIX& worldConstant, + XMVECTOR worldInverseTransposeConstant[3], + XMVECTOR& eyePositionConstant, + XMVECTOR& diffuseColorConstant, + XMVECTOR& emissiveColorConstant, + bool lightingEnabled) { if (lightingEnabled) { @@ -263,7 +325,12 @@ void EffectLights::SetConstants(int& dirtyFlags, EffectMatrices const& matrices, #endif // Helper for turning one of the directional lights on or off. -_Use_decl_annotations_ int EffectLights::SetLightEnabled(int whichLight, bool value, XMVECTOR* lightDiffuseConstant, XMVECTOR* lightSpecularConstant) +_Use_decl_annotations_ +int EffectLights::SetLightEnabled( + int whichLight, + bool value, + XMVECTOR* lightDiffuseConstant, + XMVECTOR* lightSpecularConstant) { ValidateLightIndex(whichLight); diff --git a/Src/EffectCommon.h b/Src/EffectCommon.h index c5bae970..54381752 100644 --- a/Src/EffectCommon.h +++ b/Src/EffectCommon.h @@ -63,6 +63,14 @@ namespace DirectX XMMATRIX worldView; void SetConstants(_Inout_ int& dirtyFlags, _Inout_ XMMATRIX& worldViewProjConstant); + + // Helper for effects that don't use EffectLights, but have eye position. + void SetConstants( + _Inout_ int& dirtyFlags, + _Inout_ XMMATRIX& worldConstant, + _Inout_updates_(3) XMVECTOR worldInverseTransposeConstant[3], + _Inout_ XMMATRIX& worldViewProjConstant, + _Inout_ XMVECTOR& eyePositionConstant); }; @@ -75,7 +83,10 @@ namespace DirectX float start; float end; - void XM_CALLCONV SetConstants(_Inout_ int& dirtyFlags, _In_ FXMMATRIX worldView, _Inout_ XMVECTOR& fogVectorConstant); + void XM_CALLCONV SetConstants( + _Inout_ int& dirtyFlags, + _In_ FXMMATRIX worldView, + _Inout_ XMVECTOR& fogVectorConstant); }; @@ -109,12 +120,34 @@ namespace DirectX // Methods. - void InitializeConstants(_Out_ XMVECTOR& specularColorAndPowerConstant, _Out_writes_all_(MaxDirectionalLights) XMVECTOR* lightDirectionConstant, _Out_writes_all_(MaxDirectionalLights) XMVECTOR* lightDiffuseConstant, _Out_writes_all_(MaxDirectionalLights) XMVECTOR* lightSpecularConstant) const; - void SetConstants(_Inout_ int& dirtyFlags, _In_ EffectMatrices const& matrices, _Inout_ XMMATRIX& worldConstant, _Inout_updates_(3) XMVECTOR worldInverseTransposeConstant[3], _Inout_ XMVECTOR& eyePositionConstant, _Inout_ XMVECTOR& diffuseColorConstant, _Inout_ XMVECTOR& emissiveColorConstant, bool lightingEnabled); - - int SetLightEnabled(int whichLight, bool value, _Inout_updates_(MaxDirectionalLights) XMVECTOR* lightDiffuseConstant, _Inout_updates_(MaxDirectionalLights) XMVECTOR* lightSpecularConstant); - int XM_CALLCONV SetLightDiffuseColor(int whichLight, FXMVECTOR value, _Inout_updates_(MaxDirectionalLights) XMVECTOR* lightDiffuseConstant); - int XM_CALLCONV SetLightSpecularColor(int whichLight, FXMVECTOR value, _Inout_updates_(MaxDirectionalLights) XMVECTOR* lightSpecularConstant); + void InitializeConstants( + _Out_ XMVECTOR& specularColorAndPowerConstant, + _Out_writes_all_(MaxDirectionalLights) XMVECTOR* lightDirectionConstant, + _Out_writes_all_(MaxDirectionalLights) XMVECTOR* lightDiffuseConstant, + _Out_writes_all_(MaxDirectionalLights) XMVECTOR* lightSpecularConstant) const; + void SetConstants( + _Inout_ int& dirtyFlags, + _In_ EffectMatrices const& matrices, + _Inout_ XMMATRIX& worldConstant, + _Inout_updates_(3) XMVECTOR worldInverseTransposeConstant[3], + _Inout_ XMVECTOR& eyePositionConstant, + _Inout_ XMVECTOR& diffuseColorConstant, + _Inout_ XMVECTOR& emissiveColorConstant, + bool lightingEnabled); + + int SetLightEnabled( + int whichLight, + bool value, + _Inout_updates_(MaxDirectionalLights) XMVECTOR* lightDiffuseConstant, + _Inout_updates_(MaxDirectionalLights) XMVECTOR* lightSpecularConstant); + int XM_CALLCONV SetLightDiffuseColor( + int whichLight, + FXMVECTOR value, + _Inout_updates_(MaxDirectionalLights) XMVECTOR* lightDiffuseConstant); + int XM_CALLCONV SetLightSpecularColor( + int whichLight, + FXMVECTOR value, + _Inout_updates_(MaxDirectionalLights) XMVECTOR* lightSpecularConstant); static void ValidateLightIndex(int whichLight); static void EnableDefaultLighting(_In_ IEffectLights* effect); diff --git a/Src/NPREffect.cpp b/Src/NPREffect.cpp index a493644d..62ac253b 100644 --- a/Src/NPREffect.cpp +++ b/Src/NPREffect.cpp @@ -683,32 +683,12 @@ int NPREffect::Impl::GetPipelineStatePermutation(NPREffect::Mode nprMode, uint32 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; - } + matrices.SetConstants( + dirtyFlags, + constants.world, + constants.worldInverseTranspose, + constants.worldViewProj, + constants.eyePosition); UpdateConstants(); diff --git a/Src/PBREffect.cpp b/Src/PBREffect.cpp index dcbe1be0..59b98f7a 100644 --- a/Src/PBREffect.cpp +++ b/Src/PBREffect.cpp @@ -521,33 +521,12 @@ void PBREffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList) constants.prevWorldViewProj = constants.worldViewProj; // 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 vector. - if (dirtyFlags & EffectDirtyFlags::EyePosition) - { - const XMMATRIX viewInverse = XMMatrixInverse(nullptr, matrices.view); - - constants.eyePosition = viewInverse.r[3]; - - dirtyFlags &= ~EffectDirtyFlags::EyePosition; - dirtyFlags |= EffectDirtyFlags::ConstantBuffer; - } + matrices.SetConstants( + dirtyFlags, + constants.world, + constants.worldInverseTranspose, + constants.worldViewProj, + constants.eyePosition); // Set constants to GPU UpdateConstants(); @@ -874,6 +853,7 @@ void PBREffect::SetRenderTargetSizeInPixels(int width, int height) SkinnedPBREffect::~SkinnedPBREffect() {} + // Animation settings. void SkinnedPBREffect::SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) {