Rendering using shaders

Started by ghillieLEAD, April 10, 2012, 00:03:23

Previous topic - Next topic

ghillieLEAD

I've been trying to use shaders, but can't get them working.  I create and compile the shaders (no error there), attach them to a program, link the program and validate it.  No errors.  I then use the program and draw a triangle, but the triangle is always drawn using fixed function mode rather than using my basic shader program.  It doesn't matter if I use immediate mode, vertex arrays, or VBOs, the shader is never used.  If you could help me determine the issue I would appreciate it a lot.

The code:

Main class

package net.midnightindie.dungeon;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Frame;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import net.midnightindie.dungeon.render.ProgramManager;
import net.midnightindie.dungeon.util.WindowListener;

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Controllers;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.util.glu.GLU;

public class Dungeon extends Applet implements Runnable
{
	private static final long serialVersionUID = 1L;
	private static final boolean isApplet = false;

	protected Frame frame;
	protected Canvas canvas;
	protected int displayWidth;
	protected int displayHeight;

	protected boolean areControllersAvailable;

	protected Thread gameThread;
	protected boolean isRunning;
	protected boolean isPaused;

	protected long lastSecond;
	protected long lastFrame;
	protected int frames;

	protected FloatBuffer vertexArray;

	protected FloatBuffer vboVertexArray;
	protected int vboVertexArrayHandle;

	public Dungeon()
	{

	}

	public void initialize()
	{
		displayWidth = 900;
		displayHeight = 600;
		CreateDisplay();

		areControllersAvailable = false;
		CreateInputDevices();

		initializeGL();

		ProgramManager.instance().createPrograms();
		ProgramManager.instance().useProgram("basic");

		initializeVertexArray();
		initializeVBO();

		lastSecond = lastFrame = System.currentTimeMillis();
		frames = 0;

		isRunning = true;
		isPaused = false;
	}

	protected void initializeGL()
	{
		glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

		glEnable(GL_DEPTH_TEST);
		glDepthFunc(GL_LEQUAL);
		glClearDepth(1.0f);

		glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();

		GLU.gluPerspective(45, displayWidth / displayHeight, 0.1f, 100);

		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
	}

	public void initializeVertexArray()
	{
		vertexArray = BufferUtils.createFloatBuffer(9);

		vertexArray.put(-1).put(-1).put(0);
		vertexArray.put(0).put(1).put(0);
		vertexArray.put(1).put(-1).put(0);

		vertexArray.flip();
	}

	public void initializeVBO()
	{
		vboVertexArray = BufferUtils.createFloatBuffer(9);

		vboVertexArray.put(-1).put(-1).put(0);
		vboVertexArray.put(0).put(1).put(0);
		vboVertexArray.put(1).put(-1).put(0);

		vboVertexArray.flip();

		IntBuffer buffer = BufferUtils.createIntBuffer(1);
		glGenBuffers(buffer);
		vboVertexArrayHandle = buffer.get(0);

		glBindBuffer(GL_ARRAY_BUFFER, vboVertexArrayHandle);
		glBufferData(GL_ARRAY_BUFFER, vboVertexArray, GL_STATIC_DRAW);
	}

	public void cleanupVBO()
	{
		IntBuffer buffer = BufferUtils.createIntBuffer(1);
		buffer.put(vboVertexArrayHandle);
		buffer.flip();
		glDeleteBuffers(buffer);
	}

	@SuppressWarnings("serial")
	protected void CreateDisplay()
	{
		if (isApplet)
		{
			canvas = new Canvas()
			{
				public final void addNotify()
				{
					super.addNotify();

					try
					{
						Display.setParent(this);
						Display.create();
					} catch (Exception ex)
					{
					}
				}

				public final void removeNotify()
				{
					Display.destroy();

					super.removeNotify();
				}
			};
			canvas.setSize(getWidth(), getHeight());
			add(canvas);
			canvas.setFocusable(true);
			canvas.requestFocus();
			canvas.setIgnoreRepaint(true);
			setVisible(true);
		}
		else
		{
			frame = new Frame();
			frame.setTitle("glint");
			frame.setSize(displayWidth, displayHeight);
			frame.setLocationRelativeTo(null);
			frame.setLayout(new BorderLayout());
			frame.addWindowListener(new WindowListener(this, gameThread));

			canvas = new Canvas();
			frame.add(canvas, "Center");

			frame.setVisible(true);

			try
			{
				Display.setParent(canvas);
				Display.create();

			} catch (LWJGLException ex)
			{
				HandleError(ex.getMessage());
			}
		}

	}

