Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Uso correcto de Shaders y alguna duda mas...

Iniciado por nsL, 15 de Septiembre de 2011, 01:07:15 PM

« anterior - próximo »

nsL

Buenas,

Como ya he puesto en varios post, estoy empezando con OpenGL ES 2.0 y son nuevos para mi todos los conceptos de shaders, y demas, y tengo alguna duda respecto. A ver si me podeis ayudar.

Duda 1
Mas o menos tengo clara la funcion tanto del vertex shader como del fragment shader. Pongo los pasos en Android para el uso de los mismos, y luego os planeteo mi duda:

int vertex_shader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);  
GLES20.glShaderSource(vertex_shader , vrtxShaderCode);
GLES20.glCompileShader(vertex_shader );

int fragment_shader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);  
GLES20.glShaderSource(fragment_shader , frgmntShaderCode);
GLES20.glCompileShader(fragment_shader );

mProgram = GLES20.glCreateProgram();            
GLES20.glAttachShader(mProgram, vertexShader);  
GLES20.glAttachShader(mProgram, fragmentShader);
GLES20.glLinkProgram(mProgram);

maPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

Pues bien, esos pasos, los hace en una función de carga previa al pintado de la escena. Luego ya en la tipica funcion loop de pintado antes de dibujar el polígono, usa esto:

GLES20.glUseProgram(mProgram);

Entiendo todo el código, por ahi no van mis dudas. El caso es, si yo tengo varios Shaders, ¿lo normal es cargarlos todos y luego en funcion a que "objeto" voy a pintar hacer un glUseProgram de ese Shader? ¿Hay limite de shaders a tener cargados? No se si van cargados en la GPU o que, y eso pueda conllevar en problemas. Vamos, que mi duda es cual es el uso real que se le da a los shaders, porque básicamente los ejemplos que veo son para 1 shader (de cada) y pintar 1 poligono, y no se como se manejaría todo en una situacion con varios Shaders, con diferentes elementos usando cada uno de ellos.

Me estoy haciendo unas clases (Shader y ShaderProgram) para manejar todo este tipo de elementos. En la primera cargo, compilo y demás el shader, y en la segunda asocio 2 Shader (vertex & fragment) , creo el programa y obtengo los handle a los atributos/uniforms.

Luego por otro lado tengo una clase Drawable, de la que heredan los elementos dibujables. Dicha clase tiene asociado un ShaderProgram. De esta manera cada vez que lanzo un draw() de la clase Drawable (o sus heredadas), su ShaderProgram hace un glUseProgram.

Con esto estoy haciendo un glUseProgram por cada entidad que dibujo, lo cual no se si es muy eficiente. En fin, si alguien puede explicarme cual es el uso tipico que se le suele dar a esto....

Por otro lado, no se que uso se le da a los shaders (supongo que por desconocimiento aun, que estoy aprendiendo), mas alla de especificar posicion y color/transparencia. Ni que efectos puedo conseguir, o que sean aprovechables en 2D, que es lo que yo estoy usando.

Y ahora, la segunda duda.

Duda 2

Es probable que esta duda sea de tarjeta roja, porque incluso yo me he extrañado de como no tengo claro aun esto, pero aun así la pregunto.

Suponed un vertex shader asi:

   private final String vertexShaderCode =
        "uniform mat4 uMVPMatrix;   \n" +
        "attribute vec4 vPosition;  \n" +
        "void main(){               \n" +
        " gl_Position = uMVPMatrix * vPosition; \n" +
       "}  \n";

En los ejemplos que yo veo, obtienen un handle de uMVPMatrix por una parte y otro handle a vPosition por otro lado. Por otra parte aplican las transformaciones necesarias (rotaciones, escalados, etc) multiplicados por la matrix de cámara y por la de la proyeccion (como siempre). Al final lo que se tiene es en uMVPMatrix la matriz de las transformaciones ya calculada, y en vPosition el vector por cada vertice del poligono. Con ello conseguimos en gl_Position la posicion transformada de dicho vértice en coordenadas de la proyeccion.

