我们之前使用撞点P来索引一个固体纹理,比如大理石。我们还可以读取图像,并使用2D纹理坐标索引到图像上。
在图像中缩放(u,v)的直接方法是将u和v四舍五入为整数,并将其用作(i,j)像素。但是这很尴尬,因为当我们改变图像分辨率时,我们不想改变代码。因此,最普遍的非官方标准之一是使用纹理坐标而不是图像像素坐标。这些只是图像中分数位置的一种形式。例如,对于nx中的像素(i,j),图像纹理的位置为:
u = i/(nx-1)
v = j/(nx-1)
这只是一个分数位置。对于一个真正的撞击物体,我们还需要返回一个u和v在命中记录。对于球体,这通常是基于某种形式的经度和纬度,即。球坐标。如果我们有一个(theta,phi)在球坐标系中我们只需要缩放和分数。如果是从极点向下的角度,是绕轴穿过极点的角度,那么归一化到[0,1]的归一化就是:
u = phi / (2*Pi)
v = theta / Pi
为了计算θ和π,对于给定的击中点,单位半径球在原点上的球面坐标公式为:
x = cos(phi) cos(theta)
y = sin(phi) cos(theta)
z = sin(theta)
我们需要反转过来。 由于可爱的math.h的函数atan2()--- 取任意数字与正弦和余弦成比例并返回角度,我们可以传入x和y (cos(theta)取消):
phi = atan2(y, x)
atan2在-Pi到Pi的范围内返回,所以我们需要在那里稍加注意。 theta更直截了当:
theta = asin(z)
它返回-Pi / 2到Pi / 2范围内的数字。
因此,对于球体,u,v coord计算由效用函数完成,该函数期望单位球体上的物体以原点为中心。 sphere :: hit中的调用应该是:
get_sphere_uv((rec.p-center)/radius, rec.u, rec.v);
效用函数是:
现在我们还需要创建一个包含图像的纹理类。我将使用我最喜欢的图像工具stb_image。它将图像读入一个大的无符号字符数组。这些就是每个范围为0的RGBs。255为黑色至全亮。
按这个顺序排列的填充数组的表示是相当标准的。值得庆幸的是,stb_image包使这变得非常简单——只需在main中用#define包含header的定义:
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
为了从一个文件eathmap.jpg中读取一个图像(我刚刚从web上抓取了一个随机的地球地图——任何标准的投影都可以实现我们的目的),然后将其分配给一个漫反射的材料,代码是:
int nx, ny, nn;
unsigned char *tex_data = stbi_load("earthmap.jpg", &nx, &ny, &nn, 0);
material *mat = new lambertian(new image_texture(tex_data, nx, ny));
我们开始看到所有颜色的一些能量都是纹理 - 我们可以为朗伯物质分配任何类型的纹理,理想散射不需要意识到它。