Java tutorial
/******************************************************************************** * * Copyright 2014 Theodor Angergard * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *******************************************************************************/ package se.angergard.engine.graphics; import java.util.*; import org.lwjgl.opengl.*; import se.angergard.engine.color.*; import se.angergard.engine.interfaces.*; import se.angergard.engine.math.matrix.*; import se.angergard.engine.math.vector.*; import se.angergard.engine.util.Util; /** A GLSL Shader class that creates a program and attaches a vertex and fragment shader. Supports uniforms. Every method return * it's self to be able to create a shader in one line for example new * ShaderProgram().addVertexShader("code").addFragmentShader("code").compileShader().addUniform("uniform"); * @author Theodor */ public class ShaderProgram implements Disposable { /** Creates a new ShaderProgram, the constructor creates an empty program and creates */ public ShaderProgram() { program = GL20.glCreateProgram(); uniforms = new HashMap<String, Integer>(); fragmentShader = vertexShader = geometryShader = -1; } /** The shader program, will never be re created */ private final int program; /** The shaders, wil be detached as soon as the program has been linked. */ private int fragmentShader, vertexShader, geometryShader; /** A hashmap that consists of the name and the location. */ private final HashMap<String, Integer> uniforms; /** Binds the program to the graphic card. * @return This ShaderProgram */ public ShaderProgram bind() { GL20.glUseProgram(program); return this; } /** Links the program and validates it * @return This ShaderProgram */ public ShaderProgram compileProgram() { GL20.glLinkProgram(program); if (program == -1) { System.err.println("ShaderProgram wasn't succsessfully created"); } String error = GL20.glGetProgramInfoLog(program, GL20.GL_INFO_LOG_LENGTH); if (!error.equals("")) { System.out.println(error); } GL20.glValidateProgram(program); if (vertexShader != -1) { GL20.glDetachShader(program, vertexShader); GL20.glDeleteShader(vertexShader); } if (fragmentShader != -1) { GL20.glDetachShader(program, fragmentShader); GL20.glDeleteShader(fragmentShader); } if (geometryShader != -1) { GL20.glDetachShader(program, geometryShader); GL20.glDeleteShader(geometryShader); } return this; } /** Add the uniform to the uniforms hashmap * @param Uniform name * @return This ShaderProgram */ public ShaderProgram addUniform(String uniform) { int location = GL20.glGetUniformLocation(program, uniform); if (location == -1) { System.err.println("Can't find uniform location :" + uniform); } else { uniforms.put(uniform, location); } return this; } /** Sets the uniform with the given name with the given vector2f. You need the {@link #addUniform(String) addUniform} before * being able to set the uniform. * * @param uniform The uniform name * @param v Vector2f * @return This ShaderProgram */ public ShaderProgram setUniformVec2(String uniform, Vector2f v) { GL20.glUniform2f(uniforms.get(uniform), v.getX(), v.getY()); return this; } public ShaderProgram setUniformVec3(String uniform, Vector3f v) { GL20.glUniform3f(uniforms.get(uniform), v.getX(), v.getY(), v.getZ()); return this; } public ShaderProgram setUniformVec4(String uniform, Color color) { GL20.glUniform4f(uniforms.get(uniform), color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()); return this; } /** Sets the uniform with the given name with the given Matrix2f. You need the {@link #addUniform(String) addUniform} before * being able to set the uniform. * * @param uniform The uniform name * @param mat Matrix2f * @return This ShaderProgram */ public ShaderProgram setUniformMat2f(String uniform, Matrix2f mat) { GL20.glUniformMatrix2(uniforms.get(uniform), false, Util.toFlippedFloatBuffer(mat)); return this; } /** Sets the uniform with the given name with the given Matrix3f. You need the {@link #addUniform(String) addUniform} before * being able to set the uniform. * * @param uniform The uniform name * @param mat Matrix3f * @return This ShaderProgram */ public ShaderProgram setUniformMat3f(String uniform, Matrix3f mat) { GL20.glUniformMatrix3(uniforms.get(uniform), false, Util.toFlippedFloatBuffer(mat)); return this; } /** Sets the uniform with the given name with the given Matrix4f. You need the {@link #addUniform(String) addUniform} before * being able to set the uniform. * * @param uniform The uniform name * @param mat Matrix4f * @return This ShaderProgram */ public ShaderProgram setUniformMat4f(String uniform, Matrix4f mat) { // Invalid enum GL20.glUniformMatrix4(uniforms.get(uniform), false, Util.toFlippedFloatBuffer(mat)); return this; } /** Sets the uniform with the given name with the given float. You need the {@link #addUniform(String) addUniform} before being * able to set the uniform. * * @param uniform The uniform name * @param f Float value * @return This ShaderProgram */ public ShaderProgram setUniform1f(String uniform, float f) { GL20.glUniform1f(uniforms.get(uniform), f); return this; } /** Sets the uniform with the given name with the given Integer. You need the {@link #addUniform(String) addUniform} before * being able to set the uniform. * * @param uniform The uniform name * @param i Integer * @return This ShaderProgram */ public ShaderProgram setUniform1i(String uniform, int i) { GL20.glUniform1i(uniforms.get(uniform), i); return this; } public ShaderProgram createDefault2DShader() { addVertexShader(Default2DShader.getVertexShader()); addFragmentShader(Default2DShader.getFragmentShader()); compileProgram(); bind(); addUniform("projection"); addUniform("color"); return this; } public ShaderProgram createDefault3DShader() { addVertexShader(Default3DShader.getVertexShader()); addFragmentShader(Default3DShader.getFragmentShader()); compileProgram(); bind(); addUniform("camera"); addUniform("color"); return this; } public ShaderProgram addVertexShader(String code) { return addShader(code, GL20.GL_VERTEX_SHADER); } public ShaderProgram addFragmentShader(String code) { return addShader(code, GL20.GL_FRAGMENT_SHADER); } @Override public void dispose() { GL20.glDeleteProgram(program); } private ShaderProgram addShader(String code, int type) { int shader = GL20.glCreateShader(type); GL20.glShaderSource(shader, code); GL20.glCompileShader(shader); if (type == GL20.GL_VERTEX_SHADER) { vertexShader = shader; } else if (type == GL20.GL_FRAGMENT_SHADER) { fragmentShader = shader; } else if (type == GL32.GL_GEOMETRY_SHADER) { geometryShader = shader; } String error = GL20.glGetShaderInfoLog(shader, GL20.GL_INFO_LOG_LENGTH); if (!error.equals("")) { System.out.println(error); } if (shader == -1) { System.err.println("Shader wasn't succsessfully created"); } GL20.glAttachShader(program, shader); return this; } //@formatter-off protected static class Default2DShader { protected static final String getVertexShader() { return "#version 330 \n" + "layout(location = 0) in vec2 pos; \n" + "layout(location = 1) in vec2 texCoord \n;" + "uniform mat4 projection; \n" + "out vec2 texCoord0; \n" + "void main(){ \n" + "gl_Position = vec4(pos.xy, 0, 1) * projection; \n" + "texCoord0 = texCoord; \n" + "} \n"; } protected final static String getFragmentShader() { return "#version 330 \n" + "out vec4 fragColor; \n" + "in vec2 texCoord0; \n" + "uniform vec4 color; \n" + "uniform sampler2D sampler; \n" + "void main(){ \n" + "fragColor = texture2D(sampler, texCoord0.xy) * color; \n" + "} \n"; } } //@formatter-on //@formatter-off protected static class Default3DShader { protected static final String getVertexShader() { return "#version 330 \n" + "layout(location = 0) in vec3 pos; \n" + "layout(location = 1) in vec2 texCoord \n;" + "uniform mat4 camera; \n" + "out vec2 texCoord0; \n" + "void main(){ \n" + "gl_Position = vec4(pos.xyz, 1) * camera; \n" + "texCoord0 = texCoord; \n" + "} \n"; } protected final static String getFragmentShader() { return "#version 330 \n" + "out vec4 fragColor; \n" + "in vec2 texCoord0; \n" + "uniform vec4 color = {1, 1, 1, 0.5}; \n" + "uniform sampler2D sampler; \n" + "void main(){ \n" + "fragColor = texture2D(sampler, texCoord0.xy) * color; \n" + "} \n"; } } //@formatter-on }