光线追踪输出双倍?
Raytracing outputting double?
一个周末我正在研究 Peter Shirley 的光线追踪,我在尝试将球体渲染为红色时遇到了一个问题。我的数学似乎是准确的,但它输出的是并排的 2 个球体和 skewed/stretched,而不是场景中的 1 个球体。我怀疑它与光线投射的视野宽度有关,但不确定如何确认。这是代码:
main.cpp
#include "vectors.h"
#include "Geometry3D.h"
#include <fstream>
vec3 Color(const Ray& r, const Sphere& s) {
if (Raycast(s, r) != -1)
return vec3(1, 0, 0);
else
return vec3(0, 0, 0);
}
int main() {
const int WIDTH = 200;
const int HEIGHT = 100;
std::ofstream out;
out.open("image.ppm");
if (out.is_open()) {
out << "P3\n" << WIDTH << ' ' << HEIGHT << "\n255\n";
vec3 lowerLeftCorner(-2.0f, -1.0f, -1.0f);
vec3 horizontal(4.0f, 0.0f, 0.0f);
vec3 vertical(0.0, 2.0f, 0.0);
vec3 origin(0.0, 0.0, 0.0);
for (int i = 0; i < WIDTH; i++) {
for (int j = 0; j < HEIGHT; j++) {
float u = float(i) / float(WIDTH);
float v = float(j) / float(HEIGHT);
Sphere s(vec3(0, 0, -5.0f), 1.0f);
Ray r(origin, lowerLeftCorner + (horizontal * u) + (vertical * v));
vec3 color = Color(r, s);
int ir = int(255.99f * color.x);
int ig = int(255.99f * color.y);
int ib = int(255.99f * color.z);
out << ir << ' ' << ig << ' ' << ib << '\n';
}
}
}
}
Geometry3D.h
#ifndef _GEOMETRY_3D_H
#define _GEOMETRY_3D_H
#include "vectors.h"
#include <cmath>
#include <cfloat>
typedef vec3 Point;
typedef struct Ray {
Point origin;
vec3 direction;
Ray() : direction(0.0f, 0.0f, 1.0f) { }
Ray(const Point& o, const vec3& d) : origin(o), direction(d) {
NormalizeDirection();
}
inline void NormalizeDirection() {
Normalize(direction);
}
} Ray;
typedef struct Sphere {
Point position;
float radius;
Sphere() : radius(1.0f) { }
Sphere(const Point& p, float r) : position(p), radius(r) { }
} Sphere;
float Raycast(const Sphere& sphere, const Ray& ray);
#endif
Geometry3D.cpp
#include "Geometry3D.h"
#include <iostream>
float Raycast(const Sphere& sphere, const Ray& ray) {
vec3 e = sphere.position - ray.origin;
float rSq = sphere.radius * sphere.radius;
float eSq = MagnitudeSq(e);
float a = Dot(e, ray.direction);
float bSq = eSq - (a * a);
float f = sqrt(rSq - bSq);
if (rSq - (eSq - (a * a)) < 0.0f)
return -1;
else if (eSq < rSq) {
return a + f;
}
return a - f;
}
这是输出:
感谢任何帮助。
错误在于写入.ppm图像。
for (int i = 0; i < WIDTH; i++) {
for (int j = 0; j < HEIGHT; j++) {
float u = float(i) / float(WIDTH);
float v = float(j) / float(HEIGHT);
...
out << ir << ' ' << ig << ' ' << ib << '\n';
}
}
您正在使用普通 PPM 图像 (P3)。
根据 PPM Specification 栅格(颜色数据)应如下所示:
A raster of Height rows, in order from top to bottom. Each row consists of Width pixels, in order from left to right. Each pixel is a triplet of red, green, and blue samples, in that order.
但是,您正在使用循环构建具有 HEIGHT 像素的 WIDTH 行栅格。
外层循环创建一行,内层循环创建行中的像素。
更改索引将解决问题。
//switch the loop indices
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < WIDTH; j++) {
float u = float(i) / float(HEIGHT);
float v = float(j) / float(WIDTH);
...
//added spaces between each color value
//this way each pixel is separated with 2 spaces
out << ' '<< ir << ' ' << ig << ' ' << ib << ' ';
}
//and each row ends on a newline
//this makes it easier to read them with a text editor
out << '\n';
}
您遇到的情况如下:
宽度是高度的两倍,
const int WIDTH = 200;
const int HEIGHT = 100;
然后您将 200 行 100 像素写入 ppm 图像。然而,图像查看器读取 100 行 200 像素,这会导致 "mirror" 效果。
考虑下图:
B为黑色像素,R为红色像素
我试图用绘画图像来说明它,但实际上图像数据只不过是一个矩阵;)
宽度:8
身高:4
B B B B B B B B
B B B B R R R R
R R R R B B B B
B B B B B B B B
此图像的顶部有一条黑色像素线,后面是一行半黑半红像素。下一行具有相同的图案,只是方向相反:一半是红色,一半是黑色。最后一行是纯黑色。
您的代码基本上是转置图像。它用高度切换宽度并写下:
B B R B
B B R B
B B R B
B B R B
B R B B
B R B B
B R B B
B R B B
所以图片 reader 是这样写的:
B B R B B B R B
B B R B B B R B
B R B B B R B B
B R B B B R B B
如果你遍历每一行,你可以看到图像在第 4 行之后被镜像。
这就是您在图像中看到的,但是由于您使用的是圆圈,因此由于对称性,图像看起来像是写了两次。你没有看到图像被颠倒了。
一个周末我正在研究 Peter Shirley 的光线追踪,我在尝试将球体渲染为红色时遇到了一个问题。我的数学似乎是准确的,但它输出的是并排的 2 个球体和 skewed/stretched,而不是场景中的 1 个球体。我怀疑它与光线投射的视野宽度有关,但不确定如何确认。这是代码:
main.cpp
#include "vectors.h"
#include "Geometry3D.h"
#include <fstream>
vec3 Color(const Ray& r, const Sphere& s) {
if (Raycast(s, r) != -1)
return vec3(1, 0, 0);
else
return vec3(0, 0, 0);
}
int main() {
const int WIDTH = 200;
const int HEIGHT = 100;
std::ofstream out;
out.open("image.ppm");
if (out.is_open()) {
out << "P3\n" << WIDTH << ' ' << HEIGHT << "\n255\n";
vec3 lowerLeftCorner(-2.0f, -1.0f, -1.0f);
vec3 horizontal(4.0f, 0.0f, 0.0f);
vec3 vertical(0.0, 2.0f, 0.0);
vec3 origin(0.0, 0.0, 0.0);
for (int i = 0; i < WIDTH; i++) {
for (int j = 0; j < HEIGHT; j++) {
float u = float(i) / float(WIDTH);
float v = float(j) / float(HEIGHT);
Sphere s(vec3(0, 0, -5.0f), 1.0f);
Ray r(origin, lowerLeftCorner + (horizontal * u) + (vertical * v));
vec3 color = Color(r, s);
int ir = int(255.99f * color.x);
int ig = int(255.99f * color.y);
int ib = int(255.99f * color.z);
out << ir << ' ' << ig << ' ' << ib << '\n';
}
}
}
}
Geometry3D.h
#ifndef _GEOMETRY_3D_H
#define _GEOMETRY_3D_H
#include "vectors.h"
#include <cmath>
#include <cfloat>
typedef vec3 Point;
typedef struct Ray {
Point origin;
vec3 direction;
Ray() : direction(0.0f, 0.0f, 1.0f) { }
Ray(const Point& o, const vec3& d) : origin(o), direction(d) {
NormalizeDirection();
}
inline void NormalizeDirection() {
Normalize(direction);
}
} Ray;
typedef struct Sphere {
Point position;
float radius;
Sphere() : radius(1.0f) { }
Sphere(const Point& p, float r) : position(p), radius(r) { }
} Sphere;
float Raycast(const Sphere& sphere, const Ray& ray);
#endif
Geometry3D.cpp
#include "Geometry3D.h"
#include <iostream>
float Raycast(const Sphere& sphere, const Ray& ray) {
vec3 e = sphere.position - ray.origin;
float rSq = sphere.radius * sphere.radius;
float eSq = MagnitudeSq(e);
float a = Dot(e, ray.direction);
float bSq = eSq - (a * a);
float f = sqrt(rSq - bSq);
if (rSq - (eSq - (a * a)) < 0.0f)
return -1;
else if (eSq < rSq) {
return a + f;
}
return a - f;
}
这是输出:
感谢任何帮助。
错误在于写入.ppm图像。
for (int i = 0; i < WIDTH; i++) {
for (int j = 0; j < HEIGHT; j++) {
float u = float(i) / float(WIDTH);
float v = float(j) / float(HEIGHT);
...
out << ir << ' ' << ig << ' ' << ib << '\n';
}
}
您正在使用普通 PPM 图像 (P3)。 根据 PPM Specification 栅格(颜色数据)应如下所示:
A raster of Height rows, in order from top to bottom. Each row consists of Width pixels, in order from left to right. Each pixel is a triplet of red, green, and blue samples, in that order.
但是,您正在使用循环构建具有 HEIGHT 像素的 WIDTH 行栅格。 外层循环创建一行,内层循环创建行中的像素。 更改索引将解决问题。
//switch the loop indices
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < WIDTH; j++) {
float u = float(i) / float(HEIGHT);
float v = float(j) / float(WIDTH);
...
//added spaces between each color value
//this way each pixel is separated with 2 spaces
out << ' '<< ir << ' ' << ig << ' ' << ib << ' ';
}
//and each row ends on a newline
//this makes it easier to read them with a text editor
out << '\n';
}
您遇到的情况如下: 宽度是高度的两倍,
const int WIDTH = 200;
const int HEIGHT = 100;
然后您将 200 行 100 像素写入 ppm 图像。然而,图像查看器读取 100 行 200 像素,这会导致 "mirror" 效果。 考虑下图:
B为黑色像素,R为红色像素
我试图用绘画图像来说明它,但实际上图像数据只不过是一个矩阵;)
宽度:8
身高:4
B B B B B B B B
B B B B R R R R
R R R R B B B B
B B B B B B B B
您的代码基本上是转置图像。它用高度切换宽度并写下:
B B R B
B B R B
B B R B
B B R B
B R B B
B R B B
B R B B
B R B B
所以图片 reader 是这样写的:
B B R B B B R B
B B R B B B R B
B R B B B R B B
B R B B B R B B
如果你遍历每一行,你可以看到图像在第 4 行之后被镜像。 这就是您在图像中看到的,但是由于您使用的是圆圈,因此由于对称性,图像看起来像是写了两次。你没有看到图像被颠倒了。