📄 scene.cpp
字号:
/*+-------------------------------------------------------------------
Ben Landon
CSCI E-235
Final Project
*/
#include <math.h>
#include "Scene.hpp"
#include "ppm_util.hpp"
#include "gl_common.h"
#include "geom_util.hpp"
#include "HemiCube.hpp"
// Polka dot texture that is used for
// debugging purposes.
extern GLuint g_debugging_texture;
/*+-------------------------------------------------------------------
index_to_color
Convert an index to RGB GLbytes. This is useful for rendering the
item buffer.
*/
static void index_to_color (unsigned int index,
GLbyte& r, GLbyte& g, GLbyte& b)
{
r = (index & 0x000000FF);
g = (index & 0x0000FF00) >> 8;
b = (index & 0x00FF0000) >> 16;
}
// Index to start numbering the scene triangles.
int SceneTriangle::m_next_index = 64;
/*+-------------------------------------------------------------------
SceneTriangle::SceneTriangle
SceneTriangle constructor. This takes color, normal and
vertex data and copies into this SceneTriangle's internal
represtation.
Note that SceneTriangle does not have a default constructor.
*/
SceneTriangle::SceneTriangle (Color& diffuse_color, Vector& normal,
const Point* pts, int num_points)
:
m_normal(normal),
m_diffuse_color(diffuse_color),
m_scene_tex_width(64),
m_scene_tex_height(64)
{
// Normalize the normal
m_normal.normalize();
// The number of points passed into this contructor
// in an array must be 3.
assert(num_points == 3);
assert(pts != NULL);
// Zero the texture coordinates
memset(m_tex_coords, 0, 6 * sizeof(float));
m_pt0 = pts[0];
m_pt1 = pts[1];
m_pt2 = pts[2];
// Add some way of setting the resolution
// according to area.
// I'm probably going to need a bigger texture.
m_accumulation_texture =
new SceneTexture(m_scene_tex_width, m_scene_tex_height);
m_emissive_texture =
new SceneTexture(m_scene_tex_width, m_scene_tex_height);
// Set the element index for this triangle and increment
// the next_index marker.
m_element_index = SceneTriangle::m_next_index;
SceneTriangle::m_next_index++;
this->clear_splats();
}
/*+-------------------------------------------------------------------
SceneTriangle destructor
Delete the two textures associated with this SceneTriangle.
*/
SceneTriangle::~SceneTriangle ()
{
if (m_accumulation_texture)
{
delete m_accumulation_texture;
m_accumulation_texture = NULL;
}
if (m_emissive_texture)
{
delete m_emissive_texture;
m_emissive_texture = NULL;
}
}
/*+-------------------------------------------------------------------
SceneTriangle::draw
This drawing method is basically used for debugging.
The methods draw_flat, draw_diffuse_shaded, and
draw_with_accumulation_texture are the ones that are actually
used for the real work of PBR.
*/
void SceneTriangle::draw (void) const
{
m_accumulation_texture->make_current();
GLfloat diffuse_material_color[4];
diffuse_material_color[0] = m_diffuse_color.r();
diffuse_material_color[1] = m_diffuse_color.g();
diffuse_material_color[2] = m_diffuse_color.b();
diffuse_material_color[3] = m_diffuse_color.a();
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse_material_color);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
glBegin(GL_TRIANGLES);
glNormal3f(m_normal.x(), m_normal.y(), m_normal.z());
glTexCoord2f(m_tex_coords[0], m_tex_coords[1]);
glVertex3f(m_pt0.x(), m_pt0.y(), m_pt0.z());
glTexCoord2f(m_tex_coords[2], m_tex_coords[3]);
glVertex3f(m_pt1.x(), m_pt1.y(), m_pt1.z());
glTexCoord2f(m_tex_coords[4], m_tex_coords[5]);
glVertex3f(m_pt2.x(), m_pt2.y(), m_pt2.z());
glEnd();
}
/*+-------------------------------------------------------------------
SceneTriangle::draw_flat
This method is used for rendering the item buffer. This
method renders the scene with flat shading and no lighting.
This allows code reading the framebuffer rendered with
draw_flat to figure out which triangles from the scene are
visible for a particular side of the HemiCube. See
HemiCube::reconstruct or HemiCube::reconstruct_one_side
for details.
*/
void SceneTriangle::draw_flat (void) const
{
GLbyte r, g, b;
index_to_color(this->id(), r, g, b);
glBegin(GL_TRIANGLES);
glColor3ub(r, g, b);
glVertex3f(m_pt0.x(), m_pt0.y(), m_pt0.z());
glVertex3f(m_pt1.x(), m_pt1.y(), m_pt1.z());
glVertex3f(m_pt2.x(), m_pt2.y(), m_pt2.z());
glEnd();
}
/*+-------------------------------------------------------------------
SceneTriangle::draw_diffuse_shaded
Draw a triangle with the material color set. This is used
to figure out the color of the points to be reprojected
into the scene. See HemiCube::reconstruct and
HemiCube::reconstruct_one_side for details.
*/
void SceneTriangle::draw_diffuse_shaded (void) const
{
GLfloat diffuse_color[4];
GLfloat no_color[] = { 0.0f, 0.0f, 0.0f, 1.0f };
m_diffuse_color.to_float_array(diffuse_color, 4);
glMaterialfv(GL_FRONT_AND_BACK,
GL_DIFFUSE,
diffuse_color);
glMaterialfv(GL_FRONT_AND_BACK,
GL_AMBIENT,
no_color);
glMaterialfv(GL_FRONT_AND_BACK,
GL_SPECULAR,
no_color);
glMaterialf(GL_FRONT_AND_BACK,
GL_SHININESS,
0.0f);
glMaterialfv(GL_FRONT_AND_BACK,
GL_EMISSION,
no_color);
m_diffuse_color.make_current();
glBegin(GL_TRIANGLES);
glNormal3f(m_normal.x(), m_normal.y(), m_normal.z());
glVertex3f(m_pt0.x(), m_pt0.y(), m_pt0.z());
glVertex3f(m_pt1.x(), m_pt1.y(), m_pt1.z());
glVertex3f(m_pt2.x(), m_pt2.y(), m_pt2.z());
glEnd();
}
/*+-------------------------------------------------------------------
SceneTriangle::draw_with_accumulation_texture
Draw the SceneTriangle with the accumulation texture
shown. This is used in the reconstruction phase also.
*/
void SceneTriangle::draw_with_accumulation_texture (void) const
{
GLenum err = glGetError();
m_accumulation_texture->make_current();
err = glGetError();
// glBindTexture(GL_TEXTURE_2D, g_debugging_texture);
// Make the color black. I'm pretty sure that this
// is correct since we're using the color in the
// diffuse lighting part.
err = glGetError();
glColor3f(0.0f, 0.0f, 0.0f);
err = glGetError();
glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR);
err = glGetError();
glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_MAG_FILTER,
GL_LINEAR);
err = glGetError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
err = glGetError();
// Draw the polygon faces with the accumulation textures first
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
err = glGetError();
glBegin(GL_TRIANGLES);
glTexCoord2f(m_tex_coords[0], m_tex_coords[1]);
glVertex3f(m_pt0.x(), m_pt0.y(), m_pt0.z());
glTexCoord2f(m_tex_coords[2], m_tex_coords[3]);
glVertex3f(m_pt1.x(), m_pt1.y(), m_pt1.z());
glTexCoord2f(m_tex_coords[4], m_tex_coords[5]);
glVertex3f(m_pt2.x(), m_pt2.y(), m_pt2.z());
glEnd();
err = glGetError();
}
/*+-------------------------------------------------------------------
SceneTriangle::draw_wireframe
Draw the scene as a wireframe. This is useful for debugging,
since it is hard to see the scene before it is lighted.
*/
void SceneTriangle::draw_wireframe (void) const
{
// Now draw it in wireframe
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
blue.make_current();
glBegin(GL_TRIANGLES);
glVertex3f(m_pt0.x(), m_pt0.y(), m_pt0.z());
glVertex3f(m_pt1.x(), m_pt1.y(), m_pt1.z());
glVertex3f(m_pt2.x(), m_pt2.y(), m_pt2.z());
glEnd();
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
/*+-------------------------------------------------------------------
SceneTriangle::init_light_source_in_patch (method)
This is used to add light to a texture corresponding to
a scene element. The x, y, width and height parameters are
in texture space, i.e. between 0.0 and 1.0.
The rectangle (x, y, x + width, y + height) in the accumulation
texture and emissive texture are set to light_color.
No bounds checking to make sure that the rectangle lies in the
texture coordinates of the triangle.
This also just uses pick nearest to add light to the textures.
FIXME - Maybe I should use OpenGL to do this somehow.
*/
void SceneTriangle::init_light_source_in_patch (const Color& light_color,
float x, float y,
float width, float height)
{
assert (m_emissive_texture && m_accumulation_texture);
float urx = x + width;
float ury = y + height;
// FIXME - Is this quantization correct?
int u0, v0, u1, v1;
// Set the accumulation texture first
u0 = (int)(m_accumulation_texture->width() * x - 0.5f);
v0 = (int)(m_accumulation_texture->height() * y - 0.5f);
u1 = (int)(m_accumulation_texture->width() * urx - 0.5f);
v1 = (int)(m_accumulation_texture->height() * ury - 0.5f);
int i, j;
for (i = u0; i <= u1; i++)
{
for (j = v0; j <= v1; j++)
{
m_accumulation_texture->set(light_color, i, j);
}
}
// Set the emissive texture next
u0 = (int)(m_emissive_texture->width() * x - 0.5f);
v0 = (int)(m_emissive_texture->height() * y - 0.5f);
u1 = (int)(m_emissive_texture->width() * urx - 0.5f);
v1 = (int)(m_emissive_texture->height() * ury - 0.5f);
for (i = u0; i <= u1; i++)
{
for (j = v0; j <= v1; j++)
{
m_emissive_texture->set(light_color, i, j);
}
}
}
/*+-------------------------------------------------------------------
SceneTriangle::get_brightest_texel
It is an error to invoke this method on a SceneTriangle that
has no texture. The emissive texture associated with this
SceneTriangle must be at least 1 x 1.
FIXME - This can be optimized with Mipmaps.
*/
void SceneTriangle::get_brightest_texel (Point& point, Color& color,
int& u, int& v) const
{
Color texel;
float result_brightness = 0.0f;
int result_u = -1;
int result_v = -1;
assert(m_emissive_texture != NULL);
assert(m_emissive_texture->width() > 0);
assert(m_emissive_texture->height() > 0);
unsigned int tex_width = m_emissive_texture->width();
unsigned int tex_height = m_emissive_texture->height();
m_emissive_texture->get_brightest_texel(result_u, result_v, texel);
float t1, t2;
get_barycentric_parameters_2D (float(result_u) / float(tex_width - 1) ,
float(result_v) / float(tex_height - 1),
m_tex_coords[0], m_tex_coords[1],
m_tex_coords[2], m_tex_coords[3],
m_tex_coords[4], m_tex_coords[5],
t1, t2);
float texel_x =
m_pt0.x() +
t1 * (m_pt1.x() - m_pt0.x()) +
t2 * (m_pt2.x() - m_pt0.x());
float texel_y =
m_pt0.y() +
t1 * (m_pt1.y() - m_pt0.y()) +
t2 * (m_pt2.y() - m_pt0.y());
float texel_z =
m_pt0.z() +
t1 * (m_pt1.z() - m_pt0.z()) +
t2 * (m_pt2.z() - m_pt0.z());
color = texel;
point.set(texel_x, texel_y, texel_z);
u = result_u;
v = result_v;
}
/*+-------------------------------------------------------------------
SceneTriangle::get_emissive_texture_sum
This is actually the sum of the emissive texture
over the pixel area of the emissive texture. I'm
not sure what this quantity is called.
*/
float SceneTriangle::get_emissive_texture_energy_density (void) const
{
assert(m_emissive_texture);
assert(m_emissive_texture->width() > 0);
assert(m_emissive_texture->height() > 0);
float emissive_texture_sum =
this->m_emissive_texture->get_texel_sum();
int area_in_texels =
this->m_emissive_texture->get_texel_area();
assert(area_in_texels > 0);
return (emissive_texture_sum / float(area_in_texels));
}
/*+-------------------------------------------------------------------
SceneTriangle::area
Compute the area of the SceneTriangle. This value can be cached.
Recalling the problems associated with extra state, just compute it
each time for now. It's not that bad. There is a square root
involved though.
*/
float SceneTriangle::area (void) const
{
float dx1 = m_pt1.x() - m_pt0.x();
float dy1 = m_pt1.y() - m_pt0.y();
float dz1 = m_pt1.z() - m_pt0.z();
float dx2 = m_pt2.x() - m_pt0.x();
float dy2 = m_pt2.y() - m_pt0.y();
float dz2 = m_pt2.z() - m_pt0.z();
float cp_x = dy1 * dz2 - dy2 * dz1;
float cp_y = dx2 * dz1 - dx1 * dz2;
float cp_z = dx1 * dy2 - dx2 * dy1;
double cross_product_length =
sqrt(cp_x * cp_x + cp_y * cp_y + cp_z * cp_z);
return (float)(cross_product_length / 2.0);
}
/*+-------------------------------------------------------------------
SceneTriangle::clear_splats
Empty the set of splats associated with this SceneTriangle
object.
*/
void SceneTriangle::clear_splats (void)
{
m_splats.clear();
}
/*+-------------------------------------------------------------------
SceneTriangle::add_splat (public method)
Add a splat to this SceneTriangle object. A splat is a
reprojected point in point-based radiosity.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -