[FEAT] Add truncated cylinders
This commit is contained in:
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@@ -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",
|
||||||
|
|||||||
@@ -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
102
apps/chapter_13.cpp
Normal 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;
|
||||||
|
}
|
||||||
@@ -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_y0 = the_ray_origin.y() + the_t0 * the_ray_direction.y();
|
||||||
|
if ((m_minimum < the_y0) && (the_y0 < m_maximum))
|
||||||
|
{
|
||||||
the_intersections.add(Intersection(the_t0, this));
|
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));
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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) |
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user