-
-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
354 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Minimal graphics support routines from Computer Graphics: Principles and Practice 3rd edition | ||
for loading a 3D model and image, and displaying and saving images. | ||
|
||
Morgan McGuire 2012 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
/** | ||
Created by Morgan McGuire in 2012 | ||
Released into the public domain | ||
*/ | ||
#include <GLG3D/GLG3D.h> | ||
#include <GL/glut.h> | ||
#include "supportclasses.h" | ||
|
||
int Image::PPMGammaCorrect(float radiance, float e) const { | ||
// Note that the PPM gamma is fixed at 2.2 | ||
return int(pow(std::min(1.0f, std::max(0.0f, radiance * e)), 1.0f / 2.2f) * 255.0f); | ||
} | ||
|
||
|
||
void Image::save(const std::string& filename, float e) const { | ||
FILE* file = fopen(filename.c_str(), "wt"); | ||
fprintf(file, "P3 %d %d 255\n", m_width, m_height); | ||
for (int y = 0; y < m_height; ++y) { | ||
fprintf(file, "\n# y = %d\n", y); | ||
for (int x = 0; x < m_width; ++x) { | ||
const Color3& c(get(x, y)); | ||
fprintf(file, "%d %d %d\n", | ||
PPMGammaCorrect(c.r, e), | ||
PPMGammaCorrect(c.g, e), | ||
PPMGammaCorrect(c.b, e)); | ||
} | ||
} | ||
fclose(file); | ||
} | ||
|
||
|
||
#if USE_OPENGL | ||
|
||
static void quitOnEscape(unsigned char key, int x, int y) { | ||
if (key == 27) { ::exit(0); } | ||
} | ||
|
||
|
||
static void render() { | ||
glClear(GL_COLOR_BUFFER_BIT); | ||
// Draw a full-screen quad of the image | ||
glDrawArrays(GL_QUADS, 0, 4); | ||
glutSwapBuffers(); | ||
} | ||
|
||
|
||
void Image::display(float exposureConstant, float deviceGamma) const { | ||
int argc = 0; | ||
|
||
// Initialize OpenGL | ||
glutInit(&argc, NULL); | ||
glutInitWindowSize(m_width, m_height); | ||
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); | ||
glutCreateWindow("Rendered result"); | ||
glutKeyboardFunc(&quitOnEscape); | ||
glutDisplayFunc(&render); | ||
|
||
// Initialize OpenGL extensions | ||
glewInit(); | ||
|
||
// Set the color scale applied as textures are uploaded to be the exposure constant | ||
glMatrixMode(GL_COLOR); | ||
glLoadIdentity(); | ||
glScalef(exposureConstant, exposureConstant, exposureConstant); | ||
|
||
// Create a gamma correction color table for texture load | ||
std::vector<Color3> gammaTable(256); | ||
for (unsigned int i = 0; i < gammaTable.size(); ++i) { | ||
gammaTable[i] = (Color3::white() * i / (gammaTable.size() - 1.0f)).pow(1.0f / deviceGamma); | ||
} | ||
glColorTable(GL_POST_COLOR_MATRIX_COLOR_TABLE, GL_RGB, gammaTable.size(), GL_RGB, GL_FLOAT, &gammaTable[0]); | ||
glEnable(GL_POST_COLOR_MATRIX_COLOR_TABLE); | ||
|
||
// Create a texture, upload our image, and bind it (assume a | ||
// version of GL that supports NPOT textures) | ||
GLuint texture; | ||
glGenTextures(1, &texture); | ||
glBindTexture(GL_TEXTURE_2D, texture); | ||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_width, m_height, 0, GL_RGB, GL_FLOAT, &m_data[0]); | ||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); | ||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); | ||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | ||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); | ||
glEnable(GL_TEXTURE_2D); | ||
|
||
// The vertices of a 2D quad mesh containing a single CCW square | ||
static const Vector2 corner[] = {Vector2(0,0), Vector2(0,1), Vector2(1,1), Vector2(1,0)}; | ||
|
||
// Bind the quad mesh as the active geometry | ||
glVertexPointer(2, GL_FLOAT, 0, corner); | ||
glTexCoordPointer(2, GL_FLOAT, 0, corner); | ||
glEnableClientState(GL_VERTEX_ARRAY); | ||
glEnableClientState(GL_TEXTURE_COORD_ARRAY); | ||
|
||
// Set orthographic projection that stretches the unit square to the | ||
// dimensions of the image | ||
glMatrixMode(GL_PROJECTION); | ||
glLoadIdentity(); | ||
glOrtho(0, 1, 1, 0, 0, 2); | ||
glutMainLoop(); | ||
} | ||
|
||
#endif | ||
|
||
/////////////////////////////////////////////////////////////////////////////////// | ||
|
||
void Scene::addModel | ||
(const std::string& filename, | ||
const BSDF& bsdf, | ||
const float scale, | ||
const Vector3& origin, | ||
float y, float p, float r) { | ||
|
||
const G3D::CFrame& frame = G3D::CFrame::fromXYZYPRDegrees(origin.x, origin.y, origin.z, y, p, r); | ||
|
||
// (It is much easier to use G3D::ArticulatedModel to load a | ||
// model, however, that requires that OpenGL already be | ||
// initialized. The following code works even without an OpenGL | ||
// context and spells out some of the details of the model loading | ||
// process.) | ||
G3D::Array<int> index; | ||
G3D::Array<Vector3> vertex, wvertex; | ||
G3D::Array<Vector3> normal, wnormal; | ||
G3D::Array<Vector2> texCoord; | ||
std::string ignore; | ||
|
||
G3D::IFSModel::load(filename, ignore, index, vertex, texCoord); | ||
G3D::Welder::weld(vertex, texCoord, normal, index, G3D::Welder::Settings()); | ||
|
||
// Transform to a new position | ||
for (int i = 0; i < vertex.size(); ++i) { | ||
vertex[i] *= scale; | ||
} | ||
frame.pointToWorldSpace(vertex, wvertex); | ||
frame.normalToWorldSpace(normal, wnormal); | ||
|
||
//index.resize(12); | ||
for (int i = 0; i < index.size(); i += 3) { | ||
int i0 = index[i + 0]; | ||
int i1 = index[i + 1]; | ||
int i2 = index[i + 2]; | ||
triangleArray.push_back(Triangle(wvertex[i0], wvertex[i1], wvertex[i2], | ||
wnormal[i0], wnormal[i1], wnormal[i2], | ||
bsdf)); | ||
/* | ||
G3D::debugPrintf("%s ", wvertex[i0].toString().c_str()); | ||
G3D::debugPrintf("%s ", wvertex[i1].toString().c_str()); | ||
G3D::debugPrintf("%s\n", wvertex[i2].toString().c_str());*/ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
/** | ||
This is a tiny support code base that gives you enough | ||
infrastructure to write a "setPixel"-based renderer without any | ||
boilerplate in your own code. | ||
It defines Vector3, Vector2, Color3, Ray, Triangle, and Image | ||
classes. Image can save to a PPM or display on the screen. It | ||
works on Windows, Linux, and OS X. Images are gamma-corrected and | ||
exposure-adjusted both on disk and on screen. | ||
This implementation uses a few pieces of some freely available | ||
cross-platform libraries. You need to link to the non-OpenGL part | ||
of G3D (for the geometry classes), OpenGL, GLUT, and GLEW. If you | ||
don't like to depend on libraries with "GL" in the name, then change | ||
the USE_OPENGL macro definition and the preprocessor will remove | ||
Image::display(). If you don't like the G3D dependency either, then | ||
download the alternative stripped-down version of the geometry | ||
clases and you will have zero external dependencies. | ||
Consider using the G3D library's GApp framework instead of this | ||
minimal base. Doing so requires learning a little more C++ and a | ||
new API. In return for that investment, G3D provides cross-platform | ||
GUI support, model loading, in-engine debugging tools, and an | ||
extensive set of utility routines (some of which a professor may | ||
choose to outlaw in a graphics course). | ||
Created by Morgan McGuire in 2012 | ||
Released into the public domain | ||
*/ | ||
#ifndef supportclasses_h | ||
#define supportclasses_h | ||
|
||
#include <G3D/platform.h> | ||
#include <G3D/Vector2.h> | ||
#include <G3D/Vector3.h> | ||
#include <G3D/Color3.h> | ||
#include <G3D/Ray.h> | ||
#include <G3D/Triangle.h> | ||
|
||
// Make Microsoft Windows programs start from the main() entry point | ||
G3D_START_AT_MAIN(); | ||
|
||
// If set to false, removes the Image::display() method and the GLUT, | ||
// GLEW, and OpenGL dependencies. | ||
#define USE_OPENGL true | ||
|
||
using G3D::Vector2; | ||
using G3D::Vector3; | ||
using G3D::Color3; | ||
using G3D::Ray; | ||
|
||
#ifdef INFINITY | ||
#undef INFINITY | ||
#endif | ||
|
||
#ifdef PI | ||
#undef PI | ||
#endif | ||
|
||
#define INFINITY (std::numeric_limits<float>::infinity()) | ||
#define PI (3.1415926536f) | ||
|
||
/** The scattering distribution function */ | ||
class BSDF { | ||
public: | ||
/** This is a probability */ | ||
Color3 lambertian; | ||
Color3 glossy; | ||
float glossySharpness; | ||
|
||
BSDF() {} | ||
BSDF(const Color3& lamb, const Color3& glos = Color3::zero(), float sharp = 100.0f) : | ||
lambertian(lamb), | ||
glossy(glos), | ||
glossySharpness(sharp) {} | ||
|
||
/** Returns L_o / L_i = f * w_i.dot(n) */ | ||
Color3 evaluateCos(const Vector3& w_i, const Vector3& w_o, const Vector3& n) const { | ||
const Vector3& w_h = (w_i + w_o).direction(); | ||
return | ||
(lambertian + | ||
glossy * ((glossySharpness + 8.0f) * | ||
powf(std::max(0.0f, w_h.dot(n)), glossySharpness) / | ||
(8.0f * PI))) * | ||
std::max(0.0f, w_i.dot(n)); | ||
} | ||
}; | ||
|
||
class Camera { | ||
public: | ||
float zNear; | ||
float zFar; | ||
float fieldOfViewX; | ||
|
||
Camera() : zNear(-0.1f), zFar(-100.0f), fieldOfViewX(PI / 2.0f) {} | ||
}; | ||
|
||
class Triangle { | ||
private: | ||
Vector3 m_vertex[3]; | ||
Vector3 m_normal[3]; | ||
BSDF m_bsdf; | ||
|
||
public: | ||
|
||
Triangle() {} | ||
|
||
Triangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, | ||
const Vector3& n0, const Vector3& n1, const Vector3& n2, | ||
const BSDF& bsdf) : m_bsdf(bsdf) { | ||
m_vertex[0] = v0; m_normal[0] = n0; | ||
m_vertex[1] = v1; m_normal[1] = n1; | ||
m_vertex[2] = v2; m_normal[2] = n2; | ||
} | ||
|
||
const Vector3& vertex(int i) const { | ||
return m_vertex[i]; | ||
} | ||
|
||
const Vector3& normal(int i) const { | ||
return m_normal[i]; | ||
} | ||
|
||
const BSDF& bsdf() const { | ||
return m_bsdf; | ||
} | ||
}; | ||
|
||
class Image { | ||
private: | ||
int m_width; | ||
int m_height; | ||
std::vector<Color3> m_data; | ||
|
||
/** \param displayConstant Radiance is scaled by this value, which should be chosen to | ||
scale the brightest values to about 1.0.*/ | ||
int PPMGammaCorrect(float radiance, float displayConstant) const; | ||
|
||
public: | ||
|
||
Image(int width, int height, const Color3& init = Color3::black()) : | ||
m_width(width), m_height(height), m_data(width * height, init) {} | ||
|
||
int width() const { return m_width; } | ||
|
||
int height() const { return m_height; } | ||
|
||
void set(int x, int y, const Color3& value) { | ||
m_data[x + y * m_width] = value; | ||
} | ||
|
||
const Color3& get(int x, int y) const { | ||
return m_data[x + y * m_width]; | ||
} | ||
|
||
void save(const std::string& filename, float displayConstant = 15.0f) const; | ||
|
||
# if USE_OPENGL | ||
/** Does not return */ | ||
void display(float displayConstant = 15.0f, float deviceGamma = 2.0f) const; | ||
# endif | ||
}; | ||
|
||
|
||
class Light { | ||
public: | ||
Vector3 position; | ||
|
||
/** Over the entire sphere. */ | ||
Color3 power; | ||
}; | ||
|
||
class Scene { | ||
public: | ||
std::vector<Triangle> triangleArray; | ||
std::vector<Light> lightArray; | ||
|
||
/** Load an IFS format model and add it to the scene. | ||
\param origin Object position in meters | ||
\param y Yaw rotation in degrees | ||
\param p Pitch rotation in degrees | ||
\param r Roll rotation in degrees | ||
*/ | ||
void addModel | ||
(const std::string& filename, | ||
const BSDF& bsdf = Color3::white() * 0.9f, | ||
float scale = 1.0f, | ||
const Vector3& origin = Vector3::zero(), | ||
float y = 0, float p = 0, float r = 0); | ||
}; | ||
|
||
#endif |