EXT_separate_specular_color faulty?

Hi,

For images showing what I’m talking about below, see here:

http://www.software3d.com/BadSpecular

Until now I’ve been handling texture + specular with two-pass rendering. I decided to use GL_EXT_separate_specular_color when available to reduce this to one pass. But it seems that they still have the lighting calculation wrong. Let me explain…

When first implementing my two-pass algorithm, I did the texture pass first, then the specular pass, using this call:

glBlendFunc(GL_ONE, GL_ONE);

But I noticed that this just didn’t look right. Bright parts of the texture would burn out first, making it appear as if the specular was spreading from the bright parts to the dark parts. I realised that specular shouldn’t just be added, but rather it should REPLACE the existing texture, so I changed to this:

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);

And it worked beautifully. As the specular gets close to white, we belend out the original colour.

But I was disappointed when I tried the GL_EXT_separate_specular_color extension with this call:

glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);

It appears to be equivalent to using the faulty glBlendFunc(GL_ONE, GL_ONE). Is there any way to make it behave correctly? Another extension maybe?

For those not sure, think about viewing a shiny poster side-on, so that the light shines off it. The light replaces the image on the poster, it does not add a constant amount of brightness to it so that the image is still fully visible, just brighter. Arguments aside, that old computer graphics adage holds here, “if it looks right, it is right”, and the standard solution here just looks wrong.

How can I get the behaviour I want, the correct behaviour, in a single pass?

Thanks,
Rob.

The fixed function pipe add specular :
clamp((ambient+diffuse+emission)*texture+specular, 0.0, 1.0).

You seem to be doing
clamp([clamp((ambient+diffuse+emission)*texture-specular], 0.0, 1.0)+specular, 0.0, 1.0)
in your 2 pass approach.

which would need shaders for single pass.

You seem to be doing
clamp([clamp((ambient+diffuse+emission)*texture-specular], 0.0, 1.0)+specular, 0.0, 1.0)

Almost. Rather than “-specular” it would be “*(1-specular)”. So ignoring the clamping it would be:

(ambient+diffuse+emission)texture(1-specular)+specular

Do people agree that OpenGL just has it wrong? I checked my Siggraph 2000 advanced OpenGL course notes and even they recommend using (GL_ONE, GL_ONE), so it seems this is common wisdom, but the difference in the results is compelling.

Rob.

Robert,
What you were doing with the blend mode ONE, ONE_MINUS_SRC_COLOR is commonly referred to as “soft add”.

A softadd B =
A + B - A * B =
A + ( 1 - A ) * B =
B + ( 1 - B ) * A

Soft add is sometimes quite useful, but clealy nature doesn’t do soft add when adding lights.

Instead, nature has high dynamic range. It is not before capturing an image on film, or looking with the eye, when soft saturation effects kick in.

So instead of soft add, the more correct solution would be to use a tone map operator at the shader output that compresses values > 1 into the range 0…1 again, like ( 1 - exp(-x) ), for instance.

There is no problem or bug here.

The secondary color is added after the texture operation. It appears to exceed 1.0 then clamp. This is correct and perhaps even desirable.

lmost. Rather than “-specular” it would be “*(1-specular)”. So ignoring the clamping it would be:
Right. I think the lighting equation can be rewritten to be more in line with reality. Forget about ambient light, specular light, diffuse light. You just need “light color”
Forget material ambient as well.

If I would rewrite the lighting equation, I would do it like this :

diffusefactormaterialdiffusetexturelightcolor(1-specularFactor)+lightcolor*specularFactor+
materialemission

Thanks for the term “soft add”. I didn’t know there was a name for it. Glad to see I’m not the only one.

Originally posted by dorbie:
[b]There is no problem or bug here.

The secondary color is added after the texture operation. It appears to exceed 1.0 then clamp. This is correct and perhaps even desirable.[/b]
It’s not a bug in that it works the way it’s meant to, but back to the old phrase “if it looks right, it is right”. It really looks so much better doing the soft add. It just looks 100% right. The other way just feels all wrong.

