《Ray Tracing in a Weekend》学习笔记02

这篇博客介绍了如何在光线追踪器中添加球体,并计算光线与球体的交点。通过一元二次方程求解交点,实现对视线穿过球体情况的判断。接着,文章探讨了如何确定表面法线,以及如何优化相交检测代码。最终,通过代码示例展示了如何根据交点计算颜色并生成图像。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

想看原书可以看本系列的第一篇《Ray Tracing in a Weekend》学习笔记01

5. Adding a Sphere

让我们向光线追踪器添加一个对象。 人们通常在光线跟踪器中使用球体,因为计算射线是否撞击球体非常简单。

5.1 Ray-Sphere Intersection

当逐行扫描屏幕的所有像素点,从摄像机位置发出对应的视线,视线与球体会发生三种碰撞,一是视线不与球体相交,那屏幕上看到的还是背景颜色,二是视线与球体相切,三是视线与球体相交。我们只需推算出直线与球是否相交的公式。简单的公式推导,得出的公式:
t 2 b ⋅ b + 2 t b ⋅ ( A − C ) + ( A − C ) ⋅ ( A − C ) − r 2 = 0 t^2b⋅b+2tb⋅(A−C)+(A−C)⋅(A−C)−r^2=0 t2bb+2tb(AC)+(AC)(AC)r2=0
相当于求t的一元二次方程,b是光线的方向向量,A是光线的发射位置,C是球体的中心位置,r是球体半径。

5.2Creating Our First Raytraced Image

如果我们采用该数学并将其硬编码到我们的程序中,则可以通过将位于z轴上-1处的小球碰到的任何像素变为红色来对其进行测试:

//main.c
bool hit_sphere(const point3& center, double radius, const ray& r) {
    vec3 oc = r.origin() - center;//oc=A-C(-号在vec3.h中重载了,所以返回的还是vec3)
    auto a = dot(r.direction(), r.direction());
    auto b = 2.0 * dot(oc, r.direction());//b·(A-C)
    auto c = dot(oc, oc) - radius*radius;//(A−C)⋅(A−C)-r*r
    auto discriminant = b*b - 4*a*c;
    return (discriminant > 0);//>0表示一元二次方程有解,则光线与球体有交集
}

color ray_color(const ray& r) {
    if (hit_sphere(point3(0,0,-1), 0.5, r))
        return color(1, 0, 0);//光线击中的局域,都为(1,0,0),红色
    vec3 unit_direction = unit_vector(r.direction());
    auto t = 0.5*(unit_direction.y() + 1.0);
    return (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0);
}

A simple red sphere

小总结:vec3.h的功能就是定义了三维向量,包括三维向量之间的点乘,叉乘,加减乘除等操作。颜色、距离、位置等都用vec3来表示;color.h定义了RGB的输出,把范围固定在了[0,255]中。

6.Surface Normals and Multiple Objects
6.1Surface Normals and Multiple Objects

Surface normals(表面法线),可以帮助我们区分出背面。当视线穿过球体,穿过去交点为P,则法线向量是P-C。

Sphere surface-normal geometry

再结合视线公式 P(t) = a + t ·b,和
t 2 b ⋅ b + 2 t b ⋅ ( A − C ) + ( A − C ) ⋅ ( A − C ) − r 2 = 0 t^2b⋅b+2tb⋅(A−C)+(A−C)⋅(A−C)−r^2=0 t2bb+2tb(AC)+(AC)(AC)r2=0
①当视线与球体相交的时候,t可取两个值,分别为 t1=(-b - sqrt(b * b - 4ac))/ (2.0 * a), t2=(-b + sqrt(b * b - 4ac))/ (2.0 * a),t1是穿过去的交点P,t2是视线穿进球体的交点。当视线与球体相切时,t1=t2。

②得到t后,调用ray.h的at(),计算出该点的P(t),从而法线向量等于P(t)-C,对其进行单位化得到单位法线向量。

③返回一个color类的变量,把大小控制在(0,1)

所以需要更改main.c的代码,如下:

//main.c
double hit_sphere(const point3& center, double radius, const ray& r) {
    vec3 oc = r.origin() - center;
    auto a = dot(r.direction(), r.direction());
    auto b = 2.0 * dot(oc, r.direction());
    auto c = dot(oc, oc) - radius*radius;
    auto discriminant = b*b - 4*a*c;
    if (discriminant < 0) {
        return -1.0;
    } else {
        return (-b - sqrt(discriminant) ) / (2.0*a);
    }
}

color ray_color(const ray& r) {
    auto t = hit_sphere(point3(0,0,-1), 0.5, r);
    if (t > 0.0) {
        vec3 N = unit_vector(r.at(t) - vec3(0,0,-1));
        return 0.5*color(N.x()+1, N.y()+1, N.z()+1);
    }
    vec3 unit_direction = unit_vector(r.direction());
    t = 0.5*(unit_direction.y() + 1.0);
    return (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0);
}

A sphere colored according to its normal vectors

6.2 Simplifying the Ray-Sphere Intersection Code

对hit_sphere()进行优化:

①向量与自身的点乘,等于向量的长度的平方(在vec3.h中定义的length_squared()就是这个原理)

②假定b=2h,对二次方程的结果进行优化,
− b ± b 2 − 4 a c 2 a = − 2 h ± ( 2 h ) 2 − 4 a c 2 a = − 2 h ± 2 h 2 − a c 2 a = − h ± h 2 − a c a \frac{-b \pm \sqrt{b^2 - 4ac}}{2a} =\frac{-2h \pm \sqrt{(2h)^2 - 4ac}}{2a} =\frac{-2h \pm 2\sqrt{h^2 - ac}}{2a} =\frac{-h \pm \sqrt{h^2 - ac}}{a} 2ab±b24ac =2a2h±(2h)24ac =2a2h±2h2ac =ah±h2ac

//main.c
double hit_sphere(const point3& center, double radius, const ray& r) {
    vec3 oc = r.origin() - center;
    auto a = r.direction().length_squared();
    auto half_b = dot(oc, r.direction());//b=2h,只取原来的一半
    auto c = oc.length_squared() - radius*radius;
    auto discriminant = half_b*half_b - a*c;

    if (discriminant < 0) {
        return -1.0;
    } else {
        return (-half_b - sqrt(discriminant) ) / a;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值