简单的光线追踪教程(一)

1. 概述

最近闲来无事,想学习一下java,并且了解一下C++。网上搜索了很多得相关教程,学了一段时间之后发现,还是得自己亲自动手写一点东西。所以学了一点简单的光线追踪,与大家一起分享

里面涉及一点矩阵的操作,以及简单的C++/Java

2. 如何输出图象

我采用的就是最简单的方法,就是从纯文本ppm文件开始,不了解ppm文件的可以从下面的链接中简单了解一下,这里也不需要更深入的了解。总之,PPM是一种简单的图片格式,我们可以通过PPM进行图像相关的学习。

https://blog.csdn.net/kinghzkingkkk/article/details/70226214

让我们写一个最简单的C++/Java来输入一个简单的PPM文件

C++ :

#include <iostream>int main() {// Imageconst int image_width = 256;const int image_height = 256;// Renderstd::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";for (int j = image_height-1; j >= 0; --j) {for (int i = 0; i < image_width; ++i) {auto r = double(i) / (image_width-1);auto g = double(j) / (image_height-1);auto b = 0.25;int ir = static_cast<int>(255.999 * r);int ig = static_cast<int>(255.999 * g);int ib = static_cast<int>(255.999 * b);std::cout << ir << ' ' << ig << ' ' << ib << '\n';}}
}

对于这个简单的文件需要注意

1. 图片中的像素按从左到右打的像素按行写出
2.行从上到下写出

之后我们会将这段代码重定向/写入到图像文件

在C++中我们使用重定向符来完成,在java中我们可以创建一个类来完成这个功能

  public void writeToFile(String path) throws IOException {FileWriter fw = new FileWriter(path);BufferedWriter bw = new BufferedWriter(fw);double scale = 1.0 / samplesPerPixel;bw.write("P3\n" + imageWidth + " " + imageHeight + "\n255\n");for (int j = imageHeight - 1; j >= 0; j--) {System.out.println(j);for (int i = 0; i < imageWidth; i++) {double r = pixelColor[j][i].getX() * scale;double g = pixelColor[j][i].getY() * scale;double b = pixelColor[j][i].getZ() * scale;bw.write(   String.valueOf((int)(255.999 * r + ' ' +String.valueOf((int)255.999 * g)) + ' ' +String.valueOf((int)(255.999 * b)) + ' ');bw.write("\n");}}}

最终我们可以看到我们写的图像为

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JePhTtOm-1623992609952)(https://raytracing.github.io/images/img-1.01-first-ppm-image.png)]

一个渐变的色块,这样,我们的第一个ppm图象生成成功了!

由于以后的写入工作会变得很缓慢,有时我们会怀疑它是否在正常工作,我们为我们的写入函数/写入方法加上一个简单的进度条,注意要写入错误输出流。

for (int j = image_height-1; j >= 0; --j) {std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush;for (int i = 0; i < image_width; ++i) {auto r = double(i) / (image_width-1);auto g = double(j) / (image_height-1);auto b = 0.25;int ir = static_cast<int>(255.999 * r);int ig = static_cast<int>(255.999 * g);int ib = static_cast<int>(255.999 * b);std::cout << ir << ' ' << ig << ' ' << ib << '\n';}}std::cerr << "\nDone.\n";

3. vec3 类

说明一下,vec3其实就是每个像素的颜色类以及其他的性质(例如反射),其中包含着r,g,b。我们把这三个元素分别视为三个向量,用向量操作来完成图像的操作,以及我们光线追踪的要求。

3.1. vec3类的变量与方法

代码如下,使用了C++中运算符重载的特性

#ifndef VEC3_H
#define VEC3_H#include <cmath>
#include <iostream>using std::sqrt;class vec3 {public:vec3() : e{0,0,0} {}vec3(double e0, double e1, double e2) : e{e0, e1, e2} {}double x() const { return e[0]; }double y() const { return e[1]; }double z() const { return e[2]; }vec3 operator-() const { return vec3(-e[0], -e[1], -e[2]); }double operator[](int i) const { return e[i]; }double& operator[](int i) { return e[i]; }vec3& operator+=(const vec3 &v) {e[0] += v.e[0];e[1] += v.e[1];e[2] += v.e[2];return *this;}vec3& operator*=(const double t) {e[0] *= t;e[1] *= t;e[2] *= t;return *this;}vec3& operator/=(const double t) {return *this *= 1/t;}double length() const {return sqrt(length_squared());}double length_squared() const {return e[0]*e[0] + e[1]*e[1] + e[2]*e[2];}public:double e[3];
};// Type aliases for vec3
using point3 = vec3;   // 3D point
using color = vec3;    // RGB color#endif
3.2. 对于vec3类的简单操作
// vec3 Utility Functionsinline std::ostream& operator<<(std::ostream &out, const vec3 &v) {return out << v.e[0] << ' ' << v.e[1] << ' ' << v.e[2];
}inline vec3 operator+(const vec3 &u, const vec3 &v) {return vec3(u.e[0] + v.e[0], u.e[1] + v.e[1], u.e[2] + v.e[2]);
}inline vec3 operator-(const vec3 &u, const vec3 &v) {return vec3(u.e[0] - v.e[0], u.e[1] - v.e[1], u.e[2] - v.e[2]);
}inline vec3 operator*(const vec3 &u, const vec3 &v) {return vec3(u.e[0] * v.e[0], u.e[1] * v.e[1], u.e[2] * v.e[2]);
}inline vec3 operator*(double t, const vec3 &v) {return vec3(t*v.e[0], t*v.e[1], t*v.e[2]);
}inline vec3 operator*(const vec3 &v, double t) {return t * v;
}inline vec3 operator/(vec3 v, double t) {return (1/t) * v;
}inline double dot(const vec3 &u, const vec3 &v) {return u.e[0] * v.e[0]+ u.e[1] * v.e[1]+ u.e[2] * v.e[2];
}inline vec3 cross(const vec3 &u, const vec3 &v) {return vec3(u.e[1] * v.e[2] - u.e[2] * v.e[1],u.e[2] * v.e[0] - u.e[0] * v.e[2],u.e[0] * v.e[1] - u.e[1] * v.e[0]);
}inline vec3 unit_vector(vec3 v) {return v / v.length();
}

