gfxfundamentals / webgl-fundamentals

WebGL lessons that start with the basics
http://webglfundamentals.org
Other
4.68k stars 662 forks source link

Combining lights. #81

Open trusktr opened 7 years ago

trusktr commented 7 years ago

It would be sweet if the guide showed how to combine lights (f.e. using both directional and point lighting).

I tried enabling both lights (as in the directional and point tutorials), but it seems that the multiplication of the values makes them too small (makes sense since they are less than 1), so the objects just get darker (except for the specular which is added, that is still relatively shiny).

How would you recommend using both a directional and a point light, or any combination of lights?

Ambient lighting would also be nice. I am guessing that is easy: just add a color to gl_FragColor.rgb (I haven't read about it, just guessing).

trusktr commented 7 years ago

This is what I've tried so far:

            gl_FragColor = v_fragColor;

            gl_FragColor.rgb *= directionalLight;

            gl_FragColor.rgb *= pointLight * u_lightColor;
            gl_FragColor.rgb += specular * u_specularColor;

which isn't quite right. But if I comment out one light,

            gl_FragColor = v_fragColor;

            //gl_FragColor.rgb *= directionalLight;

            gl_FragColor.rgb *= pointLight * u_lightColor;
            gl_FragColor.rgb += specular * u_specularColor;

or the other,

            gl_FragColor = v_fragColor;

            gl_FragColor.rgb *= directionalLight;

            //gl_FragColor.rgb *= pointLight * u_lightColor;
            //gl_FragColor.rgb += specular * u_specularColor;

then either light looks good.

stefnotch commented 7 years ago

How about:

gl_FragColor.a = v_fragColor.a;
gl_FragColor.rgb =  v_fragColor * (directionalLight + pointLight * u_lightColor + specular * u_specularColor);

^^the fragment color times every light

trusktr commented 7 years ago

Ah, thanks @stefnotch, so we have to multiply the frag color by the sum of all the lights, not the product of all the lights.

It is looking a better now, but something still doesn't seem right.

Here's my example with only a directional light, which looks as expected (see around line 704): https://jsfiddle.net/bm0mubm8/9

Here's my example with only a point light orbiting around the object: https://jsfiddle.net/bm0mubm8/10

Here's my first attempt at combining lights, by multiplying the lights, which is wrong: https://jsfiddle.net/bm0mubm8/7

Here's my example using your suggestion of adding directional+point+specular before finally multiplying the color: https://jsfiddle.net/bm0mubm8/8

Using your suggestion, it looks better, but still not right because when the orbiting point light goes behind, the directional lighting should still leave the front relatively illuminated. Also, adding the specular to the other lights seems to make the shininess go away compared to before.

Do we add the specular to the lights? Or add it to the final color?

For example, here's is the example with specular added to the frag color after multiply the frag color by the sum of the lights, which makes the shiny effect seem more as expected: https://jsfiddle.net/bm0mubm8/11

But still, the directional light seems to be mostly ignored, and the object is dark in front (in the cirectional-only example the front-right of the object is lit up).

Do you know what's going on?

stefnotch commented 7 years ago

I'm not entirely sure, but it looks like some lights contain negative values which cancel out your directional light. You could try using clamp().

Something like this might work:

clamp(directionalLight, 0.0, 1.0) +
clamp(pointLight, 0.0, 1.0) * u_lightColor + 
clamp(specular, 0.0, 1.0) * u_specularColor
trusktr commented 7 years ago

Interesting, that does yield the best results so far, for example: https://jsfiddle.net/bm0mubm8/12

@greggman What are your thoughts? Maybe we can add this to the guide. If we add it, should it live on the point light page, or have a new page? And which to update? Maybe just webgl2fundamentals as it is too much work to update both. I personally think it may be good to share some lessong across both guides. For example I would think that this lighting stuff remains exactly the same across both versions?

trusktr commented 7 years ago

As for ambient lighting, I gave the following a shot, which for the most part seems like ambient lighting, though maybe there's a way to "weigh" the lights or something?

            gl_FragColor = v_fragColor;

            vec3 ambientLight = vec3(0.361, 0.184, 0.737); // teal
            gl_FragColor.rgb *= clamp(directionalLight, 0.0, 1.0) + clamp(pointLight, 0.0, 1.0) * u_lightColor + ambientLight;

            // Just add in the specular
            gl_FragColor.rgb += specular * u_specularColor;

Here's the last fiddle with this added "ambient lighting", and the camera moved more to the left of the object to see the darker side: https://jsfiddle.net/bm0mubm8/13/

The fiddle shows directional light hitting on the right side of the object (from the camera's perspective) and the darker teal ambient light on the left instead of darkness, with the point light still orbiting.

Any more suggestions on how to improve the combination of lights? How close is this to modern standards (like what most games may do)?

trusktr commented 7 years ago

I notice that it seems if I add white colored ambient, or similarly bright colors for other lights, then the object starts to look really shiny due to the way the colors are adding together. So there's obviously more to this lighting story...

Here's the example, with a white-colored ambient light: https://jsfiddle.net/bm0mubm8/14/

The object looks sort of shiny, not just from specular, but due to how the color gradients change when adding the static ambient light with the point light and directional light for example.

Maybe I just need to scale down the lights by a factor? For example instead of just + ambientLight maybe + ambientLight * ambientLightIntensity where ambientLightIntensity may be 0.6 for example. How would you handle the light in, say, a game you are making from scratch?

Gonna go hunt down some GL lighting theory later, but I think this will do for now for my current project.

greggman commented 7 years ago

I'll put it on my todo list to add something for multiple lights. Along with spotlights and shadows 😅

There are many big topics though if you're making an engine. Some examples

  1. You shouldn't make a single shader that support N lights. You should generate shaders that support the options you need. So, a shader generation article

  2. Issues with large scenes like

    1. lods
    2. occlusion culling
    3. potentially visible sets
    4. portals
    5. light databases. If you have a large scene you can't apply all the lights to all models. You have to apply just the close lights, often the 2 to 4 closest lights.
  3. materials

    A way to organize and separate the uniforms of a shader related to materials vs those related to projection

  4. phyiscally based rendering

    Which in itself is a huge topic but the major game engines all default to physically based rendering now.

That's just a short list and there's no way I have time to write all of them (not without serious funding 😜)

trusktr commented 7 years ago

I'll put it on my todo list to add something for multiple lights. Along with spotlights and shadows 😅

That would be great! I think that would be the most desirable minimal fundamentals, since right now lights go through walls. 🤣

(not without serious funding 😜)

Hmm, what about a kickstarter or some similar crowd funding campaign? With an awesome video made by a graphic designer, using some 3D graphics of yours, depicting what the most awesome WebGL Fundamentals could be/contain, I bet serious funding could become true!