diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
index 77eb78d6..eb2c710a 100644
--- a/.github/copilot-instructions.md
+++ b/.github/copilot-instructions.md
@@ -319,6 +319,43 @@ When reviewing documentation, do the following:
- Read the documentation on the wiki located in [this git repository](https://github.com/microsoft/DirectXTK.wiki.git).
- Report any specific gaps in the documentation compared to the public interface.
+## Code Change Rules
+
+- Make precise, surgical changes that **fully** address the request. Don't modify unrelated code, but ensure changes are complete and correct.
+- 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.
+
+### 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.
+- 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.
+- **directxtk-usage**: Provides usage guidance for the DirectX Tool Kit for DX11 library. Located in the `skills/directxtk-usage/` directory.
+- **directxtk-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/directxtk-tutorial` directory.
+
+To use these skills in the Copilot CLI:
+
+```bash
+/skills list
+/skills search directxtk
+```
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 141347a4..1da832dd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -119,6 +119,7 @@ set(LIBRARY_SOURCES
Src/DDSTextureLoader.cpp
Src/DebugEffect.cpp
Src/DGSLEffect.cpp
+ Src/NPREffect.cpp
Src/DGSLEffectFactory.cpp
Src/DirectXHelpers.cpp
Src/DualPostProcess.cpp
@@ -150,6 +151,7 @@ set(SHADER_SOURCES
Src/Shaders/AlphaTestEffect.fx
Src/Shaders/BasicEffect.fx
Src/Shaders/DebugEffect.fx
+ Src/Shaders/NPREffect.fx
Src/Shaders/DGSLEffect.fx
Src/Shaders/DGSLLambert.hlsl
Src/Shaders/DGSLPhong.hlsl
diff --git a/DirectXTK_Desktop_2022.vcxproj b/DirectXTK_Desktop_2022.vcxproj
index d03abf3d..d66aefc4 100644
--- a/DirectXTK_Desktop_2022.vcxproj
+++ b/DirectXTK_Desktop_2022.vcxproj
@@ -75,6 +75,7 @@
+
@@ -179,6 +180,9 @@
Document
+
+ Document
+
{E0B52AE7-E160-4D32-BF3F-910B785E5A8E}
diff --git a/DirectXTK_Desktop_2022.vcxproj.filters b/DirectXTK_Desktop_2022.vcxproj.filters
index 6f61bad4..05534581 100644
--- a/DirectXTK_Desktop_2022.vcxproj.filters
+++ b/DirectXTK_Desktop_2022.vcxproj.filters
@@ -248,6 +248,9 @@
Src
+
+ Src
+
Src
@@ -352,6 +355,9 @@
Src\Shaders
+
+ Src\Shaders
+
Src\Shaders\Shared
diff --git a/DirectXTK_Desktop_2022_Win10.vcxproj b/DirectXTK_Desktop_2022_Win10.vcxproj
index 8ff0f6c1..0f6540d6 100644
--- a/DirectXTK_Desktop_2022_Win10.vcxproj
+++ b/DirectXTK_Desktop_2022_Win10.vcxproj
@@ -83,6 +83,7 @@
+
@@ -189,6 +190,9 @@
Document
+
+ Document
+
{E0B52AE7-E160-4D32-BF3F-910B785E5A8E}
diff --git a/DirectXTK_Desktop_2022_Win10.vcxproj.filters b/DirectXTK_Desktop_2022_Win10.vcxproj.filters
index 3eead7e8..cfea2c70 100644
--- a/DirectXTK_Desktop_2022_Win10.vcxproj.filters
+++ b/DirectXTK_Desktop_2022_Win10.vcxproj.filters
@@ -272,6 +272,9 @@
Src
+
+ Src
+
Src
@@ -352,6 +355,9 @@
Src\Shaders
+
+ Src\Shaders
+
Src\Shaders\Shared
diff --git a/DirectXTK_Desktop_2026.vcxproj b/DirectXTK_Desktop_2026.vcxproj
index bf6e87ba..5418c4d3 100644
--- a/DirectXTK_Desktop_2026.vcxproj
+++ b/DirectXTK_Desktop_2026.vcxproj
@@ -83,6 +83,7 @@
+
@@ -189,6 +190,9 @@
Document
+
+ Document
+
{E0B52AE7-E160-4D32-BF3F-910B785E5A8E}
diff --git a/DirectXTK_Desktop_2026.vcxproj.filters b/DirectXTK_Desktop_2026.vcxproj.filters
index 3eead7e8..cfea2c70 100644
--- a/DirectXTK_Desktop_2026.vcxproj.filters
+++ b/DirectXTK_Desktop_2026.vcxproj.filters
@@ -272,6 +272,9 @@
Src
+
+ Src
+
Src
@@ -352,6 +355,9 @@
Src\Shaders
+
+ Src\Shaders
+
Src\Shaders\Shared
diff --git a/DirectXTK_GDKW_2022.vcxproj b/DirectXTK_GDKW_2022.vcxproj
index 4accaa96..f309eeb1 100644
--- a/DirectXTK_GDKW_2022.vcxproj
+++ b/DirectXTK_GDKW_2022.vcxproj
@@ -403,6 +403,7 @@
+
@@ -469,6 +470,7 @@
+
diff --git a/DirectXTK_GDKW_2022.vcxproj.filters b/DirectXTK_GDKW_2022.vcxproj.filters
index f3b9fda7..511fb417 100644
--- a/DirectXTK_GDKW_2022.vcxproj.filters
+++ b/DirectXTK_GDKW_2022.vcxproj.filters
@@ -274,6 +274,9 @@
Src
+
+ Src
+
Src
@@ -354,6 +357,9 @@
Src\Shaders
+
+ Src\Shaders
+
Src\Shaders\Shared
diff --git a/DirectXTK_GDK_2022.vcxproj b/DirectXTK_GDK_2022.vcxproj
index 0e9b32c8..d4f512a9 100644
--- a/DirectXTK_GDK_2022.vcxproj
+++ b/DirectXTK_GDK_2022.vcxproj
@@ -228,6 +228,7 @@
+
@@ -291,6 +292,7 @@
+
diff --git a/DirectXTK_GDK_2022.vcxproj.filters b/DirectXTK_GDK_2022.vcxproj.filters
index f3b9fda7..511fb417 100644
--- a/DirectXTK_GDK_2022.vcxproj.filters
+++ b/DirectXTK_GDK_2022.vcxproj.filters
@@ -274,6 +274,9 @@
Src
+
+ Src
+
Src
@@ -354,6 +357,9 @@
Src\Shaders
+
+ Src\Shaders
+
Src\Shaders\Shared
diff --git a/DirectXTK_Windows10_2022.vcxproj b/DirectXTK_Windows10_2022.vcxproj
index 6b97f483..25ee0c87 100644
--- a/DirectXTK_Windows10_2022.vcxproj
+++ b/DirectXTK_Windows10_2022.vcxproj
@@ -96,6 +96,7 @@
+
@@ -191,6 +192,9 @@
Document
+
+ Document
+
{f4776924-619c-42c7-88b2-82c947ccc9e7}
diff --git a/DirectXTK_Windows10_2022.vcxproj.filters b/DirectXTK_Windows10_2022.vcxproj.filters
index e875551d..354afcf7 100644
--- a/DirectXTK_Windows10_2022.vcxproj.filters
+++ b/DirectXTK_Windows10_2022.vcxproj.filters
@@ -206,6 +206,9 @@
Src\Shaders
+
+ Src\Shaders
+
Src\Shaders\Shared
@@ -347,6 +350,9 @@
Src
+
+ Src
+
Src
diff --git a/Inc/Effects.h b/Inc/Effects.h
index 695f7bb4..be5eb548 100644
--- a/Inc/Effects.h
+++ b/Inc/Effects.h
@@ -225,7 +225,7 @@ namespace DirectX
// Vertex color setting.
DIRECTX_TOOLKIT_API void __cdecl SetVertexColorEnabled(bool value);
- // Texture setting.
+ // Texture settings.
DIRECTX_TOOLKIT_API void __cdecl SetTextureEnabled(bool value);
DIRECTX_TOOLKIT_API void __cdecl SetTexture(_In_opt_ ID3D11ShaderResourceView* value);
@@ -878,6 +878,103 @@ namespace DirectX
std::unique_ptr pImpl;
};
+ //------------------------------------------------------------------------------
+ // 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 explicit NPREffect(_In_ ID3D11Device* device);
+
+ 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_ ID3D11DeviceContext* deviceContext) override;
+
+ DIRECTX_TOOLKIT_API void __cdecl GetVertexShaderBytecode(
+ _Out_ void const** pShaderByteCode,
+ _Out_ size_t* pByteCodeLength) 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 settings.
+ DIRECTX_TOOLKIT_API void __cdecl SetTextureEnabled(bool value);
+ DIRECTX_TOOLKIT_API void __cdecl SetTexture(_In_opt_ ID3D11ShaderResourceView* value);
+
+ // Shader mode setting.
+ DIRECTX_TOOLKIT_API void __cdecl SetMode(Mode mode);
+
+ // 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 setting.
+ DIRECTX_TOOLKIT_API void __cdecl SetMatCap(_In_opt_ ID3D11ShaderResourceView* value);
+
+ // 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();
+
+ // Vertex color setting.
+ DIRECTX_TOOLKIT_API void __cdecl SetVertexColorEnabled(bool value);
+
+ // Normal compression settings.
+ DIRECTX_TOOLKIT_API void __cdecl SetBiasedVertexNormals(bool value);
+
+ // Instancing settings.
+ DIRECTX_TOOLKIT_API void __cdecl SetInstancingEnabled(bool value);
+
+ private:
+ // Private implementation.
+ class Impl;
+
+ std::unique_ptr pImpl;
+
+ // Unsupported interface methods.
+ DIRECTX_TOOLKIT_API void __cdecl SetLightingEnabled(bool value) override;
+ DIRECTX_TOOLKIT_API void __cdecl SetPerPixelLighting(bool value) override;
+ 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 for sharing effects and texture resources
class DIRECTX_TOOLKIT_API IEffectFactory
diff --git a/Src/NPREffect.cpp b/Src/NPREffect.cpp
new file mode 100644
index 00000000..37cbe799
--- /dev/null
+++ b/Src/NPREffect.cpp
@@ -0,0 +1,780 @@
+//--------------------------------------------------------------------------------------
+// File: NPREffect.cpp
+//
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+//
+// https://go.microsoft.com/fwlink/?LinkId=248929
+//--------------------------------------------------------------------------------------
+
+#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 = 16;
+ static constexpr int PixelShaderCount = 6;
+ static constexpr int ShaderPermutationCount = 48;
+
+ static constexpr int ModeCount = 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:
+ explicit Impl(_In_ ID3D11Device* device);
+
+ Impl(const Impl&) = delete;
+ Impl& operator=(const Impl&) = delete;
+
+ Impl(Impl&&) = default;
+ Impl& operator=(Impl&&) = default;
+
+ bool vertexColorEnabled;
+ bool biasedVertexNormals;
+ bool instancing;
+ bool textureEnabled;
+ NPREffect::Mode nprMode;
+ Microsoft::WRL::ComPtr texture;
+ Microsoft::WRL::ComPtr matcap;
+
+ int GetCurrentShaderPermutation() const noexcept;
+
+ void Apply(_In_ ID3D11DeviceContext* deviceContext);
+};
+
+
+#pragma region Shaders
+// Include the precompiled shader code.
+namespace
+{
+#if 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_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_PSMatCapShading.inc"
+#include "NPREffect_PSMatCapShadingTx.inc"
+#endif
+}
+
+
+template<>
+const ShaderBytecode 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) },
+};
+
+
+template<>
+const int EffectBase::VertexShaderIndices[] =
+{
+ 0, // cel shading
+ 0, // gooch shading
+ 0, // matcap shading
+
+ 1, // vertex color + cel shading
+ 1, // vertex color + gooch shading
+ 1, // vertex color + matcap shading
+
+ 2, // cel shading (biased vertex normal)
+ 2, // gooch shading (biased vertex normal)
+ 2, // matcap shading (biased vertex normal)
+
+ 3, // vertex color (biased vertex normal) + cel shading
+ 3, // vertex color (biased vertex normal) + gooch shading
+ 3, // vertex color (biased vertex normal) + matcap shading
+
+ 4, // instancing + cel shading
+ 4, // instancing + gooch shading
+ 4, // instancing + matcap shading
+
+ 5, // instancing + vertex color + cel shading
+ 5, // instancing + vertex color + gooch shading
+ 5, // instancing + vertex color + matcap shading
+
+ 6, // instancing (biased vertex normal) + cel shading
+ 6, // instancing (biased vertex normal) + gooch shading
+ 6, // instancing (biased vertex normal) + matcap shading
+
+ 7, // instancing + vertex color (biased vertex normal) + cel shading
+ 7, // instancing + vertex color (biased vertex normal) + gooch shading
+ 7, // instancing + vertex color (biased vertex normal) + matcap shading
+
+ 8, // cel shading + texture
+ 8, // gooch shading + texture
+ 8, // matcap shading + texture
+
+ 9, // vertex color + cel shading + texture
+ 9, // vertex color + gooch shading + texture
+ 9, // vertex color + matcap shading + texture
+
+ 10, // cel shading (biased vertex normal) + texture
+ 10, // gooch shading (biased vertex normal) + texture
+ 10, // matcap shading (biased vertex normal) + texture
+
+ 11, // vertex color (biased vertex normal) + cel shading + texture
+ 11, // vertex color (biased vertex normal) + gooch shading + texture
+ 11, // vertex color (biased vertex normal) + matcap shading + texture
+
+ 12, // instancing + cel shading + texture
+ 12, // instancing + gooch shading + texture
+ 12, // instancing + matcap shading + texture
+
+ 13, // instancing + vertex color + cel shading + texture
+ 13, // instancing + vertex color + gooch shading + texture
+ 13, // instancing + vertex color + matcap shading + texture
+
+ 14, // instancing (biased vertex normal) + cel shading + texture
+ 14, // instancing (biased vertex normal) + gooch shading + texture
+ 14, // 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
+ 15, // instancing + vertex color (biased vertex normal) + matcap shading + texture
+};
+
+
+template<>
+const ShaderBytecode 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_ ID3D11Device* device)
+ : EffectBase(device),
+ vertexColorEnabled(false),
+ biasedVertexNormals(false),
+ instancing(false),
+ textureEnabled(false),
+ nprMode(NPREffect::Mode_Cel)
+{
+ 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;
+}
+
+
+int NPREffect::Impl::GetCurrentShaderPermutation() const noexcept
+{
+ int permutation = static_cast(nprMode);
+
+ // Support vertex coloring?
+ if (vertexColorEnabled)
+ {
+ permutation += 3;
+ }
+
+ if (biasedVertexNormals)
+ {
+ // Compressed normals need to be scaled and biased in the vertex shader.
+ permutation += 6;
+ }
+
+ if (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_ ID3D11DeviceContext* deviceContext)
+{
+ assert(deviceContext != nullptr);
+
+ // 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;
+ }
+
+ // Set shaders and constant buffers.
+ ApplyShaders(deviceContext, GetCurrentShaderPermutation());
+
+ // Set texture.
+ switch (nprMode)
+ {
+ case Mode_MatCap:
+ if (textureEnabled)
+ {
+ ID3D11ShaderResourceView* srvs[] = { texture.Get(), matcap.Get() };
+ deviceContext->PSSetShaderResources(0, 2, srvs);
+ }
+ else
+ {
+ ID3D11ShaderResourceView* srv = matcap.Get();
+ deviceContext->PSSetShaderResources(0, 1, &srv);
+ }
+ break;
+
+ default:
+ if (textureEnabled)
+ {
+ ID3D11ShaderResourceView* srv = texture.Get();
+ deviceContext->PSSetShaderResources(0, 1, &srv);
+ }
+ break;
+ }
+}
+
+
+// Public constructor.
+NPREffect::NPREffect(_In_ ID3D11Device* device)
+ : pImpl(std::make_unique(device))
+{}
+
+
+NPREffect::NPREffect(NPREffect&&) noexcept = default;
+NPREffect& NPREffect::operator= (NPREffect&&) noexcept = default;
+NPREffect::~NPREffect() = default;
+
+
+// IEffect methods.
+void NPREffect::Apply(_In_ ID3D11DeviceContext* deviceContext)
+{
+ pImpl->Apply(deviceContext);
+}
+
+
+void NPREffect::GetVertexShaderBytecode(_Out_ void const** pShaderByteCode, _Out_ size_t* pByteCodeLength)
+{
+ pImpl->GetVertexShaderBytecode(pImpl->GetCurrentShaderPermutation(), pShaderByteCode, pByteCodeLength);
+}
+
+
+// Camera settings.
+void XM_CALLCONV NPREffect::SetWorld(FXMMATRIX value)
+{
+ pImpl->matrices.world = value;
+
+ pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose;
+}
+
+
+void XM_CALLCONV NPREffect::SetView(FXMMATRIX value)
+{
+ pImpl->matrices.view = value;
+
+ pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::EyePosition;
+}
+
+
+void XM_CALLCONV NPREffect::SetProjection(FXMMATRIX value)
+{
+ pImpl->matrices.projection = value;
+
+ pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj;
+}
+
+
+void XM_CALLCONV 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::SetLightingEnabled(bool)
+{
+ // Unsupported interface.
+}
+
+
+void NPREffect::SetPerPixelLighting(bool)
+{
+ // Unsupported interface.
+}
+
+
+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 settings.
+void NPREffect::SetTextureEnabled(bool value)
+{
+ pImpl->textureEnabled = value;
+}
+
+
+void NPREffect::SetTexture(_In_opt_ ID3D11ShaderResourceView* value)
+{
+ pImpl->texture = value;
+}
+
+
+// Shader mode setting.
+void NPREffect::SetMode(Mode mode)
+{
+ if (static_cast(mode) < 0 || static_cast(mode) >= NPREffectTraits::ModeCount)
+ {
+ throw std::invalid_argument("Unsupported mode");
+ }
+
+ pImpl->nprMode = mode;
+}
+
+
+// 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(_In_opt_ ID3D11ShaderResourceView* value)
+{
+ pImpl->matcap = value;
+}
+
+
+// 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;
+}
+
+// Vertex color setting.
+void NPREffect::SetVertexColorEnabled(bool value)
+{
+ pImpl->vertexColorEnabled = value;
+}
+
+
+// Normal compression settings.
+void NPREffect::SetBiasedVertexNormals(bool value)
+{
+ pImpl->biasedVertexNormals = value;
+}
+
+
+// Instancing settings.
+void NPREffect::SetInstancingEnabled(bool value)
+{
+ pImpl->instancing = value;
+}
diff --git a/Src/Shaders/CompileShaders.cmd b/Src/Shaders/CompileShaders.cmd
index 2d7bb9fb..4ecfff9f 100644
--- a/Src/Shaders/CompileShaders.cmd
+++ b/Src/Shaders/CompileShaders.cmd
@@ -226,6 +226,35 @@ call :CompileShaderSM4%1 DebugEffect ps PSRGBNormals
call :CompileShaderSM4%1 DebugEffect ps PSRGBTangents
call :CompileShaderSM4%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 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..3f668f7d
--- /dev/null
+++ b/Src/Shaders/NPREffect.fx
@@ -0,0 +1,419 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+//
+// https://go.microsoft.com/fwlink/?LinkId=248929
+
+
+Texture2D Texture : register(t0);
+Texture2D Texture2 : register(t1);
+SamplerState Sampler : register(s0);
+
+
+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 "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.
+VSOutputPixelLighting VSNPREffect(VSInputNm vin)
+{
+ VSOutputPixelLighting vout = ComputeCommonVS(vin.Position, vin.Normal);
+ vout.Diffuse = float4(DiffuseColor, Alpha);
+ return vout;
+}
+
+VSOutputPixelLighting VSNPREffectBn(VSInputNm vin)
+{
+ float3 normal = BiasX2(vin.Normal);
+
+ VSOutputPixelLighting vout = ComputeCommonVS(vin.Position, normal);
+ vout.Diffuse = float4(DiffuseColor, Alpha);
+ return vout;
+}
+
+
+// Vertex shader: vertex color.
+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;
+}
+
+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;
+}
+
+
+// Vertex shader: instancing.
+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;
+}
+
+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;
+}
+
+
+// Vertex shader: vertex color + instancing.
+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;
+}
+
+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;
+}
+
+
+// Vertex shader: texture.
+VSOutputPixelLightingTx VSNPREffectTx(VSInputNmTx vin)
+{
+ VSOutputPixelLightingTx vout = ComputeCommonVSTx(vin.Position, vin.Normal, vin.TexCoord);
+ vout.Diffuse = float4(DiffuseColor, Alpha);
+ return vout;
+}
+
+VSOutputPixelLightingTx VSNPREffectBnTx(VSInputNmTx vin)
+{
+ float3 normal = BiasX2(vin.Normal);
+
+ VSOutputPixelLightingTx vout = ComputeCommonVSTx(vin.Position, normal, vin.TexCoord);
+ vout.Diffuse = float4(DiffuseColor, Alpha);
+ return vout;
+}
+
+
+// Vertex shader: texture + vertex color.
+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;
+}
+
+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;
+}
+
+
+// Vertex shader: texture + instancing.
+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;
+}
+
+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;
+}
+
+
+// Vertex shader: texture + vertex color + instancing.
+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;
+}
+
+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;
+}
+
+
+//--- 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.
+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.
+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.
+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.
+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.
+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.
+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/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;