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
+```
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..3fd7a300 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);
@@ -751,6 +751,90 @@ namespace DirectX
};
+ //------------------------------------------------------------------------------
+ // Built-in shader for non-photorealistic rendering
+ class NPREffect : public IEffect, public IEffectMatrices, public IEffectLights
+ {
+ public:
+ enum Mode : uint32_t
+ {
+ Mode_Cel = 0, // Cel (toon) shading
+ Mode_Gooch, // Gooch shading
+ Mode_MatCap, // Material Capture 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 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);
+
+ // Light settings.
+ void XM_CALLCONV SetLightDirection(int whichLight, FXMVECTOR value) override;
+ DIRECTX_TOOLKIT_API void __cdecl EnableDefaultLighting() override;
+
+ static constexpr int MaxDirectionalLights = 1;
+
+ // Texture setting.
+ DIRECTX_TOOLKIT_API void __cdecl SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor);
+
+ // 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);
+
+ // 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);
+ 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;
+
+ 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/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
new file mode 100644
index 00000000..62ac253b
--- /dev/null
+++ b/Src/NPREffect.cpp
@@ -0,0 +1,1019 @@
+//--------------------------------------------------------------------------------------
+// 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 specularColorAndSpecularThreshold;
+ XMVECTOR rimColorAndPower;
+ XMVECTOR extraSettings; // .x = SpecularSmoothing, .y = RimStrength, .z = RimStart, .w = RimEnd
+ 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 = 32;
+ static constexpr int PixelShaderCount = 6;
+ static constexpr int ShaderPermutationCount = 48;
+ static constexpr int RootSignatureCount = 3;
+ };
+
+
+ // 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, 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 } } };
+}
+
+
+// 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,
+ 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;
+
+ 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_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"
+
+#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"
+
+#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_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"
+
+#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"
+
+#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_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"
+
+#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"
+
+#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_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"
+
+#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
+}
+
+
+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_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) },
+};
+
+
+template<>
+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_PSMatCapShading, sizeof(NPREffect_PSMatCapShading) },
+ { NPREffect_PSCelShadingTx, sizeof(NPREffect_PSCelShadingTx) },
+ { NPREffect_PSGoochShadingTx, sizeof(NPREffect_PSGoochShadingTx) },
+ { NPREffect_PSMatCapShadingTx, sizeof(NPREffect_PSMatCapShadingTx) },
+};
+
+
+template<>
+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
+
+ 3, // cel shading + texture
+ 4, // gooch shading + texture
+ 5, // matcap shading + texture
+
+ 3, // vertex color + cel shading + texture
+ 4, // vertex color + gooch shading + texture
+ 5, // vertex color + matcap shading + texture
+
+ 3, // cel shading (biased vertex normal) + texture
+ 4, // gooch shading (biased vertex normal) + texture
+ 5, // matcap shading (biased vertex normal) + 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
+
+ 3, // instancing + cel shading + texture
+ 4, // instancing + gooch shading + texture
+ 5, // instancing + matcap shading + texture
+
+ 3, // instancing + vertex color + cel shading + texture
+ 4, // instancing + vertex color + gooch shading + texture
+ 5, // instancing + vertex color + matcap 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
+
+ 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
+
+// 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),
+ texture{},
+ 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");
+ 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.specularColorAndSpecularThreshold = s_defaultSpecular;
+ constants.rimColorAndPower = s_defaultRim;
+ constants.extraSettings = s_defaultExtraSettings;
+ constants.goochCoolColorAndAlpha = s_defaultCool;
+ constants.goochWarmColorAndBeta = s_defaultWarm;
+ constants.eyePosition = g_XMZero;
+
+ switch (nprMode)
+ {
+ case Mode_Cel:
+ case Mode_Gooch:
+ matCapEnabled = false;
+ break;
+
+ case Mode_MatCap:
+ matCapEnabled = true;
+ break;
+
+ default:
+ throw std::invalid_argument("Invalid nprMode");
+ }
+
+ textureEnabled = (effectFlags & EffectFlags::Texture) != 0;
+
+ // 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
+ ;
+
+ // 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 = {};
+
+ if (textureEnabled && matCapEnabled)
+ {
+ // 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
+ {
+ // only use constant
+ 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 += 3;
+ }
+
+ if (effectFlags & EffectFlags::BiasedVertexNormals)
+ {
+ // Compressed normals need to be scaled and biased in the vertex shader.
+ permutation += 6;
+ }
+
+ if (effectFlags & EffectFlags::Instancing)
+ {
+ // Vertex shader needs to use vertex matrix transform.
+ permutation += 12;
+ }
+
+ // Use shaders without texture coordinates?
+ if (textureEnabled)
+ {
+ permutation += 24;
+ }
+
+ return permutation;
+}
+
+
+// Sets our state onto the D3D device.
+void NPREffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList)
+{
+ // Compute derived parameter values.
+ matrices.SetConstants(
+ dirtyFlags,
+ constants.world,
+ constants.worldInverseTranspose,
+ constants.worldViewProj,
+ constants.eyePosition);
+
+ UpdateConstants();
+
+ // 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);
+ }
+
+ 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());
+
+ // 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 threshold).
+ pImpl->constants.specularColorAndSpecularThreshold = XMVectorSelect(pImpl->constants.specularColorAndSpecularThreshold, value, g_XMSelect1110);
+
+ pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer;
+}
+
+
+void NPREffect::SetSpecularThreshold(float threshold, float smoothing)
+{
+ 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;
+}
+
+
+void NPREffect::DisableSpecular()
+{
+ // Set specular color to black, threshold to 1
+ pImpl->constants.specularColorAndSpecularThreshold = g_XMIdentityR3;
+
+ 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 setting.
+void NPREffect::SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor)
+{
+ pImpl->texture = srvDescriptor;
+ pImpl->sampler = samplerDescriptor;
+}
+
+
+// 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));
+
+ 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;
+}
+
+
+// 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)
+{
+ // 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/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)
{
diff --git a/Src/Shaders/CompileShaders.cmd b/Src/Shaders/CompileShaders.cmd
index f1f2317a..3a817f0b 100644
--- a/Src/Shaders/CompileShaders.cmd
+++ b/Src/Shaders/CompileShaders.cmd
@@ -258,6 +258,55 @@ 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 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 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
new file mode 100644
index 00000000..b33d495c
--- /dev/null
+++ b/Src/Shaders/NPREffect.fx
@@ -0,0 +1,544 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+//
+// https://go.microsoft.com/fwlink/?LinkID=615561
+
+
+Texture2D Texture : register(t0);
+SamplerState Sampler : register(s0);
+Texture2D Texture2 : register(t1);
+
+
+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 SpecularThreshold : packoffset(c2.w);
+
+ float3 RimColor : packoffset(c3);
+ float RimPower : packoffset(c3.w);
+
+ float SpecularSmoothing : packoffset(c4.x);
+ float RimStrength : packoffset(c4.y);
+ float RimStart : packoffset(c4.z);
+ float RimEnd : packoffset(c4.w);
+
+ float3 GoochCoolColor : packoffset(c5);
+ float GoochAlpha : packoffset(c5.w);
+
+ float3 GoochWarmColor : packoffset(c6);
+ float GoochBeta : packoffset(c6.w);
+
+ float3 EyePosition : packoffset(c7);
+
+ float4x4 World : packoffset(c8);
+ float3x3 WorldInverseTranspose : packoffset(c12);
+ float4x4 WorldViewProj : packoffset(c15);
+};
+
+
+#include "RootSig.fxh"
+#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 = ComputeCommonVS(vin.Position, vin.Normal);
+ vout.Diffuse = float4(DiffuseColor, Alpha);
+ return vout;
+}
+
+[RootSignature(MainRS)]
+VSOutputPixelLighting VSNPREffectMC(VSInputNm vin)
+{
+ return VSNPREffect(vin);
+}
+
+[RootSignature(NoTextureRS)]
+VSOutputPixelLighting VSNPREffectBn(VSInputNm vin)
+{
+ float3 normal = BiasX2(vin.Normal);
+
+ VSOutputPixelLighting vout = ComputeCommonVS(vin.Position, normal);
+ vout.Diffuse = float4(DiffuseColor, Alpha);
+ return vout;
+}
+
+[RootSignature(MainRS)]
+VSOutputPixelLighting VSNPREffectBnMC(VSInputNm vin)
+{
+ return VSNPREffectBn(vin);
+}
+
+
+// Vertex shader: vertex color.
+[RootSignature(NoTextureRS)]
+VSOutputPixelLighting VSNPREffectVc(VSInputNmVc vin)
+{
+ VSOutputPixelLighting vout = ComputeCommonVS(vin.Position, vin.Normal);
+ vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor;
+ vout.Diffuse.a = vin.Color.a * Alpha;
+ return vout;
+}
+
+[RootSignature(MainRS)]
+VSOutputPixelLighting VSNPREffectVcMC(VSInputNmVc vin)
+{
+ return VSNPREffectVc(vin);
+}
+
+[RootSignature(NoTextureRS)]
+VSOutputPixelLighting VSNPREffectVcBn(VSInputNmVc vin)
+{
+ float3 normal = BiasX2(vin.Normal);
+
+ VSOutputPixelLighting vout = ComputeCommonVS(vin.Position, normal);
+ vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor;
+ vout.Diffuse.a = vin.Color.a * Alpha;
+ return vout;
+}
+
+[RootSignature(MainRS)]
+VSOutputPixelLighting VSNPREffectVcBnMC(VSInputNmVc vin)
+{
+ return VSNPREffectVcBn(vin);
+
+}
+
+
+// Vertex shader: instancing.
+[RootSignature(NoTextureRS)]
+VSOutputPixelLighting VSNPREffectInst(VSInputNmInst vin)
+{
+ CommonInstancing inst = ComputeCommonInstancing(vin.Position, vin.Normal, vin.Transform);
+ VSOutputPixelLighting vout = ComputeCommonVS(inst.Position, inst.Normal);
+ vout.Diffuse = float4(DiffuseColor, Alpha);
+ return vout;
+}
+
+[RootSignature(MainRS)]
+VSOutputPixelLighting VSNPREffectInstMC(VSInputNmInst vin)
+{
+ return VSNPREffectInst(vin);
+}
+
+[RootSignature(NoTextureRS)]
+VSOutputPixelLighting VSNPREffectBnInst(VSInputNmInst vin)
+{
+ float3 normal = BiasX2(vin.Normal);
+
+ CommonInstancing inst = ComputeCommonInstancing(vin.Position, normal, vin.Transform);
+ VSOutputPixelLighting vout = ComputeCommonVS(inst.Position, inst.Normal);
+ vout.Diffuse = float4(DiffuseColor, Alpha);
+ return vout;
+}
+
+[RootSignature(MainRS)]
+VSOutputPixelLighting VSNPREffectBnInstMC(VSInputNmInst vin)
+{
+ return VSNPREffectBnInst(vin);
+}
+
+
+// Vertex shader: vertex color + instancing.
+[RootSignature(NoTextureRS)]
+VSOutputPixelLighting VSNPREffectVcInst(VSInputNmVcInst vin)
+{
+ CommonInstancing inst = ComputeCommonInstancing(vin.Position, vin.Normal, vin.Transform);
+ VSOutputPixelLighting vout = ComputeCommonVS(inst.Position, inst.Normal);
+ vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor;
+ vout.Diffuse.a = vin.Color.a * Alpha;
+ return vout;
+}
+
+[RootSignature(MainRS)]
+VSOutputPixelLighting VSNPREffectVcInstMC(VSInputNmVcInst vin)
+{
+ return VSNPREffectVcInst(vin);
+}
+
+[RootSignature(NoTextureRS)]
+VSOutputPixelLighting VSNPREffectVcBnInst(VSInputNmVcInst vin)
+{
+ float3 normal = BiasX2(vin.Normal);
+
+ CommonInstancing inst = ComputeCommonInstancing(vin.Position, normal, vin.Transform);
+ VSOutputPixelLighting vout = ComputeCommonVS(inst.Position, inst.Normal);
+ vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor;
+ vout.Diffuse.a = vin.Color.a * Alpha;
+ return vout;
+}
+
+[RootSignature(MainRS)]
+VSOutputPixelLighting VSNPREffectVcBnInstMC(VSInputNmVcInst vin)
+{
+ return VSNPREffectVcBnInst(vin);
+
+}
+
+
+// Vertex shader: texture.
+[RootSignature(MainRS)]
+VSOutputPixelLightingTx VSNPREffectTx(VSInputNmTx vin)
+{
+ VSOutputPixelLightingTx vout = ComputeCommonVSTx(vin.Position, vin.Normal, vin.TexCoord);
+ vout.Diffuse = float4(DiffuseColor, Alpha);
+ return vout;
+}
+
+[RootSignature(DualTextureOneSamplerRS)]
+VSOutputPixelLightingTx VSNPREffectTxMC(VSInputNmTx vin)
+{
+ return VSNPREffectTx(vin);
+}
+
+[RootSignature(MainRS)]
+VSOutputPixelLightingTx VSNPREffectBnTx(VSInputNmTx vin)
+{
+ float3 normal = BiasX2(vin.Normal);
+
+ VSOutputPixelLightingTx vout = ComputeCommonVSTx(vin.Position, normal, vin.TexCoord);
+ vout.Diffuse = float4(DiffuseColor, Alpha);
+ return vout;
+}
+
+[RootSignature(DualTextureOneSamplerRS)]
+VSOutputPixelLightingTx VSNPREffectBnTxMC(VSInputNmTx vin)
+{
+ return VSNPREffectBnTx(vin);
+
+}
+
+
+// Vertex shader: texture + vertex color.
+[RootSignature(MainRS)]
+VSOutputPixelLightingTx VSNPREffectVcTx(VSInputNmTxVc vin)
+{
+ VSOutputPixelLightingTx vout = ComputeCommonVSTx(vin.Position, vin.Normal, vin.TexCoord);
+ vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor;
+ vout.Diffuse.a = vin.Color.a * Alpha;
+ return vout;
+}
+
+[RootSignature(DualTextureOneSamplerRS)]
+VSOutputPixelLightingTx VSNPREffectVcTxMC(VSInputNmTxVc vin)
+{
+ return VSNPREffectVcTx(vin);
+}
+
+[RootSignature(MainRS)]
+VSOutputPixelLightingTx VSNPREffectVcBnTx(VSInputNmTxVc vin)
+{
+ float3 normal = BiasX2(vin.Normal);
+
+ VSOutputPixelLightingTx vout = ComputeCommonVSTx(vin.Position, normal, vin.TexCoord);
+ vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor;
+ vout.Diffuse.a = vin.Color.a * Alpha;
+ return vout;
+}
+
+[RootSignature(DualTextureOneSamplerRS)]
+VSOutputPixelLightingTx VSNPREffectVcBnTxMC(VSInputNmTxVc vin)
+{
+ return VSNPREffectVcBnTx(vin);
+
+}
+
+
+// Vertex shader: texture + instancing.
+[RootSignature(MainRS)]
+VSOutputPixelLightingTx VSNPREffectInstTx(VSInputNmTxInst vin)
+{
+ CommonInstancing inst = ComputeCommonInstancing(vin.Position, vin.Normal, vin.Transform);
+ VSOutputPixelLightingTx vout = ComputeCommonVSTx(inst.Position, inst.Normal, vin.TexCoord);
+ vout.Diffuse = float4(DiffuseColor, Alpha);
+ return vout;
+}
+
+[RootSignature(DualTextureOneSamplerRS)]
+VSOutputPixelLightingTx VSNPREffectInstTxMC(VSInputNmTxInst vin)
+{
+ return VSNPREffectInstTx(vin);
+}
+
+[RootSignature(MainRS)]
+VSOutputPixelLightingTx VSNPREffectBnInstTx(VSInputNmTxInst vin)
+{
+ float3 normal = BiasX2(vin.Normal);
+
+ CommonInstancing inst = ComputeCommonInstancing(vin.Position, normal, vin.Transform);
+ VSOutputPixelLightingTx vout = ComputeCommonVSTx(inst.Position, inst.Normal, vin.TexCoord);
+ vout.Diffuse = float4(DiffuseColor, Alpha);
+ return vout;
+}
+
+[RootSignature(DualTextureOneSamplerRS)]
+VSOutputPixelLightingTx VSNPREffectBnInstTxMC(VSInputNmTxInst vin)
+{
+ return VSNPREffectBnInstTx(vin);
+
+}
+
+
+// Vertex shader: texture + vertex color + instancing.
+[RootSignature(MainRS)]
+VSOutputPixelLightingTx VSNPREffectVcInstTx(VSInputNmTxVcInst vin)
+{
+ CommonInstancing inst = ComputeCommonInstancing(vin.Position, vin.Normal, vin.Transform);
+ VSOutputPixelLightingTx vout = ComputeCommonVSTx(inst.Position, inst.Normal, vin.TexCoord);
+ vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor;
+ vout.Diffuse.a = vin.Color.a * Alpha;
+ return vout;
+}
+
+[RootSignature(DualTextureOneSamplerRS)]
+VSOutputPixelLightingTx VSNPREffectVcInstTxMC(VSInputNmTxVcInst vin)
+{
+ return VSNPREffectVcInstTx(vin);
+}
+
+[RootSignature(MainRS)]
+VSOutputPixelLightingTx VSNPREffectVcBnInstTx(VSInputNmTxVcInst vin)
+{
+ float3 normal = BiasX2(vin.Normal);
+
+ CommonInstancing inst = ComputeCommonInstancing(vin.Position, normal, vin.Transform);
+ VSOutputPixelLightingTx vout = ComputeCommonVSTx(inst.Position, inst.Normal, vin.TexCoord);
+ vout.Diffuse.rgb = vin.Color.rgb * DiffuseColor;
+ vout.Diffuse.a = vin.Color.a * Alpha;
+ return vout;
+}
+
+[RootSignature(DualTextureOneSamplerRS)]
+VSOutputPixelLightingTx VSNPREffectVcBnInstTxMC(VSInputNmTxVcInst vin)
+{
+ return VSNPREffectVcBnInstTx(vin);
+
+}
+
+
+//--- 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);
+ float NdotL = dot(normal, lightDir);
+
+ 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));
+
+ // 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);
+}
+
+
+// Pixel shader: cel shading + texture.
+[RootSignature(MainRS)]
+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 quantized = Quantize(NdotL, CelBands);
+ float3 color = pin.Diffuse.rgb * texColor.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 * 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);
+ 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));
+
+ // Gooch diffuse term: blend between cool and warm based
+ float3 color = GoochShading(NdotL, pin.Diffuse.rgb);
+
+ // 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);
+}
+
+
+// Pixel shader: Gooch shading + texture.
+[RootSignature(MainRS)]
+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
+ float3 color = GoochShading(NdotL, pin.Diffuse.rgb * texColor.rgb);
+
+ // Rim lighting
+ float outline = RimLighting(NdotV);
+ color += lerp(color, RimColor, outline);
+
+ // Specular highlight
+ float specular = HardEdgeSpecular(RdotV);
+ color += SpecularColor * specular;
+
+ 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
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;