ShaderProgram Utility
Este pagina demonstra passos necessários para se criar o sua própria classe re-utilizável através do utilitário ShaderProgram. Você também pode usar o mais avançado utilitário de ShaderProgram já incluído no LWJGL-básico: ver aqui.
Para nossos propósitos, vamos usar apenas um vértice e um fragmento de shader para criar os nossos programas de sombreamento. Planejamos visar o GL 2.1, que também terá de especificar os locais de atributos manualmente. Se for usar versões mais recentes do OpenGL (ou seja, GLSL 330 +), então podemos especificar os locais de atributos com qualificadores do tipo em seu lugar. Os passos básicos para a criação de um programa de shader como este são:
Compilar o código fonte em um vértice shader shader objeto.
Compilar o código fonte em um fragmento de shader shader objeto.
Criar um programa com ID glCreateProgram.
Anexar os objetos vertex e shader para o nosso programa com glAttachShader.
Se nós estamos usando 2.1, é aqui que ligaria quaisquer atributos manualmente. Por exemplo, poderíamos vincular o atributo posição de índice 0. Para isso, usamos glBindAttribLocation. Se nós estamos usando versões mais recentes do GLSL, podemos pular essa etapa.
Em seguida, ligar o programa com glLinkProgram.
Se o programa conseguiu compilar, agora podemos separar e excluir os objetos de sombreamento da vértice e fragmento como eles não são mais necessários - usando glDetachShader e glDeleteShader, respectivamente. Estes são apenas avisam para o OpenGL, que os objetos serão apagados quando eles não estão mais associados com quaisquer estados de renderização.
Exemplo:
public ShaderProgram(String vertexShader, String fragmentShader, Map<Integer, String> attributes) throws LWJGLException { //compile the String source vertex = compileShader(vertexShader, GL_VERTEX_SHADER); fragment = compileShader(fragmentShader, GL_FRAGMENT_SHADER); //create the program program = glCreateProgram(); //attach the shaders glAttachShader(program, vertex); glAttachShader(program, fragment); //bind the attrib locations for GLSL 120 if (attributes != null) for (Entry<Integer, String> e : attributes.entrySet()) glBindAttribLocation(program, e.getKey(), e.getValue()); //link our program glLinkProgram(program); //grab our info log String infoLog = glGetProgramInfoLog(program, glGetProgrami(program, GL_INFO_LOG_LENGTH)); //if some log exists, append it if (infoLog!=null && infoLog.trim().length()!=0) log += infoLog; //if the link failed, throw some sort of exception if (glGetProgrami(program, GL_LINK_STATUS) == GL_FALSE) throw new LWJGLException( "Failure in linking program. Error log:\n" + infoLog); //detach and delete the shaders which are no longer needed glDetachShader(program, vertex); glDetachShader(program, fragment); glDeleteShader(vertex); glDeleteShader(fragment); } protected int compileShader(String source, int type) throws LWJGLException { //create a shader object int shader = glCreateShader(type); //pass the source string glShaderSource(shader, source); //compile the source glCompileShader(shader); //if info/warnings are found, append it to our shader log String infoLog = glGetShaderInfoLog(shader, glGetShaderi(shader, GL_INFO_LOG_LENGTH)); if (infoLog!=null && infoLog.trim().length()!=0) log += getName(type) +": "+infoLog + "\n"; //if the compiling was unsuccessful, throw an exception if (glGetShaderi(shader, GL_COMPILE_STATUS) == GL_FALSE) throw new LWJGLException("Failure in compiling " + getName(type) + ". Error log:\n" + infoLog); return shader; }
Usando o programa:
Em OpenGL, só podemos ter um programa único de shader em uso ao mesmo tempo. Chamamos glUseProgram (programa) para especificar o programa ativo. Devemos especificar o glUseProgram (0) para usar o "default" do shader. No entanto, uma vez que estamos tentando trabalhar com o pipeline programável, não devemos mais nos preocupar com o shader padrão, já que não existe tal coisa no GL moderna. Na verdade, ele pode causar erros se tentarmos renderização com o shader padrão no GL 3.1 +
Assim, nossos métodos para se criar um programa final ficará assim::
/** * Make this shader the active program. */ public void use() { glUseProgram(program); } /** * Destroy this shader program. */ public void destroy() { //a flag for GL -- the program will not actually be deleted until it's no longer in use glDeleteProgram(program); } /** * Gets the location of the specified uniform name. * @param str the name of the uniform * @return the location of the uniform in this program */ public int getUniformLocation(String str) { return glGetUniformLocation(program, str); }
Definir valores uniformes:
Tal como discutido anteriormente na série, usamos glUniform para passar dados uniformes para os nossos shaders. Um utilitário ShaderProgram completa pode incluir diversos utilitários para obter e definir uniformes (veja aqui). Nosso exemplo simples irá lidar com o mínimo de: matrizes e uniformes inteiros (para sampler2D).
/** * Sets the uniform data at the specified location (the uniform type may be int, bool or sampler2D). * @param loc the location of the int/bool/sampler2D uniform * @param i the value to set */ public void setUniformi(int loc, int i) { if (loc==-1) return; glUniform1i(loc, i); } /** * Sends a 4x4 matrix to the shader program. * @param loc the location of the mat4 uniform * @param transposed whether the matrix should be transposed * @param mat the matrix to send */ public void setUniformMatrix(int loc, boolean transposed, Matrix4f mat) { if (loc==-1) return; if (buf16Pool == null) buf16Pool = BufferUtils.createFloatBuffer(16); buf16Pool.clear(); mat.store(buf16Pool); buf16Pool.flip(); glUniformMatrix4(loc, transposed, buf16Pool); }
Código fonte completo:
public class ShaderProgram { protected static FloatBuffer buf16Pool; /** * Makes the "default shader" (0) the active program. In GL 3.1+ core profile, * you may run into glErrors if you try rendering with the default shader. */ public static void unbind() { glUseProgram(0); } public final int program; public final int vertex; public final int fragment; protected String log; public ShaderProgram(String vertexSource, String fragmentSource) throws LWJGLException { this(vertexSource, fragmentSource, null); } /** * Creates a new shader from vertex and fragment source, and with the given * map ofattrib locations * @param vertexShader the vertex shader source string * @param fragmentShader the fragment shader source string * @param attributes a map of attrib locations for GLSL 120 * @throws LWJGLException if the program could not be compiled and linked */ public ShaderProgram(String vertexShader, String fragmentShader, Map<Integer, String> attributes) throws LWJGLException { //compile the String source vertex = compileShader(vertexShader, GL_VERTEX_SHADER); fragment = compileShader(fragmentShader, GL_FRAGMENT_SHADER); //create the program program = glCreateProgram(); //attach the shaders glAttachShader(program, vertex); glAttachShader(program, fragment); //bind the attrib locations for GLSL 120 if (attributes != null) for (Entry<Integer, String> e : attributes.entrySet()) glBindAttribLocation(program, e.getKey(), e.getValue()); //link our program glLinkProgram(program); //grab our info log String infoLog = glGetProgramInfoLog(program, glGetProgrami(program, GL_INFO_LOG_LENGTH)); //if some log exists, append it if (infoLog!=null && infoLog.trim().length()!=0) log += infoLog; //if the link failed, throw some sort of exception if (glGetProgrami(program, GL_LINK_STATUS) == GL_FALSE) throw new LWJGLException( "Failure in linking program. Error log:\n" + infoLog); //detach and delete the shaders which are no longer needed glDetachShader(program, vertex); glDetachShader(program, fragment); glDeleteShader(vertex); glDeleteShader(fragment); } /** Compile the shader source as the given type and return the shader object ID. */ protected int compileShader(String source, int type) throws LWJGLException { //create a shader object int shader = glCreateShader(type); //pass the source string glShaderSource(shader, source); //compile the source glCompileShader(shader); //if info/warnings are found, append it to our shader log String infoLog = glGetShaderInfoLog(shader, glGetShaderi(shader, GL_INFO_LOG_LENGTH)); if (infoLog!=null && infoLog.trim().length()!=0) log += getName(type) +": "+infoLog + "\n"; //if the compiling was unsuccessful, throw an exception if (glGetShaderi(shader, GL_COMPILE_STATUS) == GL_FALSE) throw new LWJGLException("Failure in compiling " + getName(type) + ". Error log:\n" + infoLog); return shader; } protected String getName(int shaderType) { if (shaderType == GL_VERTEX_SHADER) return "GL_VERTEX_SHADER"; if (shaderType == GL_FRAGMENT_SHADER) return "GL_FRAGMENT_SHADER"; else return "shader"; } /** * Make this shader the active program. */ public void use() { glUseProgram(program); } /** * Destroy this shader program. */ public void destroy() { glDeleteProgram(program); } /** * Gets the location of the specified uniform name. * @param str the name of the uniform * @return the location of the uniform in this program */ public int getUniformLocation(String str) { return glGetUniformLocation(program, str); } /* ------ UNIFORM SETTERS/GETTERS ------ */ /** * Sets the uniform data at the specified location (the uniform type may be int, bool or sampler2D). * @param loc the location of the int/bool/sampler2D uniform * @param i the value to set */ public void setUniformi(int loc, int i) { if (loc==-1) return; glUniform1i(loc, i); } /** * Sends a 4x4 matrix to the shader program. * @param loc the location of the mat4 uniform * @param transposed whether the matrix should be transposed * @param mat the matrix to send */ public void setUniformMatrix(int loc, boolean transposed, Matrix4f mat) { if (loc==-1) return; if (buf16Pool == null) buf16Pool = BufferUtils.createFloatBuffer(16); buf16Pool.clear(); mat.store(buf16Pool); buf16Pool.flip(); glUniformMatrix4(loc, transposed, buf16Pool); } }
Fonte: https://github.com/mattdesl/lwjgl-basics/wiki/ShaderProgram-Utility