【问题标题】:Raytracing - lighting equations光线追踪 - 光照方程
【发布时间】:2016-01-08 08:30:43
【问题描述】:

我正在尝试用 C++ 编写光线追踪器,但目前的结果并不如我所愿,所以我猜照明方程中存在错误。 这是我到目前为止所得到的:

Picture of my result. In the upper line what I've got, in the bottom line what I would like to get

我正在使用 Blinn(-Phong) 模型并假设所有灯光都是点灯。这是我的代码:

vec3f raytrace_ray(Scene* scene, ray3f ray) {
// get scene intersection
auto intersection = intersect_surfaces(scene, ray);

// if not hit, return background
if (!intersection.hit) {
    return scene->background;
}

// accumulate color starting with ambient
vec3f c = zero3f;
c += scene->ambient*intersection.mat->kd;

//Add emission
c += intersection.mat->ke;

// foreach light
for (Light* light : scene->lights) {
    // compute light response
    auto lightRadiance = light->intensity / distSqr(light->frame.o, intersection.pos);
    if(lightRadiance == zero3f) continue;

    // compute light direction
    auto l = normalize(light->frame.o - intersection.pos);

    auto v = normalize(intersection.pos - ray.e);

    auto h = normalize(l+v); //bisector

    auto n = intersection.mat->n;
    auto normal = intersection.norm;

    // compute the material response (brdf*cos)
    auto brdf = intersection.mat->ks*pow(max(0.0f, dot(normal,h)), intersection.mat->n);

    // check for shadows and accumulate if needed
    auto shadowRay = ray3f(intersection.pos, l, 0.001f, length(light->frame.o - intersection.pos));

    float visibleTerm;
    auto shadowIntersect = intersect_surfaces(scene, shadowRay);
    if (shadowIntersect.hit) {
        visibleTerm = 0;
    }
    else {
        visibleTerm = 1;
    }

    //Accumulate
    c += lightRadiance*visibleTerm*(intersection.mat->kd + brdf)*abs(dot(normal, l));
}

// if the material has reflections
    // create the reflection ray
    // accumulate the reflected light (recursive call) scaled by the material reflection

// return the accumulated color∫
return c;
}

现在我的问题是:

  1. 等式哪里错了?
  2. 从上次编辑中,我还添加了阴影,方法是创建一条射线(射线构造函数的最后两个参数是 tmin 和 tmax)并检查它是否击中了某些东西。如果是,我将 visibileTerm 设置为零(我还更新了屏幕截图)。我不确定这是否正确。
  3. 如何进一步添加反射和折射?我知道我必须递归调用该函数,但首先我必须计算什么以及以哪种方式?我尝试使用此代码在灯光的 for 循环中进行反射,但在某些测试无限递归中它不起作用:

    ray3f reflectionRay = ray3f(intersection.pos, -l + 2 * dot(l, normal)*normal);
    c += intersection.mat->kr*raytrace_ray(scene, reflectionRay);
    
  4. 从图二测试来看,路口也有问题,但不知道在哪里。我也尝试以这种方式用逆来改变正常计算:

    auto normal = transform_normal_inverse(scene->camera->frame, surface->frame.z);
    

    但是我只得到了飞机的另一个倾斜度,而没有解决第二次测试。这是我的交集代码(我只使用了四边形和球体):

    intersection3f intersect_surfaces(Scene* scene, ray3f ray) {
      auto intersection = intersection3f();
    
      float currentDistance = INFINITY;
      float t;
    
     // foreach surface
    for (Surface* surface : scene->surfaces) {
    // if it is a quad
    if (surface->isquad) {
    
        /// compute ray intersection (and ray parameter), continue if not hit
        auto normal = transform_normal(scene->camera->frame, surface->frame.z);
    
        if (dot(ray.d, normal) == 0) {
            continue;
        }
        t = dot(surface->frame.o - ray.e, normal)/dot(ray.d,normal);
    
        // check if computed param is within ray.tmin and ray.tmax
        if (t < ray.tmin or t > ray.tmax) continue;
    
        // check if this is the closest intersection, continue if not
        auto p = ray.eval(t);//Intersection point
    
        if (dist(ray.e, p) >= currentDistance) {
            continue;
        }
    
        currentDistance = dist(ray.e, p);
    
        // if hit, set intersection record values
        intersection.ray_t = t;
        intersection.pos = p;
        intersection.norm = normalize(ray.e - p);
        intersection.mat = surface->mat;
        intersection.hit = true;
    }
    // if it is a sphere
    else {
        // compute ray intersection (and ray parameter), continue if not hit
        auto a = lengthSqr(ray.d);
        auto b = 2 * dot(ray.d, ray.e - surface->frame.o);
        auto c = lengthSqr(ray.e - surface->frame.o) - surface->radius*surface->radius;
        auto det = b*b - 4 * a*c;
    
        if (det < 0) {
            continue;
        }
    
        float t1 = (-b - sqrt(det)) / (2 * a);
        float t2 = (-b + sqrt(det)) / (2 * a);
    
        // check if computed param is within ray.tmin and ray.tmax
        if (t1 >= ray.tmin && t1 <= ray.tmax) t = t1;
        else if (t2 >= ray.tmin && t2 <= ray.tmax) t = t2;
        else continue;
    
        auto p = ray.eval(t); //Intersection point
    
        // check if this is the closest intersection, continue if not
        if (dist(ray.e, p) > currentDistance) {
            continue;
        }
    
        currentDistance = dist(ray.e, p);
    
        // if hit, set intersection record values
        intersection.ray_t = t;
        intersection.pos = p;
        intersection.norm = normalize(ray.e - p);
        intersection.mat = surface->mat;
        intersection.hit = true;
    
        intersection.ray_t = 2;
    
    
       }
    }
    return intersection;
    }
    

