Back to project page droidengine2d.
The source code is released under:
Apache License
If you think the Android project droidengine2d listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
/* Copyright 2013-2014 Miguel Vicente Linares *//www.j a v a2 s . c o m * 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 com.miviclin.droidengine2d.graphics.shader; import java.nio.FloatBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import android.opengl.GLES20; import android.util.Log; import com.miviclin.droidengine2d.graphics.GLDebugger; /** * ShaderProgram contains a vertex shader and a fragment shader. * * @author Miguel Vicente Linares * */ public class ShaderProgram { private String vertexShaderSource; private String fragmentShaderSource; private HashMap<String, Integer> attributesLocations; private HashMap<String, Integer> uniformsLocations; private int programId; private boolean linked; /** * Creates a ShaderProgram. */ public ShaderProgram() { this.vertexShaderSource = ""; this.fragmentShaderSource = ""; this.attributesLocations = new HashMap<String, Integer>(); this.uniformsLocations = new HashMap<String, Integer>(); this.linked = false; } /** * Returns a string containing the GLSL code of the vertex shader. * * @return GLSL code of the vertex shader */ public String getVertexShaderSource() { return vertexShaderSource; } /** * Returns a string containing the GLSL code of the fragment shader. * * @return GLSL code of the fragment shader */ public String getFragmentShaderSource() { return fragmentShaderSource; } /** * Returns the location of the specified attribute in the shader. * * @param attributeName Name of the attribute. * @return Location of the attribute */ public int getAttributeLocation(String attributeName) { int attributeLocation = attributesLocations.get(attributeName).intValue(); if (attributeLocation == -1) { throw new IllegalArgumentException("The specified attribute is not linked: " + attributeName); } return attributeLocation; } /** * Returns the location of the specified uniform in the shader. * * @param uniformName Name of the uniform. * @return Location of the uniform */ public int getUniformLocation(String uniformName) { int uniformLocation = uniformsLocations.get(uniformName).intValue(); if (uniformLocation == -1) { throw new IllegalArgumentException("The specified uniform is not linked: " + uniformName); } return uniformLocation; } /** * Installs this shader program as part of the current rendering state. */ public void use() { GLES20.glUseProgram(programId); } /** * Returns the ID (used by the OpenGL context) of this program. * * @return the ID of this program */ public int getProgramId() { return programId; } /** * Sets the ID (used by the OpenGL context) of this program.<br> * This method should only be called to set the ID generated by OpenGL where the shaders are compiled and the OpenGL * program is created. * * @param programId ID. */ protected void setProgramId(int programId) { this.programId = programId; } /** * Returns true if the attributes of the shaders are linked to the program. * * @return true if the attributes of the shaders are linked to the program, false otherwise. */ public boolean isLinked() { return linked; } /** * Sets the linked flag to true.<br> * This method should be called only when the attributes of the shaders are linked to the program, usually from * {@link #compileAndLink()}. */ protected void setLinked() { this.linked = true; } /** * Sets the vertex and fragment shaders of this program. This method should be called from {@link #compileAndLink()} * . * * @param vertexShaderSource GLSL code of the vertex shader. * @param fragmentShaderSource GLSL code of the fragment shader. * @param attributes List of attributes of the shader program. * @param uniforms List of uniforms of the shader program. */ public void setShaders(String vertexShaderSource, String fragmentShaderSource, ArrayList<String> attributes, ArrayList<String> uniforms) { this.vertexShaderSource = vertexShaderSource; this.fragmentShaderSource = fragmentShaderSource; for (int i = 0; i < attributes.size(); i++) { attributesLocations.put(attributes.get(i), -1); } for (int i = 0; i < uniforms.size(); i++) { uniformsLocations.put(uniforms.get(i), -1); } } /** * 1) Compiles the shaders, generating the ID of this program.<br> * 2) Sets the ID of this program with setProgram(programId).<br> * 3) Links the attributes of the shaders to this program and calls {@link #setLinked()}. */ public void compileAndLink() { if (vertexShaderSource.equals("") || fragmentShaderSource.equals("")) { throw new ShaderProgramException("The source code of the shaders is not loaded.\n" + "Ensure setShaders(...) has been called before trying to compile the shader program."); } int programId = ShaderProgram.createProgram(vertexShaderSource, fragmentShaderSource); if (programId == 0) { return; } setProgramId(programId); link(programId); setLinked(); } /** * Links the attributes of the shaders to this program.<br> * This method is called from {@link #compileAndLink()}. * * @param programId The ID (used by the OpenGL context) of this program. */ protected void link(int programId) { String variableName; int location; for (Map.Entry<String, Integer> entry : attributesLocations.entrySet()) { variableName = entry.getKey(); location = GLES20.glGetAttribLocation(programId, variableName); GLDebugger.getInstance().passiveCheckGLError(); if (location == -1) { throw new RuntimeException("Could not get attribute location for " + variableName); } entry.setValue(location); } for (Map.Entry<String, Integer> entry : uniformsLocations.entrySet()) { variableName = entry.getKey(); location = GLES20.glGetUniformLocation(programId, variableName); GLDebugger.getInstance().passiveCheckGLError(); if (location == -1) { throw new RuntimeException("Could not get uniform location for " + variableName); } entry.setValue(location); } } /** * Sets the specified attribute's values for each vertex that will be sent to the vertex shader. * * @param attributeName Name of the attribute. * @param size Number of components of the specified vertex attribute (For example: position (x, y, z) => size=3). * @param strideBytes Byte offset between consecutive generic vertex attributes. * @param dataBuffer Buffer where vertices are stored. * @param dataOffset Offset of the first component of the first attribute with the specified attributeName in the * dataBuffer. */ public void setAttribute(String attributeName, int size, int strideBytes, FloatBuffer dataBuffer, int dataOffset) { int attributeLocation = getAttributeLocation(attributeName); GLES20.glEnableVertexAttribArray(attributeLocation); GLDebugger.getInstance().passiveCheckGLError(); dataBuffer.position(dataOffset); GLES20.glVertexAttribPointer(attributeLocation, size, GLES20.GL_FLOAT, false, strideBytes, dataBuffer); GLDebugger.getInstance().passiveCheckGLError(); } /** * Specifies the value of the specified 1-component uniform of this shader program. * * @param uniformName Name of the uniform. * @param x Value of the uniform. */ public void setUniform1f(String uniformName, float x) { int uniformLocation = getUniformLocation(uniformName); GLES20.glUniform1f(uniformLocation, x); GLDebugger.getInstance().passiveCheckGLError(); } /** * Specifies the value of the specified 1-component uniform array of this shader program. * * @param uniformName Name of the uniform. * @param count Number of elements of the uniform array. * @param data Array where the value of the uniform array is stored. * @param dataOffset Offset of the first element of the uniform in the specified array. */ public void setUniform1fv(String uniformName, int count, float[] data, int dataOffset) { int uniformLocation = getUniformLocation(uniformName); GLES20.glUniform1fv(uniformLocation, count, data, dataOffset); GLDebugger.getInstance().passiveCheckGLError(); } /** * Specifies the value of the specified 2-component uniform of this shader program. * * @param uniformName Name of the uniform. * @param x First component of the uniform. * @param y Second component of the uniform. */ public void setUniform1f(String uniformName, float x, float y) { int uniformLocation = getUniformLocation(uniformName); GLES20.glUniform2f(uniformLocation, x, y); GLDebugger.getInstance().passiveCheckGLError(); } /** * Specifies the value of the specified 2-component uniform of this shader program. * * @param uniformName Name of the uniform. * @param count Number of elements of the uniform array. * @param data Array where the value of the uniform array is stored. * @param dataOffset Offset of the first element of the uniform in the specified array. */ public void setUniform2fv(String uniformName, int count, float[] data, int dataOffset) { int uniformLocation = getUniformLocation(uniformName); GLES20.glUniform2fv(uniformLocation, count, data, dataOffset); GLDebugger.getInstance().passiveCheckGLError(); } /** * Specifies the value of the specified 3-component uniform of this shader program. * * @param uniformName Name of the uniform. * @param x First component of the uniform. * @param y Second component of the uniform. * @param z Third component of the uniform. */ public void setUniform3f(String uniformName, float x, float y, float z) { int uniformLocation = getUniformLocation(uniformName); GLES20.glUniform3f(uniformLocation, x, y, z); GLDebugger.getInstance().passiveCheckGLError(); } /** * Specifies the value of the specified 3-component uniform of this shader program. * * @param uniformName Name of the uniform. * @param count Number of elements of the uniform array. * @param data Array where the value of the uniform array is stored. * @param dataOffset Offset of the first element of the uniform in the specified array. */ public void setUniform3fv(String uniformName, int count, float[] data, int dataOffset) { int uniformLocation = getUniformLocation(uniformName); GLES20.glUniform3fv(uniformLocation, count, data, dataOffset); GLDebugger.getInstance().passiveCheckGLError(); } /** * Specifies the value of the specified 4-component uniform of this shader program. * * @param uniformName Name of the uniform. * @param x First component of the uniform. * @param y Second component of the uniform. * @param z Third component of the uniform. * @param w Fourth component of the uniform. */ public void setUniform4f(String uniformName, float x, float y, float z, float w) { int uniformLocation = getUniformLocation(uniformName); GLES20.glUniform4f(uniformLocation, x, y, z, w); GLDebugger.getInstance().passiveCheckGLError(); } /** * Specifies the value of the specified 4-component uniform of this shader program. * * @param uniformName Name of the uniform. * @param count Number of elements of the uniform array. * @param data Array where the value of the uniform array is stored. * @param dataOffset Offset of the first element of the uniform in the specified array. */ public void setUniform4fv(String uniformName, int count, float[] data, int dataOffset) { int uniformLocation = getUniformLocation(uniformName); GLES20.glUniform4fv(uniformLocation, count, data, dataOffset); GLDebugger.getInstance().passiveCheckGLError(); } /** * Specifies the value of the specified 2x2 matrix uniform of this shader program. * * @param numMatrices Number of matrices in the uniform array. * @param data Data of the matrices we want to send, in the same array. * @param dataOffset Offset of the first element of the first matrix. */ public void setUniformMatrix2fv(String uniformName, int numMatrices, float[] data, int dataOffset) { int uniformLocation = getUniformLocation(uniformName); GLES20.glUniformMatrix2fv(uniformLocation, numMatrices, false, data, dataOffset); GLDebugger.getInstance().passiveCheckGLError(); } /** * Specifies the value of the specified 3x3 matrix uniform of this shader program. * * @param numMatrices Number of matrices in the uniform array. * @param data Data of the matrices we want to send, in the same array. * @param dataOffset Offset of the first element of the first matrix. */ public void setUniformMatrix3fv(String uniformName, int numMatrices, float[] data, int dataOffset) { int uniformLocation = getUniformLocation(uniformName); GLES20.glUniformMatrix3fv(uniformLocation, numMatrices, false, data, dataOffset); GLDebugger.getInstance().passiveCheckGLError(); } /** * Specifies the value of the specified 4x4 matrix uniform of this shader program. * * @param numMatrices Number of matrices in the uniform array. * @param data Data of the matrices we want to send, in the same array. * @param dataOffset Offset of the first element of the first matrix. */ public void setUniformMatrix4fv(String uniformName, int numMatrices, float[] data, int dataOffset) { int uniformLocation = getUniformLocation(uniformName); GLES20.glUniformMatrix4fv(uniformLocation, numMatrices, false, data, dataOffset); GLDebugger.getInstance().passiveCheckGLError(); } /** * Creates a GLSL shader program from the specified vertex and fragment shaders. * * @param vertexSource GLSL code of the vertex shader. * @param fragmentSource GLSL code of the fragment shader. * @return the ID of the compiled program generated by OpenGL ES 2.0. Or 0 if an error occurred. */ protected static int createProgram(String vertexSource, String fragmentSource) { int vertexShader, fragmentShader, program; vertexShader = compileShader(GLES20.GL_VERTEX_SHADER, vertexSource); if (vertexShader == 0) { return 0; } fragmentShader = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); if (fragmentShader == 0) { return 0; } program = GLES20.glCreateProgram(); if (program != 0) { GLES20.glAttachShader(program, vertexShader); GLDebugger.getInstance().passiveCheckGLError(); GLES20.glAttachShader(program, fragmentShader); GLDebugger.getInstance().passiveCheckGLError(); GLES20.glLinkProgram(program); int[] linkStatus = new int[1]; GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] != GLES20.GL_TRUE) { Log.e(ShaderProgram.class.getSimpleName(), "Could not link program: "); Log.e(ShaderProgram.class.getSimpleName(), GLES20.glGetProgramInfoLog(program)); GLES20.glDeleteProgram(program); program = 0; } } return program; } /** * Compiles the shader. * * @param shaderType Type of the shader. Use {@link GLES20#GL_VERTEX_SHADER } or {@link GLES20#GL_FRAGMENT_SHADER }. * @param source GLSL code of the shader. * @return the ID of the compiled shader generated by OpenGL ES 2.0 */ private static int compileShader(int shaderType, String source) { int shader = GLES20.glCreateShader(shaderType); if (shader != 0) { GLES20.glShaderSource(shader, source); GLES20.glCompileShader(shader); int[] compiled = new int[1]; GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); if (compiled[0] == 0) { Log.e(ShaderProgram.class.getSimpleName(), "Could not compile shader " + shaderType + ":"); Log.e(ShaderProgram.class.getSimpleName(), GLES20.glGetShaderInfoLog(shader)); GLES20.glDeleteShader(shader); shader = 0; } } return shader; } @Override public String toString() { return vertexShaderSource + "\n\n" + fragmentShaderSource; } }