[FEAT] Add truncated cylinders

This commit is contained in:
NADAL Jean-Baptiste
2024-03-18 18:39:25 +01:00
parent e17ac42b4a
commit cff3d78450
7 changed files with 287 additions and 4 deletions

View File

@@ -108,6 +108,7 @@
"/home/jbnadal/sources/jb/raytracer_challenge/build/apps/CMakeFiles/chapter_11_p1.dir", "/home/jbnadal/sources/jb/raytracer_challenge/build/apps/CMakeFiles/chapter_11_p1.dir",
"/home/jbnadal/sources/jb/raytracer_challenge/build/apps/CMakeFiles/chapter_11_p2.dir", "/home/jbnadal/sources/jb/raytracer_challenge/build/apps/CMakeFiles/chapter_11_p2.dir",
"/home/jbnadal/sources/jb/raytracer_challenge/build/apps/CMakeFiles/chapter_12.dir", "/home/jbnadal/sources/jb/raytracer_challenge/build/apps/CMakeFiles/chapter_12.dir",
"/home/jbnadal/sources/jb/raytracer_challenge/build/apps/CMakeFiles/chapter_13.dir",
"/home/jbnadal/sources/jb/raytracer_challenge/build/tests/CMakeFiles/raytracing_test.dir", "/home/jbnadal/sources/jb/raytracer_challenge/build/tests/CMakeFiles/raytracing_test.dir",
"/home/jbnadal/sources/jb/raytracer_challenge/build/build/_deps/catch2-src/src/CMakeFiles/Catch2.dir/catch2/benchmark", "/home/jbnadal/sources/jb/raytracer_challenge/build/build/_deps/catch2-src/src/CMakeFiles/Catch2.dir/catch2/benchmark",
"/home/jbnadal/sources/jb/raytracer_challenge/build/build/_deps/catch2-src/src/CMakeFiles/Catch2.dir/catch2/benchmark/detail", "/home/jbnadal/sources/jb/raytracer_challenge/build/build/_deps/catch2-src/src/CMakeFiles/Catch2.dir/catch2/benchmark/detail",

View File

@@ -30,3 +30,6 @@ target_link_libraries(chapter_11_p2 PRIVATE raytracing OpenMP::OpenMP_CXX)
add_executable(chapter_12 chapter_12.cpp) add_executable(chapter_12 chapter_12.cpp)
target_link_libraries(chapter_12 PRIVATE raytracing OpenMP::OpenMP_CXX) target_link_libraries(chapter_12 PRIVATE raytracing OpenMP::OpenMP_CXX)
add_executable(chapter_13 chapter_13.cpp)
target_link_libraries(chapter_13 PRIVATE raytracing OpenMP::OpenMP_CXX)

102
apps/chapter_13.cpp Normal file
View File

@@ -0,0 +1,102 @@
/*!
* chapter_13.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: 18/03/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
// Coordinate came from:
// https://github.com/sraaphorst/raytracer-kotlin/blob/main/src/main/kotlin/apps/ch13_world.kt
#include <chrono>
#include <cstdio>
#include <raytracing.h>
/* ------------------------------------------------------------------------- */
using namespace Raytracer;
using namespace std;
/* ------------------------------------------------------------------------- */
int main(void)
{
World the_world;
Camera the_camera;
Canvas the_canvas;
Plane *the_floor;
Cylinder *the_cylinder;
chrono::time_point<chrono::high_resolution_clock> the_start, the_end;
printf("Chapter 13 example.\n");
// Floor is an extremely flattened sphere with a matte texture.
the_floor = new Plane();
the_floor->set_transform(Matrix::rotation_y(0.3) *
Matrix::scaling(0.25, 0.25, 0.25));
Material &the_floor_material = the_floor->material();
the_floor_material.set_pattern(new CheckersPattern(Color(0.5, 0.5, 0.5), Color(0.75, 0.75, 0.75)));
the_floor_material.set_ambient(0.9);
the_floor_material.set_diffuse(0.9);
the_floor_material.set_specular(0);
the_world.add_object(the_floor);
// Cylinder
the_cylinder = new Cylinder();
the_cylinder->set_minimum(0);
the_cylinder->set_maximum(0.75);
the_cylinder->set_transform(Matrix::translation(-1, 0, 1) *
Matrix::scaling(0.5, 1, 0.5));
Material &the_cylinder_material = the_cylinder->material();
the_cylinder_material.set_color(Color(0, 0, 0.6));
the_cylinder_material.set_diffuse(0.1);
the_cylinder_material.set_specular(0.9);
the_cylinder_material.set_shininess(300);
the_cylinder_material.set_reflective(0.9);
the_world.add_object(the_cylinder);
// The Light source is white, shining from above and to the left
the_world.set_light(PointLight(Tuple::Point(1, 6.9, -4.9), Color(1, 1, 1)));
// Configure the camera.
// the_camera = Camera(100, 50, std::numbers::pi / 10);
the_camera = Camera(320, 200, std::numbers::pi / 10);
// the_camera = Camera(640, 480, std::numbers::pi / 10);
the_camera.show_progress_bar();
the_camera.set_transform(
Matrix::view_transform(Tuple::Point(8, 3.5, -9), Tuple::Point(0, 0.3, 0), Tuple::Vector(0, 1, 0)));
the_start = chrono::high_resolution_clock::now();
the_camera.show_progress_bar();
the_canvas = the_camera.render(the_world);
the_end = chrono::high_resolution_clock::now();
the_canvas.save_to_file("chapter13.ppm");
chrono::duration<double> the_elapsed_time = the_end - the_start;
printf("Execution Time: %f secondes\n", the_elapsed_time.count());
return 0;
}

