[SOLVED] Weird OpenGL Blending Issue

So I’m working on implementing a basic 2D forward rendering system right now with lighting, but there is a slight problem that I can’t wrap my head around. All of the articles that I have read about blending point me right where I am now. In forward rendering, you need to sum up all of the fragment shading calculations to get your final scene with blending, but somehow blending is just not working for me. I apologize if this problem is extremely simple ahead of time as I haven’t really worked with blending in OpenGL until just now.

I have included what I think to be the relevant code. If you need to see any more of the code, or details, just let me know.

Core class:


package gel.engine.core;

import gel.engine.graphics.RenderingEngine;

import static org.lwjgl.opengl.GL11.*;
import gel.engine.input.Input;
import gel.engine.window.Window;

public abstract class Core
{

	private static Core instance;

	protected boolean running;
	protected int targetFps;
	protected int targetUps;
	protected ProfileTimer profileTimer;
	protected Window window;
	protected Input input;
	protected RenderingEngine renderingEngine;

	public Core()
	{
		if (instance == null)
		{

			instance = this;

			this.running = false;
			this.targetFps = -1;
			this.targetUps = 60;

			window = new Window();

		} else
		{
			throw new IllegalStateException("Can not make more than one instance of Core");
		}
	}

	public static Core getInstance()
	{
		return instance;
	}

	public static boolean created()
	{
		return (instance == null);
	}

	public boolean isRunning()
	{
		return running;
	}

	public int getTargetFps()
	{
		return targetFps;
	}

	public int getTargetUps()
	{
		return targetUps;
	}

	public ProfileTimer getProfileTimer()
	{
		return profileTimer;
	}

	public Window getWindow()
	{
		return window;
	}

	public Input getInput()
	{
		return input;
	}

	public RenderingEngine getRenderingEngine()
	{
		return renderingEngine;
	}

	public void setTargetFps(int target)
	{
		if (!running)
		{
			this.targetFps = target;
		}
	}

	public void setTargetUps(int target)
	{
		if (!running)
		{
			this.targetUps = target;
		}
	}

	public void start()
	{
		run();
	}

	public void stop()
	{
		running = false;
	}

	private void run()
	{

		// initialize the engine
		initEngine();

		running = true;

		long lastSecond = System.nanoTime();
		long thisFrame = System.nanoTime();
		long lastFrame = System.nanoTime();
		double updateTime = 1e9f / targetUps;
		double renderTime = 1e9f / targetFps;
		double accumulativeUpdateTime = 0;
		double accumulativeFrameTime = 0;
		long sleepTime = 0;
		int updateCount = 0;
		int frameCount = 0;

		while (running)
		{

			// Get the delta between this frame and last frame
			thisFrame = System.nanoTime();
			profileTimer.updateDelta(thisFrame, lastFrame);
			lastFrame = thisFrame;

			// Accumulate the delta in the update and render accumulate variables.
			accumulativeUpdateTime += profileTimer.getDelta();
			accumulativeFrameTime += profileTimer.getDelta();

			// Update and take into account any missed frames.
			if (targetUps != -1)
			{
				while (accumulativeUpdateTime >= updateTime)
				{
					updateGame();
					updateCount++;
					accumulativeUpdateTime -= updateTime;
				}
			} else
			{
				updateGame();
				updateCount++;
				accumulativeUpdateTime = 0;
			}

			// Render if possible.
			if (targetFps != -1)
			{
				if (accumulativeFrameTime >= renderTime)
				{
					renderGame();
					frameCount++;
					accumulativeFrameTime = 0;
				} else
				{

					// Calculate sleep time and sleep for that amount.

					sleepTime = Math.round((renderTime - accumulativeFrameTime) / (1024 * 1024));

					try
					{
						Thread.sleep(sleepTime);
					} catch (InterruptedException e)
					{
						e.printStackTrace();
					}

				}
			} else
			{
				renderGame();
				frameCount++;
				accumulativeFrameTime = 0;
			}

			// Check if a second has passed, and if so, update fps and ups.
			if (System.nanoTime() - lastSecond >= 1e9)
			{
				profileTimer.updateFps(frameCount);
				profileTimer.updateUps(updateCount);
				frameCount = 0;
				updateCount = 0;
				lastSecond = System.nanoTime();
			}

			// Check if the game needs to stop.
			if (window.isCloseRequested())
			{
				stop();
			}

		}

		// Unload the game content.
		unloadGame();

	}

	private void initEngine()
	{

		// create gl context and initialize rendering engine

		window.create();

		renderingEngine = new RenderingEngine();
		profileTimer = new ProfileTimer();
		input = new Input();
		input.create();

		// load game content
		loadGame();
	}

	private void loadGame()
	{
		load();
	}

	private void updateGame()
	{
		input.update();
		update();
	}

