HRESULT DirectX::SHProjectCubeMap()

in shared/ext/DirectXMath/SHMath/DirectXSHD3D11.cpp [178:376]


HRESULT DirectX::SHProjectCubeMap(
    ID3D11DeviceContext *context,
    size_t order,
    ID3D11Texture2D *cubeMap,
    float *resultR,
    float *resultG,
    float* resultB) noexcept
{
    if (!context || !cubeMap)
        return E_INVALIDARG;

    if (order < XM_SH_MINORDER || order > XM_SH_MAXORDER)
        return E_INVALIDARG;

    D3D11_TEXTURE2D_DESC desc;
    cubeMap->GetDesc(&desc);

    if ((desc.ArraySize != 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;
    }

    //--- Create a staging resource copy (if needed) to be able to read data
    ID3D11Texture2D* texture = nullptr;

    ComPtr<ID3D11Texture2D> staging;
    if (!(desc.CPUAccessFlags & D3D11_CPU_ACCESS_READ))
    {
        D3D11_TEXTURE2D_DESC sdesc = desc;
        sdesc.BindFlags = 0;
        sdesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
        sdesc.Usage = D3D11_USAGE_STAGING;

        ComPtr<ID3D11Device> device;
        context->GetDevice(&device);

        HRESULT hr = device->CreateTexture2D(&sdesc, nullptr, &staging);
        if (FAILED(hr))
            return hr;

        context->CopyResource(staging.Get(), cubeMap);

        texture = staging.Get();
    }
    else
        texture = cubeMap;

    assert(texture != nullptr);

    //--- Setup for SH projection
    ScopedAlignedArrayXMVECTOR scanline(reinterpret_cast<XMVECTOR*>(_aligned_malloc(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)
    {
        UINT dindex = D3D11CalcSubresource(0, face, desc.MipLevels);

        D3D11_MAPPED_SUBRESOURCE mapped;
        HRESULT hr = context->Map(texture, dindex, D3D11_MAP_READ, 0, &mapped);
        if (FAILED(hr))
            return hr;

        const uint8_t *pSrc = reinterpret_cast<const uint8_t*>(mapped.pData);
        for (UINT y = 0; y < desc.Height; ++y)
        {
            XMVECTOR* ptr = scanline.get();
            if (!_LoadScanline(ptr, desc.Width, pSrc, mapped.RowPitch, desc.Format))
            {
                context->Unmap(texture, dindex);
                return E_FAIL;
            }

            const float v = y * fS + fB;

            XMVECTOR* pixel = ptr;
            for (UINT x = 0; x < desc.Width; ++x, ++pixel)
            {
                const float u = 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 += mapped.RowPitch;
        }

        context->Unmap(texture, dindex);
    }

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