提前致谢。

【问题讨论】:

  • 发布渲染外观的屏幕截图。对于 2.:您已经编写了一个光线投射器。光线追踪器在击中某物后会反弹多次以获得更准确的照明。
  • @Joonazan :我在帖子中添加了一些截图。关于第二个项目符号:我知道我必须以某种方式递归调用该函数,但是以哪种方式?此外,现在我很确定 Quad 路口也有错误,但我不知道在哪里。即使是这里的一些建议也会受到赞赏。
  • @Joonazan :我也尝试添加阴影,并相应更新了屏幕截图,但不确定是否正确。

标签: c++ graphics 3d raytracing lighting


【解决方案1】:

关于 phong 着色模型:它是描述光在表面上的散射的模型。 因此,每个表面都有不同的属性,由环境、漫反射和镜面反射系数表示。 根据您拥有的表面,它们会有所不同(例如,玻璃、木材、..)

表面的环境颜色就像对象具有的最小颜色,即使它被另一个对象遮蔽。 基本上,每个对象都有一种颜色,由 float (0 - 1) 或 int (0 - 255) 表示。 要应用 phong 模型的第一部分,您假设对象有阴影,因为光线不会到达对象的每一英寸。 要应用环境阴影,您只需将环境系数(它是一个表面属性,通常在 0 和 1 之间)乘以对象的颜色。

现在我们需要检查物体是否有阴影。如果对象没有被遮蔽(并且只有这样),您需要应用 phong 模型的其他两个部分,因为它们仅在对象被光照射时才相关。

漫反射系数是光散射多少的量度。镜面反射分量是光泽度的量度。 有一些近似公式可以有效地计算这两个系数。

我从我的代码中添加了一个摘录来指导您。我剪掉了很多计算交叉点和反射光线的代码。 我离开了 cmets 以使步骤清晰。

从您的屏幕截图看来,您似乎忘记添加镜面组件。

