1. 程式人生 > >PBRT_V2 總結記錄 VolumeGridDensity 和 ExponentialDensity

PBRT_V2 總結記錄 VolumeGridDensity 和 ExponentialDensity

VolumeGridDensity 

class VolumeGridDensity : public DensityRegion {
public:
    // VolumeGridDensity Public Methods
    VolumeGridDensity(const Spectrum &sa, const Spectrum &ss, float gg,
            const Spectrum &emit, const BBox &e, const Transform &v2w,
            int x, int y, int z, const float *d)
        : DensityRegion(sa, ss, gg, emit, v2w), nx(x), ny(y), nz(z), extent(e) {
        density = new float[nx*ny*nz];
        memcpy(density, d, nx*ny*nz*sizeof(float));
    }
    ~VolumeGridDensity() { delete[] density; }
    BBox WorldBound() const { return Inverse(WorldToVolume)(extent); }
    bool IntersectP(const Ray &r, float *t0, float *t1) const {
        Ray ray = WorldToVolume(r);
        return extent.IntersectP(ray, t0, t1);
    }
    float Density(const Point &Pobj) const;
    float D(int x, int y, int z) const {
        x = Clamp(x, 0, nx-1);
        y = Clamp(y, 0, ny-1);
        z = Clamp(z, 0, nz-1);
        return density[z*nx*ny + y*nx + x];
    }
private:
    // VolumeGridDensity Private Data
    float *density;
    const int nx, ny, nz;
    const BBox extent;
};

類的作用:

(外部傳入一個 三維陣列 的 作為 density ,所以才叫做Grid)

The VolumeGridDensity class stores densities at a regular 3D grid of positions, similar
to the way that the ImageTexture represents images with a 2D grid of samples. These
samples are interpolated to compute the density at positions between the sample points.

The constructor takes a 3D array of user-supplied density values, thus allowing a variety
of sources of data (physical simulation, CT scan, etc.).

 

1. 建構函式

    VolumeGridDensity(const Spectrum &sa, const Spectrum &ss, float gg,
            const Spectrum &emit, const BBox &e, const Transform &v2w,
            int x, int y, int z, const float *d)
        : DensityRegion(sa, ss, gg, emit, v2w), nx(x), ny(y), nz(z), extent(e) {
        density = new float[nx*ny*nz];
        memcpy(density, d, nx*ny*nz*sizeof(float));
    }

作用:

(初始化 scattering properties,density )

The constructor does the usual initialization of the basic scattering properties, stores an
object space bounding box for the region, and makes a local copy of the density values.

 

2. float Density(const Point &Pobj) const;

float VolumeGridDensity::Density(const Point &Pobj) const {
    if (!extent.Inside(Pobj)) return 0;
    // Compute voxel coordinates and offsets for _Pobj_
    Vector vox = extent.Offset(Pobj);
    vox.x = vox.x * nx - .5f;
    vox.y = vox.y * ny - .5f;
    vox.z = vox.z * nz - .5f;
    int vx = Floor2Int(vox.x), vy = Floor2Int(vox.y), vz = Floor2Int(vox.z);
    float dx = vox.x - vx, dy = vox.y - vy, dz = vox.z - vz;

    // Trilinearly interpolate density values to compute local density
    float d00 = Lerp(dx, D(vx, vy, vz),     D(vx+1, vy, vz));
    float d10 = Lerp(dx, D(vx, vy+1, vz),   D(vx+1, vy+1, vz));
    float d01 = Lerp(dx, D(vx, vy, vz+1),   D(vx+1, vy, vz+1));
    float d11 = Lerp(dx, D(vx, vy+1, vz+1), D(vx+1, vy+1, vz+1));
    float d0 = Lerp(dy, d00, d10);
    float d1 = Lerp(dy, d01, d11);
    return Lerp(dz, d0, d1);
}


float D(int x, int y, int z) const {
    x = Clamp(x, 0, nx-1);
    y = Clamp(y, 0, ny-1);
    z = Clamp(z, 0, nz-1);
    return density[z*nx*ny + y*nx + x];
}

作用:

(使用 一個 點來 獲得 密度)

The task of the Density() method of the VolumeGridDensity is to use the samples to
reconstruct the volume density function at the given point.

 

細節

a.

    // Compute voxel coordinates and offsets for _Pobj_
    Vector vox = extent.Offset(Pobj);
    vox.x = vox.x * nx - .5f;
    vox.y = vox.y * ny - .5f;
    vox.z = vox.z * nz - .5f;
    int vx = Floor2Int(vox.x), vy = Floor2Int(vox.y), vz = Floor2Int(vox.z);
    float dx = vox.x - vx, dy = vox.y - vy, dz = vox.z - vz;

    // 補充
    Vector Offset(const Point &p) const {
        return Vector((p.x - pMin.x) / (pMax.x - pMin.x),
                      (p.y - pMin.y) / (pMax.y - pMin.y),
                      (p.z - pMin.z) / (pMax.z - pMin.z));
    }

    

作用:

(Offset 先講 point 變換到 extent 中,得到的vox 的x,y,z 都是【0,1】,那麼 這裡其實就是 根據一個 點的座標,來確定 這個點在 density  三維陣列中的 偏移值)

