本文主要参照 Ray Tracing: The Next Week,其中只是主要精炼光追相关理论,具体实现可参照原文。

一、纹理实现

实现之前,你应该已经充分理解了【光线追踪系列六】反射与金属类特性。
在定义兰伯特材质时,我们将各个通道的反射率赋值给lambertian的构造函数,然后发射的射线命中该位置后,如果产生了散射光线,将该位置的反射率作为系数,乘以散射光线所采样得到的颜色值,从而实现该材质对射线的颜色吸收与反射。

1.1 纹理类实现

新增texture抽象类:

class texture  {public://返回一个三通道的颜色值,p为命中终点坐标virtual vec3 value(float u, float v, const vec3& p) const = 0;
};

hit_record结构体增加两个变量u和v,目前还不会用上,后面贴图才会用上,加进来先:

struct hit_record
{...float u;float v;
};

之后我们修改之前的lambertian类以实现原有效果:

class lambertian : public material
{public:texture *albedo; //反射率lambertian(texture *a) : albedo(a) {}virtual bool scatter(const ray &r_in, const hit_record &rec, vec3 &attenuation, ray &scattered) const{vec3 s_world = rec.p + rec.normal + random_in_unit_sphere();scattered = ray(rec.p, s_world - rec.p, r_in.time()); //scattered为散射光线attenuation = albedo->value(rec.u, rec.v, rec.p);     //注意这是各通道的反射率!return true;}
};

其中我们可以定义一个纯色纹理来替换之前直接写入颜色:

class constant_texture : public texture {public:vec3 color;constant_texture() { }constant_texture(vec3 c) : color(c) { }//返回一个三通道的colorvirtual vec3 value(float u, float v, const vec3& p) const {return color;}
};

在创建物体时,我们也需要对应修改lambertian的实现:

hittable *random_scene() {int n = 500;hittable **list = new hittable*[n + 1];list[0] = new sphere(vec3(0, -1000, 0), 1000, new lambertian(new constant_texture(vec3(0.5, 0.5, 0.5))));//texture *checker = new checker_texture(new constant_texture(vec3(0.2, 0.3, 0.1)), new constant_texture(vec3(0.9, 0.9, 0.9)));//list[0] = new sphere(vec3(0, -1000, 0), 1000, new lambertian(checker));int i = 1;for (int a = -11; a < 11; a++) {for (int b = -11; b < 11; b++) {float choose_mat = random_double();vec3 center(a + 0.9*random_double(), 0.2, b + 0.9*random_double());if ((center - vec3(4, 0.2, 0)).length() > 0.9) {if (choose_mat < 0.8) {  // diffuseif (b % 2 == 0) //动态模糊的球体{auto center2 = center + vec3(0, random_double(), 0);list[i++] = new moving_sphere(center, center2, 0.0, 1.0, 0.2,new lambertian(new constant_texture(vec3(random_double()*random_double(),random_double()*random_double(),random_double()*random_double()))));}else{list[i++] = new sphere(center, 0.2,new lambertian(new constant_texture(vec3(random_double()*random_double(),random_double()*random_double(),random_double()*random_double()))));}}else if (choose_mat < 0.95) { // metallist[i++] = new sphere(center, 0.2,new metal(vec3(0.5*(1 + random_double()),0.5*(1 + random_double()),0.5*(1 + random_double())),0.5*random_double()));}else {  // glasslist[i++] = new sphere(center, 0.2, new dielectric(1.5));}}}}list[i++] = new sphere(vec3(0, 1, 0), 1.0, new dielectric(1.5));list[i++] = new sphere(vec3(-4, 1, 0), 1.0, new lambertian(new constant_texture(vec3(0.4, 0.2, 0.1))));list[i++] = new sphere(vec3(4, 1, 0), 1.0, new metal(vec3(0.7, 0.6, 0.5), 0.0));list[i++] = new sphere(vec3(5, 0.35, 4), 0.35, new dielectric(1.2));list[i++] = new sphere(vec3(0, 0.4, 3), 0.4, new lambertian(new constant_texture(vec3(0.0, 1.0, 1.0))));list[i++] = new sphere(vec3(-6, 0.5, 2), 0.5, new metal(vec3(0.8, 0.8, 0.8), 0.1));//return new hittable_list(list, i);return new bvh_node(list, i, 0.0, 1.0);
}