	private void renderGame()
	{
		glClear(GL_COLOR_BUFFER_BIT);
		renderingEngine.update();
		render();
		window.update();
		
	}

	private void unloadGame()
	{
		unload();
	}

	public abstract void load();

	public abstract void update();

	public abstract void render();

	public abstract void unload();

}

RenderingEngine Class:


package gel.engine.graphics;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL11.glDisable;
import gel.engine.core.Core;
import gel.engine.graphics.lighting.AmbientLight;
import gel.engine.graphics.lighting.PointLight;
import gel.engine.graphics.matrix.RotationMatrix;
import gel.engine.graphics.matrix.ScaleMatrix;
import gel.engine.graphics.matrix.TransformationMatrix;
import gel.engine.graphics.matrix.TranslationMatrix;
import gel.engine.math.Vector2f;

public class RenderingEngine
{

	public static final String UNIFORM_MODEL_MATRIX = "modelMatrix";
	public static final String UNIFORM_VIEW_MATRIX = "viewMatrix";
	public static final String UNIFORM_PROJECTION_MATRIX = "projectionMatrix";

	public static final String UNIFORM_AMBIENTCOLOR = "ambientColor";

	private static RenderingEngine instance;

	private VertexArray vertexArray;
	private OrthographicCamera camera;
	private ShaderProgram ambientShader;
	private ShaderProgram pointLightShader;

	private AmbientLight ambient;

	public RenderingEngine()
	{
		if (instance == null)
		{
			instance = this;

			vertexArray = new VertexArray();
			camera = new OrthographicCamera(0, Core.getInstance().getWindow().getWidth(), 0, Core.getInstance().getWindow().getHeight(), 1, -1);

			ambientShader = new ShaderProgram();
			ambientShader.createAndCompileVertexShader("res\\shaders\\ambientShader.vs");
			ambientShader.createAndCompileFragmentShader("res\\shaders\\ambientShader.fs");
			ambientShader.linkProgram();

			pointLightShader = new ShaderProgram();
			pointLightShader.createAndCompileVertexShader("res\\shaders\\pointLightShader.vs");
			pointLightShader.createAndCompileFragmentShader("res\\shaders\\pointLightShader.fs");
			pointLightShader.linkProgram();

			ambient = new AmbientLight(new Color(255, 255, 255));

		} else
		{
			throw new IllegalStateException("Can not make more than one instance of RenderingEngine");
		}
	}

	public static RenderingEngine getInstance()
	{
		return instance;
	}

	public OrthographicCamera getCamera()
	{
		return camera;
	}

	public AmbientLight getAmbientLight()
	{
		return ambient;
	}

	public void update()
	{
		camera.update();

		ambientShader.setMatrix4f(UNIFORM_VIEW_MATRIX, camera.getViewMatrix().getMatrix());
		ambientShader.setMatrix4f(UNIFORM_PROJECTION_MATRIX, camera.getProjectionMatrix().getMatrix());

		pointLightShader.setMatrix4f(UNIFORM_VIEW_MATRIX, camera.getViewMatrix().getMatrix());
		pointLightShader.setMatrix4f(UNIFORM_PROJECTION_MATRIX, camera.getProjectionMatrix().getMatrix());

	}

	// update uniforms for rendering passes

	public void renderTexture(Texture t, float x, float y, float rotation)
	{
		if (x + t.getWidth() >= 0 && x < Core.getInstance().getWindow().getWidth() + t.getWidth() && y + t.getHeight() >= 0 && y < Core.getInstance().getWindow().getHeight() + t.getHeight())
		{
			ambientShader.bind();
			vertexArray.clear();
			
			vertexArray.addVertex(new Vertex(new Vector2f(-t.getWidth() / 2, -t.getHeight() / 2), new Color(0), new Vector2f(1, 1)));
			vertexArray.addVertex(new Vertex(new Vector2f(-t.getWidth() / 2, t.getHeight() / 2), new Color(0), new Vector2f(1, 0)));
			vertexArray.addVertex(new Vertex(new Vector2f(t.getWidth() / 2, t.getHeight() / 2), new Color(0), new Vector2f(0, 0)));
			vertexArray.addVertex(new Vertex(new Vector2f(t.getWidth() / 2, -t.getHeight() / 2), new Color(0), new Vector2f(0, 1)));
			
			ambientShader.setMatrix4f(UNIFORM_MODEL_MATRIX, new TransformationMatrix(new TranslationMatrix(x + t.getWidth() / 2, y + t.getHeight() / 2), new RotationMatrix(rotation), new ScaleMatrix(1, 1)).getMatrix());
			ambientShader.setVector3f(UNIFORM_AMBIENTCOLOR, ambient.getColor().toVector3f());
			
			vertexArray.render();
		}
	}

