[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_p2.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/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",
|
||||
|
||||
@@ -30,3 +30,6 @@ target_link_libraries(chapter_11_p2 PRIVATE raytracing OpenMP::OpenMP_CXX)
|
||||
|
||||
add_executable(chapter_12 chapter_12.cpp)
|
||||
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_t0, the_t1;
|
||||
double the_y0, the_y1;
|
||||
Intersections the_intersections;
|
||||
const Tuple &the_ray_direction = a_ray.direction();
|
||||
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_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_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;
|
||||
}
|
||||
@@ -79,5 +93,33 @@ Intersections Cylinder::local_intersect(const Ray &a_ray)
|
||||
|
||||
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"
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
@@ -40,6 +42,16 @@ namespace Raytracer
|
||||
Cylinder(void) = default;
|
||||
Intersections local_intersect(const Ray &a_ray) 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
|
||||
|
||||
|
||||
@@ -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]")
|
||||
{
|
||||
// | origin | normal |
|
||||
// | point | normal |
|
||||
// | point(1, 0.5, -0.8) | vector(1, 0, 0) |
|
||||
// | point(-1, -0.2, 0.9) | vector(-1, 0, 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]")
|
||||
{
|
||||
// | 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