constant_texture的物体表面的反射率不会随着射线命中点的位置而变化,仅仅只是返回一个三通道的color而已,所以即使反射率已经和命中点的位置挂钩了,着色还是跟之前一样是纯色。

注意,lambertian中的反射率变量,当texture为constant_texture的时候,该值的意义为constant_texture中的color,也就是说,反射率就相当于color。其实光追是一个逆向的过程,当前的反射率乘以散射光线采样到的颜色值,其实是散射光线逆向照在命中点所对应的物体表面后反射的颜色,因为颜色值相乘可以用来模拟光源照在物体后反射的颜色。
实现效果如下图:

1.2 棋盘纹理实现

如果要实现棋盘纹理的话,就要让表面的反射率跟射线命中点的位置关联起来:

//棋盘纹理
class checker_texture : public texture {public:texture *odd;texture *even;checker_texture() { }checker_texture(texture *t0, texture *t1): even(t0), odd(t1) { }virtual vec3 value(float u, float v, const vec3& p) const {float sines = sin(10*p.x())*sin(10*p.y())*sin(10*p.z());if (sines < 0)return odd->value(u, v, p);elsereturn even->value(u, v, p);}
};

其中,sines为了区分球体纹理位置。

这样一来,我们可以将棋盘的两种constant_texture(纯色材质)的指针赋值给checker_texture,实现棋盘纹理。

修改random_scene():

hittable *random_scene() {...hittable **list = new hittable*[n + 1];texture *checker = new checker_texture(new constant_texture(vec3(0.2, 0.3, 0.1)), new constant_texture(vec3(0.9, 0.9, 0.9)));list[0] = new sphere(vec3(0, -1000, 0), 1000, new lambertian(checker));...return new bvh_node(list, i, 0.0, 1.0);
}

实现后效果如下:

二、球面纹理贴图

2.1 球面贴图映射公式

在直角坐标中,对于一个宽高为nx*ny的图片,坐标为( i , j )的像素点的纹理坐标( u , v ) 定义如下:

这样就能够将( i , j ) 映射到( u , v ) ,并缩放到[0,1]

在球坐标中,我们同样可以将角度映射到( u , v ) :

假设( θ , ϕ )为球坐标上的一点,将球坐标想象成地球,则ϕ 为环绕着地轴旋转的角度(共360度),θ 为球心从北极点方向到南极点方向的角度(共180度),纹理坐标( u , v ) 为:


对于单位球表面上的一个命中点,球坐标转换到直角坐标的转换关系如下;

我们希望通过单位球上的命中点的直角坐标(x,y,z),得到球坐标,变形得:

atan2()返回值范围为[ − π , π ]
asin()的返回值范围为[ − π / 2 , π / 2 ]
综上,在我们的场景中(y轴朝上),单位球上一个命中点的直角坐标,到纹理坐标的映射为:

写成代码如下:

//输入命中点p的坐标,输出纹理坐标u,v
void get_sphere_uv(const vec3& p, double& u, double& v) {auto phi = atan2(p.z(), p.x());auto theta = asin(p.y());u = 1-(phi + pi) / (2*pi);v = (theta + pi/2) / pi;
}

同时记住要更新圆类,在hit函数中记录uv:

class sphere : public hittable
{public:vec3 center;float radius;material *mat_ptr; /* NEW */sphere() {}sphere(vec3 cen, float r, material *m) : center(cen), radius(r), mat_ptr(m) {}; //new//如果命中了,命中记录保存到recvirtual bool hit(const ray &r, float t_min, float t_max, hit_record &rec) const{vec3 oc = r.origin() - center;float a = dot(r.direction(), r.direction());float b = dot(oc, r.direction());float c = dot(oc, oc) - radius * radius;float discriminant = b * b - a * c;if (discriminant > 0){float temp = (-b - sqrt(discriminant)) / a; //小实数根if (temp < t_max && temp > t_min){rec.t = temp;rec.p = r.point_at_parameter(rec.t);rec.normal = (rec.p - center) / radius;rec.mat_ptr = mat_ptr; /* NEW */vec3 outward_normal = (rec.p - center) / radius;get_sphere_uv(outward_normal, rec.u, rec.v);return true;}temp = (-b + sqrt(discriminant)) / a; //大实数根if (temp < t_max && temp > t_min){rec.t = temp;rec.p = r.point_at_parameter(rec.t);rec.normal = (rec.p - center) / radius;rec.mat_ptr = mat_ptr; /* NEW */vec3 outward_normal = (rec.p - center) / radius;get_sphere_uv(outward_normal, rec.u, rec.v);return true;}}return false;}bool bounding_box(float t0, float t1, aabb &box) const{box = aabb(center - vec3(radius, radius, radius), center + vec3(radius, radius, radius));return true;}
};

