Ejemplo n.º 1
0
bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<glm::vec3> & output, const uint order) {
    int width = cubeTexture.getWidth();
    if(width != cubeTexture.getHeight()) {
        return false;
    }

    PROFILE_RANGE(render_gpu, "sphericalHarmonicsFromTexture");

    auto mipFormat = cubeTexture.getStoredMipFormat();
    std::function<glm::vec3(uint32)> unpackFunc;

    switch (mipFormat.getSemantic()) {
        case gpu::R11G11B10:
            unpackFunc = glm::unpackF2x11_1x10;
            break;
        case gpu::RGB9E5:
            unpackFunc = glm::unpackF3x9_E1x5;
            break;
        default:
            assert(false);
            break;
    }

    const uint sqOrder = order*order;

    // allocate memory for calculations
    output.resize(sqOrder);
    std::vector<float> resultR(sqOrder);
    std::vector<float> resultG(sqOrder);
    std::vector<float> resultB(sqOrder);

    // initialize values
    float fWt = 0.0f;
    for(uint i=0; i < sqOrder; i++) {
        output[i] = glm::vec3(0.0f);
        resultR[i] = 0.0f;
        resultG[i] = 0;
        resultB[i] = 0;
    }
    std::vector<float> shBuff(sqOrder);
    std::vector<float> shBuffB(sqOrder);

    // We trade accuracy for speed by breaking the image into 32x32 parts
    // and approximating the distance for all the pixels in each part to be
    // the distance to the part's center.
    int numDivisionsPerSide = 32;
    if (width < numDivisionsPerSide) {
        numDivisionsPerSide = width;
    }
    int stride = width / numDivisionsPerSide;
    int halfStride = stride / 2;

    // for each face of cube texture
    for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) {
        PROFILE_RANGE(render_gpu, "ProcessFace");

        auto data = reinterpret_cast<const uint32*>( cubeTexture.accessStoredMipFace(0, face)->readData() );
        if (data == nullptr) {
            continue;
        }

        // step between two texels for range [0, 1]
        float invWidth = 1.0f / float(width);
        // initial negative bound for range [-1, 1]
        float negativeBound = -1.0f + invWidth;
        // step between two texels for range [-1, 1]
        float invWidthBy2 = 2.0f / float(width);

        for(int y=halfStride; y < width-halfStride; y += stride) {
            // texture coordinate V in range [-1 to 1]
            const float fV = negativeBound + float(y) * invWidthBy2;

            for(int x=halfStride; x < width - halfStride; x += stride) {
                // texture coordinate U in range [-1 to 1]
                const float fU = negativeBound + float(x) * invWidthBy2;

                // determine direction from center of cube texture to current texel
                glm::vec3 dir;
                switch(face) {
                case gpu::Texture::CUBE_FACE_RIGHT_POS_X: {
                    dir.x = 1.0f;
                    dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth);
                    dir.z = 1.0f - (invWidthBy2 * float(x) + invWidth);
                    dir = -dir;
                    break;
                }
                case gpu::Texture::CUBE_FACE_LEFT_NEG_X: {
                    dir.x = -1.0f;
                    dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth);
                    dir.z = -1.0f + (invWidthBy2 * float(x) + invWidth);
                    dir = -dir;
                    break;
                }
                case gpu::Texture::CUBE_FACE_TOP_POS_Y: {
                    dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth);
                    dir.y = 1.0f;
                    dir.z = - 1.0f + (invWidthBy2 * float(y) + invWidth);
                    dir = -dir;
                    break;
                }
                case gpu::Texture::CUBE_FACE_BOTTOM_NEG_Y: {
                    dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth);
                    dir.y = - 1.0f;
                    dir.z = 1.0f - (invWidthBy2 * float(y) + invWidth);
                    dir = -dir;
                    break;
                }
                case gpu::Texture::CUBE_FACE_BACK_POS_Z: {
                    dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth);
                    dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth);
                    dir.z = 1.0f;
                    break;
                }
                case gpu::Texture::CUBE_FACE_FRONT_NEG_Z: {
                    dir.x = 1.0f - (invWidthBy2 * float(x) + invWidth);
                    dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth);
                    dir.z = - 1.0f;
                    break;
                }
                default:
                    return false;
                }

                // normalize direction
                dir = glm::normalize(dir);

                // scale factor depending on distance from center of the face
                const float fDiffSolid = 4.0f / ((1.0f + fU*fU + fV*fV) *
                                            sqrtf(1.0f + fU*fU + fV*fV));
                fWt += fDiffSolid;

                // calculate coefficients of spherical harmonics for current direction
                sphericalHarmonicsEvaluateDirection(shBuff.data(), order, dir);

                // index of texel in texture

                // get color from texture
                glm::vec3 color{ 0.0f, 0.0f, 0.0f };
                for (int i = 0; i < stride; ++i) {
                    for (int j = 0; j < stride; ++j) {
                        int k = (int)(x + i - halfStride + (y + j - halfStride) * width);
                        color += unpackFunc(data[k]);
                    }
                }

                // scale color and add to previously accumulated coefficients
                // red
                sphericalHarmonicsScale(shBuffB.data(), order, shBuff.data(), color.r * fDiffSolid);
                sphericalHarmonicsAdd(resultR.data(), order, resultR.data(), shBuffB.data());
                // green
                sphericalHarmonicsScale(shBuffB.data(), order, shBuff.data(), color.g * fDiffSolid);
                sphericalHarmonicsAdd(resultG.data(), order, resultG.data(), shBuffB.data());
                // blue
                sphericalHarmonicsScale(shBuffB.data(), order, shBuff.data(), color.b * fDiffSolid);
                sphericalHarmonicsAdd(resultB.data(), order, resultB.data(), shBuffB.data());
            }
        }
    }

    // final scale for coefficients
    const float fNormProj = (4.0f * glm::pi<float>()) / (fWt * (float)(stride * stride));
    sphericalHarmonicsScale(resultR.data(), order, resultR.data(), fNormProj);
    sphericalHarmonicsScale(resultG.data(), order, resultG.data(), fNormProj);
    sphericalHarmonicsScale(resultB.data(), order, resultB.data(), fNormProj);

    // save result
    for(uint i=0; i < sqOrder; i++) {
        output[i] = glm::vec3(resultR[i], resultG[i], resultB[i]);
    }

    return true;
}
Ejemplo n.º 2
0
bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<glm::vec3> & output, const uint order) {
    const uint sqOrder = order*order;

    // allocate memory for calculations
    output.resize(sqOrder);
    std::vector<float> resultR(sqOrder);
    std::vector<float> resultG(sqOrder);
    std::vector<float> resultB(sqOrder);

    int width, height;

    // initialize values
    float fWt = 0.0f;
    for(uint i=0; i < sqOrder; i++) {
        output[i] = glm::vec3(0.0f);
        resultR[i] = 0.0f;
        resultG[i] = 0;
        resultB[i] = 0;
    }
    std::vector<float> shBuff(sqOrder);
    std::vector<float> shBuffB(sqOrder);
    // get width and height
    width = height = cubeTexture.getWidth();
    if(width != height) {
        return false;
    }

    const float UCHAR_TO_FLOAT = 1.0f / float(std::numeric_limits<unsigned char>::max());

    // for each face of cube texture
    for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) {

        auto numComponents = cubeTexture.accessStoredMipFace(0,face)->_format.getDimensionCount();
        auto data = cubeTexture.accessStoredMipFace(0,face)->_sysmem.readData();
        if (data == nullptr) {
            continue;
        }

        // step between two texels for range [0, 1]
        float invWidth = 1.0f / float(width);
        // initial negative bound for range [-1, 1]
        float negativeBound = -1.0f + invWidth;
        // step between two texels for range [-1, 1]
        float invWidthBy2 = 2.0f / float(width);

        for(int y=0; y < width; y++) {
            // texture coordinate V in range [-1 to 1]
            const float fV = negativeBound + float(y) * invWidthBy2;

            for(int x=0; x < width; x++) {
                // texture coordinate U in range [-1 to 1]
                const float fU = negativeBound + float(x) * invWidthBy2;

                // determine direction from center of cube texture to current texel
                glm::vec3 dir;
                switch(face) {
                case gpu::Texture::CUBE_FACE_RIGHT_POS_X: {
                    dir.x = 1.0f;
                    dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth);
                    dir.z = 1.0f - (invWidthBy2 * float(x) + invWidth);
                    dir = -dir;
                    break;
                }
                case gpu::Texture::CUBE_FACE_LEFT_NEG_X: {
                    dir.x = -1.0f;
                    dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth);
                    dir.z = -1.0f + (invWidthBy2 * float(x) + invWidth);
                    dir = -dir;
                    break;
                }
                case gpu::Texture::CUBE_FACE_TOP_POS_Y: {
                    dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth);
                    dir.y = 1.0f;
                    dir.z = - 1.0f + (invWidthBy2 * float(y) + invWidth);
                    dir = -dir;
                    break;
                }
                case gpu::Texture::CUBE_FACE_BOTTOM_NEG_Y: {
                    dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth);
                    dir.y = - 1.0f;
                    dir.z = 1.0f - (invWidthBy2 * float(y) + invWidth);
                    dir = -dir;
                    break;
                }
                case gpu::Texture::CUBE_FACE_BACK_POS_Z: {
                    dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth);
                    dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth);
                    dir.z = 1.0f;
                    break;
                }
                case gpu::Texture::CUBE_FACE_FRONT_NEG_Z: {
                    dir.x = 1.0f - (invWidthBy2 * float(x) + invWidth);
                    dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth);
                    dir.z = - 1.0f;
                    break;
                }
                default:
                    return false;
                }

                // normalize direction
                dir = glm::normalize(dir);

                // scale factor depending on distance from center of the face
                const float fDiffSolid = 4.0f / ((1.0f + fU*fU + fV*fV) *
                                            sqrtf(1.0f + fU*fU + fV*fV));
                fWt += fDiffSolid;

                // calculate coefficients of spherical harmonics for current direction
                sphericalHarmonicsEvaluateDirection(shBuff.data(), order, dir);

                // index of texel in texture
                uint pixOffsetIndex = (x + y * width) * numComponents;

                // get color from texture and map to range [0, 1]
                glm::vec3 clr(float(data[pixOffsetIndex]) * UCHAR_TO_FLOAT,
                            float(data[pixOffsetIndex+1]) * UCHAR_TO_FLOAT,
                            float(data[pixOffsetIndex+2]) * UCHAR_TO_FLOAT);

                // Gamma correct
                clr = sRGBToLinear(clr);

                // scale color and add to previously accumulated coefficients
                sphericalHarmonicsScale(shBuffB.data(), order,
                        shBuff.data(), clr.r * fDiffSolid);
                sphericalHarmonicsAdd(resultR.data(), order,
                        resultR.data(), shBuffB.data());
                sphericalHarmonicsScale(shBuffB.data(), order,
                        shBuff.data(), clr.g * fDiffSolid);
                sphericalHarmonicsAdd(resultG.data(), order,
                        resultG.data(), shBuffB.data());
                sphericalHarmonicsScale(shBuffB.data(), order,
                        shBuff.data(), clr.b * fDiffSolid);
                sphericalHarmonicsAdd(resultB.data(), order,
                        resultB.data(), shBuffB.data());
            }
        }
    }

    // final scale for coefficients
    const float fNormProj = (4.0f * glm::pi<float>()) / fWt;
    sphericalHarmonicsScale(resultR.data(), order, resultR.data(), fNormProj);
    sphericalHarmonicsScale(resultG.data(), order, resultG.data(), fNormProj);
    sphericalHarmonicsScale(resultB.data(), order, resultB.data(), fNormProj);

    // save result
    for(uint i=0; i < sqOrder; i++) {
        // gamma Correct
        // output[i] = linearTosRGB(glm::vec3(resultR[i], resultG[i], resultB[i]));
        output[i] = glm::vec3(resultR[i], resultG[i], resultB[i]);
    }

    return true;
}