New custom lighting shader - Less code, and looks great!

Hey all - I was having some problems with the current Light.j3md shader being too dark, and I wanted to learn some shader programming anyway. I know that the lighting system is going to be updated in JME 3.1, but I just wanted to throw this out there for anybody in my position.

Default JME Lighting shader:

My custom lighting shader:

It still needs support for normal maps and stuff like that, but I think it’s a pretty good start.

If there is interest shown, I will gladly clean up the code and post it here for you all to use. Thanks for reading! :smiley:

Seems like you have no ambient light in your first pic so anything facing away from the light is going to be black. You see some of that in your second pic, also.

0 ambient is a really unrealistic lighting setup but especially in a foggy environment like you have in your sky box.

300 fps drop? What lighting model are you using?

Yeah it looks like you could probably use some ambient lighting in there. I like to do my ambient lighting something like:

void main() {
    vec4 diffCol = texture2D(m_DiffuseMap, texCoord);
    vec4 col = g_AmbientLightColor.rgb;
    vec4 norm = texture2D(m_NormalMap, texCoord);
    norm.x = norm.a;
    norm.b = sqrt(1.0 - (norm.x * norm.x) - (norm.y * norm.y));
    vec3 n = normalize(tbn * ( * vec3(2.0, 2.0, 2.0) - vec3(1.0, 1.0, 1.0)));

    float NdotL = max(0.0, dot(n,;
    if (NdotL > 0.0) {
        col.rgb = (diffCol.rgb * g_LightColor.rgb) * NdotL;
        col.rgb += (diffCol.rgb * g_AmbientLightColor.rgb) * (1.0 - NdotL);
    } else {
        col.rgb *= diffCol.rgb;

    gl_FragColor = col;

This helps keep areas of the model lit by the primary light from getting washed out by the ambient light while allowing unlit areas to receive full ambient lighting.

…or you could just use Lighting.j3md which already supports ambient lights. :slight_smile:

Yeah, Lighting.j3md makes use of ambient lighting, but calculates it different in comparison to how I, personally, prefer it.

The difference being:

AmbientSum * diffuseColor.rgb + diffuseSum.rgb * diffuseColor.rgb * vec3(light.x) + specularSum * vec3(light.y);


col.rgb = (diffCol.rgb * g_LightColor.rgb) * NdotL;
col.rgb += (diffCol.rgb * g_AmbientLightColor.rgb) * (1.0 - NdotL);

Of course the difference is purely a matter of personal preference. Lighting.frag adds ambient lighting to the whole model which, in my opinion, washes out the illuminated portions of the model, while my implementation fades the ambient light in as the primary light fades out which gives more contrast.

Yeah, I guess… right around 45 degrees you will be over lit by almost 40%… that’s where the equation would be the same as Lighting.j3md if you dropped ambient and diffuse globally to 72%.

It’s weird, and yeah, there are lots of different non-physical lighting approaches one can use. JME chose to use actual lighting in Lighting.j3md.

Not sure I see your math there. So say ambient light is 0.5, light color is 0.5 and NdotL is 0.5 you get 0.5 as a result. With Lighting.frag you would get 0.75.

Technically neither approach is realistic and Lighting.frag is probably more realistic in terms of if a light is coming from all directions it would illuminate the entire model, but in real life you rarely, if ever, have the same light coming from all directions.

Lighting.frag simulates ambient light as coming from all directions. In the above example Tryder.frag simulates ambient light as a fill light.

the dot product at 45 degrees is 0.71

There is a range of lighting angles where your overall output is > 1.

It’s actually exactly what you’d get from two directional lights pointing in opposite directions.

Edit: nevermind… I read the math wrong.

[quote=“pspeed, post:9, topic:32159, full:true”]It’s actually exactly what you’d get from two directional lights pointing in opposite directions.

Yeah, pretty much the effect I was going for, except a little better in my opinion because you don’t have dark spots in areas that neither light hits. Like I said in my last post, I edited it you may have missed that, I’m turning the ambient light into a fill light. It’s neither better or worse, just a matter of preference or better yet a matter of achieving the particular effect one desires.

Plus in most of my actual shaders I’m doing a variety of things that aren’t supported by Lighting.frag, such as the use of splat maps and color balancing. Just meaning that I have more than one reason for writing my own shaders :slight_smile:

P.S. Color balance is great, while it’s useful for post-pro it’s also a great RAM and time saver for certain scenarios. For instance if you have a particular texture that’s used over and over again, but is different colors instead of creating textures of the same thing in different colors you can just create one, say a black and white one, and add the color in at run-time with color balance. It gives finer control over the color of a texture than just multiplying the texture by a color.

pow(m_gain.rgb * (col.rgb + (m_lift.rgb - 1.0) * (1.0 - col.rgb)), 1.0 / m_gamma.rgb)

Right now I haven’t had enough time to work on a real convincing ambient lighting effect, but I was doing something really weird before. I wanted the ambient color to actually DARKEN the model, where setting the ambient color to black would darken it:

vec3 invAmbient = vec3(1.0-ambient.r, 1.0 - ambient.g, 1.0 - ambient.b);    

diffuseColor.r -= invAmbient.r*(1.0 - diffuseColor.r)*(m_AmbientIntensity);
diffuseColor.g -= invAmbient.g*(1.0 - diffuseColor.g)*(m_AmbientIntensity);
diffuseColor.b -= invAmbient.b*(1.0 - diffuseColor.b)*(m_AmbientIntensity);

This is my fragment shader.

uniform sampler2D m_Texture;

uniform float m_AlphaDiscard;
varying vec2 texCoord;
varying vec3 inNorm;
varying vec3 inVert;
uniform vec4 g_LightPosition;
uniform vec4 g_LightDirection;
uniform vec4 m_AmbientColor;
uniform vec4 m_AlbedoColor;
uniform vec4 m_LightColor;
void main() {
// Set the fragment color for example to gray, alpha 1.0
vec4 col = texture2D(m_Texture, texCoord);

vec3 albedo = m_AlbedoColor;
vec3 ambient = m_AmbientColor;
col.rgb += ambient.rgb;
vec3 n = normalize(inNorm);
vec3 v = normalize(inVert);
vec3 l = normalize(gl_LightSource[0].position);

float nDotl =  max(0.0, dot(n, l));

float nDotv =  max(0.0, dot(n, v));

float G = nDotl * nDotv;

vec3 light_color = m_LightColor.rgb;
col.rbg *= (clamp(G*(nDotv+nDotl), 0.00, 0.9)+(light_color*0.1));

if (col.a > m_AlphaDiscard) {
    gl_FragColor = col; 
} else {


You were right about the ambient light. I tested out adding an AmbientLight to the scene, and everything looked a lot better.

House using default Light.j3md and a gray ambient light:

I’m still going to use my new lighting shader, at least until 3.1. I just find the light is a lot better looking, and I love writing shaders :stuck_out_tongue:

Actually, I mentioned using regular Lighting because I thought yours looked very strange… like there was a flashlight shining on the front of the house but somehow some surfaces were still brightly lit from the top. It kind of looked like one of those pictures where the shadows are all wrong.

…but you are right that writing shaders is fun… and frustrating… and fun. :slight_smile:

Haha well to each their own, I like the light spreading effect :stuck_out_tongue: And yes you are right that they are frustrating… Debugging, using colors as your only means of getting information… can sure be a challenge.