DirectXTK/Src/EffectCommon.cpp

496 строки
16 KiB
C++
Исходник Обычный вид История

2016-10-29 01:06:51 +03:00
//--------------------------------------------------------------------------------------
// File: EffectCommon.cpp
//
2021-02-27 10:00:12 +03:00
// Copyright (c) Microsoft Corporation.
2018-02-23 22:49:48 +03:00
// Licensed under the MIT License.
2016-10-29 01:06:51 +03:00
//
// http://go.microsoft.com/fwlink/?LinkId=248929
//--------------------------------------------------------------------------------------
#include "pch.h"
#include "EffectCommon.h"
#include "DemandCreate.h"
using namespace DirectX;
using Microsoft::WRL::ComPtr;
// IEffectMatrices default method
void XM_CALLCONV IEffectMatrices::SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection)
{
SetWorld(world);
SetView(view);
SetProjection(projection);
}
// Constructor initializes default matrix values.
EffectMatrices::EffectMatrices() noexcept
2016-10-29 01:06:51 +03:00
{
2022-02-21 13:02:17 +03:00
const XMMATRIX id = XMMatrixIdentity();
2018-05-05 04:16:09 +03:00
world = id;
view = id;
projection = id;
worldView = id;
2016-10-29 01:06:51 +03:00
}
// Lazily recomputes the combined world+view+projection matrix.
2018-05-05 04:16:09 +03:00
_Use_decl_annotations_
void EffectMatrices::SetConstants(int& dirtyFlags, XMMATRIX& worldViewProjConstant)
2016-10-29 01:06:51 +03:00
{
if (dirtyFlags & EffectDirtyFlags::WorldViewProj)
{
worldView = XMMatrixMultiply(world, view);
worldViewProjConstant = XMMatrixTranspose(XMMatrixMultiply(worldView, projection));
2021-12-08 00:43:42 +03:00
2016-10-29 01:06:51 +03:00
dirtyFlags &= ~EffectDirtyFlags::WorldViewProj;
dirtyFlags |= EffectDirtyFlags::ConstantBuffer;
}
}
// Constructor initializes default fog settings.
EffectFog::EffectFog() noexcept :
2016-10-29 01:06:51 +03:00
enabled(false),
start(0),
end(1.f)
{
}
// Lazily recomputes the derived vector used by shader fog calculations.
_Use_decl_annotations_
void XM_CALLCONV EffectFog::SetConstants(int& dirtyFlags, FXMMATRIX worldView, XMVECTOR& fogVectorConstant)
{
if (enabled)
{
if (dirtyFlags & (EffectDirtyFlags::FogVector | EffectDirtyFlags::FogEnable))
{
if (start == end)
{
// Degenerate case: force everything to 100% fogged if start and end are the same.
2017-07-11 06:54:54 +03:00
static const XMVECTORF32 fullyFogged = { { { 0, 0, 0, 1 } } };
2016-10-29 01:06:51 +03:00
fogVectorConstant = fullyFogged;
}
else
{
// We want to transform vertex positions into view space, take the resulting
// Z value, then scale and offset according to the fog start/end distances.
// Because we only care about the Z component, the shader can do all this
// with a single dot product, using only the Z row of the world+view matrix.
2021-12-08 00:43:42 +03:00
2016-10-29 01:06:51 +03:00
// _13, _23, _33, _43
2022-02-21 13:02:17 +03:00
const XMVECTOR worldViewZ = XMVectorMergeXY(
XMVectorMergeZW(worldView.r[0], worldView.r[2]),
XMVectorMergeZW(worldView.r[1], worldView.r[3]));
2016-10-29 01:06:51 +03:00
// 0, 0, 0, fogStart
2022-02-21 13:02:17 +03:00
const XMVECTOR wOffset = XMVectorSwizzle<1, 2, 3, 0>(XMLoadFloat(&start));
2016-10-29 01:06:51 +03:00
2018-06-13 04:05:42 +03:00
// (worldViewZ + wOffset) / (start - end);
fogVectorConstant = XMVectorDivide(XMVectorAdd(worldViewZ, wOffset), XMVectorReplicate(start - end));
2016-10-29 01:06:51 +03:00
}
dirtyFlags &= ~(EffectDirtyFlags::FogVector | EffectDirtyFlags::FogEnable);
dirtyFlags |= EffectDirtyFlags::ConstantBuffer;
}
}
else
{
// When fog is disabled, make sure the fog vector is reset to zero.
if (dirtyFlags & EffectDirtyFlags::FogEnable)
{
fogVectorConstant = g_XMZero;
dirtyFlags &= ~EffectDirtyFlags::FogEnable;
dirtyFlags |= EffectDirtyFlags::ConstantBuffer;
}
}
}
// Constructor initializes default material color settings.
EffectColor::EffectColor() noexcept :
2018-05-05 04:16:09 +03:00
diffuseColor(g_XMOne),
2016-10-29 01:06:51 +03:00
alpha(1.f)
{
}
// Lazily recomputes the material color parameter for shaders that do not support realtime lighting.
void EffectColor::SetConstants(_Inout_ int& dirtyFlags, _Inout_ XMVECTOR& diffuseColorConstant)
{
if (dirtyFlags & EffectDirtyFlags::MaterialColor)
{
2022-02-21 13:02:17 +03:00
const XMVECTOR alphaVector = XMVectorReplicate(alpha);
2016-10-29 01:06:51 +03:00
// xyz = diffuse * alpha, w = alpha.
2018-06-13 04:05:42 +03:00
diffuseColorConstant = XMVectorSelect(alphaVector, XMVectorMultiply(diffuseColor, alphaVector), g_XMSelect1110);
2016-10-29 01:06:51 +03:00
dirtyFlags &= ~EffectDirtyFlags::MaterialColor;
dirtyFlags |= EffectDirtyFlags::ConstantBuffer;
}
}
// Constructor initializes default light settings.
2018-05-05 04:16:09 +03:00
EffectLights::EffectLights() noexcept :
emissiveColor{},
ambientLightColor{},
lightEnabled{},
lightDiffuseColor{},
lightSpecularColor{}
2016-10-29 01:06:51 +03:00
{
for (int i = 0; i < MaxDirectionalLights; i++)
{
lightEnabled[i] = (i == 0);
lightDiffuseColor[i] = g_XMOne;
}
}
2017-07-11 06:54:54 +03:00
#ifdef _PREFAST_
2016-10-29 01:06:51 +03:00
#pragma prefast(push)
#pragma prefast(disable:22103, "PREFAST doesn't understand buffer is bounded by a static const value even with SAL" )
2017-07-11 06:54:54 +03:00
#endif
2016-10-29 01:06:51 +03:00
// Initializes constant buffer fields to match the current lighting state.
_Use_decl_annotations_ void EffectLights::InitializeConstants(XMVECTOR& specularColorAndPowerConstant, XMVECTOR* lightDirectionConstant, XMVECTOR* lightDiffuseConstant, XMVECTOR* lightSpecularConstant) const
{
2017-07-11 06:54:54 +03:00
static const XMVECTORF32 defaultSpecular = { { { 1, 1, 1, 16 } } };
static const XMVECTORF32 defaultLightDirection = { { { 0, -1, 0, 0 } } };
2021-12-08 00:43:42 +03:00
2016-10-29 01:06:51 +03:00
specularColorAndPowerConstant = defaultSpecular;
for (int i = 0; i < MaxDirectionalLights; i++)
{
lightDirectionConstant[i] = defaultLightDirection;
lightDiffuseConstant[i] = lightEnabled[i] ? lightDiffuseColor[i] : g_XMZero;
2016-10-29 01:06:51 +03:00
lightSpecularConstant[i] = lightEnabled[i] ? lightSpecularColor[i] : g_XMZero;
}
}
2017-07-11 06:54:54 +03:00
#ifdef _PREFAST_
2016-10-29 01:06:51 +03:00
#pragma prefast(pop)
2017-07-11 06:54:54 +03:00
#endif
2016-10-29 01:06:51 +03:00
// Lazily recomputes derived parameter values used by shader lighting calculations.
_Use_decl_annotations_ void EffectLights::SetConstants(int& dirtyFlags, EffectMatrices const& matrices, XMMATRIX& worldConstant, XMVECTOR worldInverseTransposeConstant[3], XMVECTOR& eyePositionConstant, XMVECTOR& diffuseColorConstant, XMVECTOR& emissiveColorConstant, bool lightingEnabled)
{
if (lightingEnabled)
{
// World inverse transpose matrix.
if (dirtyFlags & EffectDirtyFlags::WorldInverseTranspose)
{
worldConstant = XMMatrixTranspose(matrices.world);
2022-02-21 13:02:17 +03:00
const XMMATRIX worldInverse = XMMatrixInverse(nullptr, matrices.world);
2016-10-29 01:06:51 +03:00
worldInverseTransposeConstant[0] = worldInverse.r[0];
worldInverseTransposeConstant[1] = worldInverse.r[1];
worldInverseTransposeConstant[2] = worldInverse.r[2];
dirtyFlags &= ~EffectDirtyFlags::WorldInverseTranspose;
dirtyFlags |= EffectDirtyFlags::ConstantBuffer;
}
// Eye position vector.
if (dirtyFlags & EffectDirtyFlags::EyePosition)
{
XMMATRIX viewInverse = XMMatrixInverse(nullptr, matrices.view);
2021-12-08 00:43:42 +03:00
2016-10-29 01:06:51 +03:00
eyePositionConstant = viewInverse.r[3];
dirtyFlags &= ~EffectDirtyFlags::EyePosition;
dirtyFlags |= EffectDirtyFlags::ConstantBuffer;
}
}
// Material color parameters. The desired lighting model is:
//
// ((ambientLightColor + sum(diffuse directional light)) * diffuseColor) + emissiveColor
//
// When lighting is disabled, ambient and directional lights are ignored, leaving:
//
// diffuseColor + emissiveColor
//
// For the lighting disabled case, we can save one shader instruction by precomputing
// diffuse+emissive on the CPU, after which the shader can use diffuseColor directly,
// ignoring its emissive parameter.
//
// When lighting is enabled, we can merge the ambient and emissive settings. If we
// set our emissive parameter to emissive+(ambient*diffuse), the shader no longer
// needs to bother adding the ambient contribution, simplifying its computation to:
//
// (sum(diffuse directional light) * diffuseColor) + emissiveColor
//
// For futher optimization goodness, we merge material alpha with the diffuse
// color parameter, and premultiply all color values by this alpha.
if (dirtyFlags & EffectDirtyFlags::MaterialColor)
{
XMVECTOR diffuse = diffuseColor;
2022-02-21 13:02:17 +03:00
const XMVECTOR alphaVector = XMVectorReplicate(alpha);
2016-10-29 01:06:51 +03:00
if (lightingEnabled)
{
// Merge emissive and ambient light contributions.
2018-06-13 04:05:42 +03:00
// (emissiveColor + ambientLightColor * diffuse) * alphaVector;
emissiveColorConstant = XMVectorMultiply(XMVectorMultiplyAdd(ambientLightColor, diffuse, emissiveColor), alphaVector);
2016-10-29 01:06:51 +03:00
}
else
{
// Merge diffuse and emissive light contributions.
2018-06-13 04:05:42 +03:00
diffuse = XMVectorAdd(diffuse, emissiveColor);
2016-10-29 01:06:51 +03:00
}
// xyz = diffuse * alpha, w = alpha.
2018-06-13 04:05:42 +03:00
diffuseColorConstant = XMVectorSelect(alphaVector, XMVectorMultiply(diffuse, alphaVector), g_XMSelect1110);
2016-10-29 01:06:51 +03:00
dirtyFlags &= ~EffectDirtyFlags::MaterialColor;
dirtyFlags |= EffectDirtyFlags::ConstantBuffer;
}
}
2017-07-11 06:54:54 +03:00
#ifdef _PREFAST_
2016-10-29 01:06:51 +03:00
#pragma prefast(push)
#pragma prefast(disable:26015, "PREFAST doesn't understand that ValidateLightIndex bounds whichLight" )
2017-07-11 06:54:54 +03:00
#endif
2016-10-29 01:06:51 +03:00
// Helper for turning one of the directional lights on or off.
_Use_decl_annotations_ int EffectLights::SetLightEnabled(int whichLight, bool value, XMVECTOR* lightDiffuseConstant, XMVECTOR* lightSpecularConstant)
{
ValidateLightIndex(whichLight);
if (lightEnabled[whichLight] == value)
return 0;
lightEnabled[whichLight] = value;
if (value)
{
// If this light is now on, store its color in the constant buffer.
lightDiffuseConstant[whichLight] = lightDiffuseColor[whichLight];
lightSpecularConstant[whichLight] = lightSpecularColor[whichLight];
}
else
{
// If the light is off, reset constant buffer colors to zero.
lightDiffuseConstant[whichLight] = g_XMZero;
lightSpecularConstant[whichLight] = g_XMZero;
}
return EffectDirtyFlags::ConstantBuffer;
}
// Helper for setting diffuse color of one of the directional lights.
_Use_decl_annotations_
int XM_CALLCONV EffectLights::SetLightDiffuseColor(int whichLight, FXMVECTOR value, XMVECTOR* lightDiffuseConstant)
{
ValidateLightIndex(whichLight);
// Locally store the new color.
lightDiffuseColor[whichLight] = value;
// If this light is currently on, also update the constant buffer.
if (lightEnabled[whichLight])
{
lightDiffuseConstant[whichLight] = value;
2021-12-08 00:43:42 +03:00
2016-10-29 01:06:51 +03:00
return EffectDirtyFlags::ConstantBuffer;
}
return 0;
}
// Helper for setting specular color of one of the directional lights.
_Use_decl_annotations_
int XM_CALLCONV EffectLights::SetLightSpecularColor(int whichLight, FXMVECTOR value, XMVECTOR* lightSpecularConstant)
{
ValidateLightIndex(whichLight);
// Locally store the new color.
lightSpecularColor[whichLight] = value;
// If this light is currently on, also update the constant buffer.
if (lightEnabled[whichLight])
{
lightSpecularConstant[whichLight] = value;
return EffectDirtyFlags::ConstantBuffer;
}
2021-12-08 00:43:42 +03:00
2016-10-29 01:06:51 +03:00
return 0;
}
2017-07-11 06:54:54 +03:00
#ifdef _PREFAST_
2016-10-29 01:06:51 +03:00
#pragma prefast(pop)
2017-07-11 06:54:54 +03:00
#endif
2016-10-29 01:06:51 +03:00
// Parameter validation helper.
void EffectLights::ValidateLightIndex(int whichLight)
{
if (whichLight < 0 || whichLight >= MaxDirectionalLights)
{
2021-01-06 00:01:34 +03:00
throw std::invalid_argument("whichLight parameter invalid");
2016-10-29 01:06:51 +03:00
}
}
// Activates the default lighting rig (key, fill, and back lights).
void EffectLights::EnableDefaultLighting(_In_ IEffectLights* effect)
{
static const XMVECTORF32 defaultDirections[MaxDirectionalLights] =
{
2017-07-11 06:54:54 +03:00
{ { { -0.5265408f, -0.5735765f, -0.6275069f, 0 } } },
{ { { 0.7198464f, 0.3420201f, 0.6040227f, 0 } } },
{ { { 0.4545195f, -0.7660444f, 0.4545195f, 0 } } },
2016-10-29 01:06:51 +03:00
};
static const XMVECTORF32 defaultDiffuse[MaxDirectionalLights] =
{
2017-07-11 06:54:54 +03:00
{ { { 1.0000000f, 0.9607844f, 0.8078432f, 0 } } },
{ { { 0.9647059f, 0.7607844f, 0.4078432f, 0 } } },
{ { { 0.3231373f, 0.3607844f, 0.3937255f, 0 } } },
2016-10-29 01:06:51 +03:00
};
static const XMVECTORF32 defaultSpecular[MaxDirectionalLights] =
{
2017-07-11 06:54:54 +03:00
{ { { 1.0000000f, 0.9607844f, 0.8078432f, 0 } } },
{ { { 0.0000000f, 0.0000000f, 0.0000000f, 0 } } },
{ { { 0.3231373f, 0.3607844f, 0.3937255f, 0 } } },
2016-10-29 01:06:51 +03:00
};
2017-07-11 06:54:54 +03:00
static const XMVECTORF32 defaultAmbient = { { { 0.05333332f, 0.09882354f, 0.1819608f, 0 } } };
2016-10-29 01:06:51 +03:00
effect->SetLightingEnabled(true);
effect->SetAmbientLightColor(defaultAmbient);
for (int i = 0; i < MaxDirectionalLights; i++)
{
effect->SetLightEnabled(i, true);
effect->SetLightDirection(i, defaultDirections[i]);
effect->SetLightDiffuseColor(i, defaultDiffuse[i]);
effect->SetLightSpecularColor(i, defaultSpecular[i]);
}
}
// Gets or lazily creates the specified vertex shader permutation.
ID3D11VertexShader* EffectDeviceResources::DemandCreateVertexShader(_Inout_ ComPtr<ID3D11VertexShader>& vertexShader, ShaderBytecode const& bytecode)
{
return DemandCreate(vertexShader, mMutex, [&](ID3D11VertexShader** pResult) -> HRESULT
{
HRESULT hr = mDevice->CreateVertexShader(bytecode.code, bytecode.length, nullptr, pResult);
2016-10-29 01:06:51 +03:00
if (SUCCEEDED(hr))
SetDebugObjectName(*pResult, "DirectXTK:Effect");
2016-10-29 01:06:51 +03:00
return hr;
});
2016-10-29 01:06:51 +03:00
}
// Gets or lazily creates the specified pixel shader permutation.
ID3D11PixelShader* EffectDeviceResources::DemandCreatePixelShader(_Inout_ ComPtr<ID3D11PixelShader>& pixelShader, ShaderBytecode const& bytecode)
{
return DemandCreate(pixelShader, mMutex, [&](ID3D11PixelShader** pResult) -> HRESULT
{
HRESULT hr = mDevice->CreatePixelShader(bytecode.code, bytecode.length, nullptr, pResult);
2016-10-29 01:06:51 +03:00
if (SUCCEEDED(hr))
SetDebugObjectName(*pResult, "DirectXTK:Effect");
2016-10-29 01:06:51 +03:00
return hr;
});
2016-10-29 01:06:51 +03:00
}
// Gets or lazily creates the default texture
ID3D11ShaderResourceView* EffectDeviceResources::GetDefaultTexture()
{
return DemandCreate(mDefaultTexture, mMutex, [&](ID3D11ShaderResourceView** pResult) -> HRESULT
{
static const uint32_t s_pixel = 0xffffffff;
2018-03-16 21:33:06 +03:00
D3D11_SUBRESOURCE_DATA initData = { &s_pixel, sizeof(uint32_t), 0 };
2016-10-29 01:06:51 +03:00
D3D11_TEXTURE2D_DESC desc = {};
desc.Width = desc.Height = desc.MipLevels = desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_IMMUTABLE;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
2016-10-29 01:06:51 +03:00
ComPtr<ID3D11Texture2D> tex;
HRESULT hr = mDevice->CreateTexture2D(&desc, &initData, tex.GetAddressOf());
2016-10-29 01:06:51 +03:00
if (SUCCEEDED(hr))
{
SetDebugObjectName(tex.Get(), "DirectXTK:Effect");
2016-10-29 01:06:51 +03:00
D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc = {};
SRVDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
SRVDesc.Texture2D.MipLevels = 1;
2016-10-29 01:06:51 +03:00
hr = mDevice->CreateShaderResourceView(tex.Get(), &SRVDesc, pResult);
if (SUCCEEDED(hr))
SetDebugObjectName(*pResult, "DirectXTK:Effect");
}
2016-10-29 01:06:51 +03:00
return hr;
});
2016-10-29 01:06:51 +03:00
}
ID3D11ShaderResourceView* EffectDeviceResources::GetDefaultNormalTexture()
{
return DemandCreate(mDefaultNormalTexture, mMutex, [&](ID3D11ShaderResourceView** pResult) -> HRESULT
{
static const uint16_t s_pixel = 0x7f7f;
D3D11_SUBRESOURCE_DATA initData = { &s_pixel, sizeof(uint16_t), 0 };
D3D11_TEXTURE2D_DESC desc = {};
desc.Width = desc.Height = desc.MipLevels = desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_IMMUTABLE;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
ComPtr<ID3D11Texture2D> tex;
HRESULT hr = mDevice->CreateTexture2D(&desc, &initData, tex.GetAddressOf());
if (SUCCEEDED(hr))
{
SetDebugObjectName(tex.Get(), "DirectXTK:Effect");
D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc = {};
SRVDesc.Format = DXGI_FORMAT_R8G8_UNORM;
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
SRVDesc.Texture2D.MipLevels = 1;
hr = mDevice->CreateShaderResourceView(tex.Get(), &SRVDesc, pResult);
if (SUCCEEDED(hr))
SetDebugObjectName(*pResult, "DirectXTK:Effect");
}
return hr;
});
}
// Gets device feature level
D3D_FEATURE_LEVEL EffectDeviceResources::GetDeviceFeatureLevel() const
{
return mDevice->GetFeatureLevel();
}