1. 程式人生 > >PBRT_V2 總結記錄 ProjectionLight

PBRT_V2 總結記錄 ProjectionLight

ProjectionLight 類

class ProjectionLight : public Light {
public:
    // ProjectionLight Public Methods
    ProjectionLight(const Transform &light2world, const Spectrum &intensity,
        const string &texname, float fov);
    ~ProjectionLight();
    Spectrum Sample_L(const Point &p, float pEpsilon, const LightSample &ls, float time,
        Vector *wi, float *pdf, VisibilityTester *vis) const;
    bool IsDeltaLight() const { return true; }
    Spectrum Projection(const Vector &w) const;
    Spectrum Power(const Scene *) const;
    Spectrum Sample_L(const Scene *scene, const LightSample &ls, float u1, float u2,
            float time, Ray *ray, Normal *Ns, float *pdf) const;
    float Pdf(const Point &, const Vector &) const;
private:
    // ProjectionLight Private Data
    MIPMap<RGBSpectrum> *projectionMap;
    Point lightPos;
    Spectrum Intensity;
    Transform lightProjection;
    float hither, yon;
    float screenX0, screenX1, screenY0, screenY1;
    float cosTotalWidth;
};

類的作用:

(這個ProjectionLight,主要的作用就是,把一張圖片投影到場景中。 )

Another useful light source acts like a slide projector(幻燈片投影機); it takes an image map and projects
its image out into the scene. The ProjectionLight class uses a projective transformation
to project points in the scene onto the light’s projection plane based on the
field of view angle given to the constructor

Figure 12.5: The Basic Setting for Projection Light Sources. A point p in the light’s coordinate
system is projected onto the plane of the image using the light’s projection matrix.

 

1. 建構函式

ProjectionLight::ProjectionLight(const Transform &light2world,
        const Spectrum &intensity, const string &texname,
        float fov)
    : Light(light2world) {
    lightPos = LightToWorld(Point(0,0,0));
    Intensity = intensity;
    // Create _ProjectionLight_ MIP-map
    int width, height;
    RGBSpectrum *texels = ReadImage(texname, &width, &height);
    if (texels)
        projectionMap = new MIPMap<RGBSpectrum>(width, height, texels);
    else
        projectionMap = NULL;
    delete[] texels;

    // Initialize _ProjectionLight_ projection matrix
    float aspect = projectionMap ? float(width) / float(height) : 1.f;
    if (aspect > 1.f)  {
        screenX0 = -aspect; screenX1 = aspect;
        screenY0 = -1.f;    screenY1 = 1.f;
    }
    else {
        screenX0 = -1.f;            screenX1 = 1.f;
        screenY0 = -1.f / aspect;   screenY1 = 1.f / aspect;
    }
    hither = 1e-3f;
    yon = 1e30f;
    lightProjection = Perspective(fov, hither, yon);

    // Compute cosine of cone surrounding projection directions
    float opposite = tanf(Radians(fov) / 2.f);
    float tanDiag = opposite * sqrtf(1.f + 1.f/(aspect*aspect));
    cosTotalWidth = cosf(atanf(tanDiag));
}

作用:主要是先計算 需要投影的圖片的 Mipmap,再計算投影矩陣。

 

細節

a.

    int width, height;
    RGBSpectrum *texels = ReadImage(texname, &width, &height);
    if (texels)
        projectionMap = new MIPMap<RGBSpectrum>(width, height, texels);
    else
        projectionMap = NULL;
    delete[] texels;

作用: 這裡主要是讀取圖片,建立一個MipMap例項。

 

b.

    // Initialize _ProjectionLight_ projection matrix
    float aspect = projectionMap ? float(width) / float(height) : 1.f;
    if (aspect > 1.f)  {
        screenX0 = -aspect; screenX1 = aspect;
        screenY0 = -1.f;    screenY1 = 1.f;
    }
    else {
        screenX0 = -1.f;            screenX1 = 1.f;
        screenY0 = -1.f / aspect;   screenY1 = 1.f / aspect;
    }
    hither = 1e-3f;
    yon = 1e30f;
    lightProjection = Perspective(fov, hither, yon);

作用:

(參考 《PBRT_V2 總結記錄 <16> Coordinate Spaces Transform(空間座標變換)》,這裡主要是計算 光源空間的 投影矩陣,主要就是把 光源空間的點,投影到 光源空間的 投影平面上)

Similar to the PerspectiveCamera, the ProjectionLight constructor computes a projection
matrix and the screen space extent of the projection.

 

c.

    // Compute cosine of cone surrounding projection directions
    float opposite = tanf(Radians(fov) / 2.f);
    float tanDiag = opposite * sqrtf(1.f + 1.f/(aspect*aspect));
    cosTotalWidth = cosf(atanf(tanDiag));

作用:

(計算 光源座標空間的+z軸 和 螢幕視窗一角的 餘弦(cos))