I even tried darkening the texture to 50%, so that no clamping would occur, and the soft add still looked better, although the difference was more subtle.

With the soft add, the highlight appears to be on top of the image. With the standard method, it appears to come from beneath the image, shining through it, which just feels wrong.

Pity there’s no way to do it in a single pass. And I want to add shadows sometime too, so each light will be a separate pass. Not sure whether soft add will be possible at all then :frowning:

Rob.

you should be able to use a custom shader to do whatever you want in a single pass.

the only complications is if you want to mix up the render states you might have to juggle multiple conditional shaders.

i really don’t think there are too many cases in rendering these days where you should have to use more than a single pass to get a desired effect no matter how layered it is.

unless you need to render to a texture (and in this case you don’t), a single pass and a shader will probably work.

I hold the strong opinion that the overexposure and clamping looks more correct that this soft blend abomination :slight_smile:

I don’t find it objectionable infact I find it desirable giving a type of extended range hackery.

You have a case where you have a very bright unmodulated base image and a fairly constant specular term but there are many circumstances where a colored or darker base result has a more localized specular highlight added that may also change your mind.

Originally posted by michagl:
you should be able to use a custom shader to do whatever you want in a single pass.
Would need a fragment/pixel shader, right? I have vertex shaders on my computer, but not pixel shaders.

I did have a long look into shaders, but I can’t find any good introductory tutorials on the subject online (any suggestions?). It seems first you need to recreate the normal OpenGL shader logic, as that is otherwise circumvented. I’m not sure whether you need to write separate shaders for every possible case (eg one texture or two, and other changes to rendering state). Then there’s the choice of GL_ARB_vertex_program or GL_NV_vertex_program, which are supposedly very similar, but seem quite different from the sample code I’ve seen. All seems very messy and complicated.

Originally posted by dorbie:
[b]I hold the strong opinion that the overexposure and clamping looks more correct that this soft blend abomination :slight_smile:

I don’t find it objectionable infact I find it desirable giving a type of extended range hackery.

You have a case where you have a very bright unmodulated base image and a fairly constant specular term but there are many circumstances where a colored or darker base result has a more localized specular highlight added that may also change your mind.[/b]
I’ve yet to find a case where the OpenGL way looks better. As I said, I tried turning the colours down to 50% to avoid all clamping, and my way still looked better (but harder to judge).

The books say that your ambient + diffuse + specular shouldn’t add to more than 1.0, but this is incredibly limiting. If you want specular of at least 0.5 then your diffuse is limited to 0.5, so everything looks grey and dark.

My app allows a user to load a bitmap and put it on the face of a polyhedron. I can’t darken it by 50% just to avoid OpenGL’s clamping problems when specular comes into play.

The soft-add solution I have now just looks great. I can’t imagine anyone prefering the standard OpenGL way, and you never have to worry about clamping. The difference is especially compelling when you see the surface turning with specular highlights coming and going (more so than in the stills).

At this point I’m resigned to doing 2 passes to have nice specular.

But the next big problem is that I want to add multiple lights, and eventually shadows!

Without shadows, multiple lights will require adding all the ambient and diffuse textured fragments together from all the lights, THEN soft-adding specular from each light. So I have (1+L) passes for L lights.

With shadows (using shadow volumes and stencil planes), the problem gets a whole lot worse. If I wanted to add ambient and diffuse from each light BEFORE adding specular from each light, I would have to render the shadow volumes (and lit scene) for each light twice, first for the ambient/diffuse, then again later for the specular! This seems like a rediculous number of passes.

Seems to me the soft-adds need to be done after the normal adds. Even fragment shaders won’t help here.

Rob.

They don’t add to more than one, but only because the result is clamped, so it’s correct behavior and quite deliberate.

