[WIP] world is wip

This commit is contained in:
2024-02-22 00:23:01 +01:00
parent 288f26358e
commit 6acf43540f
9 changed files with 431 additions and 6 deletions

View File

@@ -10,8 +10,9 @@ add_definitions(--coverage)
add_library(raytracing
src/canvas.cpp
src/common.cpp
src/color.cpp
src/common.cpp
src/intersection-data.cpp
src/intersection.cpp
src/intersections.cpp
src/material.cpp

View File

@@ -28,6 +28,7 @@
#include "canvas.h"
#include "color.h"
#include "common.h"
#include "intersection-data.h"
#include "intersection.h"
#include "intersections.h"
#include "material.h"

View File

@@ -0,0 +1,131 @@
/*!
* intersection-data.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: 21/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 "intersection-data.h"
using namespace Raytracer;
/* ------------------------------------------------------------------------- */
IntersectionData::IntersectionData(void) : m_is_inside(false), m_distance(0)
{
}
/* ------------------------------------------------------------------------- */
double IntersectionData::distance_t(void) const
{
return m_distance;
}
/* ------------------------------------------------------------------------- */
void IntersectionData::set_distance_t(double a_value)
{
m_distance = a_value;
}
/* ------------------------------------------------------------------------- */
const Shape &IntersectionData::object(void) const
{
return m_shape;
}
/* ------------------------------------------------------------------------- */
void IntersectionData::set_object(const Shape &a_shape)
{
m_shape = a_shape;
}
/* ------------------------------------------------------------------------- */
const Tuple &IntersectionData::point(void) const
{
return m_point;
}
/* ------------------------------------------------------------------------- */
void IntersectionData::set_point(const Tuple &a_point)
{
m_point = a_point;
}
/* ------------------------------------------------------------------------- */
const Tuple &IntersectionData::eyev(void) const
{
return m_eyev;
}
/* ------------------------------------------------------------------------- */
void IntersectionData::set_eyev(const Tuple &an_eyev)
{
m_eyev = an_eyev;
}
/* ------------------------------------------------------------------------- */
const Tuple &IntersectionData::normalv(void) const
{
return m_normalv;
}
/* ------------------------------------------------------------------------- */
void IntersectionData::set_normalv(const Tuple &a_normalv)
{
m_normalv = a_normalv;
}
/* ------------------------------------------------------------------------- */
const bool IntersectionData::is_inside(void) const
{
return m_is_inside;
}
/* ------------------------------------------------------------------------- */
void IntersectionData::set_inside(void)
{
if (m_normalv.dot(m_eyev) < 0)
{
m_is_inside = true;
m_normalv = -m_normalv;
}
else
{
m_is_inside = false;
}
}

View File

@@ -0,0 +1,73 @@
/*!
* intersection-data.h
*
* 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: 21/02/2024
*
*/
#ifndef _RAYTRACER_INTERSECTION_DATA_H
#define _RAYTRACER_INTERSECTION_DATA_H
/* ------------------------------------------------------------------------- */
#include "shape.h"
#include "tuple.h"
/* ------------------------------------------------------------------------- */
namespace Raytracer
{
/* ------------------------------------------------------------------------- */
class IntersectionData
{
public:
IntersectionData(void);
double distance_t(void) const;
void set_distance_t(double a_value);
const Shape &object(void) const;
void set_object(const Shape &a_shape);
const Tuple &point(void) const;
void set_point(const Tuple &a_point);
const Tuple &eyev(void) const;
void set_eyev(const Tuple &an_eyev);
const Tuple &normalv(void) const;
void set_normalv(const Tuple &a_normalv);
const bool is_inside(void) const;
void set_inside(void);
private:
bool m_is_inside;
double m_distance;
Shape m_shape; // TODO ??
Tuple m_point;
Tuple m_eyev;
Tuple m_normalv;
};
}; // namespace Raytracer
#endif /* _RAYTRACER_INTERSECTION_DATA_H */

View File

@@ -159,3 +159,22 @@ bool Intersection::is_defined(void)
{
return !m_is_nothing;
}
/* ------------------------------------------------------------------------- */
IntersectionData Intersection::prepare_computations(Ray a_ray) const
{
IntersectionData the_data;
// Copy intersections properties for conveniance
the_data.set_distance_t(m_distance_t);
the_data.set_object(m_shape);
// Precompute some useful values
the_data.set_point(a_ray.position(m_distance_t));
the_data.set_eyev(-a_ray.direction());
the_data.set_normalv(m_shape.normal_at(the_data.point()));
the_data.set_inside();
return the_data;
}

View File

@@ -28,6 +28,7 @@
/* ------------------------------------------------------------------------- */
#include "intersection-data.h"
#include "shape.h"
/* ------------------------------------------------------------------------- */
@@ -59,6 +60,8 @@ namespace Raytracer
bool is_nothing(void);
bool is_defined(void);
IntersectionData prepare_computations(Ray a_ray) const;
private:
bool m_is_nothing;
double m_distance_t;

View File

@@ -117,6 +117,13 @@ void World::add_object(Shape *a_shape)
/* ------------------------------------------------------------------------- */
Shape *World::objects(uint32_t an_index)
{
return m_objects[an_index];
}
/* ------------------------------------------------------------------------- */
bool World::contains(const Shape &a_shape)
{
@@ -148,14 +155,23 @@ Intersections World::intersect_world(const Ray &a_ray)
/* ------------------------------------------------------------------------- */
Color World::shade_hit(const IntersectionData &an_intersection_data)
{
return an_intersection_data.object().material().lighting(
m_light, an_intersection_data.point(), an_intersection_data.eyev(), an_intersection_data.normalv());
}
/* ------------------------------------------------------------------------- */
World World::default_world(void)
{
World the_world;
Sphere *s1, *s2;
the_world.set_light(PointLight(Tuple::Point(-10, 10, 10), Color::White()));
the_world.set_light(PointLight(Tuple::Point(-10, 10, -10), Color::White()));
s1 = new Sphere;
Material &the_s1_mat = s1->material();
the_s1_mat.set_color(Color(0.8, 1.0, 0.6));
the_s1_mat.set_diffuse(0.7);

View File

@@ -56,9 +56,12 @@ namespace Raytracer
void set_light(const PointLight &a_light);
void add_object(Shape *a_shape);
Shape *objects(uint32_t an_index);
bool contains(const Shape &a_shape);
Intersections intersect_world(const Ray &a_ray);
Color shade_hit(const IntersectionData &an_intersection_data);
static World default_world(void);

View File

@@ -53,9 +53,9 @@ SCENARIO("Creating a world", "[features/world.feature]")
SCENARIO("The default world", "[features/world.feature]")
{
GIVEN("light <- point_light(point(-10, 10, 10), color(1, 1, 1))")
GIVEN("light <- point_light(point(-10, 10, -10), color(1, 1, 1))")
{
PointLight light = PointLight(Tuple::Point(-10, 10, 10), Color(1, 1, 1));
PointLight light = PointLight(Tuple::Point(-10, 10, -10), Color(1, 1, 1));
AND_GIVEN("s1 <- sphere")
// with:
// | material.color | (0.8, 1.0, 0.6) |
@@ -130,3 +130,181 @@ SCENARIO("Intersect a world with a ray", "[features/world.feature]")
}
}
}
/* ------------------------------------------------------------------------- */
SCENARIO("Precompute the state of an intersection", "[features/intersections.feature]")
{
GIVEN("r <- ray(point(0, 0, -5), vector(0, 0, 1))")
{
Ray r(Tuple::Point(0, 0, -5), Tuple::Vector(0, 0, 1));
AND_GIVEN("shape <- sphere()")
{
Sphere shape;
AND_GIVEN("i <- intersection(4, shape)")
{
Intersection i(4, shape);
WHEN("comps <- prepare_computations(i,r)")
{
IntersectionData comps = i.prepare_computations(r);
THEN("comps.t = i.t")
{
REQUIRE(comps.distance_t() == i.distance_t());
}
AND_THEN("comps.object = i.object")
{
REQUIRE(comps.object() == i.object());
}
AND_THEN("comps.point = point(0, 0, -1)")
{
REQUIRE(comps.point() == Tuple::Point(0, 0, -1));
}
AND_THEN("comps.eyev = vector(0, 0, -1)")
{
REQUIRE(comps.eyev() == Tuple::Vector(0, 0, -1));
}
AND_THEN("comps.normalv = vector(0, 0, -1)")
{
REQUIRE(comps.normalv() == Tuple::Vector(0, 0, -1));
}
}
}
}
}
}
/* ------------------------------------------------------------------------- */
SCENARIO("The hit, when an intersection occurs on the outside", "[features/intersections.feature]")
{
GIVEN("r <- ray(point(0, 0, -5), vector(0, 0, 1))")
{
Ray r(Tuple::Point(0, 0, -5), Tuple::Vector(0, 0, 1));
AND_GIVEN("shape <- sphere()")
{
Sphere shape;
AND_GIVEN("i <- intersection(4, shape)")
{
Intersection i(4, shape);
WHEN("comps <- prepare_computations(i,r)")
{
IntersectionData comps = i.prepare_computations(r);
THEN("comps.inside = false")
{
REQUIRE(comps.is_inside() == false);
}
}
}
}
}
}
/* ------------------------------------------------------------------------- */
SCENARIO("The hit, when an intersection occurs on the inside", "[features/intersections.feature]")
{
GIVEN("r <- ray(point(0, 0, 0), vector(0, 0, 1))")
{
Ray r(Tuple::Point(0, 0, 0), Tuple::Vector(0, 0, 1));
AND_GIVEN("shape <- sphere()")
{
Sphere shape;
AND_GIVEN("i <- intersection(1, shape)")
{
Intersection i(1, shape);
WHEN("comps <- prepare_computations(i,r)")
{
IntersectionData comps = i.prepare_computations(r);
THEN("comps.point = point(0, 0, 1)")
{
REQUIRE(comps.point() == Tuple::Point(0, 0, 1));
}
AND_THEN("comps.eyev = vector(0, 0, -1)")
{
REQUIRE(comps.eyev() == Tuple::Vector(0, 0, -1));
}
AND_THEN("comps.inside = false")
{
REQUIRE(comps.is_inside() == true);
}
AND_THEN("comps.normalv = vector(0, 0, -1)")
{
REQUIRE(comps.normalv() == Tuple::Vector(0, 0, -1));
}
}
}
}
}
}
/* ------------------------------------------------------------------------- */
SCENARIO("Shading an intersection", "[features/world.feature]")
{
GIVEN("w <- default_world()")
{
World w = World::default_world();
AND_GIVEN("r <- ray(point(0, 0, -5), vector(0, 0, 1))")
{
Ray r(Tuple::Point(0, 0, -5), Tuple::Vector(0, 0, 1));
AND_GIVEN("shape <- first object of w")
{
Shape *shape = w.objects(0);
AND_GIVEN("i <- intersection(4, shape)")
{
Intersection i(4, *shape);
WHEN("comps <- prepare_computations(i,r)")
{
IntersectionData comps = i.prepare_computations(r);
AND_WHEN("c <- shade_hit(w, comps)")
{
Color c = w.shade_hit(comps);
THEN("c = color(0.38066, 0.47583, 0.2855)")
{
REQUIRE(c == Color(0.38066, 0.47583, 0.2855));
}
}
}
}
}
}
}
}
/* ------------------------------------------------------------------------- */
SCENARIO("Shading an intersection from the inside", "[features/world.feature]")
{
GIVEN("w <- default_world()")
{
World w = World::default_world();
AND_GIVEN("w.light <- point_light(point(0, 0.25, 0))")
{
w.set_light(PointLight(Tuple::Point(0, 0.25, 0), Color(1, 1, 1)));
AND_GIVEN("r <- ray(point(0, 0, 0), vector(0, 0, 1))")
{
Ray r(Tuple::Point(0, 0, 0), Tuple::Vector(0, 0, 1));
AND_GIVEN("shape <- second object of w")
{
Shape *shape = w.objects(1);
AND_GIVEN("i <- intersection(0.5, shape)")
{
Intersection i(0.5, *shape);
WHEN("comps <- prepare_computations(i,r)")
{
IntersectionData comps = i.prepare_computations(r);
AND_WHEN("c <- shade_hit(w, comps)")
{
Color c = w.shade_hit(comps);
THEN("c = color(0.90498, 0.90498, 0.90498)")
{
REQUIRE(c == Color(0.90498, 0.90498, 0.90498));
}
}
}
}
}
}
}
}
}