sábado, 2 de fevereiro de 2013 0 comentários

ShaderProgram Utility


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 of  attrib 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
0 comentários

Batching Sprite


Batching Sprite

Se nós tentamos usarmos o debugTexture do tutorial texturas para processar todos os nossos sprites e tiles, as chances de que haja problemas de desempenho são grandes. Isto é porque estamos apenas colocando um sprite em um momento para a GPU. O que nós precisamos é de "lote" sprites para a melhoria do desempenho, por isso usamos um SpriteBatch.

Intro

Como discutido no tutorial texturas, um sprite é nada mais do que um conjunto de vértices que compõem uma forma retangular. Cada vértice contém um certo número de atributos que a definem, tais como:

Posição (x, y) - onde o vértice encontra-se na tela
TexCoord (s, t) - o que a região da nossa textura deseja processar
Cor (r, g, b, a) - o vértice de cor, utilizado para especificar tingimento ou transparência
Doseadores mais sprites são bastante simples de usar, e pode ser parecido com isso no seu jogo:

//called on game creation
public void create() {
    //create a single batcher we will use throughout our application
    spriteBatch = new SpriteBatch();
}

//called on frame render
public void render() {
    //prepare the batch for rendering
    spriteBatch.begin(); 

    //draw all of our sprites
    spriteBatch.draw(mySprite1, x, y);
    spriteBatch.draw(mySprite2, x, y);
    ...

    //end the batch, flushing the data to GPU
    spriteBatch.end();
}

//called when the display is resized
public void resize(int width, int height) {
    //notify the sprite batcher whenever the screen changes 
    spriteBatch.resize(width, height);
}
Primeiro, begin() o batch, nós simplesmente dizemos que ele está em "modo de desenho" e chamamos o use() em nosso programa de shader. Então, chamando spriteBatch.Draw (...) colocamos as informações da vértice do sprite (posição, texcoord, cor) em uma pilha muito grande. Os vértices não são passados ​​para a GPU até que uma das seguintes situações ocorra:

O batch é forçado a render com o end () ou em outra ligação que libera o batch, como flush ()

O usuário tenta desenhar um sprite que usa uma textura diferente do que o último. O batch tem de ser limpado e novamente  texturizar antes de podermos continuar

Chegamos a capacidade de nossa pilha, por isso precisamos liberar para começar tudo de novo

Esta é a ideia básica por trás de um doseador sprite. Como você pode ver, usando muitas texturas vai levar a muitas chamadas (como o batch terá de lavar para cada nova textura). É por isso que uma textura atlas (folha de sprite AKA) é sempre recomendado, que nos permite prestar muitos sprites em uma chamada de desenho único.


A API LWJGL-básico inclui uma implementação SpriteBatch mínima para se usar


TextureRegion

Como discutido, para o melhor desempenho, deve-se usar um atlas de texturas e desenhar as regiões dele (AKA sub-imagens) para colocar as sprites em nosso jogo. Para isso temos uma classe TextureRegion. Ela nos permite especificar em pixels a posição do eixo (x, y) e seu tamanho (altura, largura) da nossa imagem 

Vamos dar um exemplo, onde queremos tornar nossa Sprite um destaque:





Imagem da Sprite








Podemos ter uma TextureRegion da Sprite com o seguinte:
//specify x, y, width, height of tile
region = new TextureRegion(64, 64, 64, 64);
Como você pode ver, o  TextureRegion nos permite obter sub-imagens sem se preocupar com o cálculo das coordenadas da textura. Podemos, então, desenhar nossa sprite do Batshing Sprite  assim:
... inside SpriteBatch begin / end ...
spriteBatch.draw(region, x, y);

Vertex Color

Nós podemos mudar a coloração e transparência de todos os sprites através da "cor de vértice."

O RGB resultante é multiplicada pela cor, textura, assim se utilizou uma textura branca (1, 1, 1, 1) e uma cor especificada na vértice como (1, 0, 0, 1), o resultado seria o vermelho. O componente alfa da cor nos permite ajustar a opacidade dos sprites desenhados na tela.

spriteBatch.begin();

//draw calls will now use 50% opacity
spriteBatch.setColor(1f, 1f, 1f, 0.5f);
spriteBatch.draw(...);
spriteBatch.draw(...);

//draw calls will now use 100% opacity (default)
spriteBatch.setColor(1f, 1f, 1f, 1f);
spriteBatch.draw(...);

spriteBatch.end();
Na série anterior, pensávamos que nossa texturas era um quadrado por ter quatro pontas, mas na realidade o Sprite Batch vai usar dois triângulos adjacentes para representar um sprite retangular. Os vértices podem ser ordenados de maneira diferente dependendo do motor (No LibGDX tende-se a usar da parte inferior para a esquerdo), mas a ideia básica é assim:














Um única sprite  possui dois triângulos ou seis vértices. Cada vértice tem oito atributos (X, Y, S, T, R, G, B, A), que juntos formam Posição, TexCoord e Cor. Isto significa que a cada sprite, estamos empurrando 48 floats para uma pilha! Um Sprite Batcher mais otimizado pode compactar o RGBA em um único float , ou pode abrir mão de algumas cores da vértice.

E agora?


Criar o seu próprio batcher Sprite não é fácil, e requer a compreensão de shaders, buffers de vértice, e matemática matriz básica. Antes de mergulhar nestes tópicos avançados,  recomendo ficar mais expete com o SpriteBatcher fornecido para você pelo LWJGL-básico. Alternativamente, você pode usar o LibGDX como a implementação, pois ele é muito semelhante. Você também deve se sentir confortável com GLSL antes de tentar a sua própria batcher Sprite.

Se você ainda está interessado em aprender mais sobre como tudo funciona, veja o ShaderProgram e os artigos SpriteBatcher.

Fonte: https://github.com/mattdesl/lwjgl-basics/wiki/Sprite-Batching
 
;