SGI

EnvMap

Environment mapping in real time usually uses a fixed environment map, this code shows how to map the actual database environment reflected by a non planar object.

It is possible to render reflections in planar surfaces by modifying the modelview matrix and switching face winding, however non planar surfaces are more problematic. Using texture coordinate generation in eye space it is possible to simulate reflections using texture mapping. A logical extension of this capability is to base that texture mapped image on the actual surroundings appropriate to the object position and its environment. This is a three stage process which is described in detail here. The first step is to render the environment using pfChannels positioned at the center of the reflecting object and looking in all directions from the reflecting object. These channels can have different topologies but the most obvious and one of the most efficient is to render the 6 faces of a cube from the object centroid. Such a rendering is displayed below. It is important to understand that the function of this algorithm is to environment map an object in eye space and this has a bearing on the channel orientation for the first pass. In this approach the various cube faces should be oriented in suitable alignment with the viewing vector used in the final rendering of the scene. So we've rendered the 6 faces of a cube and these faces are aligned with the pfChannel orientation in the desired final image.

The orientation of the cube relative to the main channel view and relative to each other is critical:

// orient the viewing channel
matey->makeCoord(&(Shared->view));
view_chan->setViewMat(*matey);
// orient the cube faces
matey->makeCoord(&(Shared->reflect));
// rotate vector to look at viewer
matey->preRot(180.0f, 0.0f, 0.0f, 1.0f, *matey);
front_chan->setViewMat(*matey);
matey->preRot(90.0f, 0.0f, 0.0f, 1.0f, *matey);
left_chan->setViewMat(*matey);
matey->preRot(90.0f, 0.0f, 0.0f, 1.0f, *matey);
back_chan->setViewMat(*matey);
matey->preRot(90.0f, 0.0f, 0.0f, 1.0f, *matey);
right_chan->setViewMat(*matey);
matey->preRot(90.0f, 1.0f, 0.0f, 0.0f, *matey);
matey->preRot(90.0f, 0.0f, 1.0f, 0.0f, *matey);
top_chan->setViewMat(*matey);
matey->preRot(180.0f, 1.0f, 0.0f, 0.0f, *matey);
bottom_chan->setViewMat(*matey);

The next step is to read these channel faces into texture memory using texture subloads. We can use either Performer based calls or OpenGL calls for this, the key is that we want to minimize the download of images and may have to pad the original texture size due to the primary channels not totalling to a 2^n image dimension. You should also note that in this example there is a lot of image readback redundancy, the black regions around the cube faces are there to assist with your understanding of the process, an efficiend implementation would pack these faces into a tight 2x3 channel configuration and use a single download to texture memory.

// load environment cube faces
if(Shared->distorttexture)
Shared->distorttexture->subload(PFTEX_SOURCE_FRAMEBUFFER, NULL, 0, 0, 0, 0, 0, IMAGE_X * 4, IMAGE_Y * 3);

The image readback is performed in a draw callback and this can be conveniently done in the environment map rendering channel draw routine. This is a channel which has a fixed orientation which looks at a single mesh of polygons, this mesh of polygons sontains coordinates and texture coordinates which map the images in the cube face texture to a single seamless view of the environment from the object and centered along the vector from the object to the eye:

The texture coordinate and mesh generation is performed procedurally in the genmesh.C module. Trawling over the code would be laborious but I'll explain simply what it does to produce the mesh. First a cube is generated around the origin with meshed faces, the texture coordinates on this cube are mapped to the various cube faces in the texture image, next this cube is inflated to the shape of a sphere by normalizing the vertex coordinates giving the sphere unit radius, the mapping of the image is still completely accurate as viewed from the origin. The final stage is to flatten the sphere to a disc but use the angle of the vertex w.r.t the z axis or (acos (z) because the vertices are normalized) to determine the radius of each vertex on the disc. one face on the cube is uniquely meshed to unwrap it's center point to the anular singularity at the rim of the disk.

Once this mesh is drawn mapping the environment map texture to the spherical rendering in the frame buffer it is read into the texture memory for use on the reflecting object, this readback can be performed in the same environment mesh draw callback immediately after the call to pfDraw(), when the sphere image is complete in the backbuffer.

There is a performer call for this:

Shared->envtexture->subload(PFTEX_SOURCE_FRAMEBUFFER, NULL, ENVXO, ENVYO, 0, 0, 0, ENVXS, ENVYS);

or the OpenGL equivalent:

glCopyTexImage2DEXT( GL_TEXTURE_2D, 0, GL_RGB, ENVXO, ENVYO, ENVXS, ENVYS, 0 );

Once this environment is in texture memory it is easily applied to an object as a reflection by using the spherical texture coordinate generation in eye space for that object. You can do this with OpenGL callbacks or using the Performer API to attach an sphere_map texgen attribute to a geostate:

sphere_gen = new pfTexGen;
sphere_gen->setMode(PF_S, PFTG_SPHERE_MAP);
sphere_gen->setMode(PF_T, PFTG_SPHERE_MAP);
gstate->setMode(PFSTATE_ENTEXGEN, PF_ON );
gstate->setAttr(PFSTATE_TEXGEN, sphere_gen);

One minor quality issue is the accurate clamping of texture coordinates for the sub faces of the cube texture. You can just make out black radial seams in the environment texture between the cube faces, this is a channel clamping issue within the texture and can be corrected by widening the field of view in the primary cube face channels very slightly and compensating for this by adjusting the texture coordinates used on the mesh, this will avoid the texture filter weighting in texels from the background colour or the colours of adjacent channels inappropriately when rendering the mesh.


Corporate Office
2011 N. Shoreline Boulevard
Mountain View, CA 94043
(650) 960-1980
U.S. 1(800) 800-7441
Europe (44) 118-925.75.00
Asia Pacific (81) 3-54.88.18.11
Latin America 1(650) 933.46.37
Canada 1(905) 625-4747
Australia/New Zealand (61) 2.9879.95.00
SAARC/India (91) 11.621.13.55
Sub-Saharan Africa (27) 11.884.41.47

Copyright © 1998 Silicon Graphics, Inc. All rights reserved.