什么会导致 Phong 镜面反射着色产生色域溢出?

发布于 2024-08-23 21:07:13 字数 4296 浏览 8 评论 0原文

我目前正在用 C++ 实现一个基本的光线追踪器。到目前为止效果很好,哑光材质(具有环境和漫反射 brdf)到目前为止按预期工作。

添加镜面高光将产生完整的 Phong 模型,而这正是我试图做的。

不幸的是,我遇到了色域溢出,镜面反射常数 ks 和指数有各种值。以下是一些示例。

// Sphere material definition:
ka    = 0.9;
kd    = 1.0;
ks    = 0.3;
exp   = 1.0;
color = rgb(1.0, 1.0, 0.98);

图片:http://dl.dropbox.com/u/614366/cornell_1.png

// Sphere material definition:
ka    = 0.9;
kd    = 1.0;
ks    = 0.3;
exp   = 20.0;                  // only changed exp
color = rgb(1.0, 1.0, 0.98);

图片:http://dl.dropbox.com/u/614366/cornell_2.png

// Sphere material definition:
ka    = 0.9;
kd    = 1.0;
ks    = 0.1;                  // only changes here
exp   = 0.1;                  // and here
color = rgb(1.0, 1.0, 0.98);

图片:http://dl.dropbox.com/ u/614366/cornell_3.png

以下是代码的一些相关摘录:

in raycast.cpp

namespace {
    const float floatmax = std::numeric_limits<float>::max();
}

rgb
RayCast::trace ( const Ray& ray ) const
{
    HitRecord rec(scene_ptr_);
    float tmax = floatmax;
    float tmin = 0.0;

    if ( scene_ptr_->shapes.hit(ray,tmin,tmax,rec) )
    {
        rec.ray = ray;
        return rec.material_ptr->shade(rec);
    }

    return scene_ptr_->bgcolor;
}

in phong.cpp

rgb
Phong::shade ( HitRecord& hitrec ) const
{
    Vector wo = - hitrec.ray.dir();

    rgb L = ambient_brdf_ptr_->rho(hitrec,wo) *
        hitrec.scene_ptr->ambient_ptr->L(hitrec);

    int num_lights = hitrec.scene_ptr->lights.size();

    for (int i = 0; i < num_lights; ++i)
    {
        Vector wi     = hitrec.scene_ptr->lights[i]->get_direction(hitrec);
        float  ndotwi = dot(hitrec.normal, wi);

        if ( ndotwi > 0.0 )
        {
            L += ( diffuse_brdf_ptr_->f (hitrec, wo, wi)  +
                   specular_brdf_ptr_->f(hitrec, wo, wi)
                 ) * hitrec.scene_ptr->lights[i]->L(hitrec) * ndotwi;
        }
    }

    return L;
}

in specular.cpp

namespace {
    const rgb black(0.0,0.0,0.0);
}

rgb
Specular::f ( const HitRecord& hitrec, const Vector& wo, const Vector& wi ) const
{
    rgb L(0,0,0);
    float ndotwi = dot(hitrec.normal, wi);

    Vector r = -wi + 2.0 * hitrec.normal * ndotwi;
    float rdotwo = dot(r, wo);

    // reflection detected
    if ( rdotwo > 0.0 )
        L = ks_ * pow(rdotwo, exp_);

    return L;
}

rgb
Specular::rho ( const HitRecord& hitrec, const Vector& wo ) const
{
    return black;
}

in sphere.cpp

bool
Sphere::hit ( const Ray& ray, interval_t tmin, interval_t tmax, HitRecord& hitrec ) const
{
    Vector org = ray.origin() - center_;
    Vector dir = ray.dir();
    float a = dot(dir, dir);
    float b = dot(dir, org) * 2;
    float c = dot(org, org) - pow(radius_, 2);
    float discriminant = pow(b,2) - 4*a*c;

    if ( discriminant > 0 )
    {
        discriminant = sqrt(discriminant);
        double t = ( -b - discriminant ) / ( 2*a );

        if ( t < tmin )
            t = ( -b + discriminant ) / ( 2*a );

        if ( t > tmin and t < tmax )
        {
            // hit detected
            hitrec.t            = t;
            hitrec.hit          = true;
            hitrec.normal       = unify( t*ray.dir() + org );
            hitrec.material_ptr = material_ptr_;
            hitrec.hitpoint     = ray.origin() + t * ray.dir();
            hitrec.ray          = ray;

            return true;
        }
    }

    return false;
}

您知道可能导致错误的地方吗?导致这种结果的可能因素有哪些?

提前致谢, 帕特里克.