2.2 读取图片

使用stb_image的stbi_load()函数读取图片,该函数返回一个unsigned char数组,该数组按顺序保存了图片的RGB颜色值,范围为[0,255]。

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
unsigned char *imgEarth = stbi_load("earthmap.jpg", &nx, &ny, &nn, 0);


需要注意的是:注意png与jpg图片读取的区别。

2.3 贴图纹理类的构造和使用

贴图纹理类image_texture继承自texture,定义如下:

class image_texture : public texture
{public:unsigned char* data;int nx, ny;image_texture(){}image_texture(unsigned char* pixels, int A, int B) : data(pixels), nx(A), ny(B) {}//输入u和v,输出对应图片像素的rgb值virtual vec3 value(float u, float v, const vec3 &p) const{int i = int((u)* nx);//求出像素索引int j = int((1 - v)*ny - 0.001f);if (i < 0) i = 0;if (j < 0) j = 0;if (i > nx - 1) i = nx - 1;if (j > ny - 1) j = ny - 1;float r = int(data[3 * i + 3 * nx*j]) / 255.0f;float g = int(data[3 * i + 3 * nx*j + 1]) / 255.0f;float b = int(data[3 * i + 3 * nx*j + 2]) / 255.0f;return vec3(r, g, b);}
};

lambertian材质的贴图纹理球体定义示例:

hittable *random_scene() {hittable **list = new hittable*[n + 1];
texture *checker = new checker_texture(new constant_texture(vec3(0.2, 0.3, 0.1)), new constant_texture(vec3(0.9, 0.9, 0.9)));list[0] = new sphere(vec3(0, -1000, 0), 1000, new lambertian(checker));int nx, ny, nn;unsigned char *earthmapjpg = stbi_load("F://earthmap.jpg", &nx, &ny, &nn, 0);material *earthmapJpg = new lambertian(new image_texture(earthmapjpg, nx, ny));unsigned char *Cristiano = stbi_load("F://Cristiano.jpg", &nx, &ny, &nn, 0);material *CristianoJpg = new lambertian(new image_texture(Cristiano, nx, ny));unsigned char *earthmappng = stbi_load("F://earthmap.png", &nx, &ny, &nn, 0);material *earthmapPng = new lambertian(new image_texture(earthmappng, nx, ny));list[i++] = new sphere(vec3(5, 1, 0), 1.0, earthmapJpg);list[i++] = new sphere(vec3(0, 1, 0), 1.0, CristianoJpg);list[i++] = new sphere(vec3(-5, 1, 0), 1.0, earthmapPng);list[i++] = new sphere(vec3(5, 0.35, 4), 0.35, new dielectric(1.2));list[i++] = new sphere(vec3(0, 0.4, 3), 0.4, new lambertian(new constant_texture(vec3(0.0, 1.0, 1.0))));list[i++] = new sphere(vec3(-6, 0.5, 2), 0.5, new metal(vec3(0.8, 0.8, 0.8), 0.1));return new bvh_node(list, i, 0.0, 1.0);
}

运行效果如下:

