我们之前使用撞点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));

  我们开始看到所有颜色的一些能量都是纹理 - 我们可以为朗伯物质分配任何类型的纹理,理想散射不需要意识到它。

  

posted on 2018-07-20 17:03  图样司  阅读(129)  评论(0编辑  收藏  举报