Saludos, buena gente de Stratos ;) En este documento se explicarán los pormenores de este pequeño programa, pero antes un aviso: no me hago responsable de los problemas o daños que pueda ocasionar el ejemplo, aunque ciertamente seria raro que produjese alguno. Bueno, ya estais avisados... Tambien decir que el ejemplo es con Direct 3D y que se necesita el SDK de Direct X 9 para compilar el ejemplo y tener los runtime del mismo para ejecutarlo.
El programa es muy simple: pone en pantalla 10 sprites que van botando como una pelota por la pantalla mientras rotan, cambian su nivel de transparencia y su color. Nada en especial. Esto se podria conseguir perfectamente con tecnicas “tradicionales”. Por ejemplo, bloqueando el vertex buffer, cambiando el componente diffuse y cambiando la matriz de mundo. El ejemplo evita todo esto usando un pequeño vertex shader muy simple.
Pero empecemos por el principio: ¿qué es un vertex shader? Un vertex shader es un programa que ejecutara la GPU (basicamente el procesador de la tarjeta) y no la CPU. Asi, las operaciones que se aplican a los vértices se ejecutan en la GPU, librando a la CPU de la tarea.
No todas las tarjetas gráficas pueden ejecutar los vertex shaders (se supone que de Geforce 2 en adelante todas las aceleran...), pero no hay que preocuparse: la CPU puede ejecutarlas, y con un rendimiento “sorprendentemente alto”, según el SDK de DirectX.
Los vertex shaders funcionan cogiendo vértices que les llegan de uno o varios streams, los modifican y luego los pasan al rasterizador. Pueden modificar los diferentes componentes, como la posicion, las coordenadas de textura, etc. Vamos, que se pueden hacer todo tipo de guarreridas :)
¿Y como se programan los vertex shaders? Bien, hay 2 opciones: escribir el programa en el codigo ensamblador de Direct 3D, como hacen los ejemplos de vertex shader y dolphinVS del SDK, o bien usar el nuevo High Level Shader Language (HLSL) que trae DirectX 9. En nuestro ejemplo usaremos la primera opción, ya que no tengo ni idea de cómo hacerlo con el segundo. Si alguien me lo muestra le estare muy agradecido.
Como se ha dicho antes, nuestro vertex shader sera muy simple: lo único que hará será tomar lo que le viene de entrada por el stream y transformarlo con las matrices que le pasamos y también enchufarle el componente diffuse. El código es este:
;-------------------------------------------------------------
; Constantes:
; c0-c3: Matriz de Transformacion + Vista + Proyeccion
; c4-c7: Matriz con la transformar las coordenadas de textura
; c8: El componente diffuse
;-------------------------------------------------------------
vs.1.1
dcl_position v0 ; posicion
del vertice
dcl_texcoord0 v1 ; coordenadas de textura del vertice
m4x4 oPos, v0, c0 ; transformamos el vertice
m4x4 oT0, v1, c4 ; transformamos las coordenadas de textura
mov oD0, c8 ; fijamos el componente diffuse
La primera linea indica la versión del vertex shader. Aquí usamos la 1.1, osea lo más básico. Las siguientes 2 lineas indican en que registros iran la posición y las coordenadas de textura del vértice. Así, en el registro v0 tendremos la posición y en v1 las coordenadas de textura, obviamente. Hay que decir que al programar un vertex shader, tenemos 4 tipos de registros: los del stream o flujo, que son los vx. En estos registros tendremos los datos de los vertex buffers que activamos con la función SetStreamSource. Luego están los registros de constantes, que son los cx. Aquí tendremos los valores que le pasamos mediante las funciones SetVertexShaderConstant*. Como se ve en el ejemplo, a este VS le pasamos 3 datos: una matriz con la que transformaremos la posición del vértice (con el comando m4x4 oPos, v0, c0), otra matriz con la transformaremos las coordenadas de textura del vértice (m4x4 oT0, v1, c4) y por último, en el registro c8 tenemos el componente diffuse que simplemente lo añadiremos al vértice de salida (con el comando mov oD0, c8). Estos registros se actualizaran cada frame, pues cada sprite fijará sus propios valores.
En tercer lugar tenemos los registros temporales, que son los rx. Aquí no los usamos, ya que no nos hacen falta. Por último tenemos los registros de salida, que son los oxx. Es en estos registros donde dejaremos los resultados de nuestros cálculos. El funcionamiento de un VS será coger los datos de los registros vx y cx, realizar los cálculos pertinentes apoyandose en los registros rx y escribir el resultado final en los registros oxx. Para saber todos los detalles sobre los registros (el número minimo de los rx y cosas asi) consultad el SDK, como hacen los hombres, joer XDD
El VS ya no tiene secretos, pero antes hay que decir una cosa: la instrucción m4x4 NECESITA matrices TRANSPUESTAS. Ya se que insisto mucho en esto, pero es uno de esos puñeteros errores tontos que te llevan 3 horas encontrarlo...
En fin, creo que con esto ya es suficiente para entender el ejemplo. El código esta ampliamente comentado en las partes que hacen falta, asi que ya no queda mas que decir, excepto que podeis escribirme a gammenon@msn.com para cualquier comentario, duda o lo que sea (spam no :P). Espero que este pequeño ejemplo os sea util :)