Add renderstat to get some info about rendering.
This commit is contained in:
@@ -12,6 +12,11 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/external/covera
|
||||
option(PACKAGE_TESTS "Build the tests" ON)
|
||||
option(ENABLE_COVERAGE "Build for code coverage" OFF)
|
||||
|
||||
option(SHOW_STATS "Show rendering stat" ON)
|
||||
if (SHOW_STATS)
|
||||
add_compile_options(-DRENDER_STATS)
|
||||
endif()
|
||||
|
||||
if (ENABLE_COVERAGE AND COVERALLS)
|
||||
message(FATAL_ERROR "You can't enable both ENABLE_COVERAGE and COVERALLS at the same time")
|
||||
endif()
|
||||
|
||||
@@ -16,6 +16,7 @@ file(GLOB RAY_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_D
|
||||
target_include_directories(rayonnement PUBLIC include pattern)
|
||||
target_sources(rayonnement PRIVATE ${RAY_HEADERS} ${RAY_SOURCES})
|
||||
target_link_libraries(rayonnement LodePNG)
|
||||
|
||||
if (USE_OPENMP)
|
||||
target_link_libraries(rayonnement OpenMP::OpenMP_CXX)
|
||||
endif()
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <camera.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <renderstat.h>
|
||||
|
||||
Camera::Camera(uint32_t hsize, uint32_t vsize, double fov) : verticalSize(vsize), horizontalSize(hsize), fieldOfView(fov)
|
||||
{
|
||||
@@ -33,7 +34,6 @@ Camera::Camera(uint32_t hsize, uint32_t vsize, double fov) : verticalSize(vsize)
|
||||
this->pixelSize = (this->halfWidth * 2) / this->horizontalSize;
|
||||
|
||||
this->setTransform(Matrix4().identity());
|
||||
|
||||
}
|
||||
|
||||
void Camera::setTransform(Matrix transform)
|
||||
@@ -62,7 +62,7 @@ Canvas Camera::render(World world, uint32_t depth)
|
||||
uint32_t x, y;
|
||||
Canvas image = Canvas(this->horizontalSize, this->verticalSize);
|
||||
|
||||
#pragma omp parallel private(x, y) shared(image)
|
||||
#pragma omp parallel private(x, y) shared(image, stats)
|
||||
{
|
||||
#pragma omp for
|
||||
for (y = 0 ; y < this->verticalSize ; y++)
|
||||
@@ -71,10 +71,14 @@ Canvas Camera::render(World world, uint32_t depth)
|
||||
{
|
||||
Ray r = this->rayForPixel(x, y);
|
||||
Tuple colour = world.colourAt(r, depth);
|
||||
|
||||
stats.addPixel();
|
||||
image.putPixel(x, y, colour);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stats.printStats();
|
||||
|
||||
return image;
|
||||
}
|
||||
@@ -9,6 +9,8 @@
|
||||
#ifndef DORAYME_BOUNDINGBOX_H
|
||||
#define DORAYME_BOUNDINGBOX_H
|
||||
|
||||
#include <renderstat.h>
|
||||
|
||||
struct BoundingBox
|
||||
{
|
||||
private:
|
||||
@@ -110,9 +112,12 @@ public:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
stats.addDiscardedIntersect();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //DORAYME_BOUNDINGBOX_H
|
||||
#endif /* DORAYME_BOUNDINGBOX_H */
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <shape.h>
|
||||
#include <ray.h>
|
||||
#include <intersect.h>
|
||||
#include <renderstat.h>
|
||||
|
||||
class Cone : public Shape {
|
||||
protected:
|
||||
@@ -27,7 +28,7 @@ public:
|
||||
double minCap;
|
||||
double maxCap;
|
||||
|
||||
Cone() : minCap(-INFINITY), maxCap(INFINITY), isClosed(false), Shape(SHAPE_CONE) {};
|
||||
Cone() : minCap(-INFINITY), maxCap(INFINITY), isClosed(false), Shape(SHAPE_CONE) { stats.addCone(); };
|
||||
BoundingBox getBounds();
|
||||
bool haveFiniteBounds() { return !(isinf(this->minCap) || isinf(this->maxCap)); };
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <shape.h>
|
||||
#include <ray.h>
|
||||
#include <intersect.h>
|
||||
#include <renderstat.h>
|
||||
|
||||
class Cube : public Shape {
|
||||
private:
|
||||
@@ -22,7 +23,7 @@ private:
|
||||
Tuple localNormalAt(Tuple point);
|
||||
|
||||
public:
|
||||
Cube() : Shape(SHAPE_CUBE) {};
|
||||
Cube() : Shape(SHAPE_CUBE) { stats.addCube(); };
|
||||
};
|
||||
|
||||
#endif /* DORAYME_CUBE_H */
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <shape.h>
|
||||
#include <ray.h>
|
||||
#include <intersect.h>
|
||||
#include <renderstat.h>
|
||||
|
||||
class Cylinder : public Shape {
|
||||
private:
|
||||
@@ -27,7 +28,7 @@ public:
|
||||
double minCap;
|
||||
double maxCap;
|
||||
|
||||
Cylinder() : minCap(-INFINITY), maxCap(INFINITY), isClosed(false), Shape(SHAPE_CYLINDER) {};
|
||||
Cylinder() : minCap(-INFINITY), maxCap(INFINITY), isClosed(false), Shape(SHAPE_CYLINDER) { stats.addCylinder(); };
|
||||
|
||||
BoundingBox getBounds();
|
||||
bool haveFiniteBounds() { return !(isinf(this->minCap) || isinf(this->maxCap)); };
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <ray.h>
|
||||
#include <material.h>
|
||||
#include <renderstat.h>
|
||||
|
||||
class Shape;
|
||||
class Intersect;
|
||||
@@ -74,7 +75,7 @@ public:
|
||||
Shape *object;
|
||||
|
||||
public:
|
||||
Intersection(double t, Shape *object) : t(t), object(object) { };
|
||||
Intersection(double t, Shape *object) : t(t), object(object) { stats.addIntersection(); };
|
||||
bool nothing() { return (this->object == nullptr); };
|
||||
|
||||
Computation prepareComputation(Ray r, Intersect *xs = nullptr);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <tuple.h>
|
||||
#include <colour.h>
|
||||
#include <renderstat.h>
|
||||
|
||||
enum LightType
|
||||
{
|
||||
@@ -26,7 +27,8 @@ public:
|
||||
|
||||
public:
|
||||
Light(LightType type = POINT_LIGHT, Tuple position=Point(0, 0, 0),
|
||||
Colour intensity=Colour(1, 1, 1)) : type(type), position(position), intensity(intensity) { };
|
||||
Colour intensity=Colour(1, 1, 1)) : type(type), position(position), intensity(intensity)
|
||||
{ stats.addLight(); };
|
||||
|
||||
bool operator==(const Light &b) const { return this->intensity == b.intensity &&
|
||||
this->position == b.position &&
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#ifndef DORAYME_PLANE_H
|
||||
#define DORAYME_PLANE_H
|
||||
|
||||
#include <renderstat.h>
|
||||
|
||||
class Plane : public Shape
|
||||
{
|
||||
private:
|
||||
@@ -16,7 +18,7 @@ private:
|
||||
Tuple localNormalAt(Tuple point);
|
||||
|
||||
public:
|
||||
Plane() : Shape(SHAPE_PLANE) { };
|
||||
Plane() : Shape(SHAPE_PLANE) { stats.addPlane(); };
|
||||
BoundingBox getBounds();
|
||||
bool haveFiniteBounds() { return false; };
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#define DORAYME_RAY_H
|
||||
|
||||
#include <tuple.h>
|
||||
#include <renderstat.h>
|
||||
|
||||
class Ray
|
||||
{
|
||||
@@ -17,7 +18,7 @@ public:
|
||||
Tuple direction;
|
||||
Tuple origin;
|
||||
|
||||
Ray(Tuple origin, Tuple direction) : origin(origin), direction(direction) { };
|
||||
Ray(Tuple origin, Tuple direction) : origin(origin), direction(direction) { stats.addRay(); };
|
||||
|
||||
Tuple position(double t) { return this->origin + this->direction * t; };
|
||||
};
|
||||
|
||||
125
source/include/renderstat.h
Normal file
125
source/include/renderstat.h
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Render statistics header
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#ifndef DORAYME_RENDERSTAT_H
|
||||
#define DORAYME_RENDERSTAT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef RENDER_STATS
|
||||
#include <stdio.h>
|
||||
|
||||
class RenderStats
|
||||
{
|
||||
private:
|
||||
uint64_t coneCount; /* Total number of cones */
|
||||
uint64_t cylinderCount; /* Total number of cylinder */
|
||||
uint64_t cubeCount; /* Total number of cubes */
|
||||
uint64_t groupCount; /* Total number of groups */
|
||||
uint64_t lightCount; /* Total number of light */
|
||||
uint64_t planeCount; /* Total number of plane */
|
||||
uint64_t sphereCount; /* Total number of sphere */
|
||||
uint64_t triangleCount; /* Total number of triangle */
|
||||
|
||||
uint64_t pixelCount; /* Total number of rendered pixels */
|
||||
uint64_t rayCount; /* Total number of rays */
|
||||
uint64_t lightRayEmitedCount; /* Total number of ray launched for light tests */
|
||||
uint64_t reflectionRayCount; /* Total number of reflection ray launched */
|
||||
uint64_t refractedRayCount; /* Total number of refracted ray launched */
|
||||
uint64_t intersectCount; /* Total number of intersect object created */
|
||||
uint64_t intersectionCount; /* Total number of intersection for all casted rays, including light and reflections */
|
||||
uint64_t reallocCallCount; /* Total number of time realloc being called */
|
||||
uint64_t mallocCallCount; /* Total number of time malloc/calloc being called */
|
||||
uint64_t discardedIntersectCount; /* Number of time a bounding box check said "no need to test me" */
|
||||
uint64_t maxDepthAttained; /* Report the lowest depth attained during ray recursion */
|
||||
uint64_t maxIntersectOnARay; /* Biggest intersect done */
|
||||
|
||||
public:
|
||||
RenderStats() : coneCount(0), cylinderCount(0), cubeCount(0), groupCount(0), lightCount(0), planeCount(0), sphereCount(0), triangleCount(0),
|
||||
pixelCount(0), rayCount(0), lightRayEmitedCount(0), reflectionRayCount(0), refractedRayCount(0),
|
||||
intersectCount(0), intersectionCount(0), reallocCallCount(0), mallocCallCount(0),
|
||||
discardedIntersectCount(0), maxDepthAttained(UINT64_MAX), maxIntersectOnARay(0) {};
|
||||
|
||||
void addCone() { this->coneCount++; };
|
||||
void addCylinder() { this->cylinderCount++; };
|
||||
void addCube() { this->cubeCount++; };
|
||||
void addGroup() { this->groupCount++; };
|
||||
void addLight() { this->lightCount++; };
|
||||
void addPlane() { this->planeCount++; };
|
||||
void addSphere() { this->sphereCount++; };
|
||||
void addTriangle() { this->triangleCount++; };
|
||||
|
||||
void addPixel() { this->pixelCount++; };
|
||||
void addRay() { this->rayCount++; };
|
||||
void addLightRay() { this->lightRayEmitedCount++; };
|
||||
void addReflectRay() { this->reflectionRayCount++; };
|
||||
void addRefractRay() { this->refractedRayCount++; };
|
||||
void addIntersect() { this->intersectCount++; };
|
||||
void addIntersection() { this->intersectionCount++; };
|
||||
void addMalloc() { this->mallocCallCount++; };
|
||||
void addRealloc() { this->reallocCallCount++; };
|
||||
void addDiscardedIntersect() { this->discardedIntersectCount++; };
|
||||
void setMaxDepth(uint32_t depth) { if (this->maxDepthAttained>depth) { this->maxDepthAttained = depth; } };
|
||||
void setMaxIntersect(uint32_t count) { if (this->maxIntersectOnARay<count) { this->maxIntersectOnARay = count; } };
|
||||
void printStats() {
|
||||
printf("Rendering statistics:\n");
|
||||
printf("Cones : %ld\n", this->coneCount);
|
||||
printf("Cubes : %ld\n", this->cubeCount);
|
||||
printf("Cylinders : %ld\n", this->cylinderCount);
|
||||
printf("Groups : %ld\n", this->groupCount);
|
||||
printf("Lights : %ld\n", this->lightCount);
|
||||
printf("Planes : %ld\n", this->planeCount);
|
||||
printf("Spheres : %ld\n", this->sphereCount);
|
||||
printf("Triangles : %ld\n", this->triangleCount);
|
||||
printf("==================================================\n");
|
||||
printf("Pixel rendered : %ld\n", this->pixelCount);
|
||||
printf("Ray casted : %ld\n", this->rayCount);
|
||||
printf("Light Ray casted : %ld\n", this->lightRayEmitedCount);
|
||||
printf("Reflection ray casted : %ld\n", this->reflectionRayCount);
|
||||
printf("Refraction ray casted : %ld\n", this->refractedRayCount);
|
||||
printf("Intersect object created: %ld\n", this->intersectCount);
|
||||
printf("Intersection created : %ld\n", this->intersectionCount);
|
||||
printf("Malloc called : %ld\n", this->mallocCallCount);
|
||||
printf("Realloc called : %ld\n", this->reallocCallCount);
|
||||
printf("Bounding box missed : %ld\n", this->discardedIntersectCount);
|
||||
printf("Min depth atteined : %ld\n", this->maxDepthAttained);
|
||||
printf("Max intersect count : %ld\n", this->maxIntersectOnARay);
|
||||
printf("==================================================\n");
|
||||
};
|
||||
};
|
||||
#else
|
||||
class RenderStats
|
||||
{
|
||||
public:
|
||||
static void addCone() {};
|
||||
static void addCylinder() {};
|
||||
static void addCube() {};
|
||||
static void addGroup() {};
|
||||
static void addLight() {};
|
||||
static void addPlane() {};
|
||||
static void addSphere() {};
|
||||
static void addTriangle() {};
|
||||
static void printStats() {};
|
||||
static void addPixel() {};
|
||||
static void addRay() {};
|
||||
static void addLightRay() {};
|
||||
static void addReflectRay() {};
|
||||
static void addRefractRay() {};
|
||||
static void addIntersection() {};
|
||||
static void addDiscardedIntersect() {};
|
||||
static void setMaxDepth(uint32_t depth) {};
|
||||
static void addIntersect() {};
|
||||
static void addMalloc() {};
|
||||
static void addRealloc() {};
|
||||
static void setMaxIntersect(uint32_t count) {};
|
||||
};
|
||||
#endif
|
||||
|
||||
extern RenderStats stats;
|
||||
|
||||
#endif /* DORAYME_RENDERSTAT_H */
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <shape.h>
|
||||
#include <ray.h>
|
||||
#include <intersect.h>
|
||||
#include <renderstat.h>
|
||||
|
||||
class Sphere : public Shape
|
||||
{
|
||||
@@ -20,7 +21,7 @@ protected:
|
||||
Tuple localNormalAt(Tuple point);
|
||||
|
||||
public:
|
||||
Sphere() : Shape(SHAPE_SPHERE) { };
|
||||
Sphere() : Shape(SHAPE_SPHERE) { stats.addSphere(); };
|
||||
/* All sphere are at (0, 0, 0) and radius 1 in the object space */
|
||||
};
|
||||
|
||||
|
||||
@@ -11,13 +11,21 @@
|
||||
#include <intersect.h>
|
||||
|
||||
#include <float.h>
|
||||
#include <renderstat.h>
|
||||
|
||||
#define MIN_ALLOC (2)
|
||||
|
||||
/* TODO: Memory allocation, even if using standard calloc/realloc have a huge impact on performances. need to find a way
|
||||
* to reuse the intersect object without reallocating from scratch all the time. We use a lot of Intersect objects as
|
||||
* there is at least 2 per ray (one for Ray intersect object, one object per light)
|
||||
*/
|
||||
|
||||
Intersect::Intersect()
|
||||
{
|
||||
this->allocated = MIN_ALLOC;
|
||||
this->list = (Intersection **)calloc(sizeof(Intersection *), MIN_ALLOC);
|
||||
stats.addMalloc();
|
||||
stats.addIntersect();
|
||||
this->num = 0;
|
||||
}
|
||||
|
||||
@@ -35,10 +43,13 @@ void Intersect::add(Intersection i)
|
||||
if ((this->num + 1) > this->allocated)
|
||||
{
|
||||
this->allocated *= 2;
|
||||
stats.addRealloc();
|
||||
this->list = (Intersection **)realloc(this->list, sizeof(Intersection *) * this->allocated);
|
||||
}
|
||||
this->list[this->num++] = new Intersection(i.t, i.object);
|
||||
|
||||
stats.setMaxIntersect(this->num);
|
||||
|
||||
/* Now sort.. */
|
||||
for(j = 1; j < (this->num); j++)
|
||||
{
|
||||
|
||||
@@ -101,6 +101,7 @@ Matrix Matrix::operator*(const Matrix &b) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* TODO: Check if we can optimise this function. It is called a lot */
|
||||
Tuple Matrix::operator*(const Tuple &b) const
|
||||
{
|
||||
return Tuple(b.x * this->get(0, 0) + b.y * this->get(0, 1) + b.z * this->get(0, 2) + b.w * this->get(0, 3),
|
||||
|
||||
11
source/renderstat.cpp
Normal file
11
source/renderstat.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Render statistics implementation
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <renderstat.h>
|
||||
|
||||
RenderStats stats;
|
||||
@@ -9,13 +9,14 @@
|
||||
#include <tuple.h>
|
||||
#include <ray.h>
|
||||
#include <group.h>
|
||||
#include <cone.h>
|
||||
#include <math_helper.h>
|
||||
#include <renderstat.h>
|
||||
|
||||
#define MIN_ALLOC (2)
|
||||
|
||||
Group::Group() : Shape(SHAPE_GROUP)
|
||||
{
|
||||
stats.addGroup();
|
||||
this->allocatedObjectCount = MIN_ALLOC;
|
||||
this->objectList = (Shape **)calloc(sizeof(Shape *), MIN_ALLOC);
|
||||
this->objectCount = 0;
|
||||
|
||||
@@ -10,9 +10,12 @@
|
||||
#include <shape.h>
|
||||
#include <triangle.h>
|
||||
#include <math_helper.h>
|
||||
#include <renderstat.h>
|
||||
|
||||
Triangle::Triangle(Point p1, Point p2, Point p3) : Shape(SHAPE_TRIANGLE), p1(p1), p2(p2), p3(p3)
|
||||
{
|
||||
stats.addTriangle();
|
||||
|
||||
this->e1 = p2 - p1;
|
||||
this->e2 = p3 - p1;
|
||||
this->normal = e2.cross(e1).normalise();
|
||||
|
||||
@@ -123,6 +123,8 @@ Tuple World::colourAt(Ray r, uint32_t depthCount)
|
||||
Intersect allHits = this->intersect(r);
|
||||
Intersection hit = allHits.hit();
|
||||
|
||||
stats.setMaxDepth(depthCount);
|
||||
|
||||
if (hit.nothing())
|
||||
{
|
||||
return Colour(0, 0, 0);
|
||||
@@ -140,6 +142,7 @@ bool World::isShadowed(Tuple point, uint32_t light)
|
||||
Tuple direction = v.normalise();
|
||||
|
||||
Ray r = Ray(point, direction);
|
||||
stats.addLightRay();
|
||||
Intersect xs = this->intersect(r);
|
||||
|
||||
int i;
|
||||
@@ -166,6 +169,7 @@ Colour World::reflectColour(Computation comps, uint32_t depthCount)
|
||||
|
||||
/* So it is reflective, even just a bit. Let'sr reflect the ray! */
|
||||
Ray reflectedRay = Ray(comps.overHitPoint, comps.reflectVector);
|
||||
stats.addReflectRay();
|
||||
|
||||
Tuple hitColour = this->colourAt(reflectedRay, depthCount - 1);
|
||||
hitColour = hitColour * comps.material->reflective;
|
||||
@@ -188,6 +192,7 @@ Colour World::refractedColour(Computation comps, uint32_t depthCount)
|
||||
Tuple direction = comps.normalVector * (nRatio * cos_i - cos_t) - comps.eyeVector * nRatio;
|
||||
|
||||
Ray refractedRay = Ray(comps.underHitPoint, direction);
|
||||
stats.addRefractRay();
|
||||
|
||||
Tuple hitColour = this->colourAt(refractedRay, depthCount - 1) * comps.material->transparency;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user