View File

@@ -43,6 +43,7 @@ Intersections Cylinder::local_intersect(const Ray &a_ray)
{ {
double the_a, the_b, the_c, the_disc; double the_a, the_b, the_c, the_disc;
double the_t0, the_t1; double the_t0, the_t1;
double the_y0, the_y1;
Intersections the_intersections; Intersections the_intersections;
const Tuple &the_ray_direction = a_ray.direction(); const Tuple &the_ray_direction = a_ray.direction();
const Tuple &the_ray_origin = a_ray.origin(); const Tuple &the_ray_origin = a_ray.origin();
@@ -68,9 +69,22 @@ Intersections Cylinder::local_intersect(const Ray &a_ray)
the_t0 = (-the_b - std::sqrt(the_disc)) / (2 * the_a); the_t0 = (-the_b - std::sqrt(the_disc)) / (2 * the_a);
the_t1 = (-the_b + std::sqrt(the_disc)) / (2 * the_a); the_t1 = (-the_b + std::sqrt(the_disc)) / (2 * the_a);
if (the_t0 > the_t1)
{
std::swap(the_t0, the_t1);
}
the_intersections.add(Intersection(the_t0, this)); the_y0 = the_ray_origin.y() + the_t0 * the_ray_direction.y();
the_intersections.add(Intersection(the_t1, this)); if ((m_minimum < the_y0) && (the_y0 < m_maximum))
{
the_intersections.add(Intersection(the_t0, this));
}
the_y1 = the_ray_origin.y() + the_t1 * the_ray_direction.y();
if ((m_minimum < the_y1) && (the_y1 < m_maximum))
{
the_intersections.add(Intersection(the_t1, this));
}
return the_intersections; return the_intersections;
} }
@@ -79,5 +93,33 @@ Intersections Cylinder::local_intersect(const Ray &a_ray)
Tuple Cylinder::local_normal_at(const Tuple &a_local_point) const Tuple Cylinder::local_normal_at(const Tuple &a_local_point) const
{ {
return Tuple::Vector(0, 0, 0); return Tuple::Vector(a_local_point.x(), 0, a_local_point.z());
}
/* ------------------------------------------------------------------------- */
double Cylinder::minimum(void)
{
return m_minimum;
}
/* ------------------------------------------------------------------------- */
void Cylinder::set_minimum(double a_value)
{
m_minimum = a_value;
}
/* ------------------------------------------------------------------------- */
double Cylinder::maximum(void)
{
return m_maximum;
}
/* ------------------------------------------------------------------------- */
void Cylinder::set_maximum(double a_value)
{
m_maximum = a_value;
} }

View File

@@ -28,6 +28,8 @@
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
#include <cmath>
#include "shapes/shape.h" #include "shapes/shape.h"
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
@@ -40,6 +42,16 @@ namespace Raytracer
Cylinder(void) = default; Cylinder(void) = default;
Intersections local_intersect(const Ray &a_ray) override; Intersections local_intersect(const Ray &a_ray) override;
Tuple local_normal_at(const Tuple &a_local_point) const override; Tuple local_normal_at(const Tuple &a_local_point) const override;
double minimum(void);
void set_minimum(double a_value);
double maximum(void);
void set_maximum(double a_value);
private:
double m_minimum = std::numeric_limits<double>::infinity();
double m_maximum = std::numeric_limits<double>::infinity();
}; };
}; // namespace Raytracer }; // namespace Raytracer

View File

@@ -146,7 +146,7 @@ SCENARIO("A Ray misses a cube", "[features/cubes.feature]")
SCENARIO("The normal on the surface of a cube", "[features/cubes.feature]") SCENARIO("The normal on the surface of a cube", "[features/cubes.feature]")
{ {
// | origin | normal | // | point | normal |
// | point(1, 0.5, -0.8) | vector(1, 0, 0) | // | point(1, 0.5, -0.8) | vector(1, 0, 0) |
// | point(-1, -0.2, 0.9) | vector(-1, 0, 0) | // | point(-1, -0.2, 0.9) | vector(-1, 0, 0) |
// | point(-0.4, 1, -0.1) | vector(0, 1, 0) | // | point(-0.4, 1, -0.1) | vector(0, 1, 0) |

View File

@@ -44,6 +44,25 @@ public:
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
class CylinderTestNormal
{
public:
Tuple point;
Tuple normal;
};
/* ------------------------------------------------------------------------- */
class CylinderTestConstrained
{
public:
Tuple point;
Tuple direction;
uint16_t count;
};
/* ------------------------------------------------------------------------- */
SCENARIO("A Ray misses a cylinder", "[features/cylinders.feature]") SCENARIO("A Ray misses a cylinder", "[features/cylinders.feature]")
{ {
// | origin | direction | // | origin | direction |
@@ -122,3 +141,107 @@ SCENARIO("A Ray strikes a cylinder", "[features/cylinders.feature]")
} }
} }
} }
/* ------------------------------------------------------------------------- */
SCENARIO("Normal vector on a cylinder", "[features/cylinders.feature]")
{
// | point | normal |
// | point(1, 0, 0) | vector(1, 0, 0) |
// | point(0, 5, -1) | vector(0, 0, -1) |
// | point(0, -2, 1) | vector(0, 0, 1) |
// | point(-1, 1, 0) | vector(-1, 0, 0) |
CylinderTestNormal the_test[] = {
{ Tuple::Point(1, 0, 0), Tuple::Vector(1, 0, 0)},
{ Tuple::Point(0, 5, -1), Tuple::Vector(0, 0, -1)},
{ Tuple::Point(0, -2, 1), Tuple::Vector(0, 0, 1)},
{Tuple::Point(-1, 1, 0), Tuple::Vector(-1, 0, 0)}
};
GIVEN("cyl <- cylinder()")
{
Cylinder cyl;
WHEN("n <- local_normal_at(cyl,<point>)")
{
for (int i = 0; i < 3; i++)
{
Tuple p = the_test[i].point;
Tuple normal = cyl.local_normal_at(p);
THEN("n = <normal>")
{
REQUIRE(normal == the_test[i].normal);
}
}
}
}
}
/* ------------------------------------------------------------------------- */
SCENARIO("The default minimum and maximum for a cylinder", "[features/cylinders.feature]")
{
GIVEN("cyl <- cylinder()")
{
Cylinder cyl;
THEN("cym.minimum = -infinity")
{
REQUIRE(cyl.minimum() == std::numeric_limits<double>::infinity());
}
AND_THEN("cym.maximum = -infinity")
{
REQUIRE(cyl.maximum() == std::numeric_limits<double>::infinity());
}
}
}
/* ------------------------------------------------------------------------- */
SCENARIO("Intersecting a constrained cylinder", "[features/cylinders.feature]")
{
// | | point | direction | count |
// | 1 | point(0, 1.5, 0) | vector(0.1, 1, 0) | 0 |
// | 2 | point(0, 3, -5) | vector(0, 0, 1) | 0 |
// | 3 | point(0, 0, -5) | vector(0, 0, 1) | 0 |
// | 4 | point(0, 2, -5) | vector(0, 0, 1) | 0 |
// | 5 | point(0, 1, -5) | vector(0, 0, 1) | 0 |
// | 6 | point(0, 1.5, -2) | vector(0, 0, 1) | 2 |
CylinderTestConstrained the_test[] = {
{Tuple::Point(0, 1.5, 0), Tuple::Vector(0.1, 1, 0), 0},
{Tuple::Point(0, 3, -5), Tuple::Vector(0, 0, 1), 0},
{Tuple::Point(0, 0, -5), Tuple::Vector(0, 0, 1), 0},
{Tuple::Point(0, 2, -5), Tuple::Vector(0, 0, 1), 0},
{Tuple::Point(0, 1, -5), Tuple::Vector(0, 0, 1), 0},
{Tuple::Point(0, 1.5, -20), Tuple::Vector(0, 0, 1), 2}
};
GIVEN("cyl <- cylinder()")
{
Cylinder cyl;
AND_GIVEN("cyl.minimum <- 1")
{
cyl.set_minimum(1);
AND_GIVEN("cyl.maximum <- 2")
{
cyl.set_maximum(2);
AND_GIVEN("direction <- normalize(<direction>)")
{
AND_GIVEN("r <- ray(<point>, direction)")
{
WHEN("xs <- local_intersect(cyl,r)")
{
for (int i = 0; i < 6; i++)
{
Tuple direction = the_test[i].direction.normalize();
Ray r(the_test[i].point, direction);
Intersections xs = cyl.local_intersect(r);
THEN("xs.count = <count>")
{
REQUIRE(xs.count() == the_test[i].count);
}
}
}
}
}
}
}
}
}