【光线追踪系列十一】纹理贴图相关推荐

  1. Threejs系列--14游戏开发--沙漠赛车游戏【纹理贴图之loading加载】

    Threejs系列--14游戏开发--沙漠赛车游戏[纹理贴图之loading加载] 序言 目录结构 代码一览 world/index.js代码 Application.js代码 代码解读 运行结果 序 ...

  2. Windows 8 Directx 开发学习笔记(十一)地形纹理贴图

    前一篇实现木箱贴图时,木箱的六个面都正好用一整张纹理图,即六个面的纹理坐标均在[0,1]内.然而在为比较大的模型贴图时,像山峰河谷模型,如果只用一张纹理图,那么每个三角形只得到几个纹理元素,无法为提供 ...

  3. OpenGL ES之十——纹理贴图(展示一张图片)

    概述 这是一个系列的Android平台下OpenGl ES介绍,从最基本的使用最终到VR图的展示的实现,属于基础篇.(后面针对VR视频会再有几篇文章,属于进阶篇) OpenGL ES之一--概念扫盲 ...

  4. OpenGL学习笔记(十三):将纹理贴图应用到四边形上,对VAO/VBO/EBO/纹理/着色器的使用方式进行总结

    原博主博客地址:http://blog.csdn.net/qq21497936 本文章博客地址:http://blog.csdn.net/qq21497936/article/details/7919 ...

  5. SLAM【十一】建图

    SLAM[十一]建图 概述 单目稠密重建 立体视觉 极线搜索与块匹配 高斯分布的深度滤波器 像素梯度的问题 逆深度 图像间的变换 点云地图 八叉树地图 参考 概述 建图的功能: 定位:第一次跑,就把地 ...

  6. 基于VC++的3D地形绘制与纹理贴图

    前言 随着地理信息系统产业的发展,三维产品也在生活中处处吸引着我们的眼球.作为数字城市的核心内容,城市模型的构建成为了目前研究的热点.OpenGL是独立于操作系统和硬件环境的三维图形库,其为实现逼真的 ...

  7. openGL法线贴图和纹理贴图结合使用,以增强三维物体表面细节

    openGL系列文章目录 文章目录 openGL系列文章目录 前言 一.法线贴图? 二.代码 1.主程序 2.着色器程序 运行效果 源码下载 前言 凹凸贴图的一种替代方法是使用查找表来替换法向量.这样 ...

  8. Python数据分析学习系列 十一 时间序列

    Python数据分析学习系列 十一 时间序列 资料转自(GitHub地址):https://github.com/wesm/pydata-book 有需要的朋友可以自行去github下载 时间序列(t ...

  9. 【openGL2021版】纹理贴图

     [openGL2021版]纹理贴图       大家好,我是Lampard猿奋~       欢迎来到船新的openGL基础系列的博客,今天学习的是纹理贴图  (一)回顾       上周我们学习了 ...

最新文章

  1. iframe ajax上传,ajax--iframe模拟ajax文件上传效果
  2. android 标准 action
  3. asp.net获取客户端信息
  4. 移动端取消iphone ipad默认按钮
  5. CSDN写博客时设置图片显示大小
  6. vue获取当前选中行的数据_Vue编程的团队代码规范
  7. 手机上怎么去掉a 标签中的img点击时的阴影?
  8. java环境安装菜鸟教程_Linux菜鸟教程(一:JDK安装和java环境配置)
  9. mac下配置环境变量
  10. 个人网页LOGO设计(作业)
  11. uniapp引入font-awsome字体图标-疑难解决
  12. 一图看懂ADSL拨号服务器
  13. Java岗大厂面试百日冲刺 - 日积月累,每日三题【Day1】 —— 基础篇1
  14. 适合中学生看的英文电影
  15. uni.showToast与uni.navigateTo同时使用问题
  16. NES神经干细胞标志物抗体检测方案
  17. 百度AI市场热品试用 | 台面式双目活 体检测USB摄像头
  18. Matlab/Simulink-S-function函数(MATLAB版本2020a)
  19. 四轮两驱小车(四):STM32驱动5路灰度传感器PID循迹
  20. 云原生周报 | Fluent Bit下载量达到10亿次;对 Istio 1.11的支持已经结束

热门文章

  1. 英语笔记1.2.2022
  2. 纽约州立大学环境与林业学院计算机科学专业,纽约州立大学环境与林业科学 治理环境是全球需要面对的问题...
  3. 【报告分享】2021中国品牌消费趋势报告-京东大数据研究院(附下载)
  4. 电磁阀peakhold驱动
  5. jdk1.8新特性:stream流报错:stream has already been operated upon or closed
  6. Nmos驱动电路分析
  7. 【TIC6657 DSP学习笔记】01 工程创建与代码编写——以点亮LED为例
  8. oracle用户常见job权限不足,JOB调用的权限问题
  9. 机试评判系统评判提交程序后返回结果详细说明
  10. div做表格 html5,div+css制作表格