📄 hemicube.cpp
字号:
to figure out which scene elements are visible from each
face of the hemicube. Each color in the item buffer is
treated as an identifier instead of a color.
*/
void HemiCube::debug_draw_item_buffer (void)
{
glDrawPixels(m_center_pixel_width,
m_center_pixel_height,
GL_RGB, GL_UNSIGNED_BYTE,
m_center_item_buffer);
}
/*+-------------------------------------------------------------------
HemiCube::render_buffers
Set the camera to be at center of the hemicube.
Render the scene into 5 item buffers, once corresponding
to each side of the hemicube.
Next, render the scene into 5 RC (rendering x cosine)
buffers, one corresponding to each side of the hemicube.
*/
void HemiCube::render_buffers (const Scene& scene,
const Point& camera_point,
const Vector& normal,
const Color& light_color
)
{
Vector top_normal;
Vector bottom_normal;
// Take the normal of the top face to be the
// perpendicular to the normal, as calculated by
// Vector::get_perpendicular_vector.
normal.get_perpendicular_vector(top_normal);
// The bottom normal is the negative of the top normal.
bottom_normal.set(-top_normal.x(),
-top_normal.y(),
-top_normal.z());
// Compute the left normal by takig the cross product
// of the normal with the top normal.
Vector left_normal = normal * top_normal;
// The right normal is the negation of left normal.
Vector right_normal;
right_normal.set(-left_normal.x(),
-left_normal.y(),
-left_normal.z());
for (int side_index = 0; side_index < 5; side_index++)
{
HemiCubeSide which_side = (HemiCubeSide)side_index;
const Vector* which_normal = NULL;
const Vector* which_up = NULL;
float frustum_bottom = 0.0f;
float frustum_top = 1.0f;
unsigned char* which_item_rgb_buffer = NULL;
float* which_rc_buffer = NULL;
float* which_item_depth_buffer = NULL;
GLdouble* which_modelview_array = NULL;
GLdouble* which_projection_array = NULL;
int pixel_width = m_side_pixel_width;
int pixel_height = m_side_pixel_height;
switch(which_side)
{
case Center:
which_normal = &normal;
which_up = &top_normal;
frustum_bottom = -1.0f;
which_item_rgb_buffer = m_center_item_buffer;
which_item_depth_buffer = m_center_depth_buffer;
which_rc_buffer = m_center_rc_buffer;
which_modelview_array = m_center_modelview;
which_projection_array = m_center_projection;
pixel_width = m_center_pixel_width;
pixel_height = m_center_pixel_height;
break;
case Left:
which_normal = &left_normal;
which_up = &normal;
which_item_rgb_buffer = m_left_item_buffer;
which_item_depth_buffer = m_left_depth_buffer;
which_rc_buffer = m_left_rc_buffer;
which_modelview_array = m_left_modelview;
which_projection_array = m_left_projection;
break;
case Bottom:
which_normal = &bottom_normal;
which_up = &normal;
which_item_rgb_buffer = m_bottom_item_buffer;
which_item_depth_buffer = m_bottom_depth_buffer;
which_rc_buffer = m_bottom_rc_buffer;
which_modelview_array = m_bottom_modelview;
which_projection_array = m_bottom_projection;
break;
case Right:
which_normal = &right_normal;
which_up = &normal;
which_item_rgb_buffer = m_right_item_buffer;
which_item_depth_buffer = m_right_depth_buffer;
which_rc_buffer = m_right_rc_buffer;
which_modelview_array = m_right_modelview;
which_projection_array = m_right_projection;
break;
case Top:
which_normal = &top_normal;
which_up = &normal;
which_item_rgb_buffer = m_top_item_buffer;
which_item_depth_buffer = m_top_depth_buffer;
which_rc_buffer = m_top_rc_buffer;
which_modelview_array = m_top_modelview;
which_projection_array = m_top_projection;
break;
}
assert(which_normal != NULL);
assert(which_up != NULL);
assert(which_item_rgb_buffer != NULL);
assert(which_item_depth_buffer != NULL);
assert(which_rc_buffer != NULL);
assert(which_modelview_array != NULL);
assert(which_projection_array != NULL);
assert(which_projection_array != which_modelview_array);
// Restrict drawing to the desired buffer size
glViewport(0, 0, pixel_width, pixel_height);
this->render_one_item_buffer(scene,
camera_point,
*which_normal,
*which_up,
frustum_bottom,
frustum_top);
// Now get the depth and color buffers
glReadPixels(0, 0,
pixel_width,
pixel_height,
GL_RGB,
GL_UNSIGNED_BYTE,
which_item_rgb_buffer);
// Restrict drawing to the desired buffer size
glViewport(0, 0, pixel_width, pixel_height);
this->render_one_lighted_scene_buffer(scene,
camera_point,
*which_normal,
*which_up,
light_color,
frustum_bottom,
frustum_top,
which_modelview_array,
which_projection_array);
// Now get the color and depth buffers
glReadPixels(0, 0,
pixel_width,
pixel_height,
GL_RGB,
GL_FLOAT,
which_rc_buffer);
glReadPixels(0, 0,
pixel_width,
pixel_height,
GL_DEPTH_COMPONENT,
GL_FLOAT,
which_item_depth_buffer);
// Multiply the contents of which_rc_buffer
// by the appropriate cosine map.
GrayscaleTexture* which_texture = NULL;
if (which_side == Center)
{
which_texture = m_center_cosine_texture;
}
else
{
which_texture = m_bottom_cosine_texture;
}
assert(pixel_width != -1);
assert(pixel_height != -1);
assert(which_rc_buffer != NULL);
assert(which_texture != NULL);
this->multiply_rc_buffer_by_cosine_texture(which_rc_buffer,
pixel_width,
pixel_height,
which_texture);
}
// Hang on to the maximum splat value in all of the five
// renderings. This is done because some scaling might be
// necessary during the reconstruction phase.
float max_splat_value = this->find_max_rc_value();
Splat::max_splat_value =
(max_splat_value > 0.0f ? 1.0f / max_splat_value : 0.0f);
}
/*+-------------------------------------------------------------------
HemiCube::multiply_rc_buffer_by_cosine_texture
This multiplies a single rc buffer by the specified map.
This function will be unnecssary once I figure out how to
get projective texture mapping to work.
*/
void HemiCube::multiply_rc_buffer_by_cosine_texture (float* which_rc_buffer,
int pixel_width,
int pixel_height,
GrayscaleTexture* gs_tex
)
{
assert(gs_tex != NULL);
assert(which_rc_buffer != NULL);
int pixel_x, pixel_y;
int counter = 0;
int cos_map_x = 0;
int cos_map_y = 0;
// Get the mipmap that corresponds to pixel_width, pixel_height
int mipmap_level = gs_tex->mipmap_level(pixel_width, pixel_height);
float* cosine_texels = NULL;
if (mipmap_level == -1)
{
throw HemiCubeException();
}
else
{
cosine_texels = new float[pixel_width * pixel_height];
gs_tex->mipmap_texels(mipmap_level, cosine_texels);
}
assert(cosine_texels != NULL);
// The following block of code depends on the
// RC buffer being stored in row-major format.
for (pixel_y = 0; pixel_y < pixel_height; pixel_y++)
{
for (pixel_x = 0; pixel_x < pixel_width; pixel_x++)
{
unsigned int mipmap_offset = pixel_y * pixel_width + pixel_x;
float gray_val = cosine_texels[mipmap_offset];
float r = which_rc_buffer[counter];
float g = which_rc_buffer[counter + 1];
float b = which_rc_buffer[counter + 2];
which_rc_buffer[counter] = r * gray_val;
which_rc_buffer[counter + 1] = g * gray_val;
which_rc_buffer[counter + 2] = b * gray_val;
counter += m_components_per_pixel;
}
}
if (cosine_texels)
{
delete [] cosine_texels;
cosine_texels = NULL;
}
}
/*+-------------------------------------------------------------------
HemiCube::find_max_rc_value
Go through the five RC buffers in this hemicube and find the largest
value. This method should be invoked only after the diffuse scene
is rendered to them, and after it is multiplied by the cosine
texture.
This method finds the maximum across all channels. It does not find
the max red, max green, max_blue, etc.
*/
float HemiCube::find_max_rc_value (void) const
{
float max_val = 0.0f;
for (int side_index = 0; side_index < 5; side_index++)
{
HemiCubeSide which_side = (HemiCubeSide)side_index;
int pixel_width = m_side_pixel_width;
int pixel_height = m_side_pixel_height;
float* rc_buffer = NULL;
switch(which_side)
{
case Center:
pixel_width = m_center_pixel_width;
pixel_height = m_center_pixel_height;
rc_buffer = m_center_rc_buffer;
break;
case Left:
rc_buffer = m_left_rc_buffer;
break;
case Right:
rc_buffer = m_right_rc_buffer;
break;
case Bottom:
rc_buffer = m_bottom_rc_buffer;
break;
case Top:
rc_buffer = m_top_rc_buffer;
break;
}
assert(rc_buffer != NULL);
// We're actually comparing all channels,
// so adjust pixel_width and pixel_height
// so that we hit each element.
int grid_size = m_components_per_pixel * pixel_width * pixel_height;
for (int i = 0; i < grid_size; i++)
{
if (rc_buffer[i] > max_val)
max_val = rc_buffer[i];
}
}
return max_val;
}
/*+-------------------------------------------------------------------
HemiCube::render_one_item_buffer
Render the item buffer for the specified camera position,
normal and up vector. The scene is rendered into the
specified offscreen drawable.
*/
void HemiCube::render_one_item_buffer (const Scene& scene,
const Point& camera_point,
const Vector& normal,
const Vector& up,
float frustum_bottom,
float frustum_top)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Disable lighting and texturing
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
// Flat shading, and use the depth buffer
glShadeModel(GL_FLAT);
glEnable(GL_DEPTH_TEST);
// Set the camera to be a the camera_point
Point look_at = camera_point + normal;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(camera_point.x(), camera_point.y(), camera_point.z(),
look_at.x(), look_at.y(), look_at.z(),
up.x(), up.y(), up.z());
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1.0, 1.0f, frustum_bottom, frustum_top, 1.0f, 100.0f);
scene.draw_flat();
}
/*+-------------------------------------------------------------------
HemiCube::render_one_lighted_scene_buffer (private method)
Render the scene when it is diffuse lighted and smooth
shaded. The parameters passed to this method describe
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -