Jump to content

  • Log In with Google      Sign In   
  • Create Account


Blending multiple lights in deferred renderer


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
10 replies to this topic

#1 BraXi   Members   -  Reputation: 187

Like
0Likes
Like

Posted 18 August 2012 - 06:13 AM

Hello.
I was looking for methods to render multiple lights without redrawing the scene multiple times and i found article about "Deferred Lighting". It didnt took me much time to render my scene into 4 buffers using FBO, i store 4 textures for position, normals, color and depth, then i render a fullscreen quad in ortho mode and apply lighting shader to it but, as i've suspected my shader doesn't blend multiple lights together so if i draw light B, the light A isnt rendered, now im capable of rendering just one light on scene.

I would like to blend those lights but to be honest GLSL isn't something that i'm experienced in.

This is the light pass shader that i'm using, i found it somewhere but i'm not sure who's the author of it.

Fragment:
varying vec3 p;
varying vec3 sDir;
varying vec4 lpos;
uniform sampler2D normalBuffer;
uniform sampler2D depthBuffer;
uniform sampler2D colorBuffer;
uniform vec3 lightPos;
uniform vec3 lightColor;
uniform float lightRadius;
float color_to_float(vec3 color)
{
const vec3 byte_to_float = vec3(1.0,1.0/256.0,1.0/(256.0*256.0));
return dot(color,byte_to_float);
}
vec3 lighting(vec3 SColor, vec3 SPos, float SRadius, vec3 p, vec3 n, vec3 MDiff, vec3 MSpec, float MShi)
{
vec3 l = SPos-p;
vec3 v = normalize(p);
vec3 h = normalize(v+l);
l = normalize(l);
vec3 Idiff = max(0.0, dot(l, n)) * MDiff * SColor;
float att = max(0.0,1.0-length(l)/SRadius);
vec3 Ispec = pow(max(0.0,dot(h,n)),MShi)*MSpec*SColor;
return att*(Idiff+Ispec);
}
void main()
{
vec3 depthcolor = texture2D(depthBuffer, gl_TexCoord[0].st).rgb;
vec3 n = texture2D(normalBuffer, gl_TexCoord[0].st).rgb;
float pixelDepth = color_to_float(depthcolor);
vec3 WorldPos = pixelDepth * normalize(sDir);
gl_FragColor = vec4(lighting(lightColor, lightPos, lightRadius, WorldPos, n, vec3(1.0,1.0,1.0), vec3(1,1,1) ,1) ,128);
gl_FragColor *= texture2D(colorBuffer, gl_TexCoord[0].st);
}


Vertex:
uniform vec3 lightPos;
attribute vec3 screenDir;
varying vec3 sDir;
varying vec4 lpos;
varying vec4 Ldir;
void main()
{
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1] = gl_MultiTexCoord0;
gl_TexCoord[2] = gl_MultiTexCoord0;
gl_Position = ftransform();
sDir = screenDir;
vec4 lp = vec4(lightPos,1);
lpos = lp;
}

Edited by BraXi, 18 August 2012 - 06:13 AM.


Sponsor:

#2 Hedanito   Members   -  Reputation: 134

Like
0Likes
Like

Posted 19 August 2012 - 04:14 AM

You have to call it once for every light, with glBlendFunc(GL_ONE, GL_ONE) so that it adds the results together.

#3 larspensjo   Members   -  Reputation: 1526

Like
0Likes
Like

Posted 19 August 2012 - 12:05 PM

You can make an array of light sources, using a uniform array (for lightPos, lightColor and lightRadius). Then you make a loop in the fragment shader. You also need another uniform that specifies the number of lights.
Current project: Ephenation.
Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/

#4 Radikalizm   Crossbones+   -  Reputation: 2798

Like
0Likes
Like

Posted 19 August 2012 - 01:45 PM

You can make an array of light sources, using a uniform array (for lightPos, lightColor and lightRadius). Then you make a loop in the fragment shader. You also need another uniform that specifies the number of lights.


Pushing all your lights into a single shader does not sound like a good idea to me.

The normal way of rendering deferred lights is to run your lighting shader per light using additive blending.
You could use light volumes for this (make sure you handle cases for when the camera is both inside and outside of the light radius) or plain screen quads.

I gets all your texture budgets!


#5 larspensjo   Members   -  Reputation: 1526

Like
0Likes
Like

Posted 19 August 2012 - 02:00 PM

Pushing all your lights into a single shader does not sound like a good idea to me.

Please explain why. I am doing that just now, and would sure like to know disadvantages and better ways.

One advantage with doing all lights in the deferred lighting phase is that it is easier to do HDR computation, where you do the tone mapping in the end. I suppose that can be done in a this step, but I am afraid it adds more overhead?
Current project: Ephenation.
Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/

#6 BraXi   Members   -  Reputation: 187

Like
0Likes
Like

Posted 19 August 2012 - 02:00 PM

Thanks Hedanito, it worked ;)

#7 BraXi   Members   -  Reputation: 187

Like
0Likes
Like

Posted 19 August 2012 - 02:13 PM

Thanks Hedanito, it worked ;)

This shows how i render the lighting:
-[set blending]
-[bind lighting shader]
--[for each visible light]
----[send lightPos, radius and color to shader]
----[render quad]
-[unbind shader]

PS. sorry for double post, my connection lagged a bit.

Edited by BraXi, 19 August 2012 - 02:14 PM.


