437 lines
14 KiB
C++
437 lines
14 KiB
C++
//--------------------------------------------------------------------------------------
|
|
// File: EffectCommon.cpp
|
|
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// http://go.microsoft.com/fwlink/?LinkId=248929
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
#include "pch.h"
|
|
#include "EffectCommon.h"
|
|
#include "DemandCreate.h"
|
|
|
|
using namespace DirectX;
|
|
using Microsoft::WRL::ComPtr;
|
|
|
|
|
|
// Constructor initializes default matrix values.
|
|
EffectMatrices::EffectMatrices()
|
|
{
|
|
world = XMMatrixIdentity();
|
|
view = XMMatrixIdentity();
|
|
projection = XMMatrixIdentity();
|
|
worldView = XMMatrixIdentity();
|
|
}
|
|
|
|
|
|
// Lazily recomputes the combined world+view+projection matrix.
|
|
_Use_decl_annotations_ void EffectMatrices::SetConstants(int& dirtyFlags, XMMATRIX& worldViewProjConstant)
|
|
{
|
|
if (dirtyFlags & EffectDirtyFlags::WorldViewProj)
|
|
{
|
|
worldView = XMMatrixMultiply(world, view);
|
|
|
|
worldViewProjConstant = XMMatrixTranspose(XMMatrixMultiply(worldView, projection));
|
|
|
|
dirtyFlags &= ~EffectDirtyFlags::WorldViewProj;
|
|
dirtyFlags |= EffectDirtyFlags::ConstantBuffer;
|
|
}
|
|
}
|
|
|
|
|
|
// Constructor initializes default fog settings.
|
|
EffectFog::EffectFog()
|
|
{
|
|
enabled = false;
|
|
start = 0;
|
|
end = 1;
|
|
}
|
|
|
|
|
|
// 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.
|
|
static const XMVECTORF32 fullyFogged = { 0, 0, 0, 1 };
|
|
|
|
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.
|
|
|
|
// _13, _23, _33, _43
|
|
XMVECTOR worldViewZ = XMVectorMergeXY(XMVectorMergeZW(worldView.r[0], worldView.r[2]),
|
|
XMVectorMergeZW(worldView.r[1], worldView.r[3]));
|
|
|
|
// 0, 0, 0, fogStart
|
|
XMVECTOR wOffset = XMVectorSwizzle<1, 2, 3, 0>(XMLoadFloat(&start));
|
|
|
|
fogVectorConstant = (worldViewZ + wOffset) / (start - end);
|
|
}
|
|
|
|
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()
|
|
{
|
|
diffuseColor = g_XMOne;
|
|
alpha = 1;
|
|
}
|
|
|
|
|
|
// 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)
|
|
{
|
|
XMVECTOR alphaVector = XMVectorReplicate(alpha);
|
|
|
|
// xyz = diffuse * alpha, w = alpha.
|
|
diffuseColorConstant = XMVectorSelect(alphaVector, diffuseColor * alphaVector, g_XMSelect1110);
|
|
|
|
dirtyFlags &= ~EffectDirtyFlags::MaterialColor;
|
|
dirtyFlags |= EffectDirtyFlags::ConstantBuffer;
|
|
}
|
|
}
|
|
|
|
|
|
// Constructor initializes default light settings.
|
|
EffectLights::EffectLights()
|
|
{
|
|
emissiveColor = g_XMZero;
|
|
ambientLightColor = g_XMZero;
|
|
|
|
for (int i = 0; i < MaxDirectionalLights; i++)
|
|
{
|
|
lightEnabled[i] = (i == 0);
|
|
lightDiffuseColor[i] = g_XMOne;
|
|
lightSpecularColor[i] = g_XMZero;
|
|
}
|
|
}
|
|
|
|
|
|
#pragma prefast(push)
|
|
#pragma prefast(disable:22103, "PREFAST doesn't understand buffer is bounded by a static const value even with SAL" )
|
|
|
|
// Initializes constant buffer fields to match the current lighting state.
|
|
_Use_decl_annotations_ void EffectLights::InitializeConstants(XMVECTOR& specularColorAndPowerConstant, XMVECTOR* lightDirectionConstant, XMVECTOR* lightDiffuseConstant, XMVECTOR* lightSpecularConstant)
|
|
{
|
|
static const XMVECTORF32 defaultSpecular = { 1, 1, 1, 16 };
|
|
static const XMVECTORF32 defaultLightDirection = { 0, -1, 0, 0 };
|
|
|
|
specularColorAndPowerConstant = defaultSpecular;
|
|
|
|
for (int i = 0; i < MaxDirectionalLights; i++)
|
|
{
|
|
lightDirectionConstant[i] = defaultLightDirection;
|
|
|
|
lightDiffuseConstant[i] = lightEnabled[i] ? lightDiffuseColor[i] : g_XMZero;
|
|
lightSpecularConstant[i] = lightEnabled[i] ? lightSpecularColor[i] : g_XMZero;
|
|
}
|
|
}
|
|
|
|
#pragma prefast(pop)
|
|
|
|
|
|
// 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);
|
|
|
|
XMMATRIX worldInverse = XMMatrixInverse(nullptr, matrices.world);
|
|
|
|
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);
|
|
|
|
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;
|
|
XMVECTOR alphaVector = XMVectorReplicate(alpha);
|
|
|
|
if (lightingEnabled)
|
|
{
|
|
// Merge emissive and ambient light contributions.
|
|
emissiveColorConstant = (emissiveColor + ambientLightColor * diffuse) * alphaVector;
|
|
}
|
|
else
|
|
{
|
|
// Merge diffuse and emissive light contributions.
|
|
diffuse += emissiveColor;
|
|
}
|
|
|
|
// xyz = diffuse * alpha, w = alpha.
|
|
diffuseColorConstant = XMVectorSelect(alphaVector, diffuse * alphaVector, g_XMSelect1110);
|
|
|
|
dirtyFlags &= ~EffectDirtyFlags::MaterialColor;
|
|
dirtyFlags |= EffectDirtyFlags::ConstantBuffer;
|
|
}
|
|
}
|
|
|
|
|
|
#pragma prefast(push)
|
|
#pragma prefast(disable:26015, "PREFAST doesn't understand that ValidateLightIndex bounds whichLight" )
|
|
|
|
// 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;
|
|
|
|
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;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#pragma prefast(pop)
|
|
|
|
|
|
// Parameter validation helper.
|
|
void EffectLights::ValidateLightIndex(int whichLight)
|
|
{
|
|
if (whichLight < 0 || whichLight >= MaxDirectionalLights)
|
|
{
|
|
throw std::out_of_range("whichLight parameter out of range");
|
|
}
|
|
}
|
|
|
|
|
|
// Activates the default lighting rig (key, fill, and back lights).
|
|
void EffectLights::EnableDefaultLighting(_In_ IEffectLights* effect)
|
|
{
|
|
static const XMVECTORF32 defaultDirections[MaxDirectionalLights] =
|
|
{
|
|
{ -0.5265408f, -0.5735765f, -0.6275069f },
|
|
{ 0.7198464f, 0.3420201f, 0.6040227f },
|
|
{ 0.4545195f, -0.7660444f, 0.4545195f },
|
|
};
|
|
|
|
static const XMVECTORF32 defaultDiffuse[MaxDirectionalLights] =
|
|
{
|
|
{ 1.0000000f, 0.9607844f, 0.8078432f },
|
|
{ 0.9647059f, 0.7607844f, 0.4078432f },
|
|
{ 0.3231373f, 0.3607844f, 0.3937255f },
|
|
};
|
|
|
|
static const XMVECTORF32 defaultSpecular[MaxDirectionalLights] =
|
|
{
|
|
{ 1.0000000f, 0.9607844f, 0.8078432f },
|
|
{ 0.0000000f, 0.0000000f, 0.0000000f },
|
|
{ 0.3231373f, 0.3607844f, 0.3937255f },
|
|
};
|
|
|
|
static const XMVECTORF32 defaultAmbient = { 0.05333332f, 0.09882354f, 0.1819608f };
|
|
|
|
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);
|
|
|
|
if (SUCCEEDED(hr))
|
|
SetDebugObjectName(*pResult, "DirectXTK:Effect");
|
|
|
|
return hr;
|
|
});
|
|
}
|
|
|
|
|
|
// 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);
|
|
|
|
if (SUCCEEDED(hr))
|
|
SetDebugObjectName(*pResult, "DirectXTK:Effect");
|
|
|
|
return hr;
|
|
});
|
|
}
|
|
|
|
|
|
// Gets or lazily creates the default texture
|
|
ID3D11ShaderResourceView* EffectDeviceResources::GetDefaultTexture()
|
|
{
|
|
return DemandCreate(mDefaultTexture, mMutex, [&](ID3D11ShaderResourceView** pResult) -> HRESULT
|
|
{
|
|
static const uint32_t s_pixel = 0xffffffff;
|
|
|
|
D3D11_SUBRESOURCE_DATA initData = { &s_pixel, sizeof(uint32_t), 0 };
|
|
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
memset( &desc, 0, sizeof(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;
|
|
|
|
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;
|
|
memset( &SRVDesc, 0, sizeof( SRVDesc ) );
|
|
SRVDesc.Format = DXGI_FORMAT_R8G8B8A8_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;
|
|
});
|
|
}
|