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.

猜你喜欢

转载自blog.csdn.net/aa20274270/article/details/84245303