bool XM_CALLCONV DirectX::XMSHEvalSphericalLight()

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;
}