简单的光线追踪教程(一)
簡單的光線追蹤教程(一)
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#endif3.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圖象生成的簡單封裝。
這次先說這些,以后會持續更新,感興趣的可以關注一下
總結
以上是生活随笔為你收集整理的简单的光线追踪教程(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UE4打开屏幕键盘/软键盘/虚拟键盘 无
- 下一篇: Nodejs 批量检测 Excel 中u