[FEAT] Chapter 07 is done.

This commit is contained in:
NADAL Jean-Baptiste
2024-02-26 16:55:38 +01:00
parent 2681833d1d
commit d109170ec1
11 changed files with 254 additions and 6 deletions

View File

@@ -23,3 +23,6 @@ Ray Tracing: The Next Week book: http://raytracerchallenge.com/
### Chapter 06
![chapter_06](data/chapter_06.png)
### Chapter 07
![chapter_07](data/chapter_07.png)

View File

@@ -10,3 +10,6 @@ target_link_libraries(chapter_05 PRIVATE raytracing gcov)
add_executable(chapter_06 chapter_06.cpp)
target_link_libraries(chapter_06 PRIVATE raytracing gcov)
add_executable(chapter_07 chapter_07.cpp)
target_link_libraries(chapter_07 PRIVATE raytracing gcov)

108
apps/chapter_07.cpp Normal file
View File

@@ -0,0 +1,108 @@
/*!
* chapter_07.cpp
*
* Copyright (c) 2024, NADAL Jean-Baptiste. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* @Author: NADAL Jean-Baptiste
* @Date: 26/02/2024
*
*/
// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
#include <cstdio>
#include <raytracing.h>
/* ------------------------------------------------------------------------- */
using namespace Raytracer;
/* ------------------------------------------------------------------------- */
int main(void)
{
World the_world;
Camera the_camera;
Canvas the_canvas;
Sphere *the_floor, *the_left_wall, *the_right_wall;
Sphere *the_middle, *the_right, *the_left;
printf("Chapter 07 example.\n");
// Floor is an extremely flattened sphere with a matte texture.
the_floor = new Sphere();
the_floor->set_transform(Matrix::scaling(10, 0.01, 10));
the_floor->material().set_color(Color(1, 0.9, 0.9));
the_floor->material().set_specular(0);
the_world.add_object(the_floor);
// The Wall on the left has the same scale and color as the floor,
// But is also rotated and translated into place.
the_left_wall = new Sphere();
the_left_wall->set_transform(Matrix::translation(0, 0, 5) * Matrix::rotation_y(-std::numbers::pi / 4) *
Matrix::rotation_x(std::numbers::pi / 2) * Matrix::scaling(10, 0.01, 10));
the_left_wall->set_material(the_floor->material());
the_world.add_object(the_left_wall);
// The Wall on the right is identical to the left wall, but is rotated the opposite direction in y.
the_right_wall = new Sphere();
the_right_wall->set_transform(Matrix::translation(0, 0, 5) * Matrix::rotation_y(std::numbers::pi / 4) *
Matrix::rotation_x(std::numbers::pi / 2) * Matrix::scaling(10, 0.01, 10));
the_right_wall->set_material(the_floor->material());
the_world.add_object(the_right_wall);
// The large sphere in the middle is a unit sphere, translated upward slightly and colored green.
the_middle = new Sphere();
the_middle->set_transform(Matrix::translation(-0.5, 1, 0.5));
the_middle->material().set_color(Color(0.1, 1, 0.5));
the_middle->material().set_diffuse(0.7);
the_middle->material().set_specular(0.3);
the_world.add_object(the_middle);
// The smaller green sphere on the right is scaled in half
the_right = new Sphere();
the_right->set_transform(Matrix::translation(1.5, 0.5, -0.5) * Matrix::scaling(0.5, 0.5, 0.5));
the_right->material().set_color(Color(0.5, 1, 0.1));
the_right->material().set_diffuse(0.7);
the_right->material().set_specular(0.3);
the_world.add_object(the_right);
// The smallest sphere is scaled by a third, before being translated
the_left = new Sphere();
the_left->set_transform(Matrix::translation(-1.5, 0.33, -0.75) * Matrix::scaling(0.33, 0.33, 0.33));
the_left->material().set_color(Color(1, 0.8, 0.1));
the_left->material().set_diffuse(0.7);
the_left->material().set_specular(0.3);
the_world.add_object(the_left);
// The Light source is white, shining from above and to the left
the_world.set_light(PointLight(Tuple::Point(-10, 10, -10), Color(1, 1, 1)));
// Configure the camera.
// the_camera = Camera(100, 50, std::numbers::pi / 2);
the_camera = Camera(320, 200, std::numbers::pi / 2);
the_camera.set_transform(
Matrix::view_transform(Tuple::Point(0, 1.5, -5), Tuple::Point(0, 1, 0), Tuple::Vector(0, 1, 0)));
the_canvas = the_camera.render(the_world);
the_canvas.save_to_file("chapter07.ppm");
return 0;
}