#8 Radikalizm   Crossbones+   -  Reputation: 2798

Like
1Likes
Like

Posted 19 August 2012 - 02:38 PM


Pushing all your lights into a single shader does not sound like a good idea to me.

Please explain why. I am doing that just now, and would sure like to know disadvantages and better ways.

One advantage with doing all lights in the deferred lighting phase is that it is easier to do HDR computation, where you do the tone mapping in the end. I suppose that can be done in a this step, but I am afraid it adds more overhead?


You're basically limiting yourself when pushing all your lighting calculations through a single shader. When you use a multipass approach you can truly render any amount of lights as the data required for your lighting remains constant for each pass. Your shader will also become less complex as you don't have to deal with loops.
Another optimization which only works with the multi-pass approach is the usage of light volumes.

I don't see how you're doing tonemapping directly in your light shader? How do you handle multiple types of lights? Unless your shader is heavy with branches you can't really render all your ambient lights, point lights, spot lights, directional lights, and any other type of light you might have with a single shader.

Tonemapping is generally seen as a post-processing step which is completely decoupled from your lighting pass.

I gets all your texture budgets!


#9 BraXi   Members   -  Reputation: 187

Like
0Likes
Like

Posted 19 August 2012 - 03:15 PM

Well now i'm in another trouble, my objects became transparent, i like how the lights blend but the rest should be solid.
btw. thank you Radikalizm for idea, i will try doing it your way.

Posted Image

#10 larspensjo   Members   -  Reputation: 1526

Like
0Likes
Like

Posted 19 August 2012 - 03:23 PM

You're basically limiting yourself when pushing all your lighting calculations through a single shader. When you use a multipass approach you can truly render any amount of lights as the data required for your lighting remains constant for each pass. Your shader will also become less complex as you don't have to deal with loops.
Another optimization which only works with the multi-pass approach is the usage of light volumes.

I suppose it is the same as shadow volumes?

I don't see how you're doing tonemapping directly in your light shader? How do you handle multiple types of lights? Unless your shader is heavy with branches you can't really render all your ambient lights, point lights, spot lights, directional lights, and any other type of light you might have with a single shader.

Ha, yes, it is heavy with branches and loops. Anyway, thanks, I see the advantages and disadvantages of the way you describe.
Current project: Ephenation.
Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/

#11 kalle_h   Members   -  Reputation: 1313

Like
0Likes
Like

Posted 19 August 2012 - 04:30 PM

http://code.google.c...20prelightpass/
Very simple reference implementation of prelight pass renderer for gles2.0

Renderer batch points lights up to 50 per draw call. One point light is just very crude sphere mesh where each vertex have index for light position, range and color.
With openGL you can do proper instancing but with gles2.0 you have to be creative.
Rendering flow is something like that.

1. Draw minimal g-buffer.
2. Draw visible lights to separate fbo. Upload light data as uniform arrays. 60 per batch.
3. Draw geometry second time.

[source lang="cpp"]#ifdef GL_ES#define LOWP lowpprecision mediump float;#else#define LOWP#endifattribute vec4 a_position;uniform mat4 u_projectionMatrix;uniform vec4 lightPos[60];uniform vec4 lightColor[60];varying vec3 v_lightColor;varying vec4 v_lightPos;varying vec4 v_texCoords;varying vec3 v_viewRay;void main(){int i = int(a_position.w);v_lightColor = lightColor[i].rgb;vec4 data = lightPos[i];v_lightPos = data;vec4 pos = vec4((data.xyz + a_position.xyz / data.w), 1.0);v_viewRay = pos.xyz;v_texCoords = u_projectionMatrix * pos;gl_Position = v_texCoords;v_texCoords.xy += v_texCoords.w;v_texCoords.xy *=0.5;}[/source]

[source lang="cpp"]#ifdef GL_ES#define LOWP lowpprecision mediump float;#else#define LOWP #endifuniform sampler2D u_texture0;uniform float far; // 1/farvarying vec4 v_lightPos;varying vec3 v_lightColor;varying vec4 v_texCoords;varying vec3 v_viewRay;const float shininessFactor = 25.0;const vec2 bitShifts = vec2(1.0 / 256.0, 1.0);float unPack(vec2 zz){ zz *= bitShifts; return (zz.x + zz.y);}void main(){ vec4 texture = texture2DProj(u_texture0,v_texCoords); float depth = -unPack(texture.rg); vec2 fenc = texture.ba * 4.0 - 2.0; float f = dot(fenc,fenc); vec3 surfaceNormal = (vec3(fenc * sqrt(1.0 - f * 0.25), 1.0 - f * 0.5)); vec3 viewRay = vec3(v_viewRay.xy * (far / v_viewRay.z), far); vec3 vPos = viewRay * depth; vec3 fromEye = -normalize(vPos); vec3 lightDiffer = v_lightPos.xyz - vPos; float lightDis = length(lightDiffer); vec3 lightDir = normalize(lightDiffer); float diffuse = dot(surfaceNormal, lightDir ) + 1.0; //blinn vec3 halfAngle = normalize(lightDir + fromEye); float specular = pow(clamp(dot(halfAngle, surfaceNormal),0.0,1.0), shininessFactor); specular *= diffuse ; float intensity = diffuse * max(1.0 - lightDis * v_lightPos.w,0.0); gl_FragColor = clamp(intensity * vec4(v_lightColor, specular*0.5),0.0,1.0); }[/source]




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS