Hot questions for Using Lightweight Java Game Library in lighting

Question:

I am currently working on implementing lighting to my project with OpenGL. Still working on the basic implementation (getting lights to display correctly) and I encountered a weird problem. When trying to create a spotlight with a cutoff (limited angle affected by the light) I get very hard edges, almost like stairs, as seen in this picture:

There is no ambient light (using glLightModel(GL_LIGHT_MODEL_AMBIENT, colorToFloatBuffer(new Color(0.0f, 0.0f, 0.0f, 1.0f)))) and I also disabled spot exponent (specifies how the light is distributed in the cone) and all attenuations to make the effect clearer and the background is a big white image (made black by the lack of ambient light; I am also wondering why I even need ot use an image at all in order to see the light).

Here is how I set up the light:

glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);

glLight(glLightID, GL_AMBIENT, colorToFloatBuffer(Color.blue));
glLight(glLightID, GL_DIFFUSE, colorToFloatBuffer(Color.black));
glLight(glLightID, GL_SPECULAR, colorToFloatBuffer(Color.black));

glLight(glLightID, GL_POSITION, floatBuffer.put(position.x).put(position.y).put(0.0f).put(1.0f));

glLight(glLightID, GL_SPOT_DIRECTION, otherFloatBuffer.put(1.0f, 0.0f, 0.0f)); 
glLightf(glLightID, GL_SPOT_EXPONENT, 0);
glLightf(glLightID, GL_SPOT_CUTOFF, 22.5f);

glLightf(glLightID, GL_CONSTANT_ATTENUATION, 1.0f);
glLightf(glLightID, GL_LINEAR_ATTENUATION, 0.0f);
glLightf(glLightID, GL_QUADRATIC_ATTENUATION, 0.0f);

On a different note, I also scale the context before applying any of these effects (I use perspective view for some visual effects and yet want to maintain orthographic coordinates), if that is any help.


Answer:

There are to ways you can achieve that:

  1. Continue using per-vertex lighting. You, however, should increase the resolution of your scene - put more polygons inside!
  2. Change to per-pixel lighting. You will need to learn shader programs. Here are some tutorials on the topic.

Question:

I'm trying to create a simple 2D game engine using lwjgl and java. But I got stuck during the lighting programming, because the code I wrote only works when I have one light in the scene and not when I add multiple. I just can't figure out why so i decided to ask here.

This is the fragment shader with the lighting calculation:

#version 330 core

layout (location = 0) out vec4 color;
layout (origin_upper_left, pixel_center_integer) in vec4 gl_FragCoord;

in DATA {
    vec2 tc;
} fs_in;

uniform sampler2D tex;

float map(float value, float min1, float max1, float min2, float max2) {
  return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
}

void main() {
    vec3 lightColor = vec3(1, 1, 1);
    float range = 700;
    float x = 200;
    float y = 200;
    float ambient = 0.1;
    float intensity = 0.8;
    float alpha = 1.0;

    vec3 totalDiffuse = vec3(0.0);

    for(int i=0;i<1;i++){
        alpha = 1-map(distance(gl_FragCoord.xy, vec2(x*i, y*i)), 0.0, range, 0.0, 1.0);
        totalDiffuse += alpha*lightColor;
    }

    totalDiffuse = max(totalDiffuse, ambient);

    color = vec4(totalDiffuse, 1.0) * texture(tex, fs_in.tc);
}

If I run this code with only one light in the scene, i.e. setting the times the for loop runs to 1, then it works just fine and creates something like this:

But when I change it to loop for example 3 times, you would expect it to create 3 different lights, but actually it just increases the light intensity of the first light like this:

Anybody know why?


Answer:

The result of the term

alpha = 1-map(distance(gl_FragCoord.xy, vec2(x*i, y*i)), 0.0, range, 0.0, 1.0);

can be negative.

This will cause that the totalDiffuse is decreased.

Clamp alpha to a minimum of 0:

//totalDiffuse += alpha*lightColor;
totalDiffuse += max(0.0, alpha) * lightColor;

Note, the distance between the light sources is length(vec2(200, 200)), but the illumination range (radius) of each light source is 700. So anyway the light sources are overlapping.


I recommend to use the glsl function smoothstep, which performs a Hermite interpolation between two values.

Try the following:

for(int i=0; i<3; i++ )
{
    vec2 pos = vec2(x, y) * float(i);
    float dist = distance(gl_FragCoord.xy, pos);

    alpha = smoothstep(50.0, 100.0, dist);
    totalDiffuse += clamp(1.0-alpha, 0.0, 1.0) * lightColor;
}

in this case the 2nd parameter (100.0) to smoothstep is the maximum radius of the light source and the 1st parameter (50.0) is the radius which is full lit.


See the WebGL example, where I used smoothstep:

(function loadscene() {    

var canvas, gl, vp_size, prog, bufObj = {};

function initScene() {

    canvas = document.getElementById( "ogl-canvas");
    gl = canvas.getContext( "experimental-webgl" );
    if ( !gl )
      return;

    progDraw = gl.createProgram();
    for (let i = 0; i < 2; ++i) {
        let source = document.getElementById(i==0 ? "draw-shader-vs" : "draw-shader-fs").text;
        let shaderObj = gl.createShader(i==0 ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER);
        gl.shaderSource(shaderObj, source);
        gl.compileShader(shaderObj);
        let status = gl.getShaderParameter(shaderObj, gl.COMPILE_STATUS);
        if (!status) alert(gl.getShaderInfoLog(shaderObj));
        gl.attachShader(progDraw, shaderObj);
        gl.linkProgram(progDraw);
    }
    status = gl.getProgramParameter(progDraw, gl.LINK_STATUS);
    if ( !status ) alert(gl.getProgramInfoLog(progDraw));
    progDraw.inPos = gl.getAttribLocation(progDraw, "inPos");
    progDraw.u_time = gl.getUniformLocation(progDraw, "u_time");
    progDraw.u_resolution = gl.getUniformLocation(progDraw, "u_resolution");
    gl.useProgram(progDraw);

    var pos = [ -1, -1, 1, -1, 1, 1, -1, 1 ];
    var inx = [ 0, 1, 2, 0, 2, 3 ];
    bufObj.pos = gl.createBuffer();
    gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.pos );
    gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( pos ), gl.STATIC_DRAW );
    bufObj.inx = gl.createBuffer();
    bufObj.inx.len = inx.length;
    gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufObj.inx );
    gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( inx ), gl.STATIC_DRAW );
    gl.enableVertexAttribArray( progDraw.inPos );
    gl.vertexAttribPointer( progDraw.inPos, 2, gl.FLOAT, false, 0, 0 ); 
    
    gl.enable( gl.DEPTH_TEST );
    gl.clearColor( 0.0, 0.0, 0.0, 1.0 );

    window.onresize = resize;
    resize();
    requestAnimationFrame(render);
}

function resize() {
    //vp_size = [gl.drawingBufferWidth, gl.drawingBufferHeight];
    vp_size = [window.innerWidth, window.innerHeight];
    //vp_size = [256, 256]
    canvas.width = vp_size[0];
    canvas.height = vp_size[1];
}

function render(deltaMS) {

    gl.viewport( 0, 0, canvas.width, canvas.height );
    gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
   
    gl.uniform1f(progDraw.u_time, deltaMS/2000.0);
    gl.uniform2f(progDraw.u_resolution, canvas.width, canvas.height);
    gl.drawElements( gl.TRIANGLES, bufObj.inx.len, gl.UNSIGNED_SHORT, 0 );
    
    requestAnimationFrame(render);
}  

initScene();

})();
<script id="draw-shader-vs" type="x-shader/x-vertex">
precision mediump float;
attribute vec2 inPos;

void main() {
    gl_Position = vec4( inPos.xy, 0.0, 1.0 );
}
</script>

<script id="draw-shader-fs" type="x-shader/x-fragment">
precision mediump float;
uniform float u_time;
uniform vec2 u_resolution;

vec3 HUEtoRGB(in float H)
{
    float R = abs(H * 6.0 - 3.0) - 1.0;
    float G = 2.0 - abs(H * 6.0 - 2.0);
    float B = 2.0 - abs(H * 6.0 - 4.0);
    return clamp( vec3(R,G,B), 0.0, 1.0 );
}

