Add a super special Camera from a well known constructor. Can take picture up to Infinite TeraPixel!
This commit is contained in:
74
source/camera.cpp
Normal file
74
source/camera.cpp
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Camera implementation
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <matrix.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <ray.h>
|
||||||
|
#include <camera.h>
|
||||||
|
|
||||||
|
Camera::Camera(uint32_t hsize, uint32_t vsize, double fov) : verticalSize(vsize), horizontalSize(hsize), fieldOfView(fov)
|
||||||
|
{
|
||||||
|
double aspectRatio = (double)hsize / (double)vsize;
|
||||||
|
double halfView = tan(fov / 2.0);
|
||||||
|
|
||||||
|
if (aspectRatio >= 1)
|
||||||
|
{
|
||||||
|
this->halfWidth = halfView;
|
||||||
|
this->halfHeight = halfView / aspectRatio;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->halfWidth = halfView * aspectRatio;
|
||||||
|
this->halfHeight = halfView;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->pixelSize = (this->halfWidth * 2) / this->horizontalSize;
|
||||||
|
|
||||||
|
this->setTransform(Matrix4().identity());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::setTransform(Matrix transform)
|
||||||
|
{
|
||||||
|
this->transformMatrix = transform;
|
||||||
|
this->inverseTransform = transform.inverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ray Camera::rayForPixel(uint32_t pixelX, uint32_t pixelY)
|
||||||
|
{
|
||||||
|
double xOffset = ((double)pixelX + 0.5) * this->pixelSize;
|
||||||
|
double yOffset = ((double)pixelY + 0.5) * this->pixelSize;
|
||||||
|
|
||||||
|
double worldX = this->halfWidth - xOffset;
|
||||||
|
double worldY = this->halfHeight - yOffset;
|
||||||
|
|
||||||
|
Tuple pixel = this->inverseTransform * Point(worldX, worldY, -1);
|
||||||
|
Tuple origin = this->inverseTransform * Point(0, 0, 0);
|
||||||
|
Tuple direction = (pixel - origin).normalise();
|
||||||
|
|
||||||
|
return Ray(origin, direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas Camera::render(World world)
|
||||||
|
{
|
||||||
|
uint32_t x, y;
|
||||||
|
Canvas image = Canvas(this->horizontalSize, this->verticalSize);
|
||||||
|
|
||||||
|
for(y = 0; y < this->verticalSize; y++)
|
||||||
|
{
|
||||||
|
for(x = 0; x < this->horizontalSize; x++)
|
||||||
|
{
|
||||||
|
Ray r = this->rayForPixel(x, y);
|
||||||
|
Tuple colour = world.colourAt(r);
|
||||||
|
image.putPixel(x, y, colour);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
38
source/include/camera.h
Normal file
38
source/include/camera.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Camera header
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef DORAYME_CAMERA_H
|
||||||
|
#define DORAYME_CAMERA_H
|
||||||
|
|
||||||
|
#include <matrix.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <ray.h>
|
||||||
|
#include <canvas.h>
|
||||||
|
#include <world.h>
|
||||||
|
|
||||||
|
class Camera
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
double halfWidth;
|
||||||
|
double halfHeight;
|
||||||
|
public:
|
||||||
|
uint32_t verticalSize;
|
||||||
|
uint32_t horizontalSize;
|
||||||
|
double fieldOfView;
|
||||||
|
double pixelSize;
|
||||||
|
Matrix transformMatrix;
|
||||||
|
Matrix inverseTransform;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Camera(uint32_t hsize, uint32_t vsize, double fov);
|
||||||
|
void setTransform(Matrix transform);
|
||||||
|
Ray rayForPixel(uint32_t pixelX, uint32_t pixelY);
|
||||||
|
Canvas render(World w);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DORAYME_CAMERA_H */
|
||||||
@@ -4,7 +4,7 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
|
|||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
set(TESTS_SRC tuple_test.cpp colour_test.cpp canvas_test.cpp matrix_test.cpp transformation_test.cpp ray_test.cpp
|
set(TESTS_SRC tuple_test.cpp colour_test.cpp canvas_test.cpp matrix_test.cpp transformation_test.cpp ray_test.cpp
|
||||||
intersect_test.cpp sphere_test.cpp light_test.cpp material_test.cpp world_test.cpp)
|
intersect_test.cpp sphere_test.cpp light_test.cpp material_test.cpp world_test.cpp camera_test.cpp)
|
||||||
|
|
||||||
add_executable(testMyRays)
|
add_executable(testMyRays)
|
||||||
target_include_directories(testMyRays PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
|
target_include_directories(testMyRays PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
|
||||||
@@ -28,5 +28,11 @@ target_include_directories(ch6_test PUBLIC ../source/include)
|
|||||||
target_sources(ch6_test PRIVATE ch6_test.cpp)
|
target_sources(ch6_test PRIVATE ch6_test.cpp)
|
||||||
target_link_libraries(ch6_test rayonnement)
|
target_link_libraries(ch6_test rayonnement)
|
||||||
|
|
||||||
|
add_executable(ch7_test)
|
||||||
|
target_include_directories(ch6_test PUBLIC ../source/include)
|
||||||
|
target_sources(ch7_test PRIVATE ch7_test.cpp)
|
||||||
|
target_link_libraries(ch7_test rayonnement)
|
||||||
|
|
||||||
add_test(NAME Chapter05_Test COMMAND $<TARGET_FILE:ch5_test>)
|
add_test(NAME Chapter05_Test COMMAND $<TARGET_FILE:ch5_test>)
|
||||||
add_test(NAME Chapter06_Test COMMAND $<TARGET_FILE:ch6_test>)
|
add_test(NAME Chapter06_Test COMMAND $<TARGET_FILE:ch6_test>)
|
||||||
|
add_test(NAME Chapter07_Test COMMAND $<TARGET_FILE:ch7_test>)
|
||||||
111
tests/camera_test.cpp
Normal file
111
tests/camera_test.cpp
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Camera unit tests
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <camera.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <math_helper.h>
|
||||||
|
#include <matrix.h>
|
||||||
|
#include <tuple.h>
|
||||||
|
#include <ray.h>
|
||||||
|
#include <world.h>
|
||||||
|
#include <canvas.h>
|
||||||
|
#include <colour.h>
|
||||||
|
#include <worldbuilder.h>
|
||||||
|
#include <transformation.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(CameraTest, Constructing_a_camera)
|
||||||
|
{
|
||||||
|
uint32_t hsize = 160;
|
||||||
|
uint32_t vsize = 120;
|
||||||
|
double field_of_view = M_PI / 2;
|
||||||
|
|
||||||
|
Camera c = Camera(hsize, vsize, field_of_view);
|
||||||
|
|
||||||
|
ASSERT_EQ(c.horizontalSize, 160);
|
||||||
|
ASSERT_EQ(c.verticalSize, 120);
|
||||||
|
ASSERT_TRUE(double_equal(c.fieldOfView, M_PI / 2));
|
||||||
|
ASSERT_EQ(c.transformMatrix, Matrix4().identity());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CameraTest, Pixel_size_for_a_horizontal_canvas)
|
||||||
|
{
|
||||||
|
Camera c = Camera(200, 125, M_PI / 2);
|
||||||
|
|
||||||
|
ASSERT_TRUE(double_equal(c.pixelSize, 0.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CameraTest, Pixel_size_for_a_vertical_canvas)
|
||||||
|
{
|
||||||
|
Camera c = Camera(125, 200, M_PI / 2);
|
||||||
|
|
||||||
|
ASSERT_TRUE(double_equal(c.pixelSize, 0.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CameraTest, Constructing_a_ray_through_the_center_of_the_canvas)
|
||||||
|
{
|
||||||
|
Camera c = Camera(201, 101, M_PI / 2);
|
||||||
|
Ray r = c.rayForPixel(100, 50);
|
||||||
|
|
||||||
|
ASSERT_EQ(r.origin, Point(0, 0, 0));
|
||||||
|
ASSERT_EQ(r.direction, Vector(0, 0, -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CameraTest, Constructing_a_ray_through_a_corner_of_the_canvas)
|
||||||
|
{
|
||||||
|
Camera c = Camera(201, 101, M_PI / 2);
|
||||||
|
Ray r = c.rayForPixel(0, 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(r.origin, Point(0, 0, 0));
|
||||||
|
|
||||||
|
/* Temporary lower the precision */
|
||||||
|
set_equal_precision(0.00001);
|
||||||
|
|
||||||
|
ASSERT_EQ(r.direction, Vector(0.66519, 0.33259, -0.66851));
|
||||||
|
|
||||||
|
set_equal_precision(FLT_EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CameraTest, Constructing_a_ray_when_the_camera_is_transformed)
|
||||||
|
{
|
||||||
|
Camera c = Camera(201, 101, M_PI / 2);
|
||||||
|
c.setTransform(rotationY(M_PI / 4) * translation(0, -2, 5));
|
||||||
|
Ray r = c.rayForPixel(100, 50);
|
||||||
|
|
||||||
|
ASSERT_EQ(r.origin, Point(0, 2, -5));
|
||||||
|
ASSERT_EQ(r.direction, Vector(sqrt(2)/2, 0, -sqrt(2)/2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CameraTest, Rendering_a_world_with_a_camera)
|
||||||
|
{
|
||||||
|
World w = DefaultWorld();
|
||||||
|
Camera c = Camera(11, 11, M_PI / 2);
|
||||||
|
|
||||||
|
Tuple from = Point(0, 0, -5);
|
||||||
|
Tuple to = Point(0, 0, 0);
|
||||||
|
Tuple up = Vector(0, 1, 0);
|
||||||
|
|
||||||
|
c.setTransform(viewTransform(from, to, up));
|
||||||
|
|
||||||
|
Canvas image = c.render(w);
|
||||||
|
|
||||||
|
/* Temporary lower the precision */
|
||||||
|
/* We need to lower a lot as Canvas is not keeping the
|
||||||
|
* floating point value, but a value between 0 and 255 per channel,
|
||||||
|
* as it is storing the actual frame buffer, so there is a more big different
|
||||||
|
* between the value.
|
||||||
|
*/
|
||||||
|
set_equal_precision(0.005);
|
||||||
|
|
||||||
|
Colour col = image.getPixel(5, 5);
|
||||||
|
|
||||||
|
ASSERT_EQ(col, Colour(0.38066, 0.47583, 0.2855));
|
||||||
|
|
||||||
|
set_equal_precision(FLT_EPSILON);
|
||||||
|
}
|
||||||
83
tests/ch7_test.cpp
Normal file
83
tests/ch7_test.cpp
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Render test for chapter 5 "Put it together".
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <world.h>
|
||||||
|
#include <light.h>
|
||||||
|
#include <sphere.h>
|
||||||
|
#include <material.h>
|
||||||
|
#include <colour.h>
|
||||||
|
#include <canvas.h>
|
||||||
|
#include <camera.h>
|
||||||
|
#include <transformation.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
/* First we need to construct the world */
|
||||||
|
Sphere floor = Sphere();
|
||||||
|
floor.setTransform(scaling(10, 0.01, 10));
|
||||||
|
floor.material.colour = Colour(1, 0.9, 0.9);
|
||||||
|
floor.material.specular = 0;
|
||||||
|
|
||||||
|
Sphere left_wall = Sphere();
|
||||||
|
left_wall.setTransform(translation(0, 0, 5) *
|
||||||
|
rotationY(-M_PI / 4) * rotationX((M_PI / 2)) *
|
||||||
|
scaling(10, 0.01, 10));
|
||||||
|
left_wall.material = floor.material;
|
||||||
|
|
||||||
|
Sphere right_wall = Sphere();
|
||||||
|
right_wall.setTransform(translation(0, 0, 5) *
|
||||||
|
rotationY(M_PI / 4) * rotationX((M_PI / 2)) *
|
||||||
|
scaling(10, 0.01, 10));
|
||||||
|
right_wall.material = floor.material;
|
||||||
|
|
||||||
|
Sphere middle = Sphere();
|
||||||
|
middle.setTransform(translation(-0.5, 1, 0.5));
|
||||||
|
middle.material.colour = Colour(0.1, 1, 0.5);
|
||||||
|
middle.material.diffuse = 0.7;
|
||||||
|
middle.material.specular = 0.3;
|
||||||
|
|
||||||
|
Sphere right = Sphere();
|
||||||
|
right.setTransform(translation(1.5, 0.5, -0.5) * scaling(0.5, 0.5, 0.5));
|
||||||
|
right.material.colour = Colour(0.5, 1, 0.1);
|
||||||
|
right.material.diffuse = 0.7;
|
||||||
|
right.material.specular = 0.3;
|
||||||
|
|
||||||
|
Sphere left = Sphere();
|
||||||
|
left.setTransform(translation(-1.5, 0.33, -0.75) * scaling(0.33, 0.33, 0.33));
|
||||||
|
left.material.colour = Colour(1, 0.8, 0.1);
|
||||||
|
left.material.diffuse = 0.7;
|
||||||
|
left.material.specular = 0.3;
|
||||||
|
|
||||||
|
World w = World();
|
||||||
|
|
||||||
|
w.addObject(&floor);
|
||||||
|
w.addObject(&left_wall);
|
||||||
|
w.addObject(&right_wall);
|
||||||
|
w.addObject(&middle);
|
||||||
|
w.addObject(&left);
|
||||||
|
w.addObject(&right);
|
||||||
|
|
||||||
|
/* Add light */
|
||||||
|
Light light = Light(POINT_LIGHT, Point(-10, 10, -10), Colour(1, 1, 1));
|
||||||
|
|
||||||
|
w.addLight(&light);
|
||||||
|
|
||||||
|
/* Set the camera */
|
||||||
|
Camera camera = Camera(1000, 500, M_PI / 3);
|
||||||
|
camera.setTransform(viewTransform(Point(0, 1.5, -5),
|
||||||
|
Point(0, 1, 0),
|
||||||
|
Vector(0, 1, 0)));
|
||||||
|
|
||||||
|
/* Now render it */
|
||||||
|
Canvas image = camera.render(w);
|
||||||
|
|
||||||
|
image.SaveAsPNG("ch7_test.png");
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user