I am currently implementing a basic raytracer in c++. Works pretty well so far, matte materials (with ambient and diffuse brdf) work as expected so far.

Adding specular highlights would result in the full Phong Model and that's exactly what i tried to do.

Unfortunately, i encounter gamut overflow, with all kinds of values for the specular reflection constant ks and the exponent. Here are some examples.

// Sphere material definition:
ka    = 0.9;
kd    = 1.0;
ks    = 0.3;
exp   = 1.0;
color = rgb(1.0, 1.0, 0.98);

image: http://dl.dropbox.com/u/614366/cornell_1.png

// Sphere material definition:
ka    = 0.9;
kd    = 1.0;
ks    = 0.3;
exp   = 20.0;                  // only changed exp
color = rgb(1.0, 1.0, 0.98);

image: http://dl.dropbox.com/u/614366/cornell_2.png

// Sphere material definition:
ka    = 0.9;
kd    = 1.0;
ks    = 0.1;                  // only changes here
exp   = 0.1;                  // and here
color = rgb(1.0, 1.0, 0.98);

image: http://dl.dropbox.com/u/614366/cornell_3.png

and here are some relevant excerpts of the code:

in raycast.cpp

namespace {
    const float floatmax = std::numeric_limits<float>::max();
}

rgb
RayCast::trace ( const Ray& ray ) const
{
    HitRecord rec(scene_ptr_);
    float tmax = floatmax;
    float tmin = 0.0;

    if ( scene_ptr_->shapes.hit(ray,tmin,tmax,rec) )
    {
        rec.ray = ray;
        return rec.material_ptr->shade(rec);
    }

    return scene_ptr_->bgcolor;
}

in phong.cpp

rgb
Phong::shade ( HitRecord& hitrec ) const
{
    Vector wo = - hitrec.ray.dir();

    rgb L = ambient_brdf_ptr_->rho(hitrec,wo) *
        hitrec.scene_ptr->ambient_ptr->L(hitrec);

    int num_lights = hitrec.scene_ptr->lights.size();

    for (int i = 0; i < num_lights; ++i)
    {
        Vector wi     = hitrec.scene_ptr->lights[i]->get_direction(hitrec);
        float  ndotwi = dot(hitrec.normal, wi);

        if ( ndotwi > 0.0 )
        {
            L += ( diffuse_brdf_ptr_->f (hitrec, wo, wi)  +
                   specular_brdf_ptr_->f(hitrec, wo, wi)
                 ) * hitrec.scene_ptr->lights[i]->L(hitrec) * ndotwi;
        }
    }

    return L;
}

in specular.cpp

namespace {
    const rgb black(0.0,0.0,0.0);
}

rgb
Specular::f ( const HitRecord& hitrec, const Vector& wo, const Vector& wi ) const
{
    rgb L(0,0,0);
    float ndotwi = dot(hitrec.normal, wi);

    Vector r = -wi + 2.0 * hitrec.normal * ndotwi;
    float rdotwo = dot(r, wo);

    // reflection detected
    if ( rdotwo > 0.0 )
        L = ks_ * pow(rdotwo, exp_);

    return L;
}

rgb
Specular::rho ( const HitRecord& hitrec, const Vector& wo ) const
{
    return black;
}

in sphere.cpp

bool
Sphere::hit ( const Ray& ray, interval_t tmin, interval_t tmax, HitRecord& hitrec ) const
{
    Vector org = ray.origin() - center_;
    Vector dir = ray.dir();
    float a = dot(dir, dir);
    float b = dot(dir, org) * 2;
    float c = dot(org, org) - pow(radius_, 2);
    float discriminant = pow(b,2) - 4*a*c;

    if ( discriminant > 0 )
    {
        discriminant = sqrt(discriminant);
        double t = ( -b - discriminant ) / ( 2*a );

        if ( t < tmin )
            t = ( -b + discriminant ) / ( 2*a );

        if ( t > tmin and t < tmax )
        {
            // hit detected
            hitrec.t            = t;
            hitrec.hit          = true;
            hitrec.normal       = unify( t*ray.dir() + org );
            hitrec.material_ptr = material_ptr_;
            hitrec.hitpoint     = ray.origin() + t * ray.dir();
            hitrec.ray          = ray;

            return true;
        }
    }

    return false;
}

do you have ideas where the error could be caused? what are possible factors that lead to such results?

thanks in advance,
patrick.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

罗罗贝儿 2024-08-30 21:07:13

问题的解决方案是你必须统一 wo 向量(在 Phong::shade 中)。

The solution to the problem is that you have to unify the wo vector (in Phong::shade).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文