如果您还没有听说过http://scratchapixel.com,那么您一定要检查一下。当我编写光线追踪器时,这对我有很大帮助。 注意:我不是专家,但我自己在空闲时间用 C++ 编写了一个光线追踪器,因此我尝试分享我的经验,因为我遇到了与您的屏幕截图中发布的相同的问题。

    double ka = 0.1; //ambient coefficient
    double kd; //diffuse coefficient
    double ks; //specular coefficient

    /****** First handle ambient shading ******/
    Colour ambient = ka * objectColor; //ambient component

    Colour diffuse, specular; // automatically set to 0
    double brightness;
    localColour = ambient; //localColour is the current colour of the object
    /************ Next check wether the object is in shadow or light
    * do this by casting a ray from the obj and
    * check if there is an intersection with another obj ******/
    for(int i = 0; i < objSize; i++)
    {//iterate over all lights
        if(dynamic_cast<Light *>(objects[i])) //if object is a light
        {
            //for each light
            //create a Ray to light
            //its origin is the intersection point
            //its direction is the position of the light - intersection
        //check for an intersection with a light
        //if there is no intersection then we don't need to apply the specular and the diffuse components
            if(t_light < 0) //no intersect, which is quite impossible
                continue;

            //then we check if that Ray intersects one object that is not a light

                        //we compute the distance to the object and compare it
                        //to the light distance, for each light seperately
                        //if it is smaller we know the light is behind the object
                        //--> shadowed by this light

            //we know if inersection is shadowed or not
            if(!shadowed)// if obj is not shadowed
            {
//------->  //The important part. Adding the diffuse and the specular components.
                rRefl = objects[index_nearObj]->calcReflectingRay(rShadow, intersect, normal); //reflected ray from light source, for ks
                kd = maximum(0.0, (normal|rShadow.getDirection())); // calculate the diffuse coefficient
        //the max of 0 and the cosine of the angle between the direction of the light source and 
        //the surface normal at the intersection point
                ks = pow(maximum(0.0, (r.getDirection()|rRefl.getDirection())), objects[index_nearObj]->getShiny()); //calc the specular component
        //the cosine of the angle between the incoming ray and the reflected ray to the power of a material property
                diffuse = kd * objectColor; //diffuse colour
                specular = ks * objects[i]->getColor(); //specular colour
                brightness = 1 /(1 + t_light * DISTANCE_DEPENDENCY_LIGHT); 
        //Determines the brightness by how far the light source is apart from the object
        // 1/(1 + distance to light * parameter)... change the parameter to have a stronger or weaker distance dependency                
        localColour += brightness * (diffuse + specular); //ADD the diffuse and the specular components to the object
            }
        }
    }

关于反射和折射:

由于您需要递归调用该函数,因此您需要定义一个最大跟踪深度来告诉计算机停止反射。 否则你可能会陷入无限循环。 再次来自我的光线追踪器的代码 sn-p(也修改为更具可读性的伪代码:

    Ray rRefl, rRefr; //reflected and refracted Ray
    Colour reflectionColour = finalColour, refractionColour = finalColour; //reflected and refrated objects colour;
//initialize them to the background colour
    //if there is no intersection of the reflected ray,
//then the backgroundcolour should be returned
    double reflectance = 0, transmittance = 0;
    //check if obj is reflective, and calc reflection
    if(object->isReflective && depth < MAX_TRACE_DEPTH)
    {
        //handle reflection
        rRefl = object->calcReflectingRay(r, intersect, normal);
        if(REFL_COLOUR_ADD)//if the reflected colour is added to the object's colour
        {
        reflectionColour = raytrace(rRefl, depth + 1);//trace the reflected ray
        reflectance = 1;
        }
        else   objectColor = raytrace(rRefl, depth + 1);//otherwise set the object's clolour to the reflected colour(eg: Glass)

    }

    if(object->isRefractive() && depth < MAX_TRACE_DEPTH)
    {
        //handle transmission
        rRefr = object->calcRefractingRay(r, intersect, normal, reflectance, transmittance);
        if(rRefr.getDirection() != NullVector) // ignore total inner refl
            refractionColour = raytrace(rRefr, depth + 1);//trace the refracted ray
    }

    //then calculate light ,shading and colour with the phong illumination model

    //in the end add the reflected and refracted colours

    finalColour = phongColour + transmittance * refractionColour + reflectance * reflectionColour;//Add phong, refraction and reflection
    //if transmittance is 0 the object will not let light pass through, if reflectance is 0 the object will not reflect
    return finalColour;

对不起,这个简短的答案,但已经很晚了,我变得懒惰了。当我有更多时间时,我会再次入住。我希望我能帮上一点忙。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-08
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    • 2015-11-11
    • 2021-04-30
    • 1970-01-01
    • 2012-08-10
    相关资源
    最近更新 更多