Render to Texture giving fatal JRE exceptions

Started by Turakar, June 23, 2016, 14:08:27

Previous topic - Next topic

Turakar

Hello,

I'm trying to write an example for rendering to texture with LWJGL 3.0.0. However, I do not manage to get it to work. That's the code:
package de.thoffbauer.cameraexample;

import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL13.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.opengl.GL32.*;
import static org.lwjgl.system.MemoryUtil.NULL;

import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;

import javax.imageio.ImageIO;

import org.lwjgl.BufferUtils;
import org.lwjgl.Version;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;

 
public class CameraExample {
 
    // The window handle
    private long window;
    
    private int triangleVao;
    private int triangleVbo;
    
    private int framebuffer;
    private int renderedTexture;
    
    private int screenVao;
    private int screenVbo;
 
    private int program;
    private int vertexShader;
    private int fragmentShader;
    
    public void run() {
        System.out.println("Hello LWJGL " + Version.getVersion() + "!");
 
        try {
            init();
            loop();
 
            // Free the window callbacks and destroy the window
            glfwFreeCallbacks(window);
            glfwDestroyWindow(window);
        } finally {
            // Terminate GLFW and free the error callback
            glfwTerminate();
            glfwSetErrorCallback(null).free();
        }
    }
    private void init() {
        // Setup an error callback. The default implementation
        // will print the error message in System.err.
        GLFWErrorCallback.createPrint(System.err).set();
 
        // Initialize GLFW. Most GLFW functions will not work before doing this.
        if ( !glfwInit() )
            throw new IllegalStateException("Unable to initialize GLFW");
 
        // Configure our window
        glfwDefaultWindowHints(); // optional, the current window hints are already the default
        glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation
        glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable
 
        int WIDTH = 800;
        int HEIGHT = 600;
 
        // Create the window
        window = glfwCreateWindow(WIDTH, HEIGHT, "Camera Example", NULL, NULL);
        if ( window == NULL )
            throw new RuntimeException("Failed to create the GLFW window");
 
        // Setup a key callback. It will be called every time a key is pressed, repeated or released.
        glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
            if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
                glfwSetWindowShouldClose(window, true); // We will detect this in our rendering loop
        });
 
        // Get the resolution of the primary monitor
        GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        // Center our window
        glfwSetWindowPos(
            window,
            (vidmode.width() - WIDTH) / 2,
            (vidmode.height() - HEIGHT) / 2
        );
 
        // Make the OpenGL context current
        glfwMakeContextCurrent(window);
        // Enable v-sync
        glfwSwapInterval(1);
 
        // Make the window visible
        glfwShowWindow(window);
        
        // This line is critical for LWJGL's interoperation with GLFW's
        // OpenGL context, or any context that is managed externally.
        // LWJGL detects the context that is current in the current thread,
        // creates the GLCapabilities instance and makes the OpenGL
        // bindings available for use.
        GL.createCapabilities();
 
        // Set the clear color
        glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
        
        program = glCreateProgram();
        attachVertexShader("quadtextured.vs");
        attachFragmentShader("quadtextured.fs");
        glBindAttribLocation(program, 0, "in_Position");
        glBindAttribLocation(program, 1, "in_TextureCoord");
        linkProgram();
        
        // Enable depth test
    	glEnable(GL_DEPTH_TEST);
    	// Accept fragment if it closer to the camera than the former one
    	glDepthFunc(GL_LESS);
        
        initCamera();
        initScreen();
        initTriangle();
    }
    private void loop() {
        // Run the rendering loop until the user has attempted to close
        // the window or has pressed the ESCAPE key.
    	int i = 0;
        while ( !glfwWindowShouldClose(window) ) {
        	
        	renderToCamera();

            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
        	glUseProgram(program);
            
            renderTriangle();
            renderScreen();
            
//            if(i == 100) {
//            	try {
//					saveCamera("framebuffer.png");
//				} catch (IOException e) {
//					e.printStackTrace();
//				}
//            }
            
        	glUseProgram(0);
 
            glfwSwapBuffers(window); // swap the color buffers
 
            // Poll for window events. The key callback above will only be
            // invoked during this call.
            glfwPollEvents();
            i++;
        }
    }
 
    private void initTriangle() {
    	triangleVao = glGenVertexArrays();
    	glBindVertexArray(triangleVao);
    	
    	float[] vertices = new float[] {
    			0, 0.8f, 0,
    			-0.8f, -0.8f, 0,
    			0.8f, -0.8f, 0
    	};
    	
    	FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length);
    	verticesBuffer.put(vertices).flip();
    	
    	triangleVbo = glGenBuffers();
    	glBindBuffer(GL_ARRAY_BUFFER, triangleVbo);
    	glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);
    	
    	glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
    	glBindVertexArray(0);
    }
    private void renderTriangle() {
    	glBindVertexArray(triangleVao);
    	glEnableVertexAttribArray(0);
    	
    	glDrawArrays(GL_TRIANGLES, 0, 3);
    	
    	glDisableVertexAttribArray(0);
    	glBindVertexArray(0);
    }
    
    private void initCamera() {
    	framebuffer = glGenFramebuffers();
    	glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    	
    	renderedTexture = glGenTextures();
    	glBindTexture(GL_TEXTURE_2D, renderedTexture);
    	
    	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 800, 600, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    	
    	int depthRenderBuffer = glGenRenderbuffers();
    	glBindRenderbuffer(GL_RENDERBUFFER, depthRenderBuffer);
    	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, 800, 600);
    	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderBuffer);
    	
    	glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);
    	
    	glDrawBuffers(GL_COLOR_ATTACHMENT0);
    	
    	if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    		System.err.println("Framebuffer not complete!");
    	
    	glBindFramebuffer(GL_FRAMEBUFFER, 0);
    }
    private void renderToCamera() {
    	glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    	glViewport(0, 0, 800, 600);
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    	glUseProgram(program);
    	glBindFramebuffer(GL_FRAMEBUFFER, 0);
    }
    
    private void initScreen() {
    	screenVao = glGenVertexArrays();
    	glBindVertexArray(screenVao);
    	
    	float[] vertices = new float[] {
    		// Vertices
    		-1, -1, 0,
    		0, -1, 0,
    		-1, 0, 0,
    		-1, 0, 0,
    		0, -1, 0,
    		0, 0, 0
    		// UV
    		-1, -1,
    		1, -1,
    		-1, 1,
    		-1, 1,
    		1, -1,
    		1, 1
    	};
    	
    	FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length);
    	verticesBuffer.put(vertices).flip();
    	
    	screenVbo = glGenBuffers();
    	glBindBuffer(GL_ARRAY_BUFFER, screenVbo);
    	glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);
    	
    	glVertexAttribPointer(0, vertices.length, GL_FLOAT, false, 0, 0);
    	glVertexAttribPointer(1, vertices.length, GL_FLOAT, false, 0, 3 * 6);
    	
    	glBindBuffer(GL_VERTEX_ARRAY, 0);
    }
    private void renderScreen() {
//    	glActiveTexture(GL_TEXTURE0);
    	glBindTexture(GL_TEXTURE_2D, renderedTexture);
    	
    	glBindVertexArray(screenVao);
    	glEnableVertexAttribArray(0);
    	glEnableVertexAttribArray(1);
    	
    	glDrawArrays(GL_TRIANGLES, 0, 3);
    	
    	glDisableVertexAttribArray(0);
    	glDisableVertexAttribArray(1);
    	glBindVertexArray(0);
    	
    	glBindTexture(GL_TEXTURE_2D, 0);
    }
    private void saveCamera(String name) throws IOException {
    	glBindTexture(GL_TEXTURE_2D, renderedTexture);
    	ByteBuffer buffer = ByteBuffer.allocate(800 * 600 * 3);
    	glPixelStorei(GL_PACK_ALIGNMENT, 1);
    	glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
    	BufferedImage image = new BufferedImage(800, 600, BufferedImage.TYPE_INT_RGB);
    	int width = 800;
    	int height = 600;
    	for(int x = 0; x < width; x++) {
    	    for(int y = 0; y < height; y++) {
    	        int i = (x + (width * y)) * 3;
    	        int r = buffer.get(i) & 0xFF;
    	        int g = buffer.get(i + 1) & 0xFF;
    	        int b = buffer.get(i + 2) & 0xFF;
    	        image.setRGB(x, height - (y + 1), (0xFF << 24) | (r << 16) | (g << 8) | b);
    	    }
    	}
    	ImageIO.write(image, "PNG", new File(name));
    }
    
	private String readShader(String name) {
		StringBuilder source = new StringBuilder();
	    try
	    {
	        BufferedReader reader = new BufferedReader(new FileReader("shaders/" + name));

	        String line;
	        while ((line = reader.readLine()) != null)
	        {
	            source.append(line).append("\n");
	        }

	        reader.close();
	    }
	    catch (Exception e)
	    {
	        System.err.println("Error loading source code: " + name);
	        e.printStackTrace();
	    }

	    return source.toString();
	}
	private void attachVertexShader(String name) {
		String source = readShader(name);
		
		vertexShader = glCreateShader(GL_VERTEX_SHADER);
		glShaderSource(vertexShader, source);
		
		glCompileShader(vertexShader);
		
		if(glGetShaderi(vertexShader, GL_COMPILE_STATUS) == GL_FALSE)
			System.err.println("Error creating vertexShader\n" + glGetShaderInfoLog(vertexShader));
		
		glAttachShader(program, vertexShader);
	}
	private void attachFragmentShader(String name) {
		String source = readShader(name);
		
		fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
		glShaderSource(fragmentShader, source);
		
		glCompileShader(fragmentShader);
		
		if(glGetShaderi(fragmentShader, GL_COMPILE_STATUS) == GL_FALSE)
			System.err.println("Error creating fragmentShader\n" + glGetShaderInfoLog(fragmentShader));
		
		glAttachShader(program, fragmentShader);
	}
    private void linkProgram() {
    	glLinkProgram(program);
    	
    	if(glGetProgrami(program, GL_LINK_STATUS) == GL_FALSE) {
    		System.err.println("Unable to link shader program");
    		System.err.println(glGetProgramInfoLog(program));
    	}
    }
	
    public static void main(String[] args) {
        new CameraExample().run();
    }
 
}


