⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 scene.cpp

📁 The goal of this project is to explore the idea of point-based radiosity, which is a shooting radio
💻 CPP
📖 第 1 页 / 共 3 页
字号:
/*+-------------------------------------------------------------------
  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 + -