基础光线追踪(8) - 金属材质

基础光线追踪(8) - 金属材质

实现简单的金属材质。

材质类

为了实现多种材质,封装材质类,并提供散射接口,同时封装漫反射材质。
Material.h

1
2
3
4
5
6
7
8
9
10
11
#ifndef _BASIC_RAY_TRACING_MATERIAL_H_
#define _BASIC_RAY_TRACING_MATERIAL_H_
#include "Collide.h"

struct Material {
virtual bool scatter(const Ray &rayIn, const CollideRecord &rec, Vector3f &attenuation,
Ray &scattered) const = 0;

virtual ~Material() {}
};
#endif

反射

对于光滑的金属材质,光线会发生镜面反射。

Reflection

入射光线 $u$,出射光线 $v$。
由于所有方向向量都已经标准化,$\vec v = \vec u + 2 \vec n$。

模糊

由于不是所有金属都是镜面材质的,为其添加一个模糊系数,在反射时随机偏转,并根据模糊系数决定偏转程度。

代码

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include "../3rdParty/svpng/svpng.inc"
#include "../Chapter6/Camera.h"
#include "Sphere.h"
#include "CollisibleList.h"
#include "Metal.h"
#include "Lambertian.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, int depth) {
CollideRecord rec;
if (obj.collide(r, 0.001f, std::numeric_limits<float>::max(), rec)) {
Ray scattered;
Vector3f attenuation;
if (depth < 50 && rec.material->scatter(r, rec, attenuation, scattered)) {
return attenuation * paint(scattered, obj, depth + 1);
}
return {0.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("ch8.png", "wb");

Camera camera({0.0f, 0.0f, 0.0f}, {-2.0f, -1.5f, -1.0f}, {4.0f, 0.0f, 0.0f},
{0.0f, 3.0f, 0.0f});

CollisibleList list;
Collisible *obj[4];
Material *mats[4];
mats[0] = new Lambertian({0.8, 0.3, 0.3});
mats[1] = new Lambertian({0.8, 0.8, 0.0});
mats[2] = new Metal({0.8, 0.6, 0.2}, 0.3);
mats[3] = new Metal({0.8, 0.8, 0.8}, 0.3);

obj[0] = new Sphere({0, 0, -1}, 0.5, mats[0]);
obj[1] = new Sphere({0, -100.5, -1}, 100, mats[1]);
obj[2] = new Sphere({1, 0, -1}, 0.5, mats[2]);
obj[3] = new Sphere({-1, 0, -1}, 0.5, mats[3]);

for (int i = 0; i < 4; i++) list.add(obj[i]);

const int SAMPLE_TIMES = 100;
for (int x = 0; x < WIDTH; x++) {
for (int y = 0; y < HEIGHT; y++) {
Color3f col;
for (int i = 0; i < SAMPLE_TIMES; i++) {
float u = (float)(x + genFloat()) / WIDTH;
float v = (float)(y + genFloat()) / HEIGHT;
col += paint(camera.getRay(u, v), list, 0);
}
col /= SAMPLE_TIMES;
col = {sqrtf(col[0]), sqrtf(col[1]), sqrtf(col[2])};
paint(d, x, y, col, 1.0f);
}
}

for (int i = 0; i < 4; i++) delete mats[i];
for (int i = 0; i < 4; i++) delete obj[i];
svpng(f, WIDTH, HEIGHT, d, 1);
fclose(f);
}

效果

Chapter8

添加模糊系数为 $0.3$

Chapter8 fuzz0.3

Comments

Your browser is out-of-date!

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

×