Tonemapping

This commit is contained in:
Ben
2019-10-01 22:35:35 +01:00
parent ac344343de
commit a8dc75e46b
9 changed files with 213 additions and 151 deletions

View File

@@ -35,8 +35,8 @@ find_package(OpenGL REQUIRED)
find_package(OpenMP) find_package(OpenMP)
if (OPENMP_FOUND) if (OPENMP_FOUND)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
endif() endif()
if (UNIX) if (UNIX)
@@ -48,31 +48,31 @@ if (UNIX)
endif (UNIX) endif (UNIX)
include_directories(${executable} include_directories(${executable}
#${PNG_INCLUDE_DIR} #${PNG_INCLUDE_DIR}
#${JPEG_INCLUDE_DIR} #${JPEG_INCLUDE_DIR}
${IncludeDIR} ${IncludeDIR}
) )
file(GLOB SourceFiles file(GLOB SourceFiles
${SrcDIR}/* ${SrcDIR}/*
${SrcDIR}/acceleration/* ${SrcDIR}/acceleration/*
${SrcDIR}/core/* ${SrcDIR}/core/*
${SrcDIR}/engine/* ${SrcDIR}/engine/*
${SrcDIR}/display/* ${SrcDIR}/display/*
${SrcDIR}/definitions/* ${SrcDIR}/definitions/*
${SrcDIR}/definitions/materials/* ${SrcDIR}/definitions/materials/*
${SrcDIR}/definitions/primatives/* ${SrcDIR}/definitions/primatives/*
${SrcDIR}/denoise/* ${SrcDIR}/denoise/*
${SrcDIR}/util/* ${SrcDIR}/util/*
${SrcDIR}/util/imgui/* ${SrcDIR}/util/imgui/*
${TestDIR}/${CurrentTest} ${TestDIR}/${CurrentTest}
) )
add_executable(${executable} ${SourceFiles}) add_executable(${executable} ${SourceFiles})
set_target_properties(${executable} PROPERTIES set_target_properties(${executable} PROPERTIES
CXX_STANDARD 17 CXX_STANDARD 17
CXX_EXTENSIONS OFF CXX_EXTENSIONS OFF
) )
if (UNIX) if (UNIX)
@@ -102,5 +102,5 @@ if (WIN32)
endif (WIN32) endif (WIN32)
target_link_libraries(${executable} target_link_libraries(${executable}
${CMAKE_DL_LIBS} ${CMAKE_DL_LIBS}
) )

View File

@@ -14,16 +14,16 @@
#include <string> #include <string>
enum OperationMode { enum OperationMode {
MODE_OPERATION_PROGRESSIVE_GUI, MODE_OPERATION_PROGRESSIVE_GUI,
MODE_OPERATION_PROGRESSIVE_IMG, MODE_OPERATION_PROGRESSIVE_IMG,
MODE_OPERATION_SAMPLES_IMG, MODE_OPERATION_SAMPLES_IMG,
}; };
enum AccelerationMode { enum AccelerationMode {
MODE_ACCELERATION_NONE, MODE_ACCELERATION_NONE,
MODE_ACCELERATION_KD, MODE_ACCELERATION_KD,
MODE_ACCELERATION_KD_SLOW, MODE_ACCELERATION_KD_SLOW,
MODE_ACCELERATION_BVH, MODE_ACCELERATION_BVH,
}; };
enum RenderMode { enum RenderMode {
@@ -35,7 +35,8 @@ enum RenderMode {
enum ToneMapMode { enum ToneMapMode {
MODE_TONEMAP_REINHARD, MODE_TONEMAP_REINHARD,
MODE_TONEMAP_EXP, MODE_TONEMAP_ACES_FILMATIC,
MODE_TONEMAP_UNCHARTED2,
MODE_TONEMAP_CLAMP, MODE_TONEMAP_CLAMP,
MODE_TONEMAP_BASIC MODE_TONEMAP_BASIC
}; };

View File

@@ -2,32 +2,32 @@
#include "../ray.hpp" #include "../ray.hpp"
bool Triangle::Intersect(Ray& ray, float& t) { bool Triangle::Intersect(Ray& ray, float& t) {
glm::vec3 vertex0 = points[0]; glm::vec3 vertex0 = points[0];
glm::vec3 vertex1 = points[1]; glm::vec3 vertex1 = points[1];
glm::vec3 vertex2 = points[2]; glm::vec3 vertex2 = points[2];
glm::vec3 edge1, edge2, h, s, q; glm::vec3 edge1, edge2, h, s, q;
float a,f,u,v; float a,f,u,v;
edge1 = vertex1 - vertex0; edge1 = vertex1 - vertex0;
edge2 = vertex2 - vertex0; edge2 = vertex2 - vertex0;
h = glm::cross(ray.direction, edge2); h = glm::cross(ray.direction, edge2);
a = glm::dot(edge1, h); a = glm::dot(edge1, h);
if (a > -EPSILON && a < EPSILON) if (a > -EPSILON && a < EPSILON)
return false; // This ray is parallel to this triangle. return false; // This ray is parallel to this triangle.
f = 1.0/a; f = 1.0/a;
s = ray.origin - vertex0; s = ray.origin - vertex0;
u = f * glm::dot(s, h); u = f * glm::dot(s, h);
if (u < 0.0 || u > 1.0) if (u < 0.0 || u > 1.0)
return false; return false;
q = glm::cross(s, edge1); q = glm::cross(s, edge1);
v = f * glm::dot(ray.direction, q); v = f * glm::dot(ray.direction, q);
if (v < 0.0 || u + v > 1.0) if (v < 0.0 || u + v > 1.0)
return false; return false;
// At this stage we can compute t to find out where the intersection point is on the line. // At this stage we can compute t to find out where the intersection point is on the line.
t = f * glm::dot(edge2, q); t = f * glm::dot(edge2, q);
if (t > EPSILON) // ray intersection if (t > EPSILON) // ray intersection
return true; return true;
else // This means that there is a line intersection but not a ray intersection. else // This means that there is a line intersection but not a ray intersection.
return false; return false;
} }
glm::vec3 Triangle::SurfaceTangent(glm::vec3 normal) { glm::vec3 Triangle::SurfaceTangent(glm::vec3 normal) {
@@ -35,19 +35,19 @@ glm::vec3 Triangle::SurfaceTangent(glm::vec3 normal) {
} }
glm::vec3 Triangle::SurfaceNormal(glm::vec3 hitPoint) { glm::vec3 Triangle::SurfaceNormal(glm::vec3 hitPoint) {
return ((normals[0] + normals[1] + normals[2]) / 3.0f); return ((normals[0] + normals[1] + normals[2]) / 3.0f);
} }
glm::vec2 Triangle::TexCoords(glm::vec3 hitPoint) { glm::vec2 Triangle::TexCoords(glm::vec3 hitPoint) {
return { 0.0f, 0.0f }; return { 0.0f, 0.0f };
} }
void Triangle::Translate(glm::vec3 trans) { void Triangle::Translate(glm::vec3 trans) {
points[0] += trans; points[0] += trans;
points[1] += trans; points[1] += trans;
points[2] += trans; points[2] += trans;
} }
glm::vec3 Triangle::Midpoint() { glm::vec3 Triangle::Midpoint() {
return (points[0] + points[1] + points[2]) / 3.0f; return (points[0] + points[1] + points[2]) / 3.0f;
} }

View File

@@ -22,8 +22,8 @@ glm::vec3 GradientSky::Sample(Ray& ray) {
} }
Scene::Scene(int width, int height) { Scene::Scene(int width, int height) {
w = width; w = width;
h = height; h = height;
} }
glm::vec3 Scene::SampleSky(Ray& ray) { glm::vec3 Scene::SampleSky(Ray& ray) {

View File

@@ -66,55 +66,113 @@ void FrameBuffer::PostProcess(ToneMapMode mode) {
switch (mode) { switch (mode) {
case MODE_TONEMAP_REINHARD: case MODE_TONEMAP_REINHARD:
{ {
for (int x = 0; x < XRes; x++) for (int x = 0; x < XRes; x++)
for (int y = 0; y < YRes; y++) { for (int y = 0; y < YRes; y++) {
m_swapBuffer[y * this->XRes + x] = RenderPostProcess[y * this->XRes + x] / m_swapBuffer[y * this->XRes + x] = RenderPostProcess[y * this->XRes + x] /
(RenderPostProcess[y * this->XRes + x] + 1.0f); (RenderPostProcess[y * this->XRes + x] + 1.0f);
}
break;
} }
case MODE_TONEMAP_EXP:
{ break;
for (int x = 0; x < XRes; x++) }
for (int y = 0; y < YRes; y++) { case MODE_TONEMAP_ACES_FILMATIC:
m_swapBuffer[y * this->XRes + x] = 1.0f - exp2(RenderPostProcess[y * this->XRes + x] * 1.0f); {
}
break; static const glm::mat3 inputMat{
} {0.59719, 0.35458, 0.04823},
case MODE_TONEMAP_CLAMP: {0.07600, 0.90834, 0.01566},
{ {0.02840, 0.13383, 0.83777}
for (int x = 0; x < XRes; x++) };
for (int y = 0; y < YRes; y++) {
m_swapBuffer[y * this->XRes + x] = Clamp(RenderPostProcess[y * this->XRes + x], 1.0f, 0.0f);
}
break; static const glm::mat3 outputMat{
} { 1.60475, -0.53108, -0.07367},
case MODE_TONEMAP_BASIC: {-0.10208, 1.10813, -0.00605},
{ {-0.00327, -0.07276, 1.07602}
float max = 0.0f; };
for (int x = 0; x < XRes; x++) auto ACES = [&](glm::vec3 v) -> glm::vec3 {
for (int y = 0; y < YRes; y++) { glm::vec3 a = v * (v + 0.0245786f) - 0.000090537f;
if (RenderPostProcess[y * this->XRes + x].r > max) max = RenderPostProcess[y * this->XRes + x].r; glm::vec3 b = v * (0.983729f * v + 0.4329510f) + 0.238081f;
if (RenderPostProcess[y * this->XRes + x].g > max) max = RenderPostProcess[y * this->XRes + x].g; return a / b;
if (RenderPostProcess[y * this->XRes + x].b > max) max = RenderPostProcess[y * this->XRes + x].b; };
}
for (int x = 0; x < XRes; x++) for (int x = 0; x < XRes; x++)
for (int y = 0; y < YRes; y++) { for (int y = 0; y < YRes; y++) {
m_swapBuffer[y * this->XRes + x] = RenderPostProcess[y * this->XRes + x] / max; glm::vec3 col = RenderPostProcess[y * this->XRes + x];
}
break; col = col * inputMat;
col = ACES(col);
col = col * outputMat;
col = Clamp(col, 1.0f, 0.0f);
m_swapBuffer[y * this->XRes + x] = col;
} }
break;
}
case MODE_TONEMAP_UNCHARTED2:
{
static const float exposure = 2.0f;
static const glm::vec3 W = { 11.2f, 11.2f, 11.2f };
auto Uncharted2 = [&](glm::vec3 x) -> glm::vec3 {
float A = 0.15f;
float B = 0.50f;
float C = 0.10f;
float D = 0.20f;
float E = 0.02f;
float F = 0.30f;
return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F;
};
glm::vec3 white = glm::vec3{ 1.0f, 1.0f, 1.0f } / Uncharted2(W);
for (int x = 0; x < XRes; x++)
for (int y = 0; y < YRes; y++) {
glm::vec3 col = RenderPostProcess[y * this->XRes + x];
glm::vec3 cur = Uncharted2(col * exposure);
col = cur * white;
col = Clamp(col, 1.0f, 0.0f);
m_swapBuffer[y * this->XRes + x] = col;
}
break;
}
case MODE_TONEMAP_CLAMP:
{
for (int x = 0; x < XRes; x++)
for (int y = 0; y < YRes; y++) {
m_swapBuffer[y * this->XRes + x] = Clamp(RenderPostProcess[y * this->XRes + x], 1.0f, 0.0f);
}
break;
}
case MODE_TONEMAP_BASIC:
{
float max = 0.0f;
for (int x = 0; x < XRes; x++)
for (int y = 0; y < YRes; y++) {
if (RenderPostProcess[y * this->XRes + x].r > max) max = RenderPostProcess[y * this->XRes + x].r;
if (RenderPostProcess[y * this->XRes + x].g > max) max = RenderPostProcess[y * this->XRes + x].g;
if (RenderPostProcess[y * this->XRes + x].b > max) max = RenderPostProcess[y * this->XRes + x].b;
}
for (int x = 0; x < XRes; x++)
for (int y = 0; y < YRes; y++) {
m_swapBuffer[y * this->XRes + x] = RenderPostProcess[y * this->XRes + x] / max;
}
break;
}
default: default:
{ {
break; break;
} }
} }
for (int x = 0; x < XRes; x++) for (int x = 0; x < XRes; x++)

View File

@@ -21,7 +21,7 @@
#include "../definitions/ray.hpp" #include "../definitions/ray.hpp"
ProgressiveRenderer::ProgressiveRenderer() { ProgressiveRenderer::ProgressiveRenderer() {
} }
void ProgressiveRenderer::Init(DisplayInterface* interface, Scene* scene) { void ProgressiveRenderer::Init(DisplayInterface* interface, Scene* scene) {
@@ -29,7 +29,7 @@ void ProgressiveRenderer::Init(DisplayInterface* interface, Scene* scene) {
m_engine = new RenderEngine(); m_engine = new RenderEngine();
m_engine->Mode = MODE_RENDER_PATHTRACE; m_engine->Mode = MODE_RENDER_PATHTRACE;
m_engine->SetScene(scene); m_engine->SetScene(scene);
m_interface = interface; m_interface = interface;
m_scene = scene; m_scene = scene;
} }
@@ -79,7 +79,7 @@ void ProgressiveRenderer::Input() {
ImGui::Combo("Render Mode", &m_renderModeSelected, renderItems, IM_ARRAYSIZE(renderItems)); ImGui::Combo("Render Mode", &m_renderModeSelected, renderItems, IM_ARRAYSIZE(renderItems));
m_mode = (RenderMode)m_renderModeSelected; m_mode = (RenderMode)m_renderModeSelected;
const char* toneMapItems[] = { "Reinhard Tonamap", "Exponential Tonemap", "Clamp", "Basic Tonemap" }; const char* toneMapItems[] = { "Reinhard Tonamap", "ACES Filmatic Tonemap", "Uncharted 2", "Clamp", "Basic Tonemap" };
ImGui::Combo("ToneMap Mode", &m_toneMapModeSelected, toneMapItems, IM_ARRAYSIZE(toneMapItems)); ImGui::Combo("ToneMap Mode", &m_toneMapModeSelected, toneMapItems, IM_ARRAYSIZE(toneMapItems));
ImGui::SliderFloat("Gamma", &m_gamma, 1.0f, 4.0f); ImGui::SliderFloat("Gamma", &m_gamma, 1.0f, 4.0f);

View File

@@ -10,41 +10,41 @@
#include "../maths.hpp" #include "../maths.hpp"
glm::vec3 getNormal(glm::vec3 p0, glm::vec3 p1, glm::vec3 p2) { glm::vec3 getNormal(glm::vec3 p0, glm::vec3 p1, glm::vec3 p2) {
glm::vec3 u = p1 - p0; glm::vec3 u = p1 - p0;
glm::vec3 v = p2 - p0; glm::vec3 v = p2 - p0;
glm::vec3 normal; glm::vec3 normal;
normal.x = u.y * v.z - u.z * v.y; normal.x = u.y * v.z - u.z * v.y;
normal.y = u.z * v.x - u.x * v.z; normal.y = u.z * v.x - u.x * v.z;
normal.z = u.x * v.y - u.y * v.x; normal.z = u.x * v.y - u.y * v.x;
return normal; return normal;
} }
std::vector<Triangle*> LoadTrianglesBasic(std::string path, std::string basePath, Material* baseMaterial) { std::vector<Triangle*> LoadTrianglesBasic(std::string path, std::string basePath, Material* baseMaterial) {
tinyobj::attrib_t attrib; tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes; std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials; std::vector<tinyobj::material_t> materials;
std::string warn, err; std::string warn, err;
bool canLoad = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, path.c_str(), basePath.c_str()); bool canLoad = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, path.c_str(), basePath.c_str());
if (!err.empty() || !canLoad) { if (!err.empty() || !canLoad) {
std::cerr << "Cannot load obj: '" << path << "': " << err << std::endl; std::cerr << "Cannot load obj: '" << path << "': " << err << std::endl;
exit(0); exit(0);
} }
if (!warn.empty()) { if (!warn.empty()) {
std::cerr << "Warning from obj loader while loading obj: '" << path << "': " << warn << std::endl; std::cerr << "Warning from obj loader while loading obj: '" << path << "': " << warn << std::endl;
} }
std::vector<Triangle*> triangles; std::vector<Triangle*> triangles;
for (size_t s = 0; s < shapes.size(); s++) { for (size_t s = 0; s < shapes.size(); s++) {
size_t index_offset = 0; size_t index_offset = 0;
for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) { for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) {
int fv = shapes[s].mesh.num_face_vertices[f]; int fv = shapes[s].mesh.num_face_vertices[f];
if (fv == 3) { if (fv == 3) {
tinyobj::real_t avx[3]; tinyobj::real_t avx[3];
tinyobj::real_t avy[3]; tinyobj::real_t avy[3];
@@ -77,30 +77,30 @@ std::vector<Triangle*> LoadTrianglesBasic(std::string path, std::string basePath
mtl = baseMaterial; mtl = baseMaterial;
} }
// glm::vec3 normal = getNormal( // glm::vec3 normal = getNormal(
// {avx[0], avy[0], avz[0]}, // {avx[0], avy[0], avz[0]},
// {avx[1], avy[1], avz[1]}, // {avx[1], avy[1], avz[1]},
// {avx[2], avy[2], avz[2]} // {avx[2], avy[2], avz[2]}
// ); // );
Triangle* tmp = new Triangle { Triangle* tmp = new Triangle {
{avx[0], avy[0], avz[0]}, {avx[0], avy[0], avz[0]},
{avx[1], avy[1], avz[1]}, {avx[1], avy[1], avz[1]},
{avx[2], avy[2], avz[2]}, {avx[2], avy[2], avz[2]},
// normal, normal, normal // normal, normal, normal
{anx[0], any[0], anz[0]}, {anx[0], any[0], anz[0]},
{anx[1], any[1], anz[1]}, {anx[1], any[1], anz[1]},
{anx[2], any[2], anz[2]}, {anx[2], any[2], anz[2]},
mtl, mtl,
}; };
triangles.push_back(tmp); triangles.push_back(tmp);
} }
index_offset += fv; index_offset += fv;
} }
} }
return triangles; return triangles;
} }

View File

@@ -21,7 +21,7 @@ int main(int argc, char** argv) {
Sky* sky = new GradientSky({ 35.0f / 255.0f, 148.0f / 255.0f, 235.0f / 255.0f }, { 1.0f, 1.0f, 1.0f }, 5.0f); Sky* sky = new GradientSky({ 35.0f / 255.0f, 148.0f / 255.0f, 235.0f / 255.0f }, { 1.0f, 1.0f, 1.0f }, 5.0f);
scene->sky = sky; scene->sky = sky;
scene->camera = new Camera(width, height); scene->camera = new Camera(width, height);
//Sphere sphere1({ 1.3f, -0.8f, -5.0f }, 0.2f, new Material({ 0.817f, 0.374, 0.574 })); //Sphere sphere1({ 1.3f, -0.8f, -5.0f }, 0.2f, new Material({ 0.817f, 0.374, 0.574 }));
//scene->objects.push_back(&sphere1); //scene->objects.push_back(&sphere1);
@@ -41,8 +41,8 @@ int main(int argc, char** argv) {
inferno.SetScene(scene); inferno.SetScene(scene);
inferno.Ready(); inferno.Ready();
inferno.Render(); inferno.Render();
return 0; return 0;
} }

View File

@@ -2,8 +2,8 @@
#include "../src/inferno.hpp" #include "../src/inferno.hpp"
static const int width = 2000; static const int width = 600;
static const int height = 2000; static const int height = 600;
int main(int argc, char** argv) { int main(int argc, char** argv) {
InfernoEngine inferno; InfernoEngine inferno;
@@ -18,20 +18,23 @@ int main(int argc, char** argv) {
Sky* sky = new SolidSky({ 0.0f, 0.0f, 0.0f }, 0.0f); Sky* sky = new SolidSky({ 0.0f, 0.0f, 0.0f }, 0.0f);
scene->sky = sky; scene->sky = sky;
Sphere* sphere = new Sphere({ -0.302, -0.385999, -3.74202 }, 0.03f, new Material({ 0.345f, 0.133f, 0.050f }, 300.0f, 0.0f, 0.0f, 0.0f, false, true)); //Sphere* sphere = new Sphere({ -0.302, -0.385999, -3.74202 }, 0.03f, new Material({ 0.345f, 0.133f, 0.050f }, 300.0f, 0.0f, 0.0f, 0.0f, false, true));
scene->objects.push_back(sphere); //scene->objects.push_back(sphere);
Plane* plane = new Plane({ 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 1.0f }, new Material({ 1.0f, 1.0f, 1.0f }, 0.0f, 0.0f, 0.0f, 0.0f, false, false)); Plane* plane = new Plane({ 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 1.0f }, new Material({ 1.0f, 1.0f, 1.0f }, 0.0f, 0.0f, 0.0f, 0.0f, false, false));
scene->objects.push_back(plane); scene->objects.push_back(plane);
Material* mat = new GlossyMaterial({ 1.3f, 1.3f, 1.3f }, 0.2f, fastDegreetoRadian(30.0f)); Material* mat = new GlossyMaterial({ 1.3f, 1.3f, 1.3f }, 0.2f, fastDegreetoRadian(30.0f));
std::vector<Triangle*> tris = LoadTrianglesBasic("E://Projects//Inferno//resources//models//dragon-cornell-size.obj", "E://Projects//Inferno//resources//models", mat); //std::vector<Triangle*> tris = LoadTrianglesBasic("E://Projects//Inferno//resources//models//dragon-cornell-size.obj", "E://Projects//Inferno//resources//models", mat);
// std::vector<Triangle*> tris = LoadTrianglesBasic("/home/ben/programming/inferno/resources/models/dragon-cornell-size.obj", "/home/ben/programming/inferno/resources/models/", mat); // std::vector<Triangle*> tris = LoadTrianglesBasic("/home/ben/programming/inferno/resources/models/dragon-cornell-size.obj", "/home/ben/programming/inferno/resources/models/", mat);
Mesh* mesh = new Mesh(tris); Sphere* sphere = new Sphere({ 0.0f, -0.6f, -4.0f }, 0.4f, mat);
mesh->Translate({ 0.01f, -1.0, -3.6f }); scene->objects.push_back(sphere);
mesh->Optimise();
scene->meshs.push_back(mesh); //Mesh* mesh = new Mesh(tris);
//mesh->Translate({ 0.01f, -1.0, -3.6f });
//mesh->Optimise();
//scene->meshs.push_back(mesh);
std::vector<Triangle*> tris1 = LoadTrianglesBasic("E://Projects//Inferno//resources//models//cornell-box.obj", "E://Projects//Inferno//resources//models//"); std::vector<Triangle*> tris1 = LoadTrianglesBasic("E://Projects//Inferno//resources//models//cornell-box.obj", "E://Projects//Inferno//resources//models//");
//std::vector<Triangle*> tris1 = LoadTrianglesBasic("/home/ben/programming/inferno/resources/models/cornell-box.obj", "/home/ben/programming/inferno/resources/models/"); //std::vector<Triangle*> tris1 = LoadTrianglesBasic("/home/ben/programming/inferno/resources/models/cornell-box.obj", "/home/ben/programming/inferno/resources/models/");