最近在学习光线追踪的经典教程<<Ray Tracing in One Weekend—The Book Series>>,在这个系列中作者的程序运行后生成ppm格式的图片无奈本地的图片查看器包括Photoshop都无法查看作者生成的PPM格式,令人疑惑的是在闫令琪大佬的GAMES101-现代计算机图形学
课程中生成的ppm格式文件文件用Photoshop打开就完全没问题,比如说这张斯坦福兔的图片:
无奈只好换一个方向,在Github上找到了将RGB/RGBA 格式的数据压缩为PNG格式的repo,就下载过来试一下,还真的很好用----svpng
下载过来后将里面的附件svpng.inc
放在和主程序同一级目录下.
这样作者原先写的代码就可以修改为
#include <iostream> #include "svpng.inc" int main() { // Image FILE *fp = fopen("firstppm.png", "wb"); unsigned char rgb[256 * 256 * 3], *p = rgb; const int image_width = 256; const int image_height = 256; // Render 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; *p++ = (unsigned char)static_cast<int>(255.999 * r); *p++ = (unsigned char)static_cast<int>(255.999 * g); *p++ = (unsigned char)static_cast<int>(255.999 * b); } } svpng(fp, 256, 256, rgb, 0); fclose(fp); return 0; }
编译完成后直接运行可执行文件就可以生成firstppm.png,像这样:
后续的vec3.h和color.h也要按照作者源码做对应修改:
vec3.h:
#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 //vec3 Utility Functions inline void operator<<(unsigned char *&out,const vec3 &v) { *out++ = (unsigned char)(v.e[0]); *out++ = (unsigned char)(v.e[1]); *out++ = (unsigned char)(v.e[2]); return; } 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(); } #endif
color.h:
#ifndef COLOR_H #define COLOR_H #include "vec3.h" #include <iostream> void write_color(unsigned char *&out, color pixel_color) { // Write the translated [0,255] value of each color component. *out++ = (unsigned char)static_cast<int>(255.999 * pixel_color.x()); *out++ = (unsigned char)static_cast<int>(255.999 * pixel_color.y()); *out++ = (unsigned char)static_cast<int>(255.999 * pixel_color.z()); return; } #endif
源程序也就变成这样了,注意svpng的引用路径变了:
#include"color.h" #include "vec3.h" #include <iostream> #include "../svpng.inc" int main() { // Image FILE *fp = fopen("firstppm.png", "wb"); const int image_width = 256; const int image_height = 256; unsigned char rgb[image_width * image_height * 3], *p = rgb; // Render for (int j = image_height-1; j >= 0; --j) { 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(p, pixel_color); } } svpng(fp, 256, 256, rgb, 0); fclose(fp); return 0; }
注意:在vec3.h中的重载运算符函数operator<<(unsigned char *&out,const vec3 &v),和color.h中定义的write_color(unsigned char *&out, color pixel_color) 一定要传入字符缓冲的引用,也就是unsigned char *&out,因为颜色的写入操作直接对应字符缓冲rgb的地址,不应有中间拷贝。
如果传入的是unsigned char *类型就会生成这么个奇怪的图片:
随着课程的深入我会把作者写的源码继续改成svpng兼容的版本再把代码贴出来的,未完待续~