我正在尝试用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;
}
现在我的问题是:
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;
抱歉,这个答案很短,但是已经很晚了,我变得懒惰了。当我有更多时间时,我会再次检查。我希望我能有所帮助。