BIN
data/chapter_07.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -36,6 +36,12 @@ using namespace Raytracer;
/* ------------------------------------------------------------------------- */
Camera::Camera(void) : m_h_size(0), m_v_size(0), m_field_of_view(0), m_half_width(0), m_half_height(0), m_pixel_size(0)
{
}
/* ------------------------------------------------------------------------- */
Camera::Camera(uint16_t a_h_size, uint16_t a_v_size, double a_field_of_view) :
m_h_size(a_h_size),
m_v_size(a_v_size),
@@ -65,6 +71,39 @@ Camera::Camera(uint16_t a_h_size, uint16_t a_v_size, double a_field_of_view) :
/* ------------------------------------------------------------------------- */
Camera::Camera(const Camera &an_other)
{
m_h_size = an_other.m_h_size;
m_v_size = an_other.m_v_size;
m_field_of_view = an_other.m_field_of_view;
m_transform = an_other.m_transform;
m_half_width = an_other.m_half_width;
m_half_height = an_other.m_half_height;
m_pixel_size = an_other.m_pixel_size;
}
/* ------------------------------------------------------------------------- */
const Camera &Camera::operator=(const Camera &an_other)
{
if (this == &an_other)
{
return *this;
}
m_h_size = an_other.m_h_size;
m_v_size = an_other.m_v_size;
m_field_of_view = an_other.m_field_of_view;
m_transform = an_other.m_transform;
m_half_width = an_other.m_half_width;
m_half_height = an_other.m_half_height;
m_pixel_size = an_other.m_pixel_size;
return *this;
}
/* ------------------------------------------------------------------------- */
uint16_t Camera::hsize(void) const
{
return m_h_size;
@@ -132,3 +171,22 @@ Ray Camera::ray_for_pixel(double an_x, double an_y) const
return Ray(the_origin, the_direction);
}
/* ------------------------------------------------------------------------- */
Canvas Camera::render(const World &a_world)
{
Canvas the_image(m_h_size, m_v_size);
for (int y = 0; y < m_v_size - 1; y++)
{
for (int x = 0; x < m_h_size - 1; x++)
{
Ray the_ray = ray_for_pixel(x, y);
Color the_color = a_world.color_at(the_ray);
the_image.write_pixel(x, y, the_color);
}
}
return the_image;
}

View File