对于C++,你可以写道头文件中,或者java写在工具类中

3.3. 简单封装

使用我们vec3类,我们简单封装一下我们写入ppm文件的过程

#ifndef COLOR_H
#define COLOR_H#include "vec3.h"#include <iostream>void write_color(std::ostream &out, color pixel_color) {// Write the translated [0,255] value of each color component.out << static_cast<int>(255.999 * pixel_color.x()) << ' '<< static_cast<int>(255.999 * pixel_color.y()) << ' '<< static_cast<int>(255.999 * pixel_color.z()) << '\n';
}#endif

因此,我们只需要在主函数中进行调用即可

#include "color.h"
#include "vec3.h"#include <iostream>int main() {// Imageconst int image_width = 256;const int image_height = 256;// Renderstd::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";for (int j = image_height-1; j >= 0; --j) {std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush;for (int i = 0; i < image_width; ++i) {color pixel_color(double(i)/(image_width-1), double(j)/(image_height-1), 0.25);write_color(std::cout, pixel_color);}}std::cerr << "\nDone.\n";
}

这样,我们就实现了对我们最初写的ppm图象生成的简单封装

这次先说这些,以后会持续更新,感兴趣的可以关注一下
这样,我们就实现了对我们最初写的ppm图象生成的简单封装。
这次先说这些,以后会持续更新,感兴趣的可以关注一下

