基础光线追踪(4) - 球

基础光线追踪(4) - 球

用光线追踪渲染一个球。

碰撞检测

检测光线 $r$ 是否撞上球心为 $o$,半径为 $radius$ 的球。

求出光线原点与球心连线的向量 $\vec{p}$,求 $\vec{p}$ 在 $r$ 上的投影 $a$,然后计算光线原点,球心,投影点三点形成的直角三角形中表示距球心距离的直角边,判断其与半径的大小。

1
2
3
4
5
bool isCollide(const Ray &r, const Point3f &o, float radius) {
Vector3f p = r.getOrigin() - o;
float a = dot(p, r.getDir());
return p.length2() - a * a < radius * radius;
}

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include "../3rdParty/svpng/svpng.inc"
#include "../Chapter3/Ray.h"

#include <cstdio>

const int WIDTH = 640;
const int HEIGHT = 480;

using byte = unsigned char;

void paint(byte *d, int x, int y, Color3f color, float alpha) {
d += 4 * (y * WIDTH + x);
d[0] = 255 * color.r;
d[1] = 255 * color.g;
d[2] = 255 * color.b;
d[3] = 255 * alpha;
}

Color3f mix(const Color3f &a, const Color3f &b, float t) { return a * (1.0f - t) + b * t; }

bool isCollide(const Ray &r, const Point3f &o, float radius) {
Vector3f p = r.getOrigin() - o;
float a = dot(p, r.getDir());
return p.length2() - a * a < radius * radius;
}

Color3f paint(const Ray &r) {
if (isCollide(r, {0.0f, 0.0f, -1.0f}, 0.5f)) return {1.0f, 0.0f, 0.0f};
Vector3f dir = r.getDir();
float t = 0.5f * (dir.y + 1.0f);
return mix({1.0f, 1.0f, 1.0f}, {0.5f, 0.7f, 1.0f}, t);
}

int main() {
byte d[4 * WIDTH * HEIGHT];
FILE *f = fopen("ch4.png", "wb");
Point3f lowerLeftCorner(-2.0f, -1.5f, -1.0f);
Vector3f horizonal(4.0f, 0.0f, 0.0f);
Vector3f vertical(0.0f, 3.0f, 0.0f);
Point3f origin(0.0f, 0.0f, 0.0f);

for (int x = 0; x < WIDTH; x++) {
for (int y = 0; y < HEIGHT; y++) {
float u = (float)x / WIDTH;
float v = (float)y / HEIGHT;
Ray r(origin, lowerLeftCorner + u * horizonal + v * vertical);
paint(d, x, y, paint(r), 1.0f);
}
}
svpng(f, WIDTH, HEIGHT, d, 1);
fclose(f);
}

效果

Chapter4

可以发现目前的光线追踪还有很大缺陷,没有阴影和反射,边缘不够平滑,且当球的 $z$ 坐标为正时存在 bug。

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×