void main() {
    vec2 uv = gl_FragCoord.xy / u_resolution;

    vec3 lightColor = vec3(1, 1, 1);
    float range = length(u_resolution) / 4.0;
    float x = u_resolution.x / 4.0;
    float y = u_resolution.y / 4.0;
    float ambient = 0.1;
    float intensity = 0.8;
    float alpha = 1.0;

    vec3 totalDiffuse = vec3(0.0);

    for(int i=0;i<3;i++)
    {    
        vec2 pos = vec2(x, y) * float(i+1);
        float dist = distance(gl_FragCoord.xy, pos);
        
        alpha = smoothstep(range/4.0, range/2.0, dist);
        totalDiffuse += clamp(1.0-alpha, 0.0, 1.0) * lightColor;
    }
    totalDiffuse = max(totalDiffuse, ambient);

    vec4 texcol = vec4( 1.0-uv.x, 1.0-uv.y, uv.x*uv.y, 1.0 );
    gl_FragColor = vec4(totalDiffuse, 1.0) * texcol;
}
</script>

<canvas id="ogl-canvas" style="border: none"></canvas>

Question:

So this is my first time with Deferred Shading, and it's pretty fast and simple. Until I start to not get the expected results. The light behaves in a weird way, it's like the position texture is not right. (from top to bottom clockwise: (1) Normal, (2) Position, (3) Albedo, (4) Roughness/Fresnel, (Center) Final)

Notice that the light (in the final render) is behaving in a weird way.

Here's my shader codes: GBuffer.vert

#version 330 core

// I didn't have time to change this into layout (location = ...) ...
attribute vec3 pos;
attribute vec3 norm;
attribute vec3 tangent;
attribute vec2 uv;

out DATA {
    vec3 position;
    vec3 normal;
    vec2 texCoord;
    mat3 tbn;
} VS_OUT;

void main() {
    gl_Position = gl_ModelViewProjectionMatrix * vec4(pos, 1.0);
    VS_OUT.position = (gl_ModelViewMatrix * vec4(pos, 1.0)).xyz;
    VS_OUT.normal = gl_NormalMatrix * norm;

    vec3 n = normalize(VS_OUT.normal);
    vec3 t = normalize(gl_NormalMatrix * tangent);
    t = normalize(t - dot(t, n) * n);
    vec3 b = cross(t, n);

    VS_OUT.tbn = mat3(t, b, n);
    VS_OUT.texCoord = uv;
}

GBuffer.frag

#version 330 core
layout (location = 0) out vec3 g_position;
layout (location = 1) out vec3 g_normal;
layout (location = 2) out vec4 g_diffuse;
layout (location = 3) out vec4 g_material;

struct TSlot {
    int active;
    int type;
    vec2 scale;
    float intensity;
    int blendMode;
};

struct TMaterial {
    vec4 albedo;
    float roughness;
    float fresnel;
};

struct TScene {
    vec3 ambient;
    vec3 viewPosition;
};

// Textures
uniform sampler2D textures[8];
uniform TSlot textureSlots[8];

// Scene
uniform TScene scene;

// Material
uniform TMaterial material;

// Object
in DATA {
    vec3 position;
    vec3 normal;
    vec2 texCoord;
    mat3 tbn;
} FS_IN;

[... blending functions ...]

void main() {
    vec3 N = FS_IN.normal;

    vec4 texs = vec4(1.0);
    vec4 tex0 = vec4(0.0);

    for (int i = 0; i < 8; i++) {
        if (textureSlots[i].active == 0) { continue; }
        vec2 uv = textureSlots[i].scale * FS_IN.texCoord;
        int type = textureSlots[i].type;
        if (type == 0) { // Diffuse
            vec4 diff = texture2D(textures[i], uv);
            texs = blend(texs, diff, textureSlots[i].intensity * diff.a, textureSlots[i].blendMode);
        } if (type == 1) { // Normal
            vec3 encNorm = texture2D(textures[i], uv).xyz;
            vec3 localCoords = normalize(vec3(2.0, 2.0, 1.0) * encNorm - vec3(1.0, 1.0, 0.0));
            N *= normalize(FS_IN.tbn * localCoords);
        } else if (type == 2) { // Sphere Reflection
            vec3 E = normalize(scene.viewPosition - FS_IN.position);
            vec3 R = normalize(reflect(E, N));
            float m = 2.0 * sqrt( 
                pow(R.x, 2.0) + 
                pow(R.y, 2.0) + 
                pow(R.z + 1.0, 2.0) 
            );
            vec2 tex = R.xy / m + 0.5;
            vec4 diff = texture2D(textures[i], tex * textureSlots[i].scale);
            texs = blend(texs, diff, textureSlots[i].intensity * diff.a, textureSlots[i].blendMode);
        } else if (type == 3) { // Roughness Map
            tex0.r = blendNormalFloat(tex0.r, texture2D(textures[i], uv).x, textureSlots[i].intensity);
        } else if (type == 4) { // Fresnel Map
            tex0.g = blendNormalFloat(tex0.g, texture2D(textures[i], uv).x, textureSlots[i].intensity);
        }
    }

    // Outputs
    g_position = FS_IN.position;
    g_normal = N;
    g_diffuse.rgb = texs.rgb * material.albedo.rgb;
    g_diffuse.a = 1.0;
    g_material = vec4(material.roughness + tex0.r, material.fresnel + tex0.g, 0.0, 0.0);
}