The shaders (first vertex, then fragment):
#version 150 core

in vec4 in_Position;
in vec2 in_TextureCoord;

out vec2 pass_TextureCoord;

void main(void) {
    gl_Position = in_Position;
    
    pass_TextureCoord = in_TextureCoord;
}

#version 150 core

uniform sampler2D texture_diffuse;

in vec2 pass_TextureCoord;

out vec4 out_Color;

void main(void) {
    out_Color = texture(texture_diffuse, pass_TextureCoord);
}


And the output:
WARNING in native method: JNI call made without checking exceptions when required to from CallStaticObjectMethod
Hello LWJGL 3.0.0 build 90!
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x0000000041030f41, pid=7622, tid=139991872743168
#
# JRE version: Java(TM) SE Runtime Environment (8.0_77-b03) (build 1.8.0_77-b03)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.77-b03 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# C  0x0000000041030f41
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# An error report file with more information is saved as:
# /eclipseworkspace/CameraExample/hs_err_pid7622.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#


If you need this hs_err_pid I can publish it, but I does not seem to be human-readable. (Btw, I'm really disappointed of those informative error descriptions...)
With the help of the eclipse debugger I was able to find out that the JRE exception arises at glDrawArrays in renderScreen(), that's line 273.
I'm using an Nvidia GTX 960 with Linux x64 (Fedora). I have the propietary drivers installed.

Thank you in advance for any help,
Turakar

Kai

Please look at the documentation of glVertexAttribPointer: https://www.opengl.org/sdk/docs/man/html/glVertexAttribPointer.xhtml
You supply invalid values for three of its parameters (size, stride and pointer) in the calls at line 260-261.

Turakar

Thank you for that hint. I also made some other changes to get it to work:
- unbind with glVertexArray() in initScreen
- set the texture to CLAMP_TO_BORDER
- different glClearColors to see the difference better
- set last parameter to 6 in glDrawArrays in renderScreen (prior rendered just one triangle)

The full code now: (no changes in the shaders)
package de.thoffbauer.cameraexample;

import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL13.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.opengl.GL32.*;
import static org.lwjgl.system.MemoryUtil.NULL;

import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;

import javax.imageio.ImageIO;

import org.lwjgl.BufferUtils;
import org.lwjgl.Version;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;

 
public class CameraExample {
 
	private final int width = 1024;
	private final int height = 1024;
	
    // The window handle
    private long window;
    
    private int triangleVao;
    private int triangleVbo;
    
    private int framebuffer;
    private int renderedTexture;
    
    private int screenVao;
    private int screenVbo;
 
    private int program;
    private int vertexShader;
    private int fragmentShader;
    
    public void run() {
        System.out.println("Hello LWJGL " + Version.getVersion() + "!");
 
        try {
            init();
            loop();
 
            // Free the window callbacks and destroy the window
            glfwFreeCallbacks(window);
            glfwDestroyWindow(window);
        } finally {
            // Terminate GLFW and free the error callback
            glfwTerminate();
            glfwSetErrorCallback(null).free();
        }
    }
    private void init() {
        // Setup an error callback. The default implementation
        // will print the error message in System.err.
        GLFWErrorCallback.createPrint(System.err).set();
 
        // Initialize GLFW. Most GLFW functions will not work before doing this.
        if ( !glfwInit() )
            throw new IllegalStateException("Unable to initialize GLFW");
 
        // Configure our window
        glfwDefaultWindowHints(); // optional, the current window hints are already the default
        glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation
        glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable
 
        // Create the window
        window = glfwCreateWindow(width, height, "Camera Example", NULL, NULL);
        if ( window == NULL )
            throw new RuntimeException("Failed to create the GLFW window");
 
        // Setup a key callback. It will be called every time a key is pressed, repeated or released.
        glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
            if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
                glfwSetWindowShouldClose(window, true); // We will detect this in our rendering loop
        });
 
        // Get the resolution of the primary monitor
        GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        // Center our window
        glfwSetWindowPos(
            window,
            (vidmode.width() - width) / 2,
            (vidmode.height() - height) / 2
        );
 
        // Make the OpenGL context current
        glfwMakeContextCurrent(window);
        // Enable v-sync
        glfwSwapInterval(1);
 
        // Make the window visible
        glfwShowWindow(window);
        
        // This line is critical for LWJGL's interoperation with GLFW's
        // OpenGL context, or any context that is managed externally.
        // LWJGL detects the context that is current in the current thread,
        // creates the GLCapabilities instance and makes the OpenGL
        // bindings available for use.
        GL.createCapabilities();
        
        program = glCreateProgram();
        attachVertexShader("quadtextured.vs");
        attachFragmentShader("quadtextured.fs");
        glBindAttribLocation(program, 0, "in_Position");
        glBindAttribLocation(program, 1, "in_TextureCoord");
        linkProgram();
        
        // Enable depth test
    	glEnable(GL_DEPTH_TEST);
    	// Accept fragment if it closer to the camera than the former one
    	glDepthFunc(GL_LESS);
    	
        checkError("init");
        
        initCamera();
        initScreen();
        initTriangle();

    }
	private void checkError(String where) {
		int glError = glGetError();
		if(glError != 0) {
        	System.out.println("[" + where + "] GL error: " + glError);
        }
	}
    private void loop() {
        // Run the rendering loop until the user has attempted to close
        // the window or has pressed the ESCAPE key.
    	int i = 0;
        while ( !glfwWindowShouldClose(window) ) {

            glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
        	renderToCamera();

            glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
        	glUseProgram(program);
            
            renderTriangle();
            renderScreen();
            
//            if(i == 100) {
//            	try {
//					saveCamera("framebuffer.png");
//				} catch (IOException e) {
//					e.printStackTrace();
//				}
//            }
            
        	glUseProgram(0);
 
            glfwSwapBuffers(window); // swap the color buffers
 
            // Poll for window events. The key callback above will only be
            // invoked during this call.
            glfwPollEvents();
            i++;
        }
    }
 
    private void initTriangle() {
    	triangleVao = glGenVertexArrays();
    	glBindVertexArray(triangleVao);
    	
    	float[] vertices = new float[] {
    			0, 0.8f, -1,
    			-0.8f, -0.8f, -1,
    			0.8f, -0.8f, -1
    	};
    	
    	FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length);
    	verticesBuffer.put(vertices).flip();
    	
    	triangleVbo = glGenBuffers();
    	glBindBuffer(GL_ARRAY_BUFFER, triangleVbo);
    	glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);
    	
    	glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
    	glBindVertexArray(0);
        checkError("initTriangle");
    }
    private void renderTriangle() {
    	glBindVertexArray(triangleVao);
    	glEnableVertexAttribArray(0);
    	
    	glDrawArrays(GL_TRIANGLES, 0, 3);
    	
    	glDisableVertexAttribArray(0);
    	glBindVertexArray(0);
        checkError("renderTriangle");
    }
    
    private void initCamera() {
    	framebuffer = glGenFramebuffers();
    	glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    	
    	renderedTexture = glGenTextures();
    	glBindTexture(GL_TEXTURE_2D, renderedTexture);
    	
    	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    	
    	int depthRenderBuffer = glGenRenderbuffers();
    	glBindRenderbuffer(GL_RENDERBUFFER, depthRenderBuffer);
    	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
    	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderBuffer);
    	
    	glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);
    	
    	glDrawBuffers(GL_COLOR_ATTACHMENT0);
    	
    	if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    		System.err.println("Framebuffer not complete!");
    	
    	glBindFramebuffer(GL_FRAMEBUFFER, 0);
        checkError("initCamera");
    }
    private void renderToCamera() {
    	glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    	glViewport(0, 0, width, height);
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    	glUseProgram(program);
    	
    	renderTriangle();
    	
    	glBindFramebuffer(GL_FRAMEBUFFER, 0);
        checkError("renderToCamera");
    }
    
    private void initScreen() {
    	screenVao = glGenVertexArrays();
    	glBindVertexArray(screenVao);
    	
    	float[] vertices = new float[] {
    		// Vertices  UV
    		-1, -1, 0,   1, 0,
    		0, -1, 0,    0, 0,
    		-1, 0, 0,    1, 1,
    		-1, 0, 0,    1, 1,
    		0, -1, 0,    0, 0,
    		0, 0, 0,     0, 1
    	};
    	
    	FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length);
    	verticesBuffer.put(vertices).flip();
    	
    	screenVbo = glGenBuffers();
    	glBindBuffer(GL_ARRAY_BUFFER, screenVbo);
    	glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);

    	glVertexAttribPointer(0, 3, GL_FLOAT, false, (3 + 2) * 4, 0);
    	glVertexAttribPointer(1, 2, GL_FLOAT, false, (3 + 2) * 4, 3 * 4);
    	
        glBindVertexArray(0);
        checkError("initScreen");
    }
    private void renderScreen() {
//    	glActiveTexture(GL_TEXTURE0);
    	glBindTexture(GL_TEXTURE_2D, renderedTexture);
    	
    	glBindVertexArray(screenVao);
    	glEnableVertexAttribArray(0);
    	glEnableVertexAttribArray(1);
    	
    	glDrawArrays(GL_TRIANGLES, 0, 6);
    	
    	glDisableVertexAttribArray(0);
    	glDisableVertexAttribArray(1);
    	glBindVertexArray(0);
    	
    	glBindTexture(GL_TEXTURE_2D, 0);
        checkError("renderScreen");
    }
    private void saveCamera(String name) throws IOException {
    	glBindTexture(GL_TEXTURE_2D, renderedTexture);
    	ByteBuffer buffer = ByteBuffer.allocate(width * height * 3);
    	glPixelStorei(GL_PACK_ALIGNMENT, 1);
    	glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
    	BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    	for(int x = 0; x < width; x++) {
    	    for(int y = 0; y < height; y++) {
    	        int i = (x + (width * y)) * 3;
    	        int r = buffer.get(i) & 0xFF;
    	        int g = buffer.get(i + 1) & 0xFF;
    	        int b = buffer.get(i + 2) & 0xFF;
    	        image.setRGB(x, height - (y + 1), (0xFF << 24) | (r << 16) | (g << 8) | b);
    	    }
    	}
    	ImageIO.write(image, "PNG", new File(name));
    }
    
	private String readShader(String name) {
		StringBuilder source = new StringBuilder();
	    try
	    {
	        BufferedReader reader = new BufferedReader(new FileReader("shaders/" + name));

	        String line;
	        while ((line = reader.readLine()) != null)
	        {
	            source.append(line).append("\n");
	        }

	        reader.close();
	    }
	    catch (Exception e)
	    {
	        System.err.println("Error loading source code: " + name);
	        e.printStackTrace();
	    }

	    return source.toString();
	}
	private void attachVertexShader(String name) {
		String source = readShader(name);
		
		vertexShader = glCreateShader(GL_VERTEX_SHADER);
		glShaderSource(vertexShader, source);
		
		glCompileShader(vertexShader);
		
		if(glGetShaderi(vertexShader, GL_COMPILE_STATUS) == GL_FALSE)
			System.err.println("Error creating vertexShader\n" + glGetShaderInfoLog(vertexShader));
		
		glAttachShader(program, vertexShader);
        checkError("attachVertexShader");
	}
	private void attachFragmentShader(String name) {
		String source = readShader(name);
		
		fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
		glShaderSource(fragmentShader, source);
		
		glCompileShader(fragmentShader);
		
		if(glGetShaderi(fragmentShader, GL_COMPILE_STATUS) == GL_FALSE)
			System.err.println("Error creating fragmentShader\n" + glGetShaderInfoLog(fragmentShader));
		
		glAttachShader(program, fragmentShader);
        checkError("attachFragmentShader");
	}
    private void linkProgram() {
    	glLinkProgram(program);
    	
    	if(glGetProgrami(program, GL_LINK_STATUS) == GL_FALSE) {
    		System.err.println("Unable to link shader program");
    		System.err.println(glGetProgramInfoLog(program));
    	}
        checkError("linkProgram");
    }
    
    public static void main(String[] args) {
        new CameraExample().run();
    }
 
}