	public void CreateInputDevices()
	{
		try
		{
			Keyboard.create();
			Mouse.create();

			Mouse.setGrabbed(true);
		} catch (LWJGLException ex)
		{
			HandleError("Unable to create input devices");
		}

		try
		{
			Controllers.create();
			areControllersAvailable = true;
		} catch (LWJGLException ex)
		{
			areControllersAvailable = false;
		}
	}

	public void Start()
	{
		gameThread = new Thread(this, "glint");
		gameThread.start();
	}

	public void Pause()
	{
		isPaused = true;
	}

	public void Resume()
	{
		isPaused = false;
	}

	public void Stop()
	{
		isRunning = false;
	}

	public void Destroy()
	{
		cleanupVBO();

		Keyboard.destroy();
		Mouse.setGrabbed(false);
		Mouse.destroy();
		Controllers.destroy();
		Display.destroy();

		System.out.println("TERMINATED");
	}

	public void PollInputDevices()
	{
		if (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) isRunning = false;
		if (Keyboard.isKeyDown(Keyboard.KEY_F11)) Mouse.setGrabbed(true);
		if (Keyboard.isKeyDown(Keyboard.KEY_F12)) Mouse.setGrabbed(false);
	}

	public void Update()
	{
		long currentTime = System.currentTimeMillis();
		long elapsedTime = currentTime - lastFrame;
		if (currentTime - lastSecond >= 1000)
		{
			System.out.println("FPS:  " + frames);
			lastSecond = currentTime;
			frames = 0;
		}

		lastFrame = currentTime;
		frames++;
	}

	public void immediateModeTriangle()
	{
		glBegin(GL_TRIANGLES);
		glColor3f(0, 0, 0);
		glVertex3f(1, 0, -4);
		glColor3f(1, 0, 0);
		glVertex3f(0, 1, -4);
		glColor3f(0, 0, 0);
		glVertex3f(-1, 0, -4);
		glEnd();
	}

	public void vertexArrayTriangle()
	{
		glEnableClientState(GL_VERTEX_ARRAY);

		glVertexPointer(3, 0, vertexArray);
		glDrawArrays(GL_TRIANGLES, 0, 3);

		glDisableClientState(GL_VERTEX_ARRAY);
	}

	public void vboTriangle()
	{
		glEnableClientState(GL_VERTEX_ARRAY);

		glBindBuffer(GL_ARRAY_BUFFER, vboVertexArrayHandle);
		glVertexPointer(3, GL_FLOAT, 0, 0);
		glDrawArrays(GL_TRIANGLES, 0, 3);

		glDisableClientState(GL_VERTEX_ARRAY);
	}

	public void Render()
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

		glColor3f(1, 0, 0);

		immediateModeTriangle();

		// glTranslatef(0, 0, -4);
		// vertexArrayTriangle();

		// glTranslatef(0, 0, -4);
		// vboTriangle();
	}

	public void run()
	{
		initialize();

		while (isRunning)
		{
			if (isPaused)
			{
				try
				{
					Thread.currentThread();
					Thread.sleep(100);
				} catch (InterruptedException ex)
				{

				}

				continue;
			}

			PollInputDevices();
			Update();
			Render();

			Display.update();
		}

		Destroy();
		System.exit(0);
	}

	public void HandleError(String errorMessage)
	{
		System.out.println("[ERROR]  " + errorMessage);
		Stop();
	}

	public void start()
	{
		Start();
	}

	public void stop()
	{
		Stop();
	}

	public static void main(String[] args)
	{
		System.out.println("Working Directory:  " + System.getProperty("user.dir"));

		Dungeon glint = new Dungeon();
		glint.Start();
	}
}