The underlying problem is the lack of extended range but even if you had this you’d need some sort of transfer function and probably some clamping. You could try implementing this by just halving all light contribs in a scene then you worry about the transfer function when you see the result but multiple lights qould cause similar issues.

There’s no reason you can’t have multiple lights contributing to the specular term in your second pass so it’s 1+1 not 1+L.

Originally posted by dorbie:
They don’t add to more than one, but only because the result is clamped, so it’s correct behavior and quite deliberate.
Right, and clamping is the thing that we want to avoid. You can see how it looks in the images I linked to.

The underlying problem is the lack of extended range
Well, I suppose so, but the apparent effect in the real world is that specular replaces the underlying surface, not that we get the original image only brighter. Turn a shiny poster sideways and the sensation is that there’s a bright spot that replaces what’s underneath.

Using soft-add may not be strictly correct, but it behaves much better in all situations than adding with clamping (well, all situations I’ve tried so far). It kind of handles the dynamic range problem for you.

And right or wrong, it’s what I want to do, but achieving this with multiple lights is the thing I’m now having trouble with.

There’s no reason you can’t have multiple lights contributing to the specular term in your second pass so it’s 1+1 not 1+L.
Depends whether I soft-add each one individually, which would look best (and require more passes I think), or if I just add all the specular together then soft-add the result (just one extra pass).

And with shadows I’m really stuffed, as I said before. It’s the mixing of normal add and soft-add that makes things difficult, requiring all the soft-adds to be done last (unless there’s a mathematical solution I haven’t found yet). One possibility is to do ALL soft-adds, ie soft-add all the ambient and diffuse light too. Then any order comes out the same, and diffuse + specular can be done together while the shadow stencil is set for that light. Diffuse from multiple lights could also add to above 1.0 and get clamped under the standard lighting model, but soft-adding would provide a smooth behaviour as brightness approaches 1.0.

I now think that even untextured surfaces could benefit from soft-adding specular highlights! For example a red surface burns bright red rather than white (the colour of the light). It’s always struck me as unnatural. The specular on my textured surfaces currently looks better than on my untextured surfaces.

Rob (soft-adding in a world of harsh-add-clamping).

I don’t get this, you do realise that a specular highlight is a reflection, don’t you? So it’s 100% correct for the highlight to be the same colour as the light, not the object reflecting the light.
I don’t think “pleasant looking” equals “natural looking”, otherwise pamela anderson would still be working in a supermarket.

Originally posted by knackered:
I don’t get this, you do realise that a specular highlight is a reflection, don’t you? So it’s 100% correct for the highlight to be the same colour as the light, not the object reflecting the light.
Yes, isn’t that the point I’ve been making? With the standard model, an image has light added to it as specular increases, so the light parts soon burn to white, but the dark parts take a while to catch up, which just doesn’t look right.

My method is to do a soft-add (defined above), which means the image interpolates equally towards white, rather than the dark parts catching up later.

All I can say is it looks spot on compared to the standard model. See the images linked at the start of this thread. (I really need to put a demo together don’t I?)

Rob.

Yes please, post a demo, I am not convinced at all.

Remember that topic about reverse lighting?
Something about starting at full white, then subtracting each lights contribution from the framebuffer, until finally inverting the whole image with a full screen quad?
Maybe you’d find that interesting, Robert.

That just reverses the arithmetic. Stuff still clamps at zero in the FB if the values going in are equivalent I think (although I never read that thread, I’d be interested in a link).

Extended range is definitely what you need. Half the intensity of the lights and work on your transfer function applied as a LUT (video if possible).

Doing a soft blend is bad enough but accumulating it from several passes with multiple lights is just heineous.

You need to go back to first principals and base your solution on some kind fo correct illumination function.

Originally posted by knackered:
I don’t think “pleasant looking” equals “natural looking”, otherwise pamela anderson would still be working in a supermarket.
:slight_smile: ))) I must give you a star!!!

yooyo