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;