Program Manager

package net.midnightindie.dungeon.render;

import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Hashtable;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.lwjgl.BufferUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

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

public class ProgramManager
{
	private static ProgramManager instance = null;

	public static ProgramManager instance()
	{
		if (instance == null)
		{
			instance = new ProgramManager();
		}

		return instance;
	}

	private Hashtable<String, Shader> vertexShaders;
	private Hashtable<String, Shader> fragmentShaders;
	private Hashtable<String, Program> programs;

	private ProgramManager()
	{
		vertexShaders = new Hashtable<String, Shader>();
		fragmentShaders = new Hashtable<String, Shader>();
		programs = new Hashtable<String, Program>();
	}

	public void createPrograms()
	{
		programs.clear();

		int numPrograms = 0;

		try
		{
			File filePrograms = new File("res/programs.xml");
			DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
			Document doc = docBuilder.parse(filePrograms);

			NodeList nodeListPrograms = doc.getElementsByTagName("program");

			for (int i = 0; i < nodeListPrograms.getLength(); i++)
			{
				Node nodeProgram = nodeListPrograms.item(i);

				if (nodeProgram.getNodeType() == Node.ELEMENT_NODE)
				{
					Element element = (Element) nodeProgram;

					NodeList nodeListName = element.getElementsByTagName("name").item(0).getChildNodes();
					String programName = ((Node) nodeListName.item(0)).getNodeValue();

					ArrayList<Shader> vshaders = new ArrayList<Shader>();
					NodeList nodeListVertexShaders = element.getElementsByTagName("vertex");
					for (int j = 0; j < nodeListVertexShaders.getLength(); j++)
					{
						NodeList nodeListVertexShadersValue = nodeListVertexShaders.item(j).getChildNodes();
						String shaderName = ((Node) nodeListVertexShadersValue.item(0)).getNodeValue();

						if (!vertexShaders.containsKey(shaderName))
						{
							loadVertexShader(shaderName);
						}

						vshaders.add(vertexShaders.get(shaderName));
					}

					ArrayList<Shader> fshaders = new ArrayList<Shader>();
					NodeList nodeListFragmentShaders = element.getElementsByTagName("fragment");
					for (int j = 0; j < nodeListFragmentShaders.getLength(); j++)
					{
						NodeList nodeListFragmentShadersValue = nodeListFragmentShaders.item(j).getChildNodes();
						String shaderName = ((Node) nodeListFragmentShadersValue.item(0)).getNodeValue();

						if (!fragmentShaders.containsKey(shaderName))
						{
							loadFragmentShader(shaderName);
						}

						fshaders.add(fragmentShaders.get(shaderName));
					}

					programs.put(programName, new Program(vshaders, fshaders));

					numPrograms++;
				}
			}

		} catch (Exception ex)
		{
			System.out.println(ex.getMessage());
			ex.printStackTrace();
			System.exit(0);
		}

		System.out.println("Number of programs created:  " + numPrograms);

		vertexShaders.clear();
		fragmentShaders.clear();
	}

	private void loadVertexShader(String name)
	{
		try
		{
			File file = new File("res/shaders/" + name + ".vert");
			FileInputStream fis = new FileInputStream(file);
			FileChannel fc = fis.getChannel();

			ByteBuffer shaderSource = BufferUtils.createByteBuffer((int) fc.size());
			fc.read(shaderSource);

			vertexShaders.put(name, new Shader(GL_VERTEX_SHADER, shaderSource));
			System.out.println("Loaded vertex shader '" + name + "'");
		} catch (Exception ex)
		{
			System.out.println("Error creating vertex shader '" + name + "'");
		}
	}

