最近在学习光线追踪的经典教程<<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兼容的版本再把代码贴出来的,未完待续~