0%

【RayTracer】(十三)图片纹理

之前我们实现了纯色和噪声纹理,但它们都是程序生成的纹理,这一节我们通过读入图片,实现图片纹理映射。

1 图片纹理类

要实现图片纹理类,我们先要读入一张图片,这可以用到一开始存图使用的轻量而强大的 stb_image 库,然后我们要找到纹理坐标和图片坐标的映射关系,非常简单,对于一张 M * N 的图片上的像素 (i, j),它的纹理坐标是:
$$
u = \frac{i}{M-1},\ v = \frac{j}{N-1}
$$
于是可以实现一个图片纹理类:

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
/*
* 纹理类,包含各种纹理
*/
#pragma once
#ifndef TEXTURE_H
#define TEXTURE_H

#include "utilities.h"
#include "perlin.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

...

// 图片纹理
class image_texture : public texture {
public:
const static int bytes_per_pixel = 3;

image_texture()
: data(nullptr), width(0), height(0), bytes_per_scanline(0) {}

image_texture(const char* filename) {
auto components_per_pixel = bytes_per_pixel;

data = stbi_load(
filename, &width, &height, &components_per_pixel, components_per_pixel);

if (!data) {
std::cerr << "ERROR: Could not load texture image file '" << filename << "'.\n";
width = height = 0;
}

bytes_per_scanline = bytes_per_pixel * width;
}

~image_texture() {
delete data;
}

virtual color value(double u, double v, const vec3& p) const override {
// 没有图片的话返回一个固定的颜色,便于debug
if (data == nullptr)
return color(0, 1, 1);

// 输入的纹理坐标截断在[0,1]区间
u = clamp(u, 0.0, 1.0);
// 由于图片是从上到下存储的,相当于原点在左上角,而纹理坐标原点在左下角,因此纵坐标要翻转一下
v = 1.0 - clamp(v, 0.0, 1.0);
// 纹理坐标映射到图片坐标
auto i = static_cast<int>(u * width);
auto j = static_cast<int>(v * height);
if (i >= width) i = width - 1;
if (j >= height) j = height - 1;
// 我们返回的颜色都在[0,1]之间,因此要除以255
const auto color_scale = 1.0 / 255.0;
auto pixel = data + j * bytes_per_scanline + i * bytes_per_pixel;

return color(color_scale * pixel[0], color_scale * pixel[1], color_scale * pixel[2]);
}

private:
unsigned char* data;
int width, height;
int bytes_per_scanline;
};

#endif

2 测试效果

新建一个场景:

1
2
3
4
5
6
7
8
9
10
// 地球
hittable_list earth() {
std::string TexturePath = "D:\\TechStack\\ComputerGraphics\\Ray Tracing in One Weekend Series\\Project\\Textures\\earthmap.jpg";
auto earth_texture = make_shared<image_texture>(TexturePath.c_str());
auto earth_surface = make_shared<lambertian>(earth_texture);
auto globe = make_shared<sphere>(
point3(0, 0, 0), point3(0, 0, 0), 0.0, 1.0, 2, earth_surface);

return hittable_list(globe);
}

并对应修改主函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...

int sence = 4;
switch (sence) {
...
default:
case 4:
world = earth();
lookfrom = point3(13, 2, 3);
lookat = point3(0, 0, 0);
vfov = 20.0;
break;
}

...

得到的效果:

Earth

---- 本文结束 知识又增加了亿点点!----

文章版权声明 1、博客名称:LycTechStack
2、博客网址:https://lz328.github.io/LycTechStack.github.io/
3、本博客的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系博主进行删除处理。
4、本博客所有文章版权归博主所有,如需转载请标明出处。