基础光线追踪(5) - 法向量及多个球

基础光线追踪(5) - 法向量及多个球

利用球的法向量来着色并简易封装代码实现多个球的渲染。

法向量

球的法向量为球心指向命中点,且通常为单位向量。
利用法向量对球进行着色可得

Normal

封装

Collide

Collide.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef _BASIC_RAY_TRACING_COLLIDE_H_
#define _BASIC_RAY_TRACING_COLLIDE_H_
#include "../Chapter3/Ray.h"

struct CollideRecord {
float t;
Point3f p;
Vector3f normal;
};

struct Collisible {
virtual bool collide(const Ray &r, float tMin, float tMax, CollideRecord &rec) const = 0;
virtual ~Collisible() {}
};
#endif

Sphere

Sphere.h

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
#ifndef _BASIC_RAY_TRACING_SPHERE_H_
#define _BASIC_RAY_TRACING_SPHERE_H_
#include "Collide.h"

struct Sphere : Collisible {
Point3f center;
float radius;

Sphere() : center(), radius() {}
Sphere(const Point3f &center, float radius) : center(center), radius(radius) {}

bool collide(const Ray &r, float tMin, float tMax, CollideRecord &rec) const override {
Vector3f p = r.getOrigin() - center;
float b = 2.0f * dot(r.getDir(), p);
float c = p.length2() - radius * radius;
float delta = b * b - 4 * c;
if (delta > 0) {
float t = (-b - sqrtf(delta)) / 2.0f;
if (tMin < t && t < tMax) {
rec.t = t;
rec.p = r.get(t);
rec.normal = (rec.p - center).normalize();
return true;
}
t = (-b + sqrtf(delta)) / 2.0f;
if (tMin < t && t < tMax) {
rec.t = t;
rec.p = r.get(t);
rec.normal = (rec.p - center).normalize();
return true;
}
}
return false;
}
};
#endif

CollisibleList

CollisibleList.h

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
#ifndef _BASIC_RAY_TRACING_COLLISIBLE_LIST_H_
#define _BASIC_RAY_TRACING_COLLISIBLE_LIST_H_
#include "Collide.h"
#include <vector>

struct CollisibleList : Collisible {
std::vector<Collisible *> d;

void add(Collisible *x) { d.push_back(x); }

bool collide(const Ray &r, float tMin, float tMax, CollideRecord &rec) const override {
bool flag = false;
float closest = tMax;
CollideRecord tmpRec;

for (const auto &i : d) {
if (i->collide(r, tMin, closest, tmpRec)) {
flag = true;
closest = tmpRec.t;
rec = tmpRec;
}
}

return flag;
}
};
#endif

测试

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
53
54
55
56
57
58
59
60
61
#include "../3rdParty/svpng/svpng.inc"
#include "Sphere.h"
#include "CollisibleList.h"
#include <cstdio>
#include <limits>

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 * ((HEIGHT - y - 1) * 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; }

Color3f paint(const Ray &r, const Collisible &obj) {
CollideRecord rec;
if (obj.collide(r, 0.0f, std::numeric_limits<float>::max(), rec))
return 0.5f * (rec.normal + Vector3f(1.0f, 1.0f, 1.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("ch5.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);

CollisibleList list;
Collisible *obj[2];
obj[0] = new Sphere({0, 0, -1}, 0.5);
obj[1] = new Sphere({0, -100.5, -1}, 100);

list.add(obj[0]);
list.add(obj[1]);

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, list), 1.0f);
}
}

delete obj[0];
delete obj[1];
svpng(f, WIDTH, HEIGHT, d, 1);
fclose(f);
}

效果

Chapter5

Comments

Your browser is out-of-date!

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

×