Image Space Lighting
Rendering and ImplementationNow that we know the theory behind image space lighting, it is time to work out the actual rendering and implementation details. The steps to rendering image space lighting are as follows:
Code SnippetsBelow is an OpenGL code snippet from the game project Aero Empire [2], which renders the point lights following the above implementation.
Shader* cur_shader;
void renderLight(Light* light){
//pass light world position to pixel shader
cur_shader->setUniformVec3("lightWorldPos", light->getWorldPosition());
//pass light color (magnitude of vector is power) to pixel shader
cur_shader->setUniformVec3("lightColor", light->getColor());
//pass the light's area of effect radius to pixel shader
cur_shader->setUniformFloat("lightAoE", light->getAoERadius());
light->render(); //render the proxy shape
}
void renderLights(const Shape* scene){
//set blend function
glBlendFunc(GL_ONE, GL_ONE);
//cull front faces
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
//disable depth testing
glDisable(GL_DEPTH_TEST);
glDepthMask(false);
//bind point light pixel shader
Shader* shader = shaders[SHADER_LIGHT];
shader->bind();
//bind position, normal and color textures from deferred shading pass
bindTexture(positionMap, POSITION_UNIT);
shader->setUniformInt("positionMap", POSITION_UNIT);
bindTexture(normalMap, NORMAL_UNIT);
shader->setUniformInt("normalMap", NORMAL_UNIT);
bindTexture(colorMap, COLOR_UNIT);
shader->setUniformInt("colorMap", COLOR_UNIT);
bindTexture(attrMap, ATTR_UNIT);
shader->setUniformInt("attrMap", ATTR_UNIT);
//set pixel shader attribute "camPosition" to the camera pos
//(as it is needed for Phong shading)
camera.loadCameraPosition(shader, "camPosition");
shader->setUniformFloat("d_sx", 1.0/width);
shader->setUniformFloat("d_sy", 1.0/height);
cur_shader = shader;
//run renderLight function on all light proxy shapes in scene
getLights(scene, &renderLight);
//unbind and reset everything to desired values
shader->unbind();
unbindTexture(POSITION_UNIT);
unbindTexture(NORMAL_UNIT);
unbindTexture(COLOR_UNIT);
unbindTexture(ATTR_UNIT);
glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glDepthMask(true);
}
Below is the GLSL pixel shader bound above which computes the lighting (the vertex shader only sets gl_Position = ftransform() ).
//input parameters
uniform sampler2D positionMap, normalMap, colorMap, attrMap;
uniform vec3 camPosition, lightWorldPos, lightColor;
uniform float lightAoE, d_sx, d_sy;
void main(void)
{
//calculate screen coord
vec2 coord = vec2(gl_FragCoord.x*d_sx, gl_FragCoord.y*d_sy);
//get the position from deferred shading
vec4 position = texture2D(positionMap, coord);
//vector between light and point
vec3 VP = lightWorldPos-position.xyz;
//get the distance between the light and point
float distance = length(VP);
//if outside of area of effect, discard pixel
if(distance > lightAoE) discard;
//normalize vector between light and point (divide by distance)
VP /= distance;
//get the normal from deferred shading
vec4 normal = texture2D(normalMap, coord);
//get the color from deferred shading
vec4 color = texture2D(colorMap, coord);
//get lighting attributes from deferred shading
vec4 attributes = texture2D(attrMap, coord);
float diff_coefficient = attributes.r;
float phong_coefficient = attributes.g;
float two_sided = attributes.b;
float cos_theda = dot(normal.xyz, VP);
//calculate two sided lighting.
cos_theda = (cos_theda < 0.0)?-two_sided*cos_theda:cos_theda;
//calculate diffuse shading
float diffuse = diff_coefficient*cos_theda;
//calculate half vector
vec3 H = normalize(VP+normalize(camPosition - position.xyz));
//calculate Phong shading
float phong = phong_coefficient*pow(max(dot(H, normal.xyz), 0.0), 100.0);
//calculate light contribution with attenuation
vec3 C = lightColor*(color.rgb*diffuse+phong)/(distance*distance+0.8);
//all lights have constant quadratic attenuation of 1.0, with a constant attenuation of 0.8 to avoid dividing by small numbers
gl_FragColor = vec4(C, 1.0); //output color
}
If you are planning to add lanterns or lamps, adding two-sided lighting allows for point lights inside a lampshade or lantern to contribute light to it. The above shader includes a very simple implementation of two-sided lighting.
Results![]() Figure 3: 2175 bright cube lanterns rendered on a flat plane in a night scene.
As you can see, image space lighting creates accurate lighting effects that add to the scene whether daytime or nighttime, interior or exterior. The ability to efficiently render a large number of point light sources allows for much more diverse lighting environments which greatly improves the rendering of the scene, yet still runs in real time.
|
|