This project contains the following files (right-click files you'd like to download):
teapot.cppteapotdata.hgeometry.h//[header]
// This program generate and render the Utah teapot
//[/header]
//[compile]
// Download the raytracetransform.cpp, geometry.h and teapot.geo file to a folder.
// Open a shell/terminal, and run the following command where the files are saved:
//
// c++ -std=c++11 -o teapot -O3 teapot.cpp
//
// Run with: ./shading. Open the file ./out.png in Photoshop or any program
// reading PPM files.
//[/compile]
//[ignore]
// Copyright (C) 2012 www.scratchapixel.com
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//[/ignore]
#include <cstdio>
#include <cstdlib>
#include <memory>
#include <vector>
#include <utility>
#include <cstdint>
#include <iostream>
#include <fstream>
#include <cmath>
#include <sstream>
#include <chrono>
#include <random>
#include "geometry.h"
#include "teapotdata.h"
// not define on every system
#ifndef M_PI
#define M_PI 3.14159265358979323846 /* pi */
#endif
static const float kInfinity = std::numeric_limits<float>::max();
static const float kEpsilon = 1e-8;
static const Vec3f kDefaultBackgroundColor = Vec3f(0.235294, 0.67451, 0.843137);
template <> const Matrix44f Matrix44f::kIdentity = Matrix44f();
inline
float clamp(const float &lo, const float &hi, const float &v)
{ return std::max(lo, std::min(hi, v)); }
inline
float deg2rad(const float °)
{ return deg * M_PI / 180; }
inline
Vec3f mix(const Vec3f &a, const Vec3f& b, const float &mixValue)
{ return a * (1 - mixValue) + b * mixValue; }
struct Options
{
uint32_t width = 640;
uint32_t height = 480;
float fov = 90;
Vec3f backgroundColor = kDefaultBackgroundColor;
Matrix44f cameraToWorld;
float bias = 0.0001;
uint32_t maxDepth = 2;
};
enum MaterialType { kDiffuse, kNothing };
class Object
{
public:
// [comment]
// Setting up the object-to-world and world-to-object matrix
// [/comment]
Object(const Matrix44f &o2w) : objectToWorld(o2w), worldToObject(o2w.inverse()) {}
virtual ~Object() {}
virtual bool intersect(const Vec3f &, const Vec3f &, float &, uint32_t &, Vec2f &) const = 0;
virtual void getSurfaceProperties(const Vec3f &, const Vec3f &, const uint32_t &, const Vec2f &, Vec3f &, Vec2f &) const = 0;
virtual void displayInfo() const = 0;
Matrix44f objectToWorld, worldToObject;
MaterialType type = kDiffuse;
Vec3f albedo = 0.18;
float Kd = 0.8; // phong model diffuse weight
float Ks = 0.2; // phong model specular weight
float n = 10; // phong specular exponent
Vec3f BBox[2] = {kInfinity, -kInfinity};
};
bool rayTriangleIntersect(
const Vec3f &orig, const Vec3f &dir,
const Vec3f &v0, const Vec3f &v1, const Vec3f &v2,
float &t, float &u, float &v)
{
Vec3f v0v1 = v1 - v0;
Vec3f v0v2 = v2 - v0;
Vec3f pvec = dir.crossProduct(v0v2);
float det = v0v1.dotProduct(pvec);
// ray and triangle are parallel if det is close to 0
if (fabs(det) < kEpsilon) return false;
float invDet = 1 / det;
Vec3f tvec = orig - v0;
u = tvec.dotProduct(pvec) * invDet;
if (u < 0 || u > 1) return false;
Vec3f qvec = tvec.crossProduct(v0v1);
v = dir.dotProduct(qvec) * invDet;
if (v < 0 || u + v > 1) return false;
t = v0v2.dotProduct(qvec) * invDet;
return (t > 0) ? true : false;
}
class TriangleMesh : public Object
{
public:
// Build a triangle mesh from a face index array and a vertex index array
TriangleMesh(
const Matrix44f &o2w,
const uint32_t nfaces,
const std::unique_ptr<uint32_t []> &faceIndex,
const std::unique_ptr<uint32_t []> &vertsIndex,
const std::unique_ptr<Vec3f []> &verts,
std::unique_ptr<Vec3f []> &normals,
std::unique_ptr<Vec2f []> &st,
bool singleVertAttr = true) :
Object(o2w),
numTris(0),
isSingleVertAttr(singleVertAttr)
{
uint32_t k = 0, maxVertIndex = 0;
// find out how many triangles we need to create for this mesh
for (uint32_t i = 0; i < nfaces; ++i) {
numTris += faceIndex[i] - 2;
for (uint32_t j = 0; j < faceIndex[i]; ++j)
if (vertsIndex[k + j] > maxVertIndex)
maxVertIndex = vertsIndex[k + j];
k += faceIndex[i];
}
maxVertIndex += 1;
// allocate memory to store the position of the mesh vertices
P = std::unique_ptr<Vec3f []>(new Vec3f[maxVertIndex]);
for (uint32_t i = 0; i < maxVertIndex; ++i) {
objectToWorld.multVecMatrix(verts[i], P[i]);
}
// allocate memory to store triangle indices
trisIndex = std::unique_ptr<uint32_t []>(new uint32_t [numTris * 3]);
Matrix44f transformNormals = worldToObject.transpose();
// [comment]
// Sometimes we have 1 vertex attribute per vertex per face. So for example of you have 2
// quads this would be defefined by 6 vertices but 2 * 4 vertex attribute values for
// each vertex attribute (normal, tex. coordinates, etc.). But in some cases you may
// want to have 1 single value per vertex. So in the quad example this would be 6 vertices
// and 6 vertex attributes values per attribute. We need to provide both option to users.
// [/comment]
if (isSingleVertAttr) {
N = std::unique_ptr<Vec3f []>(new Vec3f[maxVertIndex]);
texCoordinates = std::unique_ptr<Vec2f []>(new Vec2f[maxVertIndex]);
for (uint32_t i = 0; i < maxVertIndex; ++i) {
texCoordinates[i] = st[i];
transformNormals.multDirMatrix(normals[i], N[i]);
}
}
else {
N = std::unique_ptr<Vec3f []>(new Vec3f[numTris * 3]);
texCoordinates = std::unique_ptr<Vec2f []>(new Vec2f[numTris * 3]);
for (uint32_t i = 0, k = 0, l = 0; i < nfaces; ++i) { // for each face
for (uint32_t j = 0; j < faceIndex[i] - 2; ++j) {
transformNormals.multDirMatrix(normals[k], N[l]);
transformNormals.multDirMatrix(normals[k + j + 1], N[l + 1]);
transformNormals.multDirMatrix(normals[k + j + 2], N[l + 2]);
N[l].normalize();
N[l + 1].normalize();
N[l + 2].normalize();
texCoordinates[l] = st[k];
texCoordinates[l + 1] = st[k + j + 1];
texCoordinates[l + 2] = st[k + j + 2];
}
k += faceIndex[i];
}
}
// generate the triangle index array and set normals and st coordinates
for (uint32_t i = 0, k = 0, l = 0; i < nfaces; ++i) { // for each face
for (uint32_t j = 0; j < faceIndex[i] - 2; ++j) { // for each triangle in the face
trisIndex[l] = vertsIndex[k];
trisIndex[l + 1] = vertsIndex[k + j + 1];
trisIndex[l + 2] = vertsIndex[k + j + 2];
l += 3;
}
k += faceIndex[i];
}
}
// Test if the ray interesests this triangle mesh
bool intersect(const Vec3f &orig, const Vec3f &dir, float &tNear, uint32_t &triIndex, Vec2f &uv) const
{
uint32_t j = 0;
bool isect = false;
for (uint32_t i = 0; i < numTris; ++i) {
const Vec3f &v0 = P[trisIndex[j]];
const Vec3f &v1 = P[trisIndex[j + 1]];
const Vec3f &v2 = P[trisIndex[j + 2]];
float t = kInfinity, u, v;
if (rayTriangleIntersect(orig, dir, v0, v1, v2, t, u, v) && t < tNear) {
tNear = t;
uv.x = u;
uv.y = v;
triIndex = i;
isect = true;
}
j += 3;
}
return isect;
}
void getSurfaceProperties(
const Vec3f &hitPoint,
const Vec3f &viewDirection,
const uint32_t &triIndex,
const Vec2f &uv,
Vec3f &hitNormal,
Vec2f &hitTextureCoordinates) const
{
uint32_t vai[3]; // vertex attr index
if (isSingleVertAttr) {
vai[0] = trisIndex[triIndex * 3];
vai[1] = trisIndex[triIndex * 3 + 1];
vai[2] = trisIndex[triIndex * 3 + 2];
}
else {
vai[0] = triIndex * 3;
vai[1] = triIndex * 3 + 1;
vai[2] = triIndex * 3 + 2;
}
if (smoothShading) {
// vertex normal
const Vec3f &n0 = N[vai[0]];
const Vec3f &n1 = N[vai[1]];
const Vec3f &n2 = N[vai[2]];
hitNormal = (1 - uv.x - uv.y) * n0 + uv.x * n1 + uv.y * n2;
}
else {
// face normal
const Vec3f &v0 = P[trisIndex[triIndex * 3]];
const Vec3f &v1 = P[trisIndex[triIndex * 3 + 1]];
const Vec3f &v2 = P[trisIndex[triIndex * 3 + 2]];
hitNormal = (v1 - v0).crossProduct(v2 - v0);
}
// doesn't need to be normalized as the N's are normalized but just for safety
hitNormal.normalize();
// texture coordinates
const Vec2f &st0 = texCoordinates[vai[0]];
const Vec2f &st1 = texCoordinates[vai[1]];
const Vec2f &st2 = texCoordinates[vai[2]];
hitTextureCoordinates = (1 - uv.x - uv.y) * st0 + uv.x * st1 + uv.y * st2;
}
void displayInfo() const
{
std::cerr << "Number of triangles in this mesh: " << numTris << std::endl;
std::cerr << BBox[0] << ", " << BBox[1] << std::endl;
}
// member variables
uint32_t numTris; // number of triangles
std::unique_ptr<Vec3f []> P; // triangles vertex position
std::unique_ptr<uint32_t []> trisIndex; // vertex index array
std::unique_ptr<Vec3f []> N; // triangles vertex normals
std::unique_ptr<Vec2f []> texCoordinates; // triangles texture coordinates
bool smoothShading = true; // smooth shading by default
bool isSingleVertAttr = true;
};
class Light
{
public:
Light(const Matrix44f &l2w, const Vec3f &c = 1, const float &i = 1) : lightToWorld(l2w), color(c), intensity(i) {}
virtual ~Light() {}
virtual void illuminate(const Vec3f &P, Vec3f &, Vec3f &, float &) const = 0;
Vec3f color;
float intensity;
Matrix44f lightToWorld;
};
struct IsectInfo
{
const Object *hitObject = nullptr;
float tNear = kInfinity;
Vec2f uv;
uint32_t index = 0;
};
bool trace(
const Vec3f &orig, const Vec3f &dir,
const std::vector<std::unique_ptr<Object>> &objects,
IsectInfo &isect)
{
isect.hitObject = nullptr;
for (uint32_t k = 0; k < objects.size(); ++k) {
float tNearTriangle = kInfinity;
uint32_t indexTriangle;
Vec2f uvTriangle;
if (objects[k]->intersect(orig, dir, tNearTriangle, indexTriangle, uvTriangle) && tNearTriangle < isect.tNear) {
isect.hitObject = objects[k].get();
isect.tNear = tNearTriangle;
isect.index = indexTriangle;
isect.uv = uvTriangle;
}
}
return (isect.hitObject != nullptr);
}
Vec3f castRay(
const Vec3f &orig, const Vec3f &dir,
const std::vector<std::unique_ptr<Object>> &objects,
const std::vector<std::unique_ptr<Light>> &lights,
const Options &options,
const uint32_t & depth = 0)
{
if (depth > options.maxDepth) return 0;
Vec3f hitColor = 0;
IsectInfo isect;
if (trace(orig, dir, objects, isect)) {
Vec3f hitPoint = orig + dir * isect.tNear;
Vec3f hitNormal;
Vec2f hitTexCoordinates;
isect.hitObject->getSurfaceProperties(hitPoint, dir, isect.index, isect.uv, hitNormal, hitTexCoordinates);
hitColor = std::max(0.f, -hitNormal.dotProduct(dir)) ;//* Vec3f(hitTexCoordinates.x, hitTexCoordinates.y, 1);
}
else {
hitColor = 0.3;
}
return hitColor;
}
// [comment]
// The main render function. This where we iterate over all pixels in the image, generate
// primary rays and cast these rays into the scene. The content of the framebuffer is
// saved to a file.
// [/comment]
void render(
const Options &options,
const std::vector<std::unique_ptr<Object>> &objects,
const std::vector<std::unique_ptr<Light>> &lights)
{
Vec3f *framebuffer = new Vec3f[options.width * options.height];
Vec3f *pix = framebuffer;
float scale = tan(deg2rad(options.fov * 0.5));
float imageAspectRatio = options.width / (float)options.height;
Vec3f orig;
options.cameraToWorld.multVecMatrix(Vec3f(0), orig);
auto timeStart = std::chrono::high_resolution_clock::now();
for (uint32_t j = 0; j < options.height; ++j) {
for (uint32_t i = 0; i < options.width; ++i) {
// generate primary ray direction
float x = (2 * (i + 0.5) / (float)options.width - 1) * imageAspectRatio * scale;
float y = (1 - 2 * (j + 0.5) / (float)options.height) * scale;
Vec3f dir;
options.cameraToWorld.multDirMatrix(Vec3f(x, y, -1), dir);
dir.normalize();
*(pix++) = castRay(orig, dir, objects, lights, options);
}
fprintf(stderr, "\r%3d%c", uint32_t(j / (float)options.height * 100), '%');
}
auto timeEnd = std::chrono::high_resolution_clock::now();
auto passedTime = std::chrono::duration<double, std::milli>(timeEnd - timeStart).count();
fprintf(stderr, "\rDone: %.2f (sec)\n", passedTime / 1000);
// save framebuffer to file
float gamma = 1;
std::ofstream ofs;
ofs.open("out.ppm");
ofs << "P6\n" << options.width << " " << options.height << "\n255\n";
for (uint32_t i = 0; i < options.height * options.width; ++i) {
char r = (char)(255 * clamp(0, 1, powf(framebuffer[i].x, 1 / gamma)));
char g = (char)(255 * clamp(0, 1, powf(framebuffer[i].y, 1 / gamma)));
char b = (char)(255 * clamp(0, 1, powf(framebuffer[i].z, 1 / gamma)));
ofs << r << g << b;
}
ofs.close();
delete [] framebuffer;
}
// [comment]
// Compute the position of a point along a Bezier curve at t [0:1]
// [/comment]
Vec3f evalBezierCurve(const Vec3f *P, const float &t)
{
float b0 = (1 - t) * (1 - t) * (1 - t);
float b1 = 3 * t * (1 - t) * (1 - t);
float b2 = 3 * t * t * (1 - t);
float b3 = t * t * t;
return P[0] * b0 + P[1] * b1 + P[2] * b2 + P[3] * b3;
}
Vec3f evalBezierPatch(const Vec3f *controlPoints, const float &u, const float &v)
{
Vec3f uCurve[4];
for (int i = 0; i < 4; ++i)
uCurve[i] = evalBezierCurve(controlPoints + 4 * i, u);
return evalBezierCurve(uCurve, v);
}
Vec3f derivBezier(const Vec3f *P, const float &t)
{
return -3 * (1 - t) * (1 - t) * P[0] +
(3 * (1 - t) * (1 - t) - 6 * t * (1 - t)) * P[1] +
(6 * t * (1 - t) - 3 * t * t) * P[2] +
3 * t * t * P[3];
}
// [comment]
// Compute the derivative of a point on Bezier patch along the u parametric direction
// [/comment]
Vec3f dUBezier(const Vec3f *controlPoints, const float &u, const float &v)
{
Vec3f P[4];
Vec3f vCurve[4];
for (int i = 0; i < 4; ++i) {
P[0] = controlPoints[i];
P[1] = controlPoints[4 + i];
P[2] = controlPoints[8 + i];
P[3] = controlPoints[12 + i];
vCurve[i] = evalBezierCurve(P, v);
}
return derivBezier(vCurve, u);
}
// [comment]
// Compute the derivative of a point on Bezier patch along the v parametric direction
// [/comment]
Vec3f dVBezier(const Vec3f *controlPoints, const float &u, const float &v)
{
Vec3f uCurve[4];
for (int i = 0; i < 4; ++i) {
uCurve[i] = evalBezierCurve(controlPoints + 4 * i, u);
}
return derivBezier(uCurve, v);
}
// [comment]
// Generate a poly-mesh Utah teapot out of Bezier patches
// [/comment]
void createPolyTeapot(const Matrix44f& o2w, std::vector<std::unique_ptr<Object>> &objects)
{
uint32_t divs = 8;
std::unique_ptr<Vec3f []> P(new Vec3f[(divs + 1) * (divs + 1)]);
std::unique_ptr<uint32_t []> nvertices(new uint32_t[divs * divs]);
std::unique_ptr<uint32_t []> vertices(new uint32_t[divs * divs * 4]);
std::unique_ptr<Vec3f []> N(new Vec3f[(divs + 1) * (divs + 1)]);
std::unique_ptr<Vec2f []> st(new Vec2f[(divs + 1) * (divs + 1)]);
// face connectivity - all patches are subdivided the same way so there
// share the same topology and uvs
for (uint16_t j = 0, k = 0; j < divs; ++j) {
for (uint16_t i = 0; i < divs; ++i, ++k) {
nvertices[k] = 4;
vertices[k * 4 ] = (divs + 1) * j + i;
vertices[k * 4 + 1] = (divs + 1) * j + i + 1;
vertices[k * 4 + 2] = (divs + 1) * (j + 1) + i + 1;
vertices[k * 4 + 3] = (divs + 1) * (j + 1) + i;
}
}
Vec3f controlPoints[16];
for (int np = 0; np < kTeapotNumPatches; ++np) { // kTeapotNumPatches
// set the control points for the current patch
for (uint32_t i = 0; i < 16; ++i)
controlPoints[i][0] = teapotVertices[teapotPatches[np][i] - 1][0],
controlPoints[i][1] = teapotVertices[teapotPatches[np][i] - 1][1],
controlPoints[i][2] = teapotVertices[teapotPatches[np][i] - 1][2];
// generate grid
for (uint16_t j = 0, k = 0; j <= divs; ++j) {
float v = j / (float)divs;
for (uint16_t i = 0; i <= divs; ++i, ++k) {
float u = i / (float)divs;
P[k] = evalBezierPatch(controlPoints, u, v);
Vec3f dU = dUBezier(controlPoints, u, v);
Vec3f dV = dVBezier(controlPoints, u, v);
N[k] = dU.crossProduct(dV).normalize();
st[k].x = u;
st[k].y = v;
}
}
objects.push_back(std::unique_ptr<TriangleMesh>(new TriangleMesh(o2w, divs * divs, nvertices, vertices, P, N, st)));
}
}
// [comment]
// Bezier curve control points
// [/comment]
constexpr uint32_t curveNumPts = 22;
Vec3f curveData[curveNumPts] = {
{-0.0029370324, 0.0297554422, 0},
{-0.1556627219, 0.3293327560, 0},
{-0.2613958914, 0.9578577085, 0},
{-0.2555218265, 1.3044275420, 0},
{-0.2496477615, 1.6509973760, 0},
{-0.1262923970, 2.0445597290, 0},
{ 0.1791589818, 2.2853963930, 0},
{ 0.4846103605, 2.5262330570, 0},
{ 0.9427874287, 2.2560260680, 0},
{ 1.0132762080, 1.9212043650, 0},
{ 1.0837649880, 1.5863826610, 0},
{ 0.9369133637, 1.2750572170, 0},
{ 0.6667063748, 1.2691831520, 0},
{ 0.3964993859, 1.2633090870, 0},
{ 0.2320255666, 1.3514200620, 0},
{ 0.1850330468, 1.5276420110, 0},
{ 0.1380405269, 1.7038639600, 0},
{ 0.2026552417, 1.8918340400, 0},
{ 0.4082475158, 1.9564487540, 0},
{ 0.6138397900, 2.0210634690, 0},
{ 0.7606914144, 1.8800859100, 0},
{ 0.7606914144, 1.7038639600, 0}
};
// [comment]
// Generate a thin cylinder centred around a Bezier curve
// [/comment]
void createCurveGeometry(std::vector<std::unique_ptr<Object>> &objects)
{
uint32_t ndivs = 16;
uint32_t ncurves = 1 + (curveNumPts - 4) / 3;
Vec3f pts[4];
std::unique_ptr<Vec3f []> P(new Vec3f[(ndivs + 1) * ndivs * ncurves + 1]);
std::unique_ptr<Vec3f []> N(new Vec3f[(ndivs + 1) * ndivs * ncurves + 1]);
std::unique_ptr<Vec2f []> st(new Vec2f[(ndivs + 1) * ndivs * ncurves + 1]);
for (uint32_t i = 0; i < ncurves; ++i) {
for (uint32_t j = 0; j < ndivs; ++j) {
pts[0] = curveData[i * 3];
pts[1] = curveData[i * 3 + 1];
pts[2] = curveData[i * 3 + 2];
pts[3] = curveData[i * 3 + 3];
float s = j / (float)ndivs;
Vec3f pt = evalBezierCurve(pts, s);
Vec3f tangent = derivBezier(pts, s).normalize();
bool swap = false;
uint8_t maxAxis;
if (std::abs(tangent.x) > std::abs(tangent.y))
if (std::abs(tangent.x) > std::abs(tangent.z))
maxAxis = 0;
else
maxAxis = 2;
else if (std::abs(tangent.y) > std::abs(tangent.z))
maxAxis = 1;
else
maxAxis = 2;
Vec3f up, forward, right;
switch (maxAxis) {
case 0:
case 1:
up = tangent;
forward = Vec3f(0, 0, 1);
right = up.crossProduct(forward);
forward = right.crossProduct(up);
break;
case 2:
up = tangent;
right = Vec3f(0, 0, 1);
forward = right.crossProduct(up);
right = up.crossProduct(forward);
break;
default:
break;
};
float sNormalized = (i * ndivs + j) / float(ndivs * ncurves);
float rad = 0.1 * (1 - sNormalized);
for (uint32_t k = 0; k <= ndivs; ++k) {
float t = k / (float)ndivs;
float theta = t * 2 * M_PI;
Vec3f pc(cos(theta) * rad, 0, sin(theta) * rad);
float x = pc.x * right.x + pc.y * up.x + pc.z * forward.x;
float y = pc.x * right.y + pc.y * up.y + pc.z * forward.y;
float z = pc.x * right.z + pc.y * up.z + pc.z * forward.z;
P[i * (ndivs + 1) * ndivs + j * (ndivs + 1) + k] = Vec3f(pt.x + x, pt.y + y, pt.z + z);
N[i * (ndivs + 1) * ndivs + j * (ndivs + 1) + k] = Vec3f(x, y, z).normalize();
st[i * (ndivs + 1) * ndivs + j * (ndivs + 1) + k] = Vec2f(sNormalized, t);
}
}
}
P[(ndivs + 1) * ndivs * ncurves] = curveData[curveNumPts - 1];
N[(ndivs + 1) * ndivs * ncurves] = (curveData[curveNumPts - 2] - curveData[curveNumPts - 1]).normalize();
st[(ndivs + 1) * ndivs * ncurves] = Vec2f(1, 0.5);
uint32_t numFaces = ndivs * ndivs * ncurves;
std::unique_ptr<uint32_t []> verts(new uint32_t[numFaces]);
for (uint32_t i = 0; i < numFaces; ++i)
verts[i] = (i < (numFaces - ndivs)) ? 4 : 3;
std::unique_ptr<uint32_t []> vertIndices(new uint32_t[ndivs * ndivs * ncurves * 4 + ndivs * 3]);
uint32_t nf = 0, ix = 0;
for (uint32_t k = 0; k < ncurves; ++k) {
for (uint32_t j = 0; j < ndivs; ++j) {
if (k == (ncurves - 1) && j == (ndivs - 1)) { break; }
for (uint32_t i = 0; i < ndivs; ++i) {
vertIndices[ix] = nf;
vertIndices[ix + 1] = nf + (ndivs + 1);
vertIndices[ix + 2] = nf + (ndivs + 1) + 1;
vertIndices[ix + 3] = nf + 1;
ix += 4;
++nf;
}
nf++;
}
}
for (uint32_t i = 0; i < ndivs; ++i) {
vertIndices[ix] = nf;
vertIndices[ix + 1] = (ndivs + 1) * ndivs * ncurves;
vertIndices[ix + 2] = nf + 1;
ix += 3;
nf++;
}
objects.push_back(std::unique_ptr<TriangleMesh>(new TriangleMesh(Matrix44f::kIdentity, numFaces, verts, vertIndices, P, N, st)));
}
// [comment]
// In the main function of the program, we create the scene (create objects and lights)
// as well as set the options for the render (image widht and height, maximum recursion
// depth, field-of-view, etc.). We then call the render function().
// [/comment]
int main(int argc, char **argv)
{
// loading gemetry
std::vector<std::unique_ptr<Object>> objects;
createPolyTeapot(Matrix44f(1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1), objects);
//createCurveGeometry(objects);
// lights
std::vector<std::unique_ptr<Light>> lights;
Options options;
// aliasing example
options.fov = 39.89;
options.width = 512;
options.height = 512;
options.maxDepth = 1;
// to render the teapot
options.cameraToWorld = Matrix44f(0.897258, 0, -0.441506, 0, -0.288129, 0.757698, -0.585556, 0, 0.334528, 0.652606, 0.679851, 0, 5.439442, 11.080794, 10.381341, 1);
// to render the curve as geometry
//options.cameraToWorld = Matrix44f(0.707107, 0, -0.707107, 0, -0.369866, 0.85229, -0.369866, 0, 0.60266, 0.523069, 0.60266, 0, 2.634, 3.178036, 2.262122, 1);
// finally, render
render(options, objects, lights);
return 0;
}