Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I have created a very simple lighting shader. It currently only supports point lights, but it lights up surfaces that are completely blocked from the light. I know why, but I want to know how I can fix it. I was thinking of using a form of shadow mapping to darken those areas, but shadow mapping is difficult for me to understand and has very major performance issues. I was hoping for a more elegant and efficient version, even if it is not as nice.

Until something like that exists, I need a nice, efficient way of checking to see if a fragment or vertex is visible and computing things from there. I would also like to add that I intent to support semi-transparent objects, which should block light based on and alpha value.

The things I want:

  • Fast (25+ fps with the modified shaders, I currently run at 82)
  • Easily modified to incorporate more lights (three or four at most, probably).
  • Support for edge smoothing (user can enable it, but will lose performance)

The things I do not want:

  • Pulling information from the GPU during rendering (view stuttering is always visible when that happens)
  • Visible/noticeable moving of lighting when scene is stationary (like the light is moving when it really isn't, due to randomizing)
  • Expensive operations when the scene first renders.

I do not expect anybody to implement these things - just to leave room and maybe a comment where these would be done. I know this is the "holy grail" of rendering when combined with bump maps and such, but if you tone down the quality a bit, I hope it is possible. I may have to do some extra work on the CPU, but that is acceptable.

Now for the shaders I currently have working:

Vertex shader

varying vec4 color;
varying vec3 normal;
varying vec4 position;

void main (void) {
    color = gl_Color; // pass color to the fragment shader
    normal = normalize(gl_NormalMatrix * gl_Normal); // calculate useable normals from the basic normal data.

    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; // set position
    gl_TexCoord[0] = gl_MultiTexCoord0; // set texture coordinates
    position = gl_Vertex; // pass gl_Vertex to the fragment shader for light calculations.
}

Fragment shader

varying vec4 color;
varying vec3 normal;
varying vec4 position;

uniform sampler2D texture;
uniform vec4 light; // position of the light, same coordinate system as non-transformed objects

void main (void) {
    vec3 vector = light.xyz - position.xyz; // get the vector needed to determine length
    float distance = sqrt((vector.x * vector.x) + (vector.y * vector.y) + (vector.z * vector.z)); // determine length
    vec4 final = color; // set color

    final.xyz *= (1.0 / distance); // calculate light intensity on pixel

    gl_FragColor = final * texture2D(texture, gl_TexCoord[0].st); // multiply by texture color
}

I can do a lot of extra cool things with this current code, such as giving lights their own colors and giving objects their own material properties, but that is not the point now. I just want basic code, preferably without needing more buffers and shaders, to determine visibility. From my research, it sounds like the stencil and depth buffers may be of use here, and they already setup, so they may be a better way to do it.

I can not stress enough that I only need the bare essentials here - nothing overly fancy. Thanks in advance for your help.

share|improve this question
    
You want light occlusion (shadows) that uses only a single pass forward rendering algorithm using only a single vertex+fragment shader combination? Impossible, end of story. –  Sean Middleditch Jan 6 '13 at 5:14
    
That is not what I said. I simply said I would prefer that. I am open to doing whatever I need to. –  Justin Jan 6 '13 at 14:48
1  
In that case,the easiest option by far is to just use shadow maps. Some good tutorials you might like are fabiensanglard.net/shadowmapping/index.php and opengl-tutorial.org/intermediate-tutorials/… –  Sean Middleditch Jan 6 '13 at 19:14
    
I am working on implementing this now. That second tutorial is very thorough and geared toward beginners, so it is already giving me nice results. I've also combined the multisample and shadow shaders and buffers, so performance is now barely effected! Can you add this as an answer so I can mark it? –  Justin Jan 7 '13 at 15:09
add comment

2 Answers

up vote 1 down vote accepted

Shadow mapping is the way to go. There is no way to have shadows without adding some more shaders and passes to rendering. GPU rendering works by taking a list of triangles to render, and it does not have a list of the other triangles/occluded in the scene; even if you added them in a buffer, querying them would be much slower than shadow mapping on today's GPU hardware. You aren't going to find a simpler method of shadow generation that actually works than shadow mapping.

Some good tutorials are at:

http://fabiensanglard.net/shadowmapping/index.php

http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/

share|improve this answer
add comment

You do not use the normal at all, so nothing will tell the light calculation that the current fragment is facing away from the light. Instead of this:

final.xyz *= (1.0 / distance);

You can try this, using Lambert’s cosine law:

final.xyz *= max(0.0, dot(normalize(vector), normalize(normal))) / distance;

Also note that length(vector) will do exactly what your sqrt call does.

Edit: another problem is that vector is computed in object space, whereas normal is in view space. This page has more information. I suggest doing the light calculations in view space (as the fixed-pipeline OpenGL does), therefore multiplying position by the view matrix and providing light in view space. Instead of:

position = gl_Vertex;

You need:

position = gl_ModelView * gl_Vertex;

You should also multiply light with the view matrix, but that is more easily done in the C/C++ code. You will need to change a lot of things in the client code anyway, because most of the gl_* builtin variables are deprecated and you need to send them yourself.

It seems to me that you can find the kind of information you are looking for in the book “OpenGL 4.0 Shading Language Cookbook”. As the name suggests it targets GL 4.0, but most of the shaders, especially the ones covering lighting, only use backwards-compatible language features. It’ll also teach you to do shadows, which isn’t as difficult as you appear to think, and to get rid of deprecated features such as gl_TexCoord.

share|improve this answer
    
I have found a bit of a problem with that. As the scene rotates, the light appears to illuminate things differently. Instead of giving me a smooth, circular lighting pattern, it seems to simply apply light in a cone shape from the center of each object to the edge pointing toward the light. You would expect that, but while the objects and the light are not moving, the lighting patter changes based on the rotation and scaling of the matrices. –  Justin Jan 6 '13 at 5:15
    
@Justin Ah, there is also the model space / view space inconsistency. I added more information about that, I hope it makes sense. –  Sam Hocevar Jan 6 '13 at 5:28
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.