added ex03 solution
This commit is contained in:
parent
8453d265e0
commit
a1f7c25ecd
7 changed files with 319 additions and 1 deletions
5
light/ambientlight.cpp
Normal file
5
light/ambientlight.cpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
#include "light/ambientlight.h"
|
||||
|
||||
Light::Illumination AmbientLight::illuminate(Scene const &scene, Ray const &ray) const {
|
||||
return {this->color * this->intensity, -ray.normal};
|
||||
}
|
16
light/ambientlight.h
Normal file
16
light/ambientlight.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef AMBIENTLIGHT_H
|
||||
#define AMBIENTLIGHT_H
|
||||
|
||||
#include "light/light.h"
|
||||
|
||||
class AmbientLight : public Light {
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
AmbientLight(float intensity, Color color = Color(1, 1, 1)) : Light(intensity, color) {}
|
||||
|
||||
// Light functions
|
||||
Illumination illuminate(Scene const &scene, Ray const &ray) const override;
|
||||
};
|
||||
|
||||
#endif
|
38
light/spotlight.cpp
Normal file
38
light/spotlight.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include "light/spotlight.h"
|
||||
#include "scene/scene.h"
|
||||
|
||||
SpotLight::SpotLight(Vector3d const &position, Vector3d const &direction, float alphaMin, float alphaMax, float intensity, Color const &color)
|
||||
: Light(intensity, color), position(position), direction(normalized(direction)), alphaMin(alphaMin), alphaMax(alphaMax) {}
|
||||
|
||||
Light::Illumination SpotLight::illuminate(Scene const &scene, Ray const &ray) const {
|
||||
Vector3d const target = ray.origin + (ray.length - LGT_EPS) * ray.direction;
|
||||
|
||||
// Illumination object
|
||||
Illumination illum;
|
||||
illum.direction = normalized(target - this->position);
|
||||
|
||||
// Precompute the distance from the light source
|
||||
float const distance = length(target - this->position);
|
||||
|
||||
// Define a secondary ray from the surface point to the light source
|
||||
Ray lightRay;
|
||||
lightRay.origin = target;
|
||||
lightRay.direction = -illum.direction;
|
||||
lightRay.length = distance - LGT_EPS;
|
||||
|
||||
// Determine the angle of the inner cone
|
||||
float const alpha = std::fabs(std::acos(dotProduct(illum.direction, this->direction)) * 180.0f / float(PI));
|
||||
|
||||
// If the target is within the cone...
|
||||
if (this->alphaMax > alpha) {
|
||||
// ... and not in shadow ...
|
||||
if (!scene.findOcclusion(lightRay)) {
|
||||
// ... compute the attenuation and light color ...
|
||||
illum.color = 1.0f / (distance * distance) * this->color * this->intensity;
|
||||
// ... then compute the falloff towards the edge of the cone
|
||||
if (this->alphaMin < alpha)
|
||||
illum.color *= 1.0f - (alpha - this->alphaMin) / (this->alphaMax - this->alphaMin);
|
||||
}
|
||||
}
|
||||
return illum;
|
||||
}
|
27
light/spotlight.h
Normal file
27
light/spotlight.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef SPOTLIGHT_H
|
||||
#define SPOTLIGHT_H
|
||||
|
||||
#include "light/light.h"
|
||||
|
||||
class SpotLight : public Light {
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
SpotLight(Vector3d const &position, Vector3d const &direction, float alphaMin, float alphaMax, float intensity,
|
||||
Color const &color = Color(1, 1, 1));
|
||||
|
||||
// Set
|
||||
void setDirection(Vector3d const &direction) { this->direction = normalized(direction); }
|
||||
void setPosition(Vector3d const &position) { this->position = position; }
|
||||
void setAlphaMax(float alphaMax) { this->alphaMax = alphaMax; }
|
||||
void setAlphaMin(float alphaMin) { this->alphaMin = alphaMin; }
|
||||
|
||||
// Light functions
|
||||
Illumination illuminate(Scene const &scene, Ray const &ray) const override;
|
||||
|
||||
protected:
|
||||
Vector3d position, direction;
|
||||
float alphaMin, alphaMax;
|
||||
};
|
||||
|
||||
#endif
|
43
primitive/objmodel.cpp
Normal file
43
primitive/objmodel.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include "primitive/objmodel.h"
|
||||
#include "primitive/box.h"
|
||||
#include "primitive/triangle.h"
|
||||
#include "scene/scene.h"
|
||||
|
||||
ObjModel::ObjModel(std::shared_ptr<Shader> const &shader) : Primitive(shader), boundingBox(Vector3d(-INFINITY, -INFINITY, -INFINITY), Vector3d(INFINITY, INFINITY, INFINITY), shader) {}
|
||||
|
||||
void ObjModel::loadObj(char const *fileName, Vector3d const &scale, Vector3d const &translation) {
|
||||
// Load faces
|
||||
this->primitives = Scene::loadObj(fileName, scale, translation, shader());
|
||||
|
||||
// Extent of box
|
||||
Vector3d minVert(INFINITY, INFINITY, INFINITY);
|
||||
Vector3d maxVert(-INFINITY, -INFINITY, -INFINITY);
|
||||
|
||||
// For each face, update the extent
|
||||
for (const auto &primitive : this->primitives) {
|
||||
minVert = Vector3d(std::min(minVert.x, primitive->minimumBounds(0)), std::min(minVert.y, primitive->minimumBounds(1)), std::min(minVert.z, primitive->minimumBounds(2)));
|
||||
maxVert = Vector3d(std::max(maxVert.x, primitive->maximumBounds(0)), std::max(maxVert.y, primitive->maximumBounds(1)), std::max(maxVert.z, primitive->maximumBounds(2)));
|
||||
}
|
||||
|
||||
// Update the bounding box
|
||||
boundingBox.setCenter(0.5f * (maxVert + minVert));
|
||||
boundingBox.setSize(maxVert - minVert + Vector3d(SPLT_EPS, SPLT_EPS, SPLT_EPS));
|
||||
}
|
||||
|
||||
bool ObjModel::intersect(Ray &ray) const {
|
||||
// Ray box intersection
|
||||
Ray boxRay = ray;
|
||||
if (boundingBox.intersect(boxRay)) {
|
||||
// ray primitive intersection
|
||||
bool hit = false;
|
||||
for (const auto &p : this->primitives) {
|
||||
hit |= p->intersect(ray);
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float ObjModel::minimumBounds(int dimension) const { return this->boundingBox.minimumBounds(dimension); }
|
||||
|
||||
float ObjModel::maximumBounds(int dimension) const { return this->boundingBox.maximumBounds(dimension); }
|
29
primitive/objmodel.h
Normal file
29
primitive/objmodel.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef OBJMODEL_H
|
||||
#define OBJMODEL_H
|
||||
|
||||
#include "primitive/box.h"
|
||||
#include <vector>
|
||||
|
||||
class ObjModel : public Primitive {
|
||||
public:
|
||||
// Constructor
|
||||
ObjModel(std::shared_ptr<Shader> const &shader);
|
||||
~ObjModel() override{};
|
||||
|
||||
// Load object data
|
||||
void loadObj(char const *fileName, Vector3d const &scale = Vector3d(1, 1, 1),
|
||||
Vector3d const &translation = Vector3d(0, 0, 0));
|
||||
|
||||
// Primitive functions
|
||||
bool intersect(Ray &ray) const override;
|
||||
|
||||
// Bounding box
|
||||
float minimumBounds(int dimension) const override;
|
||||
float maximumBounds(int dimension) const override;
|
||||
|
||||
protected:
|
||||
Box boundingBox;
|
||||
std::vector<std::shared_ptr<Primitive>> primitives;
|
||||
};
|
||||
|
||||
#endif
|
162
scene/scene.cpp
162
scene/scene.cpp
|
@ -42,7 +42,167 @@ std::vector<std::shared_ptr<Primitive>> Scene::loadObj(char const *fileName, Vec
|
|||
std::vector<std::shared_ptr<Primitive>> faces;
|
||||
std::vector<std::array<int, 3>> indices;
|
||||
|
||||
// IMPLEMENT ME
|
||||
// Open file from disk
|
||||
std::ifstream file;
|
||||
file.open(fileName);
|
||||
if (!file.is_open()) {
|
||||
std::cout << "(Scene): Could not open .obj file: " << fileName << std::endl;
|
||||
return std::vector<std::shared_ptr<Primitive>>();
|
||||
}
|
||||
|
||||
// Print the file name
|
||||
std::cout << "(Scene): Loading \"" << fileName << "\"" << std::endl;
|
||||
|
||||
// Actual model data
|
||||
std::vector<Vector3d> vData;
|
||||
std::vector<Vector3d> tangentData;
|
||||
std::vector<Vector3d> bitangentData;
|
||||
std::vector<Vector3d> normalData;
|
||||
std::vector<Vector3d> vnData;
|
||||
std::vector<Vector2d> vtData;
|
||||
|
||||
// Read vertices, normals, textures, and faces from the file
|
||||
std::string line;
|
||||
while (getline(file, line)) {
|
||||
std::stringstream lineStream(trim(line));
|
||||
std::string type;
|
||||
lineStream >> type;
|
||||
|
||||
// Vertices
|
||||
if (type == "v") {
|
||||
float x, y, z;
|
||||
lineStream >> x >> y >> z;
|
||||
vData.emplace_back(componentProduct(Vector3d(x, y, z), scale) + translation);
|
||||
tangentData.emplace_back();
|
||||
bitangentData.emplace_back();
|
||||
normalData.emplace_back();
|
||||
}
|
||||
|
||||
// Texture coordinates
|
||||
if (type == "vt") {
|
||||
float u, v;
|
||||
lineStream >> u >> v;
|
||||
vtData.emplace_back(flipU ? 1.0f - u : u, flipV ? 1.0f - v : v);
|
||||
}
|
||||
|
||||
// Normals
|
||||
if (type == "vn") {
|
||||
float a, b, c;
|
||||
lineStream >> a >> b >> c;
|
||||
vnData.emplace_back(normalized(componentQuotient(
|
||||
Vector3d(a, b, c),
|
||||
scale))); // Division needed for preventing stretched normals, normals' = (transform^-1)^T * normals
|
||||
}
|
||||
|
||||
// Faces
|
||||
if (type == "f") {
|
||||
std::string vertex[3];
|
||||
std::array<int, 3> vertInd = {-1, -1, -1};
|
||||
std::array<int, 3> texInd = {-1, -1, -1};
|
||||
std::array<int, 3> normInd = {-1, -1, -1};
|
||||
lineStream >> vertex[0] >> vertex[1] >> vertex[2];
|
||||
|
||||
// triangulate polygons, like quads (which must be given in triangle fan notation)
|
||||
while (!vertex[2].empty()) {
|
||||
auto triangle = std::make_shared<Triangle>(shader);
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
std::stringstream vertexSteam(vertex[i]);
|
||||
std::string reference;
|
||||
|
||||
// vertex index
|
||||
getline(vertexSteam, reference, '/');
|
||||
try {
|
||||
vertInd[i] = stoi(reference) - 1;
|
||||
triangle->setVertex(i, vData.at(vertInd[i]));
|
||||
} catch (...) {
|
||||
std::cout << "Error: vertex index invalid on line \"" << line << "\"" << std::endl;
|
||||
}
|
||||
|
||||
// texture index
|
||||
if (getline(vertexSteam, reference, '/')) {
|
||||
if (!reference.empty()) {
|
||||
try {
|
||||
texInd[i] = stoi(reference) - 1;
|
||||
triangle->setSurface(i, vtData.at(texInd[i]));
|
||||
} catch (...) {
|
||||
std::cout << "Error: texture coordinate index invalid on line \"" << line << "\"" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// normal index
|
||||
if (getline(vertexSteam, reference, '/')) {
|
||||
try {
|
||||
normInd[i] = stoi(reference) - 1;
|
||||
triangle->setNormal(i, vnData.at(normInd[i]));
|
||||
} catch (...) {
|
||||
std::cout << "Error: normal index invalid on line \"" << line << "\"" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// calculate and accumulate tangent and bitangent vectors
|
||||
if (std::all_of(vertInd.begin(), vertInd.end(), [](int i) { return i > -1; }) &&
|
||||
std::all_of(texInd.begin(), texInd.end(), [](int i) { return i > -1; })) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
const Vector3d deltaPos1 = vData.at(vertInd[(i + 1) % 3]) - vData.at(vertInd[i]);
|
||||
const Vector3d deltaPos2 = vData.at(vertInd[(i + 2) % 3]) - vData.at(vertInd[i]);
|
||||
|
||||
const Vector2d deltaUV1 = vtData.at(texInd[(i + 1) % 3]) - vtData.at(texInd[i]);
|
||||
const Vector2d deltaUV2 = vtData.at(texInd[(i + 2) % 3]) - vtData.at(texInd[i]);
|
||||
|
||||
const float r = 1.0f / (deltaUV1.u * deltaUV2.v - deltaUV1.v * deltaUV2.u);
|
||||
tangentData[vertInd[i]] += (deltaPos1 * deltaUV2.v - deltaPos2 * deltaUV1.v) * r;
|
||||
bitangentData[vertInd[i]] += (deltaPos2 * deltaUV1.u - deltaPos1 * deltaUV2.u) * r;
|
||||
|
||||
normalData[vertInd[i]] += crossProduct(tangentData[vertInd[i]], bitangentData[vertInd[i]]);
|
||||
}
|
||||
}
|
||||
|
||||
faces.push_back(triangle);
|
||||
indices.push_back(vertInd);
|
||||
|
||||
// get the next triangle
|
||||
if (lineStream.eof())
|
||||
break;
|
||||
|
||||
vertex[1] = vertex[2];
|
||||
lineStream >> vertex[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the file
|
||||
file.close();
|
||||
|
||||
// set the normalized tangents and bitangents
|
||||
for (int i = 0; i < faces.size(); i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
Vector3d tangent = normalized(tangentData[indices[i][j]]);
|
||||
const Vector3d bitangent = normalized(bitangentData[indices[i][j]]);
|
||||
// try to use the normal from the obj file, if it doesn't exist, use the computed normal
|
||||
Vector3d normal = normalized(normalData[indices[i][j]]);
|
||||
if (vnData.size() > 0)
|
||||
normal = dynamic_cast<Triangle *>(faces[i].get())->getNormal(j);
|
||||
|
||||
// gram-schmidt orthogonalization
|
||||
tangent = normalized(tangent - normal * dotProduct(normal, tangent));
|
||||
// check handedness of coordinate system
|
||||
if (dotProduct(crossProduct(normal, tangent), bitangent) < 0.0f)
|
||||
tangent *= -1.0f;
|
||||
|
||||
dynamic_cast<Triangle *>(faces[i].get())->setTangent(j, tangent);
|
||||
dynamic_cast<Triangle *>(faces[i].get())->setBitangent(j, bitangent);
|
||||
dynamic_cast<Triangle *>(faces[i].get())->setNormal(j, normal);
|
||||
}
|
||||
}
|
||||
|
||||
// Debug output
|
||||
std::cout << " -> " << vData.size() << " vertices parsed" << std::endl;
|
||||
std::cout << " -> " << vnData.size() << " normals parsed" << std::endl;
|
||||
std::cout << " -> " << vtData.size() << " uv-positions parsed" << std::endl;
|
||||
std::cout << " -> " << faces.size() << " primitives parsed" << std::endl;
|
||||
|
||||
return faces;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue