in SHMath/DirectXSHD3D12.cpp [180:339]
HRESULT DirectX::SHProjectCubeMap(
size_t order,
const D3D12_RESOURCE_DESC& desc,
const D3D12_SUBRESOURCE_DATA cubeMap[6],
float *resultR,
float *resultG,
float *resultB) noexcept
{
if (order < XM_SH_MINORDER || order > XM_SH_MAXORDER)
return E_INVALIDARG;
if (desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D
|| (desc.DepthOrArraySize != 6)
|| (desc.Width != desc.Height)
|| (desc.SampleDesc.Count > 1))
return E_FAIL;
switch (desc.Format)
{
case DXGI_FORMAT_R32G32B32A32_FLOAT:
case DXGI_FORMAT_R32G32B32_FLOAT:
case DXGI_FORMAT_R16G16B16A16_FLOAT:
case DXGI_FORMAT_R32G32_FLOAT:
case DXGI_FORMAT_R11G11B10_FLOAT:
case DXGI_FORMAT_R16G16_FLOAT:
case DXGI_FORMAT_R32_FLOAT:
case DXGI_FORMAT_R16_FLOAT:
// See _LoadScanline to support more pixel formats
break;
default:
return E_FAIL;
}
//--- Setup for SH projection
ScopedAlignedArrayXMVECTOR scanline(reinterpret_cast<XMVECTOR*>(_aligned_malloc(static_cast<size_t>(sizeof(XMVECTOR)*desc.Width), 16)));
if (!scanline)
return E_OUTOFMEMORY;
assert(desc.Width > 0);
float fSize = static_cast<float>(desc.Width);
float fPicSize = 1.0f / fSize;
// index from [0,W-1], f(0) maps to -1 + 1/W, f(W-1) maps to 1 - 1/w
// linear function x*S +B, 1st constraint means B is (-1+1/W), plug into
// second and solve for S: S = 2*(1-1/W)/(W-1). The old code that did
// this was incorrect - but only for computing the differential solid
// angle, where the final value was 1.0 instead of 1-1/w...
float fB = -1.0f + 1.0f / fSize;
float fS = (desc.Width > 1) ? (2.0f*(1.0f - 1.0f / fSize) / (fSize - 1.0f)) : 0.f;
// clear out accumulation variables
float fWt = 0.0f;
if (resultR)
memset(resultR, 0, sizeof(float)*order*order);
if (resultG)
memset(resultG, 0, sizeof(float)*order*order);
if (resultB)
memset(resultB, 0, sizeof(float)*order*order);
float shBuff[XM_SH_MAXORDER*XM_SH_MAXORDER] = {};
float shBuffB[XM_SH_MAXORDER*XM_SH_MAXORDER] = {};
//--- Process each face of the cubemap
for (UINT face = 0; face < 6; ++face)
{
if (!cubeMap[face].pData)
return E_POINTER;
const uint8_t *pSrc = reinterpret_cast<const uint8_t*>(cubeMap[face].pData);
for (UINT y = 0; y < desc.Height; ++y)
{
XMVECTOR* ptr = scanline.get();
if (!_LoadScanline(ptr, static_cast<size_t>(desc.Width), pSrc, static_cast<size_t>(cubeMap[face].RowPitch), desc.Format))
{
return E_FAIL;
}
const float v = float(y) * fS + fB;
XMVECTOR* pixel = ptr;
for (UINT x = 0; x < desc.Width; ++x, ++pixel)
{
const float u = float(x) * fS + fB;
float ix, iy, iz;
switch (face)
{
case 0: // Positive X
iz = 1.0f - (2.0f * float(x) + 1.0f) * fPicSize;
iy = 1.0f - (2.0f * float(y) + 1.0f) * fPicSize;
ix = 1.0f;
break;
case 1: // Negative X
iz = -1.0f + (2.0f * float(x) + 1.0f) * fPicSize;
iy = 1.0f - (2.0f * float(y) + 1.0f) * fPicSize;
ix = -1;
break;
case 2: // Positive Y
iz = -1.0f + (2.0f * float(y) + 1.0f) * fPicSize;
iy = 1.0f;
ix = -1.0f + (2.0f * float(x) + 1.0f) * fPicSize;
break;
case 3: // Negative Y
iz = 1.0f - (2.0f * float(y) + 1.0f) * fPicSize;
iy = -1.0f;
ix = -1.0f + (2.0f * float(x) + 1.0f) * fPicSize;
break;
case 4: // Positive Z
iz = 1.0f;
iy = 1.0f - (2.0f * float(y) + 1.0f) * fPicSize;
ix = -1.0f + (2.0f * float(x) + 1.0f) * fPicSize;
break;
case 5: // Negative Z
iz = -1.0f;
iy = 1.0f - (2.0f * float(y) + 1.0f) * fPicSize;
ix = 1.0f - (2.0f * float(x) + 1.0f) * fPicSize;
break;
default:
ix = iy = iz = 0.f;
assert(false);
break;
}
XMVECTOR dir = XMVectorSet(ix, iy, iz, 0);
dir = XMVector3Normalize(dir);
const float fDiffSolid = 4.0f / ((1.0f + u * u + v * v)*sqrtf(1.0f + u * u + v * v));
fWt += fDiffSolid;
XMSHEvalDirection(shBuff, order, dir);
XMFLOAT3A clr;
XMStoreFloat3A(&clr, *pixel);
if (resultR) XMSHAdd(resultR, order, resultR, XMSHScale(shBuffB, order, shBuff, clr.x*fDiffSolid));
if (resultG) XMSHAdd(resultG, order, resultG, XMSHScale(shBuffB, order, shBuff, clr.y*fDiffSolid));
if (resultB) XMSHAdd(resultB, order, resultB, XMSHScale(shBuffB, order, shBuff, clr.z*fDiffSolid));
}
pSrc += cubeMap[face].RowPitch;
}
}
const float fNormProj = (4.0f*XM_PI) / fWt;
if (resultR) XMSHScale(resultR, order, resultR, fNormProj);
if (resultG) XMSHScale(resultG, order, resultG, fNormProj);
if (resultB) XMSHScale(resultB, order, resultB, fNormProj);
return S_OK;
}