When I say camera I'm referring to my engine's own camera class, which in its current configuration pretty much just moves the view via some vector transformations and glulookat. The scene is 2D, though there is a 3D component in that some things are drawn on different "layers" than others (the light texture is drawn a little on top of the underlying scene). The "light texture" is a texture to which I have rendered, through an FBO, an opaque black box and blended in shapes representing lights. These blended shapes make parts of the texture transparent, allowing you to see through to the underlying game scene. The FBO texture is specified as being the same pixel size as the screen, and uses an identical orthographic setup except for reversed Z coords (which can probably be swapped back, come to think of it). In theory, the view on the scene is moving while the opaque box is always drawn to cover the screen, thus lights should appear to move onto and off it based on the current camera location. Similarly, what you see on the underlying scene appears to move. The problem is that the apparant motion is at different rates for the underlying scene and the blended lights, such that a blended light doesn't always cover the same area of the screen... Note that this only applies to horizontal camera movement. When the camera moves vertically, the lights seem to sync up appropriately.
Relevant code:
FBO Init:
void LightingInit()
{
glEnable(GL_TEXTURE_2D);
glEnable(GL_COLOR_MATERIAL);
glGenTextures(1, &lighting_rtt_number);
glBindTexture(GL_TEXTURE_2D, lighting_rtt_number);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//GL_LINEAR_MIPMAP_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, screen_width, screen_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);
// create a renderbuffer object to store depth info
GLuint rboId = 1;
glGenRenderbuffers(1, &rboId);
glBindRenderbuffer(GL_RENDERBUFFER, rboId);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, screen_width, screen_height);
// create a framebuffer object
glGenFramebuffers(1, &lighting_fbo_number);
glBindFramebuffer(GL_FRAMEBUFFER, lighting_fbo_number);
// attach the texture to FBO color attachment point
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, lighting_rtt_number, 0);
// attach the renderbuffer to depth attachment point
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboId);
// switch back to window-system-provided framebuffer
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
Get ready to render lights through the FBO, and draw the opaque box
// Unbind any texture that might currently be bound
glBindTexture(GL_TEXTURE_2D, 0);
// set rendering destination to FBO
glBindFramebuffer(GL_FRAMEBUFFER, lighting_fbo_number);
// Set stuff up
glViewport(0,0,screen_width, screen_height);
glClearDepth(1.0f);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-0.5 * width, 0.5*width, -1, 1, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//glTranslatef(0.0, 0.0, 0.0);
//glLoadIdentity();
glEnable(GL_ALPHA_TEST);
glEnable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);
glDepthRange(0.0f, 1.0f);
glAlphaFunc(GL_GEQUAL, 0.0);
glLoadIdentity();
Camera.Render();
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glColor4d(0.0, 0.0, 0.0, 0.0); // Black, 0 alpha!?
glBegin(GL_QUADS);
glVertex3d(-0.5 * width, 1.0, 0.0);
glVertex3d(0.5 * width, 1.0, 0.0);
glVertex3d(0.5 * width, -1.0, 0.0);
glVertex3d(-0.5 * width, -1.0, 0.0);
glEnd();
Blending a light into the opaque box
void Render()
{
// STUFF NEEDED TO RENDER: COLOR (R, G, B), Max light level, Falloff point, Position, Center Angle, Arc Width
int num_segments = 128;
float r = specifications.GetFloatAttribute("color_red");
float g = specifications.GetFloatAttribute("color_green");
float b = specifications.GetFloatAttribute("color_blue");
float center_a = lights;
float arc_width = specifications.GetFloatAttribute("arc_width");
float start_angle = (specifications.GetFloatAttribute("center_angle") - (arc_width / 2.0)) * DEGTORAD;
float falloff_start = specifications.GetFloatAttribute("falloff_start") / 100;
double theta = (arc_width * DEGTORAD) / (num_segments - 1);
double tangential_factor = tan(theta);
double radial_factor = cos(theta);
double x = (rad * falloff_start) * cos(start_angle);
double y = (rad * falloff_start) * sin(start_angle);
int current_segment;
glColor4f(r, g, b, center_a);
glVertex3d(displacement_x, displacement_y, 0.0);
glBegin(GL_TRIANGLE_FAN);
for(current_segment = 0; current_segment < num_segments; current_segment++)
{
glVertex3f(x + displacement_x, y + displacement_y, 0.0);
float tx = -y;
float ty = x;
x += tx * tangential_factor;
y += ty * tangential_factor;
x *= radial_factor;
y *= radial_factor;
}
glEnd();
}
And now, blend the FBO drawn texture onto the scene
// Phase 2c: Cleanup
glDisable(GL_LINE_SMOOTH);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Phase 2d: Draw texture to screen
glViewport(0,0,screen_width,screen_height);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0f);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-0.5 * width, 0.5 * width, -1, 1, 1, 0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND); // allows blending (transparency)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // function for blending
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH); // allows smooth shading (gradients)
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GEQUAL, 0.0);
Camera.Render(); // Shift over to the correct coords
glColor4d(1.0, 1.0, 0.0, 1.0);
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, lighting_rtt_number);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5 * width,-1.0,0.01); // Bottom-Left Vertex
glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5 * width,-1.0,0.01); // Bottom-Right Vertex
glTexCoord2f(1.0f, 1.0f); glVertex3f(0.5 * width,1.0,0.01); // Top-Right Vertex
glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.5 * width,1.0,0.01); // Top-Left Vertex
glEnd();
glDisable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
glAlphaFunc(GL_GREATER, 0.0);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
Apologies for extraneous commented out stuff... A lot of that are remnants from fiddling with the code to get it to work and from a previous proof-of-concept stage. It will be cleaned up once everything works. The code I posted in my original post is the contents of the camera class's Render function (called as Camera.Render()). Its coords are the only coords changing at the moment. The rest of the scene (including blended lights) have their own static coords (drawn in different positions due to glulookat). Thanks for your help!
There was a saying we had in college: Those who walk into the engineering building are never quite the same when they walk out.