Given the eight sample values that surround a point in 3D, this method trilinearly interpolates
them to compute the density function’s value at the point. It starts by using
BBox::Offset() to find the [0, 1]3 offset of the point inside the bounding box and then
scaling by (nx, ny, nz) to find the closest volume sample whose integer coordinates are
all less than the sample location.
It then uses the Manhattan distances along each axis,
(dx, dy, and dz), as the interpolants. Note that the same conventions for discrete versus
continuous texel coordinates are used here as were used in the ImageFilm and MIPMap

 

b.

    // Trilinearly interpolate density values to compute local density
    float d00 = Lerp(dx, D(vx, vy, vz),     D(vx+1, vy, vz));
    float d10 = Lerp(dx, D(vx, vy+1, vz),   D(vx+1, vy+1, vz));
    float d01 = Lerp(dx, D(vx, vy, vz+1),   D(vx+1, vy, vz+1));
    float d11 = Lerp(dx, D(vx, vy+1, vz+1), D(vx+1, vy+1, vz+1));
    float d0 = Lerp(dy, d00, d10);
    float d1 = Lerp(dy, d01, d11);
    return Lerp(dz, d0, d1);

作用:

(偏移值得到了,就利用D函式找到對應的 density 中的值進行插值,得到最終的 密度值)

These distances can be used directly in a series of invocations of Lerp() to estimate the
density at the sample point:

 

c.

The D() utility method returns the density at the given sample position. Its only tasks
are to handle out-of-bounds sample positions with clamping and to compute the appropriate
array offset for the given sample. Unlike MIPMaps, clamping is almost always the
desired solution for out-of-bounds coordinates here. Because all lookup points are inside
the VolumeGridDensity’s bounding box, the only time we will have out-of-bounds coordinates
is at the edges, either due to the offsets for linear interpolation or the continuousto-
discrete texel coordinate conversion. In both of these cases, clamping is the most
sensible solution.

 

 

ExponentialDensity

class ExponentialDensity : public DensityRegion {
public:
    // ExponentialDensity Public Methods
    ExponentialDensity(const Spectrum &sa, const Spectrum &ss,
                       float gg, const Spectrum &emit, const BBox &e,
                       const Transform &v2w, float aa, float bb,
                       const Vector &up)
        : DensityRegion(sa, ss, gg, emit, v2w), extent(e), a(aa), b(bb) {
        upDir = Normalize(up);
    }
    BBox WorldBound() const { return Inverse(WorldToVolume)(extent); }
    bool IntersectP(const Ray &r, float *t0, float *t1) const {
        Ray ray = WorldToVolume(r);
        return extent.IntersectP(ray, t0, t1);
    }
    float Density(const Point &Pobj) const {
        if (!extent.Inside(Pobj)) return 0;
        float height = Dot(Pobj - extent.pMin, upDir);
        return a * expf(-b * height);
    }
private:
    // ExponentialDensity Private Data
    BBox extent;
    float a, b;
    Vector upDir;
};

類的作用:

(密度函式由下面的公式決定)

Another useful density class is ExponentialDensity, which describes a density that varies
as an exponential function of height h within a given 3D extent:

The a and b values are parameters that control the overall density and how quickly
it falls off as a function of height, respectively. This density function is a good model
for the Earth’s atmosphere as seen from the Earth’s surface, where the atmosphere’s(大氣)
curvature(曲率) can generally be neglected. It can also be used to model low-lying fog at
ground level.

 

1. 建構函式

    ExponentialDensity(const Spectrum &sa, const Spectrum &ss,
                       float gg, const Spectrum &emit, const BBox &e,
                       const Transform &v2w, float aa, float bb,
                       const Vector &up)
        : DensityRegion(sa, ss, gg, emit, v2w), extent(e), a(aa), b(bb) {
        upDir = Normalize(up);
    }

作用:

The ExponentialDensity constructor initializes its member variables directly from its arguments.
In addition to the volume scattering properties passed to the DensityRegion
constructor, the volume’s bound, and the a and b parameter values, this constructor
takes a vector giving an “up” direction that orients the volume and is used to compute the
height of points for the density computation.
While the up direction is not strictly necessary
(the world-to-object transformation is sufficient to orient the volume), specifying
an explicit up vector can be conceptually easier for the user.

 

2. float Density(const Point &Pobj) const

    float Density(const Point &Pobj) const {
        if (!extent.Inside(Pobj)) return 0;
        float height = Dot(Pobj - extent.pMin, upDir);
        return a * expf(-b * height);
    }

作用:

The height of a given object space point p along the “up” direction axis can be found
by projecting the vector from the lower corner of the bounding box to p onto the “up”
direction vector (Figure 11.17). The distance h along the up direction to the point of p’s
perpendicular projection is given by the dot product of these two vectors. The principles
behind this relationship are similar to those used when vectors are transformed to and
from the BSDF coordinate system, for example.

Figure 11.17: For the ExponentialDensity, it’s necessary to find the perpendicular projection of the
point p onto the “up” direction vector u and determine the distance h along u of the projection point.
This distance is given by the dot product (u · v), where v is the vector from the corner of the box (and
the up direction’s starting point) to p. This can be verified with basic properties of the dot product.