	public void renderPointLight(PointLight light)
	{
		glEnable(GL_BLEND);
		glBlendFunc(GL_ONE, GL_ONE);
		if (light.getPosition().getX() + light.getRadius() >= 0 && light.getPosition().getX() < Core.getInstance().getWindow().getWidth() && light.getPosition().getY() + light.getRadius() >= 0 && light.getPosition().getY() < Core.getInstance().getWindow().getHeight())
		{
			pointLightShader.bind();
			vertexArray.clear();
			vertexArray.addVertex(new Vertex(new Vector2f(-light.getRadius(), -light.getRadius())));
			vertexArray.addVertex(new Vertex(new Vector2f(-light.getRadius(), light.getRadius())));
			vertexArray.addVertex(new Vertex(new Vector2f(light.getRadius(), light.getRadius())));
			vertexArray.addVertex(new Vertex(new Vector2f(light.getRadius(), -light.getRadius())));
			pointLightShader.setMatrix4f(UNIFORM_MODEL_MATRIX, new TransformationMatrix(new TranslationMatrix(light.getPosition().getX(), light.getPosition().getY()), new RotationMatrix(0), new ScaleMatrix(1, 1)).getMatrix());
			pointLightShader.setVector2f("pos", light.getPosition());
			pointLightShader.setVector3f("color", light.getColor().toVector3f());
			pointLightShader.setFloat("radius", light.getRadius());
			pointLightShader.setFloat("constant", light.getConstAtten());
			pointLightShader.setFloat("linear", light.getLinearAtten());
			pointLightShader.setFloat("quadratic", light.getQuadAtten());
			vertexArray.render();
		}
		glDisable(GL_BLEND);
	}

}


Game class(Subclass of Core):


package game;

import static org.lwjgl.opengl.GL11.*;
import gel.engine.core.Core;
import gel.engine.graphics.Bitmap;
import gel.engine.graphics.Color;
import gel.engine.graphics.Texture;
import gel.engine.graphics.lighting.PointLight;
import gel.engine.math.Vector2f;

public class Game extends Core
{

	private Texture grass;
	private PointLight light;

	public static void main(String[] args)
	{
		Game g = new Game();
		g.getWindow().setTitle("Gel Engine");
		g.getWindow().setSize(16 * 60, 9 * 60);
		g.getWindow().setResizable(false);
		g.start();
	}

	@Override
	public void load()
	{
		grass = new Texture(new Bitmap("res\\grass.png"));
		light = new PointLight(new Vector2f(400, 400), new Color(255, 127, 0), 200, 0, 0, 0.01f); // pos, color, range, const, linear, quadratic attenuation
	}

	@Override
	public void update()
	{

	}

	@Override
	public void render()
	{
		renderingEngine.renderTexture(grass, 400, 400, 0);
		renderingEngine.renderPointLight(light);
	}

	@Override
	public void unload()
	{

	}

}


Texture and Ambient Vertex Shader:


#version 110

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;

void main()
{
	gl_Position = projectionMatrix * viewMatrix * modelMatrix * gl_Vertex;
	gl_TexCoord[0] = gl_MultiTexCoord0;
}

Ambient Fragment Shader:


#version 110

uniform sampler2D textureSampler;
uniform vec3 ambientColor;

void main()
{
	gl_FragColor = vec4(ambientColor, 1.0) * texture2D(textureSampler, gl_TexCoord[0].st);
}

Point light fragment shader:


#version 110

uniform vec2 pos;
uniform vec3 color;
uniform float radius;
uniform float constant;
uniform float linear;
uniform float quadratic;

void main()
{

	float dist = distance(gl_FragCoord.xy, pos);
	float atten = 1.0 / (quadratic * dist * dist + linear * dist + constant);
	gl_FragColor = vec4(atten, atten, atten, 1.0) * vec4(color, 1.0);
}

With this code, this is the current scene that I get:

If I comment out rendering the texture in the render() method in the Game class, I get the same results. Could blending be getting ignored or something alike?

If I comment out where I render the point light in the render() method of the Game class I get the actual texture rendered, like so:

Out of those results, I would expect to get the grass texture additively blended in with the light, but that is obviously not so. If anyone who has a solution to this issue, it would be greatly appreciated.

So to any of you who were wondering, what was screwing me over was an error where I was updating my uniforms - one of the least places that I would have thought the error would be. I needed to actually bind the shader before I set the uniforms. Since I wasn’t, OpenGL was secretly(me not checking glGetError) throwing error 502, which basically means you called a method with an incorrect state. That prevented blending from working.

Whenever you experience something strange about OpenGL but you get no error messages the first thing should be to make sure that you truly use glGetError() to report errors. :slight_smile:

So the issue is solved? If so, you sould mark the thread solved. :point: