releasing shaders after glEnd doesn't apply the shader?

Hey,
I found out some strange behaviour, shaders in my code work kind of wierd when you release them with ARBShaderObjects.glUseProgramObjectARB(0). In the example below after 2k ticks it starts releasing the shader in the render cycle, and as that happens, it wont apply the shaders at all even retroactively on the quad that already has been glEnded(in this case, no image is drawn on the quad). This is kind of wierd considering releasing after glEnd is suggested in most shader guides (e.g. http://lwjgl.org/wiki/index.php?title=GLSL_Shaders_with_LWJGL).

What is even more interesting but isnt demonstrated in my code here is, when i draw multiple objects with shaders and dont ever release them with ARBShaderObjects.glUseProgramObjectARB(0), just bind them, it works all good.

So, what exactly does ARBShaderObjects.glUseProgramObjectARB(0) do? Do I use it correctly? What causes this strange behaviour?

The first class is the main class that runs everything. Then I have a few utility classes that i wont post here (ShaderLoader,ImageLoader, I think you know what they do). The second class is the Shader class which is capable of loading and compiling the Shader, and some utility methods for setting up the shader and binding it.


package cz.witzany.gamev2.twlgui;

import static org.lwjgl.opengl.GL11.*;

import java.awt.Rectangle;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;

import cz.witzany.gamev2.graphics.shaders.Shader;
import cz.witzany.gamev2.graphics.shaders.ShaderLoader;
import cz.witzany.gamev2.graphics.shaders.SimpleImage;
import cz.witzany.gamev2.graphics.utils.ImageLoader;

public class TestCase implements Runnable {
	int texture;
	Shader shader;
	int timeout = 0;

	public static void main(String[] args) {
		new TestCase().run();
	}

	@Override
	public void run() {
		initGL();
		initObjects();

		while (!Display.isCloseRequested()) {
			glClear(GL11.GL_COLOR_BUFFER_BIT);

			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			glOrtho(-400, 400, -300, 300, 10, -10);
			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			
			shader.bind();
			shader.setTexture("colorMap", 0, texture);
			shader.setUniformF("tx", 1/90.0f);
			shader.setUniformF("ty", 1/90.0f);
			shader.setUniformF("meshScale", 1.0f);

			glScaled(90, -90, 1);
			glBegin(GL11.GL_QUADS);
			glTexCoord2f(0, 0);
			glVertex2d(0, 0);
			glTexCoord2f(1, 0);
			glVertex2d(1, 0);
			glTexCoord2f(1, 1);
			glVertex2d(1, 1);
			glTexCoord2f(0, 1);
			glVertex2d(0, 1);
			glEnd();
			
			if(timeout > 2000)
				shader.unbind();
			timeout++;
			
			Display.update();
		}
	}

	private void initObjects() {
		shader = ShaderLoader.loadShader("Data/Shaders/Sprite");
		texture = ImageLoader.loadImage("Data/Textures/Sprites/Creeper/Creeper.png", new Rectangle());
	}

	private void initGL() {
		try {
			Display.setDisplayMode(new DisplayMode(800, 600));
			Display.create();
		} catch (LWJGLException e) {
			e.printStackTrace();
			System.exit(0);
		}

		glShadeModel(GL_SMOOTH);
		glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT
				| GL_STENCIL_BUFFER_BIT);
		glDepthRange(1.0f, 0.0f);
		glAlphaFunc(GL_GREATER, 0x00);
		glDepthFunc(GL_GREATER);
		glStencilFunc(GL_NEVER, 0x0, 0xFFFFFFFF);
		glBlendFunc(GL_ZERO, GL_SRC_COLOR);

		GL11.glViewport(0, 0, Display.getWidth(), Display.getHeight());
	}
}


package cz.witzany.gamev2.graphics.shaders;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.ARBFragmentShader;
import org.lwjgl.opengl.ARBShaderObjects;
import org.lwjgl.opengl.ARBVertexShader;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;

public class Shader {
	private static int activeShader = -1;
	public int shader;
	public int vertShader;
	public int fragShader;
	private boolean useShader;