	private void loadFragmentShader(String name)
	{
		try
		{
			File file = new File("res/shaders/" + name + ".frag");
			FileInputStream fis = new FileInputStream(file);
			FileChannel fc = fis.getChannel();

			ByteBuffer shaderSource = BufferUtils.createByteBuffer((int) fc.size());
			fc.read(shaderSource);

			fragmentShaders.put(name, new Shader(GL_FRAGMENT_SHADER, shaderSource));
			System.out.println("Loaded fragment shader '" + name + "'");
		} catch (Exception ex)
		{
			System.out.println("Error creating fragment shader '" + name + "'");
		}
	}

	public void useProgram(String name)
	{
		programs.get(name).use();
		System.out.println("Now using program '" + name + "'");
	}

	class Shader
	{
		public final int handle;

		public Shader(int type, ByteBuffer source)
		{
			handle = glCreateShader(type);
			glShaderSource(handle, source);
			glCompileShader(handle);

			if (glGetShader(handle, GL_COMPILE_STATUS) == GL_FALSE)
			{
				System.out.println("shader compilation failed.");
			}
		}
	}

	class Program
	{
		public final int handle;

		public Program(ArrayList<Shader> vshaders, ArrayList<Shader> fshaders)
		{
			handle = glCreateProgram();

			for (int i = 0; i < vshaders.size(); i++)
			{
				glAttachShader(handle, vshaders.get(i).handle);
			}

			for (int i = 0; i < fshaders.size(); i++)
			{
				glAttachShader(handle, fshaders.get(i).handle);
			}

			glLinkProgram(handle);
			glValidateProgram(handle);
		}

		public void use()
		{
			glUseProgram(handle);
		}
	}
}


Vertex Shader

void main()
{
	gl_Position = gl_ModelViewProjectionMatrix*gl_Vertex;
}


also tried

main()
{
	gl_Position = ftransform();
}


Fragment Shader

void main()
{
	gl_FragColor = vec4(0, 1, 0, 1);
}

CodeBunny

You need to bind the shader when you render for it to work, you know.

ghillieLEAD

As in glUseProgram(), correct?  Or are you referring to something else?  I do call my ProgramManager's useProgram() (which does a glUseProgram()) in my initialize method.  Just now I also tried putting the useProgram() call in my actual render function as well, just to see if it made a difference.  It didn't.

public void initialize()
{
	...
	ProgramManager.instance().createPrograms();
	ProgramManager.instance().useProgram("basic");
	...
}


also tried

public void Render()
{
	...
	glUseProgram(ProgramManager.instance().getProgramHandle("basic"));
	immediateModeTriangle();
	...
}

ghillieLEAD

I figured it out!  When I was loading the shaders (using a ByteBuffer) I forgot to flip the buffer after loading the file.  In case it helps someone in the future, the correct code is below.  The only change is the line shaderSource.flip();

New ProgramManager:

package net.midnightindie.dungeon.render;

import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Hashtable;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.lwjgl.BufferUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

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

public class ProgramManager
{
	private static ProgramManager instance = null;

	public static ProgramManager instance()
	{
		if (instance == null)
		{
			instance = new ProgramManager();
		}

		return instance;
	}

	private Hashtable<String, Shader> vertexShaders;
	private Hashtable<String, Shader> fragmentShaders;
	private Hashtable<String, Program> programs;

	private ProgramManager()
	{
		vertexShaders = new Hashtable<String, Shader>();
		fragmentShaders = new Hashtable<String, Shader>();
		programs = new Hashtable<String, Program>();
	}