@@ -30,8 +30,10 @@
#include <cstdint>
#include "canvas.h"
#include "matrix.h"
#include "ray.h"
#include "world.h"
/* ------------------------------------------------------------------------- */
@@ -40,7 +42,11 @@ namespace Raytracer
class Camera
{
public:
Camera(void);
Camera(uint16_t a_h_size, uint16_t a_v_size, double a_field_of_view);
Camera(const Camera &a_nother);
const Camera &operator=(const Camera &an_other);
uint16_t hsize(void) const;
uint16_t vsize(void) const;
@@ -53,6 +59,8 @@ namespace Raytracer
Ray ray_for_pixel(double an_x, double an_y) const;
Canvas render(const World &a_world);
private:
uint16_t m_h_size;

View File

@@ -38,6 +38,12 @@ using namespace Raytracer;
/* ------------------------------------------------------------------------- */
Canvas::Canvas(void) : m_width(0), m_height(0)
{
}
/* ------------------------------------------------------------------------- */
Canvas::Canvas(uint16_t a_width, uint16_t a_height) : m_width(a_width), m_height(a_height)
{
m_pixels = std::vector<std::vector<Color>>(m_width, std::vector<Color>(m_height));
@@ -53,6 +59,26 @@ Canvas::Canvas(uint16_t a_width, uint16_t a_height) : m_width(a_width), m_height
/* ------------------------------------------------------------------------- */
Canvas::Canvas(const Canvas &a_canvas) :
m_width(a_canvas.m_width),
m_height(a_canvas.m_height),
m_pixels(a_canvas.m_pixels)
{
}
/* ------------------------------------------------------------------------- */
Canvas &Canvas::operator=(const Canvas &a_canvas)
{
m_width = a_canvas.m_width;
m_height = a_canvas.m_height;
m_pixels = a_canvas.m_pixels;
return *this;
}
/* ------------------------------------------------------------------------- */
uint16_t Canvas::width(void) const
{
return m_width;

View File

@@ -42,7 +42,11 @@ namespace Raytracer
class Canvas
{
public:
Canvas(void);
Canvas(uint16_t a_width, uint16_t a_height);
Canvas(const Canvas &a_canvas);
Canvas &operator=(const Canvas &a_canvas);
uint16_t width(void) const;
uint16_t height(void) const;

View File

@@ -140,7 +140,7 @@ bool World::contains(const Shape &a_shape)
/* ------------------------------------------------------------------------- */
Intersections World::intersect_world(const Ray &a_ray)
Intersections World::intersect_world(const Ray &a_ray) const
{
Intersections the_all_intersec;
@@ -155,7 +155,7 @@ Intersections World::intersect_world(const Ray &a_ray)
/* ------------------------------------------------------------------------- */
Color World::shade_hit(const IntersectionData &an_intersection_data)
Color World::shade_hit(const IntersectionData &an_intersection_data) const
{
return an_intersection_data.object().material().lighting(
m_light, an_intersection_data.point(), an_intersection_data.eyev(), an_intersection_data.normalv());
@@ -163,7 +163,7 @@ Color World::shade_hit(const IntersectionData &an_intersection_data)
/* ------------------------------------------------------------------------- */
Color World::color_at(const Ray &a_ray)
Color World::color_at(const Ray &a_ray) const
{
Color the_color = Color::Black();

View File

@@ -60,9 +60,9 @@ namespace Raytracer
bool contains(const Shape &a_shape);
Intersections intersect_world(const Ray &a_ray);
Color shade_hit(const IntersectionData &an_intersection_data);
Color color_at(const Ray &a_ray);
Intersections intersect_world(const Ray &a_ray) const;
Color shade_hit(const IntersectionData &an_intersection_data) const;
Color color_at(const Ray &a_ray) const;
static World default_world(void);

View File

@@ -635,3 +635,41 @@ SCENARIO("Constructing a ray when the camera is transformed", "[features/camera.
}
}
}
/* ------------------------------------------------------------------------- */
SCENARIO("Rendering a world with the camera", "[features/camera.feature]")
{
GIVEN("w <- default_world()")
{
World w = World::default_world();
AND_GIVEN("c <- camera(11, 11, pi/2)")
{
Camera c(11, 11, std::numbers::pi / 2);
AND_GIVEN("from <- point(0, 0, -5)")
{
Tuple from = Tuple::Point(0, 0, -5);
AND_GIVEN("to <- point(0, 0, 0)")
{
Tuple to = Tuple::Point(0, 0, 0);
AND_GIVEN("up <- vector(0, 1, 0)")
{
Tuple up = Tuple::Vector(0, 1, 0);
AND_GIVEN("c.transform <- view_transform(from, to, up)")
{
c.set_transform(Matrix::view_transform(from, to, up));
WHEN("image <- c.render(w)")
{
Canvas image = c.render(w);
THEN("pixel_at(image, 5, 5) = color(0.38066, 0.47583, 0.2855)")
{
REQUIRE(image.pixel_at(5, 5) == Color(0.38066, 0.47583, 0.2855));
}
}
}
}
}
}
}
}
}