[ADD] ray_for_pixel to camera

This commit is contained in:
2024-02-25 23:47:40 +01:00
parent 11890d6273
commit 45ff10ecc8
5 changed files with 123 additions and 8 deletions

View File

@@ -40,9 +40,11 @@ Camera::Camera(uint16_t a_h_size, uint16_t a_v_size, double a_field_of_view) :
m_h_size(a_h_size), m_h_size(a_h_size),
m_v_size(a_v_size), m_v_size(a_v_size),
m_field_of_view(a_field_of_view), m_field_of_view(a_field_of_view),
m_transform(Matrix::identity()),
m_half_width(0), m_half_width(0),
m_half_height(0), m_half_height(0),
m_pixel_size(0) m_pixel_size(0)
{ {
double the_half_view = tan(m_field_of_view / 2); double the_half_view = tan(m_field_of_view / 2);
double the_aspect_ratio = (double)m_h_size / m_v_size; double the_aspect_ratio = (double)m_h_size / m_v_size;
@@ -57,7 +59,8 @@ Camera::Camera(uint16_t a_h_size, uint16_t a_v_size, double a_field_of_view) :
m_half_width = the_half_view * the_aspect_ratio; m_half_width = the_half_view * the_aspect_ratio;
m_half_height = the_half_view; m_half_height = the_half_view;
} }
m_pixel_size = ceil(((m_half_width * 2) / m_h_size) * 100) / 100; // m_pixel_size = ceil(((m_half_width * 2) / m_h_size) * 100) / 100;
m_pixel_size = (m_half_width * 2) / m_h_size;
} }
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
@@ -83,6 +86,13 @@ double Camera::field_of_view(void) const
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
void Camera::set_transform(const Matrix &a_matrix)
{
m_transform = a_matrix;
}
/* ------------------------------------------------------------------------- */
Matrix &Camera::transform(void) Matrix &Camera::transform(void)
{ {
return m_transform; return m_transform;
@@ -92,5 +102,33 @@ Matrix &Camera::transform(void)
double Camera::pixel_size(void) double Camera::pixel_size(void)
{ {
return m_pixel_size; return ceil(((m_half_width * 2) / m_h_size) * 100) / 100;
}
/* ------------------------------------------------------------------------- */
Ray Camera::ray_for_pixel(double an_x, double an_y) const
{
double the_xoffset, the_yoffset;
double the_world_x, the_world_y;
Tuple the_pixel, the_origin, the_direction;
// the offset from the edge of the canvas to the pixel center
the_xoffset = (an_x + 0.5) * m_pixel_size;
the_yoffset = (an_y + 0.5) * m_pixel_size;
// the untransformed coordinates of the pixel in the world space
// (remember that the camera looks toward -z, so +x is to the *left*)
the_world_x = m_half_width - the_xoffset;
the_world_y = m_half_height - the_yoffset;
// using the camera matrix, transform the canvas point and the origin,
// and then compute the ray direction vector.
// (remember that the canvas is at z=-1)
Matrix the_invert = m_transform.inverse();
the_pixel = the_invert * Tuple::Point(the_world_x, the_world_y, -1);
the_origin = the_invert * Tuple::Point(0, 0, 0);
the_direction = (the_pixel - the_origin).normalize();
return Ray(the_origin, the_direction);
} }

View File

@@ -31,6 +31,7 @@
#include <cstdint> #include <cstdint>
#include "matrix.h" #include "matrix.h"
#include "ray.h"
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
@@ -44,10 +45,14 @@ namespace Raytracer
uint16_t hsize(void) const; uint16_t hsize(void) const;
uint16_t vsize(void) const; uint16_t vsize(void) const;
double field_of_view(void) const; double field_of_view(void) const;
void set_transform(const Matrix &a_matrix);
Matrix &transform(void); Matrix &transform(void);
double pixel_size(void); double pixel_size(void);
Ray ray_for_pixel(double an_x, double an_y) const;
private: private:
uint16_t m_h_size; uint16_t m_h_size;

View File

@@ -38,10 +38,9 @@
bool Raytracer::double_equal(double a, double b) bool Raytracer::double_equal(double a, double b)
{ {
if ((std::abs((a) - (b)) < kEpsilon)) #if 0
{ double the_fabs = std::fabs(a - b);
return true; bool the_test = the_fabs < kEpsilon;
} #endif
return std::fabs(a - b) < kEpsilon;
return false;
} }

View File

@@ -72,6 +72,13 @@ Tuple::Tuple(std::vector<double> a_data) : m_x(0.0), m_y(0.0), m_z(0.0), m_w(0.0
bool Tuple::operator==(const Tuple &an_other) const bool Tuple::operator==(const Tuple &an_other) const
{ {
#if 0
bool the_eqx = double_equal(m_x, an_other.m_x);
bool the_eqy = double_equal(m_y, an_other.m_y);
bool the_eqz = double_equal(m_z, an_other.m_z);
bool the_eqw = double_equal(m_w, an_other.m_w);
#endif
if (double_equal(m_x, an_other.m_x) && double_equal(m_y, an_other.m_y) && double_equal(m_z, an_other.m_z) && if (double_equal(m_x, an_other.m_x) && double_equal(m_y, an_other.m_y) && double_equal(m_z, an_other.m_z) &&
double_equal(m_w, an_other.m_w)) double_equal(m_w, an_other.m_w))
{ {

View File

@@ -569,3 +569,69 @@ SCENARIO("The pixel size for a vertical canvas", "[features/camera.feature]")
} }
} }
} }
/* ------------------------------------------------------------------------- */
SCENARIO("Constructing a ray through the center of the canvas", "[features/camera.feature]")
{
GIVEN("c <- camera(201, 101, pi/2)")
{
Camera c(201, 101, std::numbers::pi / 2);
WHEN("r <- ray_for_pixel(c, 100, 50)")
{
Ray r = c.ray_for_pixel(100, 50);
THEN("r.origin = point(0, 0, 0)")
{
REQUIRE(r.origin() == Tuple::Point(0, 0, 0));
}
}
}
}
/* ------------------------------------------------------------------------- */
SCENARIO("Constructing a ray through a corner of the canvas", "[features/camera.feature]")
{
GIVEN("c <- camera(201, 101, pi/2)")
{
Camera c(201, 101, std::numbers::pi / 2);
WHEN("r <- ray_for_pixel(c, 0, 0)")
{
Ray r = c.ray_for_pixel(0, 0);
THEN("r.origin = point(0, 0, 0)")
{
REQUIRE(r.origin() == Tuple::Point(0, 0, 0));
}
AND_THEN("r.direction = vector(0.66519, 0.33259, -0.66851)")
{
REQUIRE(r.direction() == Tuple::Vector(0.66519, 0.33259, -0.66851));
}
}
}
}
/* ------------------------------------------------------------------------- */
SCENARIO("Constructing a ray when the camera is transformed", "[features/camera.feature]")
{
GIVEN("c <- camera(201, 101, pi/2)")
{
Camera c(201, 101, std::numbers::pi / 2);
WHEN("c.transform <- rotation_y(pi/4) * translation(0, -2, 5)")
{
c.set_transform(Matrix::rotation_y(std::numbers::pi / 4) * Matrix::translation(0, -2, 5));
AND_WHEN("r <- ray_for_pixel(c, 100, 50)")
{
Ray r = c.ray_for_pixel(100, 50);
THEN("r.origin = point(0, 2, -5)")
{
REQUIRE(r.origin() == Tuple::Point(0, 2, -5));
}
AND_THEN("r.direction = vector(sqrt(2) / 2, 0, -sqrt(2) / 2)")
{
REQUIRE(r.direction() == Tuple::Vector(sqrt(2) / 2, 0, -sqrt(2) / 2));
}
}
}
}
}