Per-Pixel Lighting Issues with Point Lights and Lighting.j3md

Hello,

I’m having trouble getting Per-Pixel Lighting to work properly on simple geometries with a low vertex density.

Here are a few images to illustrate the problem:

Screenshot_LightingError_20190709_01
^ The corners of the geometry get light applied mostly correct (a point light is always near the camera location)


^ As the light (and camera) moves towards the center of the wall the light diminishes unnaturally (still at about the same distance to the wall as before, just moved slightly to the right)


^ On the other end of the wall (light and camera moved further to the right) the light is appearing strong again


^ And here is a wireframe of this simple geometry setup (moved the camera a bit backwards)

So apperently the light contributes to the walls material only at the edges of the geometry, where there are vertices nearby.

This looks like Vertex Lighting to me and I just can’t figure out how to do proper Pixel Lighting using JME’s Lighting.j3md.

Here is how I set up the material:

// Material
blockMaterial = new Material( assetManager, "Common/MatDefs/Light/Lighting.j3md" );
Texture texture = assetManager.loadTexture( "Textures/Ground.png" );
texture.setWrap( Texture.WrapMode.Repeat );
blockMaterial.setTexture( "DiffuseMap", texture );

blockMaterial.setColor( "Diffuse", ColorRGBA.White );
blockMaterial.setColor( "Specular", ColorRGBA.White );
blockMaterial.setFloat( "Shininess", 32.0f );
blockMaterial.setBoolean( "VertexLighting", false );
blockMaterial.setBoolean( "UseVertexColor", false );

And here is how I create the point light:

testlight = new PointLight( player.position, new ColorRGBA( 10.0f, 10.0f, 10.0f, 1.0f ), 164.0f );
rootNode.addLight( testlight );

I have exaggerated the light color intensity a bit, because it makes the issue more clear visually.

The lights radius is quite high because the geometries are also fairly large (the wall is about 256.0 world units wide).

I have also prepared a little test case (based on jmonkeyengine/TestLightRadius.java at master · jMonkeyEngine/jmonkeyengine · GitHub):

/*
 * Copyright (c) 2009-2012 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package main;

import com.jme3.app.SimpleApplication;
import com.jme3.light.DirectionalLight;
import com.jme3.light.PointLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.scene.shape.Torus;

public class TestLightRadius extends SimpleApplication {

    float pos, vel=1;
    PointLight pl;
    Geometry lightMdl;

    public static void main(String[] args){
        TestLightRadius app = new TestLightRadius();
        app.start();
    }

    @Override
    public void simpleInitApp() {
		
	viewPort.setBackgroundColor( ColorRGBA.Blue );
		
        Torus torus = new Torus(10, 6, 1, 3);
//        Torus torus = new Torus(50, 30, 1, 3);
        //Geometry g = new Geometry("Torus Geom", torus);
	Geometry g = new Geometry( "Geom", new Box( 10, 1, 2 ) );
        //g.rotate(-FastMath.HALF_PI, 0, 0);
        g.center();
//        g.move(0, 1, 0);
        
        Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
        mat.setFloat("Shininess", 32f);
        mat.setBoolean("UseMaterialColors", true);
        mat.setColor("Ambient",  ColorRGBA.Black);
        mat.setColor("Diffuse",  ColorRGBA.White);
        mat.setColor("Specular", ColorRGBA.White);
//        mat.setBoolean("VertexLighting", true);
//        mat.setBoolean("LowQuality", true);
        g.setMaterial(mat);

        rootNode.attachChild(g);

        lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
        lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
        rootNode.attachChild(lightMdl);

        pl = new PointLight();
        pl.setColor(ColorRGBA.Green);
        pl.setRadius(4f);
        rootNode.addLight(pl);

        DirectionalLight dl = new DirectionalLight();
        dl.setColor(ColorRGBA.Red);
        dl.setDirection(new Vector3f(0, 1, 0));
        rootNode.addLight(dl);
    }

    @Override
    public void simpleUpdate(float tpf){
//        cam.setLocation(new Vector3f(5.0347548f, 6.6481347f, 3.74853f));
//        cam.setRotation(new Quaternion(-0.19183293f, 0.80776674f, -0.37974006f, -0.40805697f));

        pos += tpf * vel * 5f;
        if (pos > 15){
            vel *= -1;
        }else if (pos < -15){
            vel *= -1;
        }
        
        pl.setPosition(new Vector3f(pos, 2, 0));
        lightMdl.setLocalTranslation(pl.getPosition());
    }

}

As you can see the moving point light contributes light to the boxes material unevenly, similar to Vertex Lighting.

Does anybody know how I could setup my materials and lights so that I get proper per pixel / fragment lighting in my scene?

Am I missing a material parameter?

to let others see video i uploaded one:

i hope this will let others see so someone might know why.
what i see is that in middle of box, there is no light. I think it might be related to PointLight distance?

when i increase Radius of light from 4 to 20 the light is visible in middle.

1 Like

I wasn’t aware that JME implemented per-pixel lighting. I thought we interpolate lighting between mesh vertices. In which case, the solution would be to subdivide the mesh.

But I’m not a rendering expert, so I might be wrong.

1 Like

Yes, for regular lighting, increase mesh subdivision is the answer. Lighting is calculated per fragment but it’s using normals, etc. that are interpolated over the whole shape.

2 Likes

OK, thanks for the clarification!

Because these simple geometries should be modifiable in game, subdividing the mesh is not really an option in my case.

So I guess my only option is to implement it myself in a custom shader.

well, you can play arround custom mesh :slight_smile:

thats how voxel terrain or crazy custom generated meshes are done.

its not easy to create it, because you would understand how vertex/etc Buffer works first.

I did a quick test to see how subdivision helps the lighting using MBox and it looks sufficiently good.

Screenshot_LightingError_20190710

Now I just have to work around how to modify these more complex meshes, but that’s for another topic…

1 Like

This handly little class lets you slice and dice.

/**
 *  A mesh implementation that creates a box with a specified
 *  number of slices, masking off one or more sides.  Separate
 *  slice counts can be provided for x,y,z axes and any or all
 *  of the sides can be turns on or off.  In other words, this
 *  can be used directly as a Box implementation with more
 *  triangles (subdivided) or it can be used as a subdivided
 *  Quad if only one side it specified.
 *
 *  @author    Paul Speed
 */

Lemur is available as a gradle dependency: Package lemur - simsilica

He’s already using it. :slight_smile: