in SHMath/DirectXSH.cpp [4545:4661]
bool XM_CALLCONV DirectX::XMSHEvalSphericalLight(
size_t order,
FXMVECTOR pos,
float radius,
FXMVECTOR color,
float *resultR,
float *resultG,
float *resultB) noexcept
{
if (!resultR)
return false;
if (radius < 0.f)
return false;
const float fDist = XMVectorGetX(XMVector3Length(pos));
// WARNING: fDist should not be < radius - otherwise light contains origin
//const float fSinConeAngle = (fDist <= radius) ? 0.99999f : radius/fDist;
const float fConeAngle = (fDist <= radius) ? (XM_PIDIV2) : asinf(radius / fDist);
XMVECTOR dir = XMVector3Normalize(pos);
float fTmpDir[XM_SH_MAXORDER* XM_SH_MAXORDER]; // rotation "vector"
float fTmpL0[XM_SH_MAXORDER];
//
// Sphere at distance fDist, the cone angle is determined by looking at the
// right triangle with one side (the hypotenuse) beind the vector from the
// origin to the center of the sphere, another side is from the origin to
// a point on the sphere whose normal is perpendicular to the given side (this
// is one of the points on the cone that is defined by the projection of the sphere
// through the origin - we want to find the angle of this cone) and the final
// side being from the center of the sphere to the point of tagency (the two
// sides conected to this are at a right angle by construction.)
// From trig we know that sin(theta) = ||opposite||/||hypotenuse||, where
// ||opposite|| = Radius, ||hypotenuse|| = fDist
// theta is the angle of the cone that subtends the sphere from the origin
//
// no default normalization is done for this case, have to be careful how
// you represent the coefficients...
const float fNewNorm = 1.0f;///(fSinConeAngle*fSinConeAngle);
ComputeCapInt(order, fConeAngle, fTmpL0);
XMFLOAT3A vd;
XMStoreFloat3(&vd, dir);
const float fX = vd.x;
const float fY = vd.y;
const float fZ = vd.z;
switch (order)
{
case 2:
sh_eval_basis_1(fX, fY, fZ, fTmpDir);
break;
case 3:
sh_eval_basis_2(fX, fY, fZ, fTmpDir);
break;
case 4:
sh_eval_basis_3(fX, fY, fZ, fTmpDir);
break;
case 5:
sh_eval_basis_4(fX, fY, fZ, fTmpDir);
break;
case 6:
sh_eval_basis_5(fX, fY, fZ, fTmpDir);
break;
default:
assert(order < XM_SH_MINORDER || order > XM_SH_MAXORDER);
return false;
}
XMFLOAT3A clr;
XMStoreFloat3A(&clr, color);
for (size_t i = 0; i < order; ++i)
{
const size_t cNumCoefs = 2 * i + 1;
const size_t cStart = i*i;
const float fValUse = fTmpL0[i] * clr.x*fNewNorm*fExtraNormFac[i];
for (size_t j = 0; j < cNumCoefs; ++j) resultR[cStart + j] = fTmpDir[cStart + j] * fValUse;
}
if (resultG)
{
for (size_t i = 0; i < order; ++i)
{
const size_t cNumCoefs = 2 * i + 1;
const size_t cStart = i*i;
const float fValUse = fTmpL0[i] * clr.y*fNewNorm*fExtraNormFac[i];
for (size_t j = 0; j < cNumCoefs; ++j) resultG[cStart + j] = fTmpDir[cStart + j] * fValUse;
}
}
if (resultB)
{
for (size_t i = 0; i < order; ++i)
{
const size_t cNumCoefs = 2 * i + 1;
const size_t cStart = i*i;
const float fValUse = fTmpL0[i] * clr.z*fNewNorm*fExtraNormFac[i];
for (size_t j = 0; j < cNumCoefs; ++j) resultB[cStart + j] = fTmpDir[cStart + j] * fValUse;
}
}
return true;
}