Lighting.vert

#version 330 core

void main() {
    gl_Position = ftransform();
    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_FrontColor = vec4(1.0, 1.0, 1.0, 1.0);
}

Lighting.frag

#version 330 core
layout (location = 0) out vec4 fragColor;

struct TLight {
    vec3 position;
    float intensity;
    vec3 color;
    float radius;
    float cutoff;
    float spotCutoff;
    vec3 direction;
    int type;
};

struct TScene {
    vec3 ambient;
    vec3 viewPosition;
};

// Textures from GBuffer
uniform sampler2D t_position;
uniform sampler2D t_normal;
uniform sampler2D t_diffuse;
uniform sampler2D t_material;

uniform TLight light;
uniform TScene scene;

void main() {
    vec2 uv = gl_TexCoord[0].xy;
    vec3 P = texture2D(t_position, uv).xyz;
    vec3 N = normalize(texture2D(t_normal, uv).xyz);
    vec4 D = texture2D(t_diffuse, uv);
    vec4 M = texture2D(t_material, uv);

    vec3 lighting = D.rgb * 0.1;
    vec3 V = normalize(scene.viewPosition - P);
    vec3 lightDir = normalize(light.position - P);
    vec3 diffuse = max(dot(N, lightDir), 0.0) * D.rgb * light.color * light.intensity;
    lighting += diffuse;

    fragColor = vec4(lighting, 1.0);
}

I was following these tutorials: http://www.codinglabs.net/tutorial_simple_def_rendering.aspx http://learnopengl.com/#!Advanced-Lighting/Deferred-Shading

EDIT: It turns out I just needed to use float textures for Normals and Positions


Answer:

two ideas:

you have to use float textures, otherwise your values are clamped to the [0;1] range, or in case of your normals, you can scale and bias them to that range (and use smaller formats than 32F/16F)

unless you are changing the light's position on CPU side to be in view coordinates, you're calculating with view positions (from your position g-buffer), and world coordinates (your light position) at the same time in lighting.frag, which would certainly mess with the output.

i hope that helps!

Question:

I'm trying to create some 2d lighting in slick2D. However the result is not quite what I'm looking for.

Currently it's working by drawing the light onto a lightmap and then drawing the lightmap to the screen. When drawing the light to the lightmap I'm just using the screen draw mode, and when drawing the lightmap I'm using the multiply draw mode.

The result is this Drawing the lightmap with normal draw mode I get this

I used the Lightmap image in photoshop and changed the blendingmode to multiply and got this.

For some reason the photoshop result is very dark. It looks much better in photoshop before saving, not as dark.

My question now is how do I get closer to the photoshop result?

This is my code:

public class Light 
{

    public Vector2f Pos;
    public static Image light;
    public Image lightCopy;
    public static Image lightmap;

    private float size=256;
    private static float maxSize=1024;
    public Color lightColor = new Color(255,255,255,255);;
    private static Graphics g2;
    private static Graphics g3;

    public static ArrayList<Light> LightList = new ArrayList<Light>();

    public static void init() throws SlickException
    {
        light = new Image("Light.png");

        lightmap = new Image(1920,1080);

        g3=lightmap.getGraphics();
        maxSize=light.getWidth();
    }


    public Light(Vector2f Pos) throws SlickException
    {
        this.Pos=Pos;
        LightList.add(this);
        lightCopy = light.copy();
        g2=lightCopy.getGraphics();
    }

