我正在尝试用C++写一个raytracer,但是到目前为止的结果并不符合我的预期,所以我猜照明方程式有错误。
到目前为止,这是我得到的:

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;
}

现在我的问题是:
  • 方程式在哪里错误?
  • 在上一次编辑中,我还通过创建ray添加了阴影(ray构造函数的最后两个参数是tmin和tmax),并检查它是否命中了东西。如果是,我将visibileTerm设置为零(我还更新了屏幕截图)。我不确定这是否正确。
  • 如何进一步添加反射和折射?我知道我必须递归地调用该函数,但是首先我必须计算什么,以哪种方式?我尝试使用此代码在光的for循环中进行反射,但是在某些测试无限递归中,它不起作用:
    ray3f reflectionRay = ray3f(intersection.pos, -l + 2 * dot(l, normal)*normal);
    c += intersection.mat->kr*raytrace_ray(scene, reflectionRay);
    
  • 从图片的第二次测试来看,十字路口也存在问题,但我不知道在哪里。我还尝试通过这种方式用反函数来更改常规计算:
    auto normal = transform_normal_inverse(scene->camera->frame, surface->frame.z);
    

    但是我只得到了飞机的另一个倾斜度,也没有解决第二个测试。这是我的交集代码(我只使用了Quads和Spheres):
    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;
    }
    

  • 提前致谢。

    最佳答案

    关于phong阴影模型:它是描述光在表面上的散射的模型。
    因此,每个表面具有不同的属性,这些属性由环境系数,漫反射系数和镜面反射系数表示。
    取决于您所拥有的表面,它们会有所不同(例如,玻璃,木材等)

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

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

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

    我从代码中添加了摘录来指导您。我剪切了很多代码,这些代码是交点和反射光线的计算。
    我留下了评论,以使步骤明确。

    根据您的屏幕看上去,您似乎已拒绝添加特殊组件。

    如果您还没有听说过http://scratchapixel.com,那么一定要检查一下。当我编写raytracer时,这对我有很大帮助。
    注意:我不是专家,但是我自己在业余时间用C++写了一个raytracer,所以我试图分享自己的经验,因为我遇到了与您的屏幕快照相同的问题。

        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
                }
            }
        }
    

    至于反射和折射:

    由于需要递归调用该函数,因此需要定义最大跟踪深度以告知计算机停止反射。
    否则,您可能会陷入无尽的循环。
    再次是我的raytracer的代码片段(也已修改为更具可读性的伪代码:
        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;
    

    抱歉,这个答案很短,但是已经很晚了,我变得懒惰了。当我有更多时间时,我会再次检查。我希望我能有所帮助。

    09-04 14:47