Hasta ahi bien. Pero ayer me surgio la duda a la hora de mover un cuadrado por la pantalla. Y es que si el movimiento de dicho cuadrado lo baso en transformaciones del tipo glTranslate, al final lo que tengo es que en uMVPMatrix tengo transformaciones acumuladas, que van haciendo que vPosition vaya cambiando de posicion (en funcion a los parametros que le pase al glTranslate por asi decirlo), pero en vPosition siempre tendre las coordenadas iniciales, porque esas no se van actualizando. Es decir, si al cabo de 1 minuto de ver mi maravilloso cuadrado dar vueltas por la pantalla, decido ver sus coordenadas para comprobar una colision por ejemplo, tendre las coordenadas iniciales, porq en ningun momento he cambiado directamente las coordenadas de los vertices, sino que les he aplicado una transformacion en el shader para que se vea reflejada simplemente.

Entonces he pensado, bueno, pues cuando quiera mover el cuadrado, actualizo la matriz de sus vertices con el desplazamiento, y la paso al shader, siendo en este caso vPosition el vector del vertice con las coordenadas ya transformadas, y en uMVPMatrix la matriz de camara y de proyeccion (ya multiplicadas) para transofrmar dichas coordenadas a las propias de la proyeccion.

Con este método siempre tengo las coordenadas actualizadas de donde esta el objeto, pero por contra estoy cargando a la CPU de operaciones (por entiendo que las operaciones del shader se hacen en GPU). Ademas de que cada vez que pinto el objeto, tengo que crear un buffer para asociarlo con vPosition. En el primer caso, como las coordenadas locales nunca cambiaban, creaba el buffer 1 vez, y siempre usaba el mismo, en la segunda opcion, como cambio las coordenadas, tengo que crear un buffer cada vez para pasarlo al shader, lo cual debe ser costoso. El buffer lo creo asi:

       ByteBuffer vbb = ByteBuffer.allocateDirect( squareCoords.length * 4);
       vbb.order(ByteOrder.nativeOrder());
       squareVB = vbb.asFloatBuffer();
       squareVB .put(squareCoords);  
       squareVB .position(0);

Siento si es algo confuso, me explico muy mal a veces. Y siento el mega post (no queria abrir 2 post para temas tan relacionados), que a mas de 1 le dara pereza leer :P

Muchas gracias de antemano cracks :)

un saludin
Yo no muero hasta la muerte -

nostromo

Holas,

Sobre lo último que comentas creo que no es buena idea leer demasiadas cosas de la memoria de la GPU. El ancho de banda de las conexiones CPU/GPU es limitado y leyendo datos saturas esta conexión, es una cuestión de rendimiento.

Para un juego, yo creo que puedes estar tranquilo manteniendo las coordenadas de mundo de los objetos en la memoria principal. Cuantas coordenadas(vertices o el centro del objeto) necesitas por objeto pues ya depende de como implementes colisiones/fisica y todo eso.
Date cuenta que la matriz MVP por objeto contiene la translación del objeto, y conseguir las coordenadas transladadas finales para un objeto es una suma de vectores, es una operación muy rapida.

Por otra parte piensa que lo que le envias a la GPU es lo que se va a ver por la pantalla. Es decir, habrán muchos objetos que no los mandarás y, sin embargo, estos objetos que no envias a la GPU tendrán su vida propia, sus coordenadas, etc...

También decir que existen casos en que la información del estado/posición de los objetos si que puedes mantenerla en la GPU, y actualizarla en la GPU(shader) como es el caso de un sistema de particulas que no quieres que interaccione con el entorno.

No soy un experto en OpenGL ES 2.0, no te tomes lo que diga como una verdad absoluta.  ;)

Saludos,











Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.