	public Shader(String shaderDir) {
		File sDir = new File(shaderDir);
		useShader = true;
		if (!sDir.exists() || !sDir.isDirectory()) {
			useShader = false;
			return;
		}
		File vShader = new File(sDir, sDir.getName() + ".vert");
		File fShader = new File(sDir, sDir.getName() + ".frag");
		if (!vShader.exists() || !fShader.exists()) {
			useShader = false;
			return;
		}
		shader = ARBShaderObjects.glCreateProgramObjectARB();
		if (shader == 0) {
			useShader = false;
			return;
		}
		vertShader = createShader(vShader.getAbsolutePath(),
				ARBVertexShader.GL_VERTEX_SHADER_ARB);
		fragShader = createShader(fShader.getAbsolutePath(),
				ARBFragmentShader.GL_FRAGMENT_SHADER_ARB);
		if (vertShader == 0 || fragShader == 0) {
			useShader = false;
			return;
		}
		ARBShaderObjects.glAttachObjectARB(shader, vertShader);
		ARBShaderObjects.glAttachObjectARB(shader, fragShader);
		ARBShaderObjects.glLinkProgramARB(shader);

		if (GL20.glGetShader(shader, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
			printLogInfo(shaderDir, shader);
			useShader = false;
			return;
		}

		ARBShaderObjects.glValidateProgramARB(shader);

		if (GL20.glGetShader(shader, GL20.GL_VALIDATE_STATUS) == GL11.GL_FALSE) {
			printLogInfo(shaderDir, shader);
			useShader = false;
			return;
		}
		useShader = true;
		System.out.println("Loaded shader " + shaderDir);
	}

	private int createShader(String filename, int type) {
		int nshader = ARBShaderObjects.glCreateShaderObjectARB(type);
		if (nshader == 0) {
			return 0;
		}
		String code = "";
		String line;
		try {
			BufferedReader reader = new BufferedReader(new FileReader(filename));
			while ((line = reader.readLine()) != null) {
				code += line + "\n";
			}
			reader.close();
		} catch (Exception e) {
			System.out.println("Fail reading vertex shading code");
			return 0;
		}
		ARBShaderObjects.glShaderSourceARB(nshader, code);
		ARBShaderObjects.glCompileShaderARB(nshader);
		printLogInfo(filename, nshader);

		if (GL20.glGetShader(nshader, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) 
			return 0;
		return nshader;
	}

	private boolean printLogInfo(String name, int obj) {
		IntBuffer iVal = BufferUtils.createIntBuffer(1);
		ARBShaderObjects.glGetObjectParameterARB(obj,
				ARBShaderObjects.GL_OBJECT_INFO_LOG_LENGTH_ARB, iVal);

		int length = iVal.get();
		if (length > 1) {
			// We have some info we need to output.
			ByteBuffer infoLog = BufferUtils.createByteBuffer(length);
			iVal.flip();
			ARBShaderObjects.glGetInfoLogARB(obj, iVal, infoLog);
			byte[] infoBytes = new byte[length];
			infoLog.get(infoBytes);
			String out = new String(infoBytes);
			System.out.println(name + " log:\n" + out);
		}
		return true;
	}

	private int getUniformLocation(String name) {
		int location = ARBShaderObjects.glGetUniformLocationARB(shader, name);
		if (location == -1)
			throw new RuntimeException(name + " not available");
		return location;
	}

	public void setTexture(String name, int index, int pointer) {
		int location = getUniformLocation(name);
		GL13.glActiveTexture(GL13.GL_TEXTURE0 + index);
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, pointer);
		ARBShaderObjects.glUniform1iARB(location, index);
	}

	public synchronized void bind() {
		if (activeShader == shader)
			return;
		if (useShader) {
			//unbind();
			ARBShaderObjects.glUseProgramObjectARB(shader);
			activeShader = shader;
		} else
			System.out.println("warning, shader not used");
	}

	public void unbind() {
		ARBShaderObjects.glUseProgramObjectARB(0);
	}
	
	public void setUniformF(String name, Float value){
		int location = getUniformLocation(name);
		ARBShaderObjects.glUniform1fARB(location, value);
	}
}

This looks suspicious:


public synchronized void bind() {
      if (activeShader == shader)
         return;
      if (useShader) {
         //unbind();
         ARBShaderObjects.glUseProgramObjectARB(shader);
         activeShader = shader;
      } else
         System.out.println("warning, shader not used");
   }

   public void unbind() {
      ARBShaderObjects.glUseProgramObjectARB(0);
   }

Your ‘bind’ attempts to not re-bind if the shader is already set, but your ‘unbind’ doesn’t update activeShader. You probably want to update activeShader to -1 in unbind (or use 0, since that indicates ‘fixed function shader’). And define a constant for it while you’re at it.

It may be worth taking out your early-exit from bind() while you’re testing things, since it should work the same without it.

Also it might be a good idea to throw an exception here rather than just fail silently:

 shader = ARBShaderObjects.glCreateProgramObjectARB();
      if (shader == 0) {
         useShader = false;
         return;
      }

Haha, great, that was it. Now i feel stupid, thank you very much sir xD

You should be consistent with entry points – right now you’re using GL20.glGetShader alongside ARBShaderObjects. And you should probably not rely on File objects if you plan to package your game in a JAR.

You might be able to get some inspiration from Slick’s ShaderProgram. Especially in regards to file reading, error checking, etc.
ShaderProgram
ShaderTest

(you’re free to use part of the class or even the whole thing within your project – see license)