Пример #1
0
bool
TextureSystemImpl::accum3d_sample_bilinear (const Imath::V3f &P, int miplevel,
                                 TextureFile &texturefile,
                                 PerThreadInfo *thread_info,
                                 TextureOpt &options,
                                 float weight, float *accum, float *daccumds,
                                 float *daccumdt, float *daccumdr)
{
    const ImageSpec &spec (texturefile.spec (options.subimage, miplevel));
    const ImageCacheFile::LevelInfo &levelinfo (texturefile.levelinfo(options.subimage,miplevel));
    // As passed in, (s,t) map the texture to (0,1).  Remap to texel coords
    // and subtract 0.5 because samples are at texel centers.
    float s = P[0] * spec.full_width  + spec.full_x - 0.5f;
    float t = P[1] * spec.full_height + spec.full_y - 0.5f;
    float r = P[2] * spec.full_depth  + spec.full_z - 0.5f;
    int sint, tint, rint;
    float sfrac = floorfrac (s, &sint);
    float tfrac = floorfrac (t, &tint);
    float rfrac = floorfrac (r, &rint);
    // Now (sint,tint,rint) are the integer coordinates of the texel to the
    // immediate "upper left" of the lookup point, and (sfrac,tfrac,rfrac) are
    // the amount that the lookup point is actually offset from the
    // texel center (with (1,1) being all the way to the next texel down
    // and to the right).

    // Wrap
    DASSERT (options.swrap_func != NULL && options.twrap_func != NULL &&
             options.rwrap_func != NULL);
    int stex[2], ttex[2], rtex[2];       // Texel coords
    stex[0] = sint;  stex[1] = sint+1;
    ttex[0] = tint;  ttex[1] = tint+1;
    rtex[0] = rint;  rtex[1] = rint+1;
//    bool svalid[2], tvalid[2], rvalid[2];  // Valid texels?  false means black border
    union { bool bvalid[6]; unsigned long long ivalid; } valid_storage;
    valid_storage.ivalid = 0;
    DASSERT (sizeof(valid_storage) >= 6*sizeof(bool));
    const unsigned long long none_valid = 0;
    const unsigned long long all_valid = 0x010101010101LL;
    DASSERT (__LITTLE_ENDIAN__ && "this trick won't work with big endian");
    bool *svalid = valid_storage.bvalid;
    bool *tvalid = valid_storage.bvalid + 2;
    bool *rvalid = valid_storage.bvalid + 4;

    svalid[0] = options.swrap_func (stex[0], spec.x, spec.width);
    svalid[1] = options.swrap_func (stex[1], spec.x, spec.width);
    tvalid[0] = options.twrap_func (ttex[0], spec.y, spec.height);
    tvalid[1] = options.twrap_func (ttex[1], spec.y, spec.height);
    rvalid[0] = options.rwrap_func (rtex[0], spec.z, spec.depth);
    rvalid[1] = options.rwrap_func (rtex[1], spec.z, spec.depth);
    // Account for crop windows
    if (! levelinfo.full_pixel_range) {
        svalid[0] &= (stex[0] >= spec.x && stex[0] < spec.x+spec.width);
        svalid[1] &= (stex[1] >= spec.x && stex[1] < spec.x+spec.width);
        tvalid[0] &= (ttex[0] >= spec.y && ttex[0] < spec.y+spec.height);
        tvalid[1] &= (ttex[1] >= spec.y && ttex[1] < spec.y+spec.height);
        rvalid[0] &= (rtex[0] >= spec.z && rtex[0] < spec.z+spec.depth);
        rvalid[1] &= (rtex[1] >= spec.z && rtex[1] < spec.z+spec.depth);
    }
//    if (! (svalid[0] | svalid[1] | tvalid[0] | tvalid[1] | rvalid[0] | rvalid[1]))
    if (valid_storage.ivalid == none_valid)
        return true; // All texels we need were out of range and using 'black' wrap

    int tilewidthmask  = spec.tile_width  - 1;  // e.g. 63
    int tileheightmask = spec.tile_height - 1;
    int tiledepthmask = spec.tile_depth - 1;
    const unsigned char *texel[2][2][2];
    TileRef savetile[2][2][2];
    static float black[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
    int tile_s = (stex[0] - spec.x) % spec.tile_width;
    int tile_t = (ttex[0] - spec.y) % spec.tile_height;
    int tile_r = (rtex[0] - spec.z) % spec.tile_depth;
    bool s_onetile = (tile_s != tilewidthmask) & (stex[0]+1 == stex[1]);
    bool t_onetile = (tile_t != tileheightmask) & (ttex[0]+1 == ttex[1]);
    bool r_onetile = (tile_r != tiledepthmask) & (rtex[0]+1 == rtex[1]);
    bool onetile = (s_onetile & t_onetile & r_onetile);
    size_t channelsize = texturefile.channelsize();
    size_t pixelsize = texturefile.pixelsize();
    if (onetile &&
        valid_storage.ivalid == all_valid) {
        // Shortcut if all the texels we need are on the same tile
        TileID id (texturefile, options.subimage, miplevel,
                   stex[0] - tile_s, ttex[0] - tile_t, rtex[0] - tile_r);
        bool ok = find_tile (id, thread_info);
        if (! ok)
            error ("%s", m_imagecache->geterror().c_str());
        TileRef &tile (thread_info->tile);
        if (! tile->valid())
            return false;
        size_t tilepel = (tile_r * spec.tile_height + tile_t) * spec.tile_width + tile_s;
        size_t offset = (spec.nchannels * tilepel + options.firstchannel) * channelsize;
        DASSERT ((size_t)offset < spec.tile_width*spec.tile_height*spec.tile_depth*pixelsize);

        const unsigned char *b = tile->bytedata() + offset;
        texel[0][0][0] = b;
        texel[0][0][1] = b + pixelsize;
        texel[0][1][0] = b + pixelsize * spec.tile_width;
        texel[0][1][1] = b + pixelsize * spec.tile_width + pixelsize;
        b += pixelsize * spec.tile_width * spec.tile_height;
        texel[1][0][0] = b;
        texel[1][0][1] = b + pixelsize;
        texel[1][1][0] = b + pixelsize * spec.tile_width;
        texel[1][1][1] = b + pixelsize * spec.tile_width + pixelsize;
    } else {
        for (int k = 0;  k < 2;  ++k) {
            for (int j = 0;  j < 2;  ++j) {
                for (int i = 0;  i < 2;  ++i) {
                    if (! (svalid[i] && tvalid[j] && rvalid[k])) {
                        texel[k][j][i] = (unsigned char *)black;
                        continue;
                    }
                    tile_s = (stex[i] - spec.x) % spec.tile_width;
                    tile_t = (ttex[j] - spec.y) % spec.tile_height;
                    tile_r = (rtex[k] - spec.z) % spec.tile_depth;
                    TileID id (texturefile, options.subimage, miplevel,
                               stex[i] - tile_s, ttex[j] - tile_t,
                               rtex[k] - tile_r);
                    bool ok = find_tile (id, thread_info);
                    if (! ok)
                        error ("%s", m_imagecache->geterror().c_str());
                    TileRef &tile (thread_info->tile);
                    if (! tile->valid())
                        return false;
                    savetile[k][j][i] = tile;
                    size_t tilepel = (tile_r * spec.tile_height + tile_t) * spec.tile_width + tile_s;
                    size_t offset = (spec.nchannels * tilepel + options.firstchannel) * channelsize;
#if DEBUG
                    if ((size_t)offset >= spec.tile_width*spec.tile_height*spec.tile_depth*pixelsize)
                        std::cerr << "offset=" << offset << ", whd " << spec.tile_width << ' ' << spec.tile_height << ' ' << spec.tile_depth << " pixsize " << pixelsize << "\n";
#endif
                    DASSERT ((size_t)offset < spec.tile_width*spec.tile_height*spec.tile_depth*pixelsize);
                    texel[k][j][i] = tile->bytedata() + offset;
                    DASSERT (tile->id() == id);
                }
            }
        }
    }
    // FIXME -- optimize the above loop by unrolling

    if (channelsize == 1) {
        // special case for 8-bit tiles
        int c;
        for (c = 0;  c < options.actualchannels;  ++c)
            accum[c] += weight * trilerp (uchar2float(texel[0][0][0][c]), uchar2float(texel[0][0][1][c]),
                                          uchar2float(texel[0][1][0][c]), uchar2float(texel[0][1][1][c]),
                                          uchar2float(texel[1][0][0][c]), uchar2float(texel[1][0][1][c]),
                                          uchar2float(texel[1][1][0][c]), uchar2float(texel[1][1][1][c]),
                                          sfrac, tfrac, rfrac);
        if (daccumds) {
            float scalex = weight * spec.full_width;
            float scaley = weight * spec.full_height;
            float scalez = weight * spec.full_depth;
            for (c = 0;  c < options.actualchannels;  ++c) {
                daccumds[c] += scalex * bilerp(
                    uchar2float(texel[0][0][1][c]) - uchar2float(texel[0][0][0][c]),
                    uchar2float(texel[0][1][1][c]) - uchar2float(texel[0][1][0][c]),

                    uchar2float(texel[1][0][1][c]) - uchar2float(texel[1][0][0][c]),
                    uchar2float(texel[1][1][1][c]) - uchar2float(texel[1][1][0][c]),
                    tfrac, rfrac
                );
                daccumdt[c] += scaley * bilerp(
                    uchar2float(texel[0][1][0][c]) - uchar2float(texel[0][0][0][c]),
                    uchar2float(texel[0][1][1][c]) - uchar2float(texel[0][0][1][c]),
                    uchar2float(texel[1][1][0][c]) - uchar2float(texel[1][0][0][c]),
                    uchar2float(texel[1][1][1][c]) - uchar2float(texel[1][0][1][c]),
                    sfrac, rfrac
                );
                daccumdr[c] += scalez * bilerp(
                    uchar2float(texel[0][1][0][c]) - uchar2float(texel[1][1][0][c]),
                    uchar2float(texel[0][1][1][c]) - uchar2float(texel[1][1][1][c]),
                    uchar2float(texel[0][0][1][c]) - uchar2float(texel[1][0][0][c]),
                    uchar2float(texel[0][1][1][c]) - uchar2float(texel[1][1][1][c]),
                    sfrac, tfrac
                );
            }
        }
    } else {
        // General case for float tiles
        trilerp_mad ((const float *)texel[0][0][0], (const float *)texel[0][0][1],
                     (const float *)texel[0][1][0], (const float *)texel[0][1][1],
                     (const float *)texel[1][0][0], (const float *)texel[1][0][1],
                     (const float *)texel[1][1][0], (const float *)texel[1][1][1],
                     sfrac, tfrac, rfrac, weight, options.actualchannels, accum);
        if (daccumds) {
            float scalex = weight * spec.full_width;
            float scaley = weight * spec.full_height;
            float scalez = weight * spec.full_depth;
            for (int c = 0;  c < options.actualchannels;  ++c) {
                daccumds[c] += scalex * bilerp(
                    ((const float *) texel[0][0][1])[c] - ((const float *) texel[0][0][0])[c],
                    ((const float *) texel[0][1][1])[c] - ((const float *) texel[0][1][0])[c],
                    ((const float *) texel[1][0][1])[c] - ((const float *) texel[1][0][0])[c],
                    ((const float *) texel[1][1][1])[c] - ((const float *) texel[1][1][0])[c],
                    tfrac, rfrac
                );
                daccumdt[c] += scaley * bilerp(
                    ((const float *) texel[0][1][0])[c] - ((const float *) texel[0][0][0])[c],
                    ((const float *) texel[0][1][1])[c] - ((const float *) texel[0][0][1])[c],
                    ((const float *) texel[1][1][0])[c] - ((const float *) texel[1][0][0])[c],
                    ((const float *) texel[1][1][1])[c] - ((const float *) texel[1][0][1])[c],
                    sfrac, rfrac
                );
                daccumdr[c] += scalez * bilerp(
                    ((const float *) texel[0][1][0])[c] - ((const float *) texel[1][1][0])[c],
                    ((const float *) texel[0][1][1])[c] - ((const float *) texel[1][1][1])[c],
                    ((const float *) texel[0][0][1])[c] - ((const float *) texel[1][0][0])[c],
                    ((const float *) texel[0][1][1])[c] - ((const float *) texel[1][1][1])[c],
                    sfrac, tfrac
                );
            }
        }
    }

    return true;
}
Пример #2
0
bool
TextureSystemImpl::environment (TextureHandle *texture_handle_,
                                Perthread *thread_info_,
                                TextureOpt &options, const Imath::V3f &_R,
                                const Imath::V3f &_dRdx, const Imath::V3f &_dRdy,
                                float *result)
{
    PerThreadInfo *thread_info = (PerThreadInfo *)thread_info_;
    TextureFile *texturefile = (TextureFile *)texture_handle_;
    ImageCacheStatistics &stats (thread_info->m_stats);
    ++stats.environment_batches;
    ++stats.environment_queries;

    if (! texturefile  ||  texturefile->broken())
        return missing_texture (options, result);

    const ImageSpec &spec (texturefile->spec(options.subimage, 0));

    options.swrap_func = texturefile->m_sample_border ?
        wrap_periodic_sharedborder : wrap_periodic;
    options.twrap_func = wrap_clamp;
    options.envlayout = LayoutLatLong;
    int actualchannels = Imath::clamp (spec.nchannels - options.firstchannel,
                                       0, options.nchannels);
    options.actualchannels = actualchannels;

    // Initialize results to 0.  We'll add from here on as we sample.
    float* dresultds = options.dresultds;
    float* dresultdt = options.dresultdt;
    for (int c = 0;  c < options.actualchannels;  ++c) {
        result[c] = 0;
        if (dresultds) dresultds[c] = 0;
        if (dresultdt) dresultdt[c] = 0;
    }
    // If the user only provided us with one pointer, clear both to simplify
    // the rest of the code, but only after we zero out the data for them so
    // they know something went wrong.
    if (!(dresultds && dresultdt))
        dresultds = dresultdt = NULL;

    // Calculate unit-length vectors in the direction of R, R+dRdx, R+dRdy.
    // These define the ellipse we're filtering over.
    Imath::V3f R  = _R;  R.normalize();       // center
    Imath::V3f Rx = _R + _dRdx;  Rx.normalize();  // x axis of the ellipse
    Imath::V3f Ry = _R + _dRdy;  Ry.normalize();  // y axis of the ellipse
    // angles formed by the ellipse axes.
    float xfilt_noblur = std::max (safe_acosf(R.dot(Rx)), 1e-8f);
    float yfilt_noblur = std::max (safe_acosf(R.dot(Ry)), 1e-8f);
    int naturalres = int((float)M_PI / std::min (xfilt_noblur, yfilt_noblur));
    // FIXME -- figure naturalres sepearately for s and t
    // FIXME -- ick, why is it x and y at all, shouldn't it be s and t?
    // N.B. naturalres formulated for latlong

    // Account for width and blur
    float xfilt = xfilt_noblur * options.swidth + options.sblur;
    float yfilt = yfilt_noblur * options.twidth + options.tblur;

    // Figure out major versus minor, and aspect ratio
    Imath::V3f Rmajor;   // major axis
    float majorlength, minorlength;
    bool x_is_majoraxis = (xfilt >= yfilt);
    if (x_is_majoraxis) {
        Rmajor = Rx;
        majorlength = xfilt;
        minorlength = yfilt;
    } else {
        Rmajor = Ry;
        majorlength = yfilt;
        minorlength = xfilt;
    }

    accum_prototype accumer;
    long long *probecount;
    switch (options.interpmode) {
    case TextureOpt::InterpClosest :
        accumer = &TextureSystemImpl::accum_sample_closest;
        probecount = &stats.closest_interps;
        break;
    case TextureOpt::InterpBilinear :
        accumer = &TextureSystemImpl::accum_sample_bilinear;
        probecount = &stats.bilinear_interps;
        break;
    case TextureOpt::InterpBicubic :
        accumer = &TextureSystemImpl::accum_sample_bicubic;
        probecount = &stats.cubic_interps;
        break;
    default:
        accumer = NULL;
        probecount = NULL;
        break;
    }

    TextureOpt::MipMode mipmode = options.mipmode;
    bool aniso = (mipmode == TextureOpt::MipModeDefault ||
                  mipmode == TextureOpt::MipModeAniso);

    float aspect, trueaspect, filtwidth;
    int nsamples;
    float invsamples;
    if (aniso) {
        aspect = anisotropic_aspect (majorlength, minorlength, options, trueaspect);
        filtwidth = minorlength;
        if (trueaspect > stats.max_aniso)
            stats.max_aniso = trueaspect;
        nsamples = std::max (1, (int) ceilf (aspect - 0.25f));
        invsamples = 1.0f / nsamples;
    } else {
        filtwidth = options.conservative_filter ? majorlength : minorlength;
        nsamples = 1;
        invsamples = 1.0f;
    }

    ImageCacheFile::SubimageInfo &subinfo (texturefile->subimageinfo(options.subimage));

    // FIXME -- assuming latlong
    bool ok = true;
    float pos = -0.5f + 0.5f * invsamples;
    for (int sample = 0;  sample < nsamples;  ++sample, pos += invsamples) {
        Imath::V3f Rsamp = R + pos*Rmajor;
        float s, t;
        vector_to_latlong (Rsamp, texturefile->m_y_up, s, t);

        // Determine the MIP-map level(s) we need: we will blend
        //  data(miplevel[0]) * (1-levelblend) + data(miplevel[1]) * levelblend
        int miplevel[2] = { -1, -1 };
        float levelblend = 0;

        int nmiplevels = (int)subinfo.levels.size();
        for (int m = 0;  m < nmiplevels;  ++m) {
            // Compute the filter size in raster space at this MIP level.
            // Filters are in radians, and the vertical resolution of a
            // latlong map is PI radians.  So to compute the raster size of
            // our filter width...
            float filtwidth_ras = subinfo.spec(m).full_height * filtwidth * M_1_PI;
            // Once the filter width is smaller than one texel at this level,
            // we've gone too far, so we know that we want to interpolate the
            // previous level and the current level.  Note that filtwidth_ras
            // is expected to be >= 0.5, or would have stopped one level ago.
            if (filtwidth_ras <= 1) {
                miplevel[0] = m-1;
                miplevel[1] = m;
                levelblend = Imath::clamp (2.0f - 1.0f/filtwidth_ras, 0.0f, 1.0f);
                break;
            }
        }
        if (miplevel[1] < 0) {
            // We'd like to blur even more, but make due with the coarsest
            // MIP level.
            miplevel[0] = nmiplevels - 1;
            miplevel[1] = miplevel[0];
            levelblend = 0;
        } else if (miplevel[0] < 0) {
            // We wish we had even more resolution than the finest MIP level,
            // but tough for us.
            miplevel[0] = 0;
            miplevel[1] = 0;
            levelblend = 0;
        }
        if (options.mipmode == TextureOpt::MipModeOneLevel) {
            // Force use of just one mipmap level
            miplevel[1] = miplevel[0];
            levelblend = 0;
        } else if (mipmode == TextureOpt::MipModeNoMIP) {
            // Just sample from lowest level
            miplevel[0] = 0;
            miplevel[1] = 0;
            levelblend = 0;
        }

        float levelweight[2] = { 1.0f - levelblend, levelblend };

        int npointson = 0;
        for (int level = 0;  level < 2;  ++level) {
            if (! levelweight[level])
                continue;
            ++npointson;
            int lev = miplevel[level];
            if (options.interpmode == TextureOpt::InterpSmartBicubic) {
                if (lev == 0 ||
                    (texturefile->spec(options.subimage,lev).full_height < naturalres/2)) {
                    accumer = &TextureSystemImpl::accum_sample_bicubic;
                    ++stats.cubic_interps;
                } else {
                    accumer = &TextureSystemImpl::accum_sample_bilinear;
                    ++stats.bilinear_interps;
                }
            } else {
                *probecount += 1;
            }

            ok &= (this->*accumer) (s, t, miplevel[level], *texturefile,
                                    thread_info, options,
                                    levelweight[level]*invsamples, result,
                                    dresultds, dresultdt);
        }
    }
    stats.aniso_probes += nsamples;
    ++stats.aniso_queries;

    if (actualchannels < options.nchannels)
        fill_channels (spec, options, result);

    return ok;
}
Пример #3
0
bool
TextureSystemImpl::accum3d_sample_closest (const Imath::V3f &P, int miplevel,
                                 TextureFile &texturefile,
                                 PerThreadInfo *thread_info,
                                 TextureOpt &options,
                                 float weight, float *accum, float *daccumds,
                                 float *daccumdt, float *daccumdr)
{
    const ImageSpec &spec (texturefile.spec (options.subimage, miplevel));
    const ImageCacheFile::LevelInfo &levelinfo (texturefile.levelinfo(options.subimage,miplevel));
    // As passed in, (s,t) map the texture to (0,1).  Remap to texel coords.
    float s = P[0] * spec.full_width  + spec.full_x;
    float t = P[1] * spec.full_height + spec.full_y;
    float r = P[2] * spec.full_depth + spec.full_z;
    int stex, ttex, rtex;    // Texel coordintes
    (void) floorfrac (s, &stex);   // don't need fractional result
    (void) floorfrac (t, &ttex);
    (void) floorfrac (r, &rtex);

    // Wrap
    DASSERT (options.swrap_func != NULL && options.twrap_func != NULL &&
             options.rwrap_func != NULL);
    bool svalid, tvalid, rvalid;  // Valid texels?  false means black border
    svalid = options.swrap_func (stex, spec.x, spec.width);
    tvalid = options.twrap_func (ttex, spec.y, spec.height);
    rvalid = options.rwrap_func (rtex, spec.z, spec.depth);
    if (! levelinfo.full_pixel_range) {
        svalid &= (stex >= spec.x && stex < (spec.x+spec.width)); // data window
        tvalid &= (ttex >= spec.y && ttex < (spec.y+spec.height));
        rvalid &= (rtex >= spec.z && rtex < (spec.z+spec.depth));
    }
    if (! (svalid & tvalid & rvalid)) {
        // All texels we need were out of range and using 'black' wrap.
        return true;
    }

    int tile_s = (stex - spec.x) % spec.tile_width;
    int tile_t = (ttex - spec.y) % spec.tile_height;
    int tile_r = (rtex - spec.z) % spec.tile_depth;
    TileID id (texturefile, options.subimage, miplevel,
               stex - tile_s, ttex - tile_t, rtex - tile_r);
    bool ok = find_tile (id, thread_info);
    if (! ok)
        error ("%s", m_imagecache->geterror().c_str());
    TileRef &tile (thread_info->tile);
    if (! tile  ||  ! ok)
        return false;
    size_t channelsize = texturefile.channelsize();
    int tilepel = (tile_r * spec.tile_height + tile_t) * spec.tile_width + tile_s;
    int offset = spec.nchannels * tilepel + options.firstchannel;
    DASSERT ((size_t)offset < spec.nchannels*spec.tile_pixels());
    if (channelsize == 1) {
        // special case for 8-bit tiles
        const unsigned char *texel = tile->bytedata() + offset;
        for (int c = 0;  c < options.actualchannels;  ++c)
            accum[c] += weight * uchar2float(texel[c]);
    } else {
        // General case for float tiles
        const float *texel = tile->data() + offset;
        for (int c = 0;  c < options.actualchannels;  ++c)
            accum[c] += weight * texel[c];
    }
    return true;
}