球体是非常简单的形体,因为只需要一个球心和一个半径,就能确认球体的位置。 若我们在场景中放置一个球体,那么对球体进行采样的时候就会涉及到如何判断射线与球体相交的问题。 对于中心在C(cx,cy,cz)的球体:有方程(x−cx)∗(x−cx)+(y−cy)∗(y−cy)+(z−cz)∗(z−cz)=R∗R p点(x,y,z)满足x∗x+y∗y+z∗z=R∗R,就表明p点在球上。 上面两个式子可以写成: dot((p−C),(p−C))=R∗R=(x−cx)∗(x−cx)+(y−cy)∗(y−cy)+(z−cz)∗(z−cz) 把点 p 换做 ray 上一点 p(t)dot((p(t)−C),(p(t)−C))=R∗R 也可以写成:dot((A+t∗B−C),(A+t∗B−C))=R∗R 可以进一步写成:t∗t∗dot(B,B)+2∗t∗dot(A−C,A−C)+dot(C,C)−R∗R=0 这是个一元二次方程,根据求根公式就可判断方程有无根,即射线有没有与球体相交。

cpp:

#include
#include
#include “ray.h”
using namespace std;

bool hit_sphere(const vec3& center, float radius, const ray& r)
{
vec3 oc = r.origin() - center;
float a = dot(r.direction(), r.direction());
float b = 2.0f * dot(oc, r.direction());
float c = dot(oc, oc) - radius * radius;
//判断这个方程有没有根,如果有2个根就是击中
float discrimiant = b * b - 4.0f * a * c;
return (discrimiant > 0.0f);
}

vec3 Color(const ray& r)
{
//如果相交,则返回红色
if (hit_sphere(vec3(0.0f, 0.0f, -1.0f), 0.5f, r))
return vec3(1.0f, 0.0f, 0.0f);

vec3 unit\_direction = unit\_vector(r.direction());
//t从0到1
float t = 0.5f \* (unit\_direction.y() + 1.0f);

//线性混合,t=1时蓝色,t=0时白色,t介于中间时是混合颜色
//blended\_value = (1-t)\*start\_value + t\*end\_value
return (1.0f - t) \* vec3(1.0f, 1.0f, 1.0f) + t \* vec3(0.5f, 0.7f, 1.0f);

}

int main()
{
ofstream outfile;
outfile.open(“IMG.ppm”);

int nx = 800;
int ny = 400;
outfile << "P3\\n" << nx << " " << ny << "\\n255\\n";

//视锥体左下角
vec3 lower\_left\_corner(-2.0f, -1.0f, -1.0f);
//距离左下角的水平距离
vec3 horizontal(4.0f, 0.0f, 0.0f);
//距离左下角的垂直距离
vec3 vertical(0.0f, 2.0f, 0.0f);
//起始点
vec3 origin(0.0f, 0.0f, 0.0f);

for (int j = ny - 1; j >= 0; j--)
{
    for (int i = 0; i < nx; i++)
    {
        //u从0开始越来越接近1;v从无限接近1开始,越来越接近0
        float u = float(i) / float(nx);
        float v = float(j) / float(ny);

        ray r(origin, lower\_left\_corner +u \* horizontal + v \* vertical);
        vec3 col = Color(r);

        int ir = int(255.99 \* col\[0\]);
        int ig = int(255.99 \* col\[1\]);
        int ib = int(255.99 \* col\[2\]);
        outfile << ir << " " << ig << " " << ib << "\\n";
    }
}
outfile.close();
return 0;

}

运行效果: 参考书籍:《Ray Tracing in One Weekend》 RTIOW系列项目地址:GitHub RTIOW系列笔记: RTIOW-ch1:Output an image RTIOW-ch2:The vec3 class RTIOW-ch3:Rays, a simple camera, and background RTIOW-ch4:Adding a sphere RTIOW-ch5:Surface normals and multiple objects RTIOW-ch6:Antialiasing RTIOW-ch7:Diffuse Materials RTIOW-ch8:Metal RTIOW-ch9:Dielectrics RTIOW-ch10:Positionable camera RTIOW-ch11:Defocus Blur RTIOW-ch12:Where next