	public void createPrograms()
	{
		programs.clear();

		int numPrograms = 0;

		try
		{
			File filePrograms = new File("res/programs.xml");
			DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
			Document doc = docBuilder.parse(filePrograms);

			NodeList nodeListPrograms = doc.getElementsByTagName("program");

			for (int i = 0; i < nodeListPrograms.getLength(); i++)
			{
				Node nodeProgram = nodeListPrograms.item(i);

				if (nodeProgram.getNodeType() == Node.ELEMENT_NODE)
				{
					Element element = (Element) nodeProgram;

					NodeList nodeListName = element.getElementsByTagName("name").item(0).getChildNodes();
					String programName = ((Node) nodeListName.item(0)).getNodeValue();

					ArrayList<Shader> vshaders = new ArrayList<Shader>();
					NodeList nodeListVertexShaders = element.getElementsByTagName("vertex");
					for (int j = 0; j < nodeListVertexShaders.getLength(); j++)
					{
						NodeList nodeListVertexShadersValue = nodeListVertexShaders.item(j).getChildNodes();
						String shaderName = ((Node) nodeListVertexShadersValue.item(0)).getNodeValue();

						if (!vertexShaders.containsKey(shaderName))
						{
							loadVertexShader(shaderName);
						}

						vshaders.add(vertexShaders.get(shaderName));
					}

					ArrayList<Shader> fshaders = new ArrayList<Shader>();
					NodeList nodeListFragmentShaders = element.getElementsByTagName("fragment");
					for (int j = 0; j < nodeListFragmentShaders.getLength(); j++)
					{
						NodeList nodeListFragmentShadersValue = nodeListFragmentShaders.item(j).getChildNodes();
						String shaderName = ((Node) nodeListFragmentShadersValue.item(0)).getNodeValue();

						if (!fragmentShaders.containsKey(shaderName))
						{
							loadFragmentShader(shaderName);
						}

						fshaders.add(fragmentShaders.get(shaderName));
					}

					programs.put(programName, new Program(vshaders, fshaders));

					numPrograms++;
				}
			}

		} catch (Exception ex)
		{
			System.out.println(ex.getMessage());
			ex.printStackTrace();
			System.exit(0);
		}

		System.out.println("Number of programs created:  " + numPrograms);

		vertexShaders.clear();
		fragmentShaders.clear();
	}

	private void loadVertexShader(String name)
	{
		try
		{
			File file = new File("res/shaders/" + name + ".vert");
			FileInputStream fis = new FileInputStream(file);
			FileChannel fc = fis.getChannel();

			ByteBuffer shaderSource = BufferUtils.createByteBuffer((int) fc.size());
			fc.read(shaderSource);
			shaderSource.flip();

			vertexShaders.put(name, new Shader(GL_VERTEX_SHADER, shaderSource));
			System.out.println("Loaded vertex shader '" + name + "'");
		} catch (Exception ex)
		{
			System.out.println("Error creating vertex shader '" + name + "'");
		}
	}

	private void loadFragmentShader(String name)
	{
		try
		{
			File file = new File("res/shaders/" + name + ".frag");
			FileInputStream fis = new FileInputStream(file);
			FileChannel fc = fis.getChannel();

			ByteBuffer shaderSource = BufferUtils.createByteBuffer((int) fc.size());
			fc.read(shaderSource);
			shaderSource.flip();

			fragmentShaders.put(name, new Shader(GL_FRAGMENT_SHADER, shaderSource));
			System.out.println("Loaded fragment shader '" + name + "'");
		} catch (Exception ex)
		{
			System.out.println("Error creating fragment shader '" + name + "'");
		}
	}

	public void useProgram(String name)
	{
		programs.get(name).use();
		System.out.println("Now using program '" + name + "'");
	}

	class Shader
	{
		public final int handle;

		public Shader(int type, ByteBuffer source)
		{
			handle = glCreateShader(type);
			glShaderSource(handle, source);
			glCompileShader(handle);

			if (glGetShader(handle, GL_COMPILE_STATUS) == GL_FALSE)
			{
				System.out.println("shader compilation failed.");
			}
		}
	}

	class Program
	{
		public final int handle;

		public Program(ArrayList<Shader> vshaders, ArrayList<Shader> fshaders)
		{
			handle = glCreateProgram();

			for (int i = 0; i < vshaders.size(); i++)
			{
				glAttachShader(handle, vshaders.get(i).handle);
			}

			for (int i = 0; i < fshaders.size(); i++)
			{
				glAttachShader(handle, fshaders.get(i).handle);
			}

			glLinkProgram(handle);
			glValidateProgram(handle);
		}

		public void use()
		{
			glUseProgram(handle);
		}
	}
}