Finally, the constructor finds the cosine of the angle between the +z axis and the vector
to a corner of the screen window.
This value is used elsewhere to define the minimal cone
of directions that encompasses the set of directions in which light is projected. This cone
is useful for algorithms like photon mapping that need to randomly sample rays leaving
the light source. We won’t derive this computation here; it is based on straightforward
trigonometry.

 

 

2. 

Spectrum ProjectionLight::Sample_L(const Point &p, float pEpsilon,
         const LightSample &ls, float time, Vector *wi,
         float *pdf, VisibilityTester *visibility) const {
    *wi = Normalize(lightPos - p);
    *pdf = 1.f;
    visibility->SetSegment(p, pEpsilon, lightPos, 0., time);
    return Intensity * Projection(-*wi) /
        DistanceSquared(lightPos, p);
}



Spectrum ProjectionLight::Projection(const Vector &w) const {
    Vector wl = WorldToLight(w);
    // Discard directions behind projection light
    if (wl.z < hither) return 0.;

    // Project point onto projection plane and compute light
    Point Pl = lightProjection(Point(wl.x, wl.y, wl.z));
    if (Pl.x < screenX0 || Pl.x > screenX1 ||
        Pl.y < screenY0 || Pl.y > screenY1) return 0.;
    if (!projectionMap) return 1;
    float s = (Pl.x - screenX0) / (screenX1 - screenX0);
    float t = (Pl.y - screenY0) / (screenY1 - screenY0);
    return Spectrum(projectionMap->Lookup(s, t), SPECTRUM_ILLUMINANT);
}

作用:

(這裡的Sample_L 類似於 之前的 Point Light 的 Sample_L, 但是主要的區別在於這裡使用了 Projection 函式)

Similar to the spotlight’s version, ProjectionLight::Sample_L() calls a utility method,
ProjectionLight::Projection(), to determine how much light is projected in the given
direction.

 

細節:

Spectrum ProjectionLight::Projection(const Vector &w) const {
    Vector wl = WorldToLight(w);
    // Discard directions behind projection light
    if (wl.z < hither) return 0.;

    // Project point onto projection plane and compute light
    Point Pl = lightProjection(Point(wl.x, wl.y, wl.z));
    if (Pl.x < screenX0 || Pl.x > screenX1 ||
        Pl.y < screenY0 || Pl.y > screenY1) return 0.;
    if (!projectionMap) return 1;
    float s = (Pl.x - screenX0) / (screenX1 - screenX0);
    float t = (Pl.y - screenY0) / (screenY1 - screenY0);
    return Spectrum(projectionMap->Lookup(s, t), SPECTRUM_ILLUMINANT);
}


inline Point Transform::operator()(const Point &pt) const {
    float x = pt.x, y = pt.y, z = pt.z;
    float xp = m.m[0][0]*x + m.m[0][1]*y + m.m[0][2]*z + m.m[0][3];
    float yp = m.m[1][0]*x + m.m[1][1]*y + m.m[1][2]*z + m.m[1][3];
    float zp = m.m[2][0]*x + m.m[2][1]*y + m.m[2][2]*z + m.m[2][3];
    float wp = m.m[3][0]*x + m.m[3][1]*y + m.m[3][2]*z + m.m[3][3];
    Assert(wp != 0);
    if (wp == 1.) return Point(xp, yp, zp);
    else          return Point(xp, yp, zp)/wp;
}

作用:

(Projection 函式最主要的思路就是,先把 世界座標系中的 點 變換到  光源座標系中,之後再 把這個點 進行投影變換,變換到 NDC 空間(看 Transform 的 operator(),執行了 透視除法),NDC空間可以進一步計算這個點 在 的 螢幕座標,【0,1】,就可以利用這個[0,1] 在 image 上進行取樣,得到 image 的值)

After being projected to the projection plane, points with coordinate values outside the
screen window are discarded. Points that pass this test are transformed to get (s , t)
texture coordinates inside [0, 1] for the lookup in the projection’s image map.

 

3. 

Spectrum ProjectionLight::Power(const Scene *) const {
    return (projectionMap ? Spectrum(projectionMap->Lookup(.5f, .5f, .5f),
                                     SPECTRUM_ILLUMINANT) : Spectrum(1.f)) *
        Intensity * 2.f * M_PI * (1.f - cosTotalWidth);
}

作用:

The total power of this light is approximated as a spotlight that subtends the same angle
as the diagonal of the projected image, scaled by the average intensity in the image
map. This approximation becomes increasingly inaccurate as the projected image’s aspect
ratio becomes less square, for example, and it doesn’t account for the fact that texels
toward the edges of the image map subtend a larger solid angle than texels in the middle
when projected with a perspective projection. Nevertheless, it’s a reasonable first-order
approximation. Note it is explicitly specified that the RGBSpectrum value passed to the
Spectrum constructor represents an illuminant’s SPD and not that of a reflectance.