    public void updateSingle(GameContainer gc, ArrayList<Object> BlockingObjects) throws SlickException
    {
        lightCopy = light.copy();
        g2.setDrawMode(Graphics.MODE_NORMAL);
        g2.setColor(Color.black);
        Rectangle lightPosAndSize = new Rectangle(0,0,maxSize,maxSize);
        g2.fill(lightPosAndSize);
        Vector2f pos = new Vector2f((maxSize/2)-(size/2),(maxSize/2)-(size/2));
        g2.drawImage(light.getScaledCopy(size/maxSize),pos.x, pos.y);
        for(int i=0;i!=BlockingObjects.size();i++)
        {
            Object temp = BlockingObjects.get(i);
            String type = temp.getClass().getSimpleName();
            switch(type)
            {
            case "Circle":
            {
                Circle c = (Circle) temp;
                caseCircle(c);
                break;
            }
            default:
            }
        }
        g2.flush();
    }

    public void renderToLightMap(Graphics g) throws SlickException
    {
        g3.setDrawMode(Graphics.MODE_SCREEN);
        g3.drawImage(lightCopy, Pos.x-maxSize/2, Pos.y-maxSize/2,lightColor);
        g3.setDrawMode(Graphics.MODE_NORMAL);
        g3.flush();
    }

    public static void renderLightmap(Graphics g)
    {
        g.setDrawMode(Graphics.MODE_COLOR_MULTIPLY);
        g.drawImage(lightmap, 0, 0);
        g.setDrawMode(Graphics.MODE_NORMAL);
    }

    private void caseCircle(Circle c)
    {

    }



    public static void renderAll(Graphics g, GameContainer gc,ArrayList<Object> BlockingObjects ) throws SlickException 
    {
        g3=lightmap.getGraphics();
        g3.setColor(new Color(0,0,0));
        g3.fill(new Rectangle(0,0,1920,1080));
        g3.flush();
        for(int i=0;i!=LightList.size();i++)
        {
            Light temp = LightList.get(i);
            temp.updateSingle(gc, BlockingObjects);
            temp.renderToLightMap(g);   
        }
        renderLightmap(g);
    }

I was going to post this at the slick2d forum, however I didn't recieve the activation mail so I couldn't create a thread there.

Hopefully you understand my problem and if you need more information I will try to provide you with it. Hopefully the links of my images works as well.


Answer:

Solved the problem myself.

I changed the render lightmap method to this

public static void renderLightmap(Graphics g)
{
    GL11.glEnable(GL11.GL_BLEND);
    GL11.glBlendFunc(GL11.GL_DST_COLOR, GL11.GL_ONE_MINUS_SRC_ALPHA);
    g.drawImage(lightmap, 0, 0);
    GL11.glDisable(GL11.GL_BLEND);
    g.setDrawMode(Graphics.MODE_NORMAL);
}

Question:

I am currently working on a game engine using OpenGL using LWJGL in Java. After adding in lighting code a white screen would display for about 2 seconds then the program would stop running with only the following message:

Process finished with exit code -1073741819 (0xC0000005)

Searching the error brought no results. I just updated by gpu driver just in case that was the problem. I updated my Nvidia driver version to 441.66.

Two of my friends pulled the exact same version of the code from git and the code worked. One used Windows 10 while the other used Linux.

Here is the screenshot from my friend on windows: Screenshot of window from friend using windows

Here is the screenshot from me: Screenshot of window from my computer

The code is kinda long so here it is: https://github.com/ryandw11/Test-OpenGL-Engine/

Links to important places in the code can be found here: Readme.md

Information regarding our Libraries can be found in readme linked above.

The master branch works just fine for me while the Lighting System branch does not. I don't think it is a code issue as it works just fine on the other two computers; however, I am stumped and have no clue how to fix this.

Further Information: Java Version: 13 I am running the code via Intellij. Graphics Card: Nvidia Geforce GTX 960. LWJGL Version: 3.2.3

I am sorry if this is not enough information. This is my first time making a question on here.


Answer:

Solved this problem. I was not setting the vertex normal uniform and that caused it to not work on the Nvidia gpus.

I solved this problem by using a debugger named renderdoc on a computer with a different gpu. After setting the normals it proceeded to work on the computer with a Nvidia gpu.