简单的光线追踪教程(一)相关推荐

  1. 简单的光线追踪教程(三)

    简单的光线追踪教程(三) 6. 表面法线和多个物体 6.1. 表面法线阴影 首先,我们先设计一个表面法线,这样我们就可以将球面遮住.我们将法线设计为单位长度,则这样方便计算与移植. 球面法线: dou ...

  2. asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程...

    最近在学习张善友老师的NanoFabric 框架的时了解到Exceptionless : https://exceptionless.com/ !因此学习了一下这个开源框架!下面对Exceptionl ...

  3. 史上最简单的 SpringCloud 教程 | 第一篇: 服务的注册与发现(Eureka)

    最新Finchley版本请访问: https://www.fangzhipeng.com/springcloud/2018/08/30/sc-f1-eureka/ 或者 http://blog.csd ...

  4. 史上最简单的SpringCloud教程 | 第十篇: 高可用的服务注册中心

    转自:https://blog.csdn.net/forezp/article/details/81041101 文章 史上最简单的 SpringCloud 教程 | 第一篇: 服务的注册与发现(Eu ...

  5. 史上最简单的SpringCloud教程 | 第六篇: 分布式配置中心(Spring Cloud Config)

    转:https://blog.csdn.net/forezp/article/details/70037291 最新版本: 史上最简单的SpringCloud教程 | 第六篇: 分布式配置中心(Spr ...

  6. 史上最简单的SpringCloud教程 | 第五篇: 路由网关(zuul)

    转:https://blog.csdn.net/forezp/article/details/69939114 最新版本: 史上最简单的SpringCloud教程 | 第五篇: 路由网关(zuul)( ...

  7. 史上最简单的SpringCloud教程 | 第四篇:断路器(Hystrix)

    转:https://blog.csdn.net/forezp/article/details/69934399 最新版本: 史上最简单的SpringCloud教程 | 第四篇:断路器(Hystrix) ...

  8. 史上最简单的SpringCloud教程 | 第三篇: 服务消费者(Feign)

    转:https://blog.csdn.net/forezp/article/details/69808079 最新版本: 史上最简单的SpringCloud教程 | 第三篇: 服务消费者(Feign ...

  9. 史上最简单的git教程搭配Github和Gitee一起食用更佳

    史上最简单的git教程 开始之前 git的最简单使用 1. 安装 2. 配置 2.1 用户信息 3. 最基本使用 Github 1. 首先你需要一个账号 2. 你需要一个仓库 Gitee 开始之前 g ...

  10. 简单数据恢复菜鸟教程 (转)

    转自:http://bbs.cfanclub.net/thread-252403-1-1.html 最近看到有不少朋友提过关于如何数据恢复,今天趁有点空,给大家写个简单的恢复教程分享一下! 我们就以U ...

最新文章

  1. Python 打印行列控制(API:pandas.set_option())
  2. html5画布显示不出来,运行后html5画布没出来
  3. SSM高级整合项目实战
  4. Spark HistoryServer日志解析清理异常
  5. java中日期计算2月份_计算两日期间2月29日总数的Java程序
  6. 【心情】今天买了ZÈRTZ!
  7. c++ lambda 重载_您会后悔对Lambdas应用重载!
  8. 极光推送android点击跳转页面,app关闭时点击推送消息实现页面跳转
  9. PyTorch | 保存和加载模型教程
  10. 关闭浏览器网页触发事件_浅析浏览器渲染和 script 加载
  11. 信息学奥赛C++语言:求各位数和2
  12. jdk的ServiceLoader
  13. 中文命名之Hibernate 5演示 - 使用注解(annotation)而非xml定义映射
  14. python 读取csv文件
  15. matlab如何画出来地球,matlab绘制地球
  16. Python 文本滚动播放
  17. 封装多帧dicm图像
  18. 分布式电源选址定容与优化配置MATLAB程序基于多目标粒子群算法
  19. Mac版本Jmeter下载安装教程
  20. 对梯度概念的直观理解

热门文章

  1. python怎么进阶_你真的会自学么?大佬整理的python进阶路径(长更)
  2. kindle刷机ttl_kindle咕咪版如何刷成普通kindle版本?
  3. CAPL学习之路-SOME/IP相关函数
  4. 熵权法 算权重系数 python
  5. 正态分布某一点的概率怎么算_正态分布的抽卡概率算法
  6. 十四届恩智浦智能车竞赛双车组-星夜兼程队2019回顾
  7. iweboffice之word——自定义菜单的使用
  8. 如何卸载服务(Service)?
  9. 机器学习中的训练集、验证集、测试集;交叉验证方法
  10. kitti数据集 Raw Data与00-10 Ground Truth的对应关系