Gráficos 3D para Wii

Page 1

Gráficos 3D de Wii

Gráficos 3D para Wii la pequeña bíblia sobre gráficos basada en las librerias homebrew

por Francisco Muñoz 'Hermes'

Versión 1.0

© 2008 Permitida la reproducción y distribución de éste documento o partes de él citando la fuente. Esta información no tiene nada que ver con Nintendo ni el autor está vinculado de forma alguna con Nintendo o cualquier otra empresa y es proporcionada por libre, sin responsabilidad alguna por parte del autor


Gráficos 3D de Wii Capítulos de Gráficos 3D de Wii Introducción -Framebuffers -Doble Buffer -Buffer Z -Stencil Buffer -Componentes de color -Iniciando el Video en Wii: con código de ejemplo Introducción a las 3D -La proyeccion de graficos en 3D -Construcción de objetos -Elementos de un vértice (atributos) Polígonos -Tipos de primitiva -Vértice directo y vértice indexado -GX_SetArray() -El descriptor de vértices -GX_ClearVtxDesc() -GX_SetVtxDesc() -El descriptor de formato de vértice -GX_SetVtxAttrFmt() -Dibujado de polígonos -GX_Begin() -GX_End() -Funciones para fijar atributos al vértice -Posición -Normal -Color -Coordenadas de textura -Ejemplo de dibujo de polígonos


Gráficos 3D de Wii

Vectores y Matrices -Matriz de proyección -Matriz de vista -Matriz Mundial -Aplicación de matrices en Wii -Configurando la proyección para gráficos 2D -Matrices de Normal y de Textura -Proyección desde fuente de luz : Capturar y aplicar sombras Iluminación -Luz Ambiental -Luz Difusa -Luz Especular El sistema de iluminación de Wii -Luz Posicional -Definición del objeto de una luz posicional -Luz Especular -Definición del objeto de una luz especular -Luz de Punto (Spot) -Definición del objeto de Luz Spot Transparencia y Translucidez -Activar la translucidez -Desactivar translucidez -Activar transparencia -Desactivar transparencia


Gr谩ficos 3D de Wii Texturas -Envoltura -Filtros -Texturas formando tiles -tiling4x4(): funci贸n de tileado de texturas -ctlut(): funci贸n de conversi贸n de colores de paleta -Cargando una textura en Wii -Cargando Texturas con Paleta -Capturar Textura Desde el Framebuffer Texture Environment (TEV) -El TEV desde un punto de vista sencillo -GX_SetTevOrder() -GX_SetTevOp() -El TEV desde un punto de vista menos sencillo -Operandos TEV de Color y Alfa -GX_SetTevColorIn() -GX_SetTevAlphaIn() -Operaciones TEV de Color y Alfa -GX_SetTevColorOp() -GX_SetTevAlphaOp() -Cargando los registros de color del TEV -GX_SetTevColor() -GX_SetTevColorS10() -Las Constantes del TEV -GX_SetTevKColorSel() -GX_SetTevKAlphaSel() -GX_SetTevKColor() -GX_SetTevKColorS10()


Gr谩ficos 3D de Wii Funciones de gu.h -guPerspective() -guOrtho() -guLightFrustum() -guLookAt() -guVecNormalize() -guVecCross(): calcular la normal a un tri谩ngulo -guMtxIdentity() -guMtxConcat() -guMtxScale() -guMtxScaleApply() -guMtxTrans(): nota: esta funci贸n tiene un bug llamar antes -guMtxTransApply() -guMtxRotRad() -guMtxRotAxisRad() -guMtxInverse() -guMtxTranspose() -guMtxCopy() -guVecMultiply() -guVecAdd() -guVecSub() Funciones de gx.h extras -GX_Flush() -GX_DrawDone() -GX_SetZMode() -GX_SetCullMode() -GX_SetClipMode() -GX_CallDispList() Conclusi贸n y Agradecimientos

a guMtxIdentity


Gráficos 3D de Wii

Introducción Los gráficos 3D están de moda, pero no todo el mundo sabe como trabajar con ellos. Mi idea es enfocar éste documento para que aquellos de vosotros, profanos en la materia totales, entendáis todo el mecanismo que lleva a representar gráficos en pantalla, de una forma mas o menos simple, mas o menos exacta, pero sobre todo, busco que la información que os proporcione, sea lo suficientemente clara para que podáis asimilarla rápidamente. Luego, nos meteremos en tareas mas complejas, pero sin la base, nada de eso resultaría útil. Framebuffers. Los framebuffers podríamos definirlos como una región de memoria RAM o VRAM que codifican el color de los píxeles de pantalla en sus octetos , con un determinado formato (por ejemplo RGB8, donde se usarían 3 bytes por píxel y un byte para especificar la cantidad de color Rojo, Verde y Azul, colores básicos desde los que se puede obtener por combinación, hasta 16.777.216 colores, en éste caso). Así pues, un framebuffer de éste tipo de 640 x 480 píxeles, tendría un tamaño de 640x480x3 bytes básicamente (puede variar algo por temas de alineación de memoria) Doble Buffer. Por regla general, se suelen usar dos framebuffers que se intercambian de forma que uno siempre es visible (front buffer), mientras que el otro es donde trabajamos realmente (llamado back buffer). La idea de todo esto, es evitar que durante el retrazado vertical de la pantalla, estemos pintando sobre ella, lo que produce molestos efectos de parpadeo o desaparición de gráficos. Wii es un tanto especial, puesto que el front buffer con el que trabaja el usuario, no es realmente un front buffer al uso, si no que debe ser copiado al front buffer real, que trabaja en formato YUV (ver función copy_buffers() en 3dtemplate) Buffer Z. Es un buffer especial que se utiliza en 3D para ordenar los píxeles en base a la profundidad, de forma que siempre veremos el píxel mas cercano al observador. Puede tener distintos tamaños, como 8, 16, 24 bits. Lo normal, será usarlo a 24 bits en Wii.


Gráficos 3D de Wii

Stencil Buffer. O buffer de estarcido. Es un buffer especial que se utiliza para delimitar determinadas zonas de la imagen con un valor de referencia que luego puede ser usado para iluminar o sombrear objetos, u otros propósitos. Wii no tiene dicha posibilidad, pero se puede simular pintando los objetos de forma previa con un color, capturando el framebuffer como textura y posteriormente aplicarla operando mediante el Texture Environment (TEV). En el fondo, no es mucho más complicado que el uso de un stencil buffer normal. Componentes de color. Normalmente, son 3 (RGB) o 4 (RGBA) Son las siglas de Red (Rojo), Green (Verde), Blue (Azul) y Alpha (Alfa) y en el caso de los tres primeros, marcan los niveles de color para obtener el color definitivo, mientras que Alfa se usa de forma mas común para efectos de translucidez o brillo. Conviene tener en cuenta que la CPU de Wii es 'big endian' y que por tanto, se invierte el orden de los colores y donde en formato RGBA8 0xff00ff00, normalmente, sería Verde y Alfa al máximo, al copiarlo en memoria se interpretará como Rojo y Azul al máximo si esto lo pasaramos como un u32 (de ahí que en las GX se usen definiciones como GXColor verde={0,0xff,0,ff}; // RGBA En Wii son comunes valores como: RGBA6: donde cada componente ocupa 6 bits con valores de 0 a 63 y un total de 24bits RGB8: 8 bits por componente, con valores de 0 a 255 RGBA8: lo mismo que RGB8 pero incluyendo Alfa RGB565: 16 bits de color donde Rojo y Azul usan 5 bits (valor de 0 a 31) pero el Verde usa 6 bits (valor de 0 a 63) RGB5A3: 16 bits y funcionamiento especial donde el bit de mas peso, decide si se trata de RGBA5551 o RGBA4443


Gr谩ficos 3D de Wii Iniciando el Video en Wii Variables globales #include <ogcsys.h> #include <gccore.h> static GXRModeObj

*screenMode;

static void *frameBuffer[2] = {NULL,NULL}; int frame=0; #define FIFO_SIZE (256*1024) static vu8 readyForCopy=FALSE; void copy_buffers(u32 count);

Inicio del video, asignaci贸n de framebuffers, FIFO GXColor backgroundColor = {0x0, 0x0, 0x0,255}; // negro void *fifoBuffer = NULL; // Inicializa el sistema de video VIDEO_Init(); // Inicializa la pantalla para doble buffer screenMode = VIDEO_GetPreferredMode (NULL); frameBuffer[0] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(screenMode)); frameBuffer[1] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(screenMode)); VIDEO_Configure(screenMode); VIDEO_SetNextFramebuffer(frameBuffer[frame]); VIDEO_SetPostRetraceCallback(copy_buffers); VIDEO_SetBlack(FALSE); VIDEO_Flush(); // asigna la memoria para el FIFO fifoBuffer = MEM_K0_TO_K1(memalign(32,FIFO_SIZE)); memset(fifoBuffer,0, FIFO_SIZE); GX_Init(fifoBuffer, FIFO_SIZE);


Gr谩ficos 3D de Wii Ajusta el formato de video, el viewport , datos de copia del framebuffer, etc // formato RGB y Buffer Z a 24 bits GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR); // asigna los valores de borrado de pantalla (color) y de relleno de buffer Z GX_SetCopyClear(backgroundColor, 0x00ffffff); GX_SetViewport(0,0,screenMode->fbWidth,screenMode->efbHeight,0.0f,1.0f); GX_SetDispCopyYScale((f32)screenMode->xfbHeight/(f32)screenMode->efbHeight); GX_SetScissor(0,0,screenMode->fbWidth,screenMode->efbHeight); GX_SetDispCopySrc(0,0,screenMode->fbWidth,screenMode->efbHeight); GX_SetDispCopyDst(screenMode->fbWidth,screenMode->xfbHeight); GX_SetCopyFilter(screenMode->aa,screenMode->sample_pattern, GX_TRUE,screenMode->vfilter); GX_SetFieldMode(screenMode->field_rendering, ((screenMode->viHeight==2*screenMode->xfbHeight)? GX_ENABLE:GX_DISABLE)); GX_SetCullMode(GX_CULL_NONE); GX_CopyDisp(frameBuffer[frame],GX_TRUE); GX_SetDispCopyGamma(GX_GM_1_0);

Funci贸n de copia de framebuffer void copy_buffers(u32 count __attribute__ ((unused))) { if (readyForCopy==GX_TRUE) { GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE); GX_SetColorUpdate(GX_TRUE); GX_CopyDisp(frameBuffer[frame],GX_TRUE); frame^=1; GX_Flush(); VIDEO_SetNextFramebuffer(frameBuffer[frame]); VIDEO_Flush(); readyForCopy = GX_FALSE; } }


Gráficos 3D de Wii Bucle típico Mtx44 projection; guPerspective(projection, 60, 1.3333f, 1.0F, 10000.0F); GX_LoadProjectionMtx(projection, GX_PERSPECTIVE); while(1) { Mtx World; GX_InvVtxCache(); GX_InvalidateTexAll(); /* aquí sigues tú, fijando la matriz mundial y dibujando tus objetos (esto es solo un ejemplo)*/ guMtxIdentity(World); GX_LoadPosMtxImm(World, GX_PNMTX0); // carga la matriz mundial GX_SetCurrentMtx(GX_PNMTX0); // selecciona la matriz // aqui ajustamos la iluminacion en Off a un solo canal y para que tome el color de los vertices GX_SetNumChans(1); GX_SetChanCtrl(GX_COLOR0A0,GX_DISABLE, GX_SRC_REG, GX_SRC_VTX, GX_LIGHTNULL, GX_DF_CLAMP, GX_AF_NONE ); // numero de texturas a 0 GX_SetNumTexGens(0); /* ajusta el texture environment a 1 y selecciona para que use el color proporcionado en los vertices */ GX_SetNumTevStages(1); GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORDNULL, GX_TEXMAP_NULL, GX_COLOR0A0); GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR); /* dibuja aquí polígonos que no usen texturas */ /* fin del frame */ GX_DrawDone(); readyForCopy = GX_TRUE; // Wait for the next frame VIDEO_WaitVSync(); }


Gráficos 3D de Wii

Introducción a las 3D -La proyeccion de graficos en 3D. Los gráficos 3D, se representan por un sistemas de coordenadas basados en los ejes X, Y y Z, siendo la posición central (0.0, 0.0, 0.0) en números de coma flotante. El eje X representa en pantalla la posición horizontal de un punto, tomando un valor positivo si el punto está a la derecha y negativo si se encuentra a la izquierda. El eje Y representa la posición vertical, tomando valor positivo si el punto está arriba y negativo si está abajo. El eje Z representa si un punto está cerca o lejos en la pantalla; dependiendo del sistema de proyección empleado puede ser que un valor negativo creciente, represente lejanía, mientras que un valor positivo quedaría detrás de nuestro punto de observación. El sistema de proyección puede ser en perspectiva u ortográfico: la diferencia principal entre ellos, es que en el primero, los objetos disminuyen/aumentan su tamaño con la distancia, mientras que el modelo ortográfico mantiene las dimensiones y por tanto, tiene una relación mas directa con el viewport (ventana al framebuffer donde se dibujarán nuestros gráficos), aunque los limites sean del tipo -1.0f a +1.0f en vez de estar basados en la posición en píxeles. - Construcción de objetos. Los objetos se construyen mediante triángulos o quads (superficie descrita con 4 puntos) y pueden ser generados matemáticamente utilizando una posición de referencia que sirve de centro y alrededor del cual se construye la malla que representa la 'piel' del objeto. Además de la posición, a un vértice se le puede especificar un color, la normal al plano o coordenadas de textura. Es importante observar ciertas reglas, ya que si el polígono define sus vértices en sentido horario o anti horario con respecto a un punto de observación, si se activa el culling (función GX_SetCullMode() en Wii) el polígono sería visible o no y también varía la dirección a la que apuntaría la normal del objeto si la


Gráficos 3D de Wii calculáramos partiendo de dichos vértices. -Elementos de un vértice (atributos): - vector posición : posición X,Y, Z para representacion espacial - Vector normal: Es un vector 3D (X,Y,Z) de tipo unitario que representa la normal al plano. Empleado para aplicar iluminación difusa o especular. - Color: Color del vértice. Puede ser reemplazado por el color del 'material', por lo que no haría falta especificarlo en ese caso. - Coordenadas de textura: especificadas como un vector bidimensional (s,t) o (u,v) y valores ( que representan un punto a la textura o texel (pixel de textura))


Gráficos 3D de Wii Polígonos Tipos de primitiva En Wii se pueden dibujar los siguientes tipos de primitiva: GX_POINTS: Lista de puntos sueltos. GX_LINES: Lista de líneas. Usa dos vértices por linea. GX_LINESTRIP: Lista de líneas concatenadas donde las primera línea usa dos vértices y las siguientes usan el vértice previo y el que se especifique nuevo para formar la línea GX_TRIANGLES: Lista de triángulos. Tres vértices por triángulo. GX_TRIANGLESTRIP: Lista de triángulos enlazados de forma que el primero utiliza tres vértices pero los siguientes utilizan los dos últimos vértices del triángulo anterior más el nuevo especificado. Ideal para objetos tipo torno GX_TRIANGLEFAN: Lista de triángulos donde el primer vértice se toma como vértice central y se especifican dos vértices nuevos por triángulo. Ideal para hacer superficies de disco o cónicas. GX_QUADS: Lista de superficies de cuatro puntos. Ideal para sprites y superficies rectangulares o trapezoidales, pero tiene el problema de que desde un punto de vista 3D, uno de los 4 vértices puede quedar fuera de plano.


Gráficos 3D de Wii Vértice directo y vértice indexado Existen dos formas de especificar vértices, el modo directo y mediante una lista indexada para cada uno de los atributos del vértice : Posición, Normal, Color, etc. El modo directo se describe más tarde, mientras que para las listas indexadas se utiliza la siguiente función:

void GX_SetArray(u32 attr, void *ptr, u8 stride); attr→ Puede ser uno desde GX_VA_POS a GX_LITMTXARRAY. Típicamente GX_VA_POS, GX_VA_NRM, GX_VA_CLR0...

ptr→ dirección de memoria alineada a 32 bytes con el contenido. Es importante usar DCFlushRange(ptr, size) antes de utilizar el indexado. Se puede indexar con 8 o 16 bits, lo que daría hasta un total de 65536 elementos

stride→ 8 bits para especificar el tamaño en bytes del contenido del dato de la tabla. Si la tabla contiene posiciones X,Y,Z con un tamaño de dato s16, stride sería 3*sizeof(s16)


Gráficos 3D de Wii

El descriptor de vértices El descriptor de vértices son una serie de registros mediante los cuales se le dice a la GPU que debe esperar en cada vértice. Es muy importante especificar todos los atributos de vértice que estén descritos aquí. funciones: void GX_ClearVtxDesc(); Esta funcion borra el descriptor y se debe llamar cuando no conocemos que atributos están activados para limpiarlo de "basura"

void GX_SetVtxDesc(u8 attr,u8 type); attr→ Típicamente: GX_VA_POS para posición del vértice GX_VA_NRM para especificar normal GX_VA_CLR0, GX_VA_CLR1 para especificar color GX_VA_TEX0 a GX_VA_TEX7 para especificar coordenada de textura. Conviene que sepas que los datos en las listas se suministran en ese orden que muestro, saltando los atributos que estén desactivados (GX_ClearVtxDesc() los desactiva todos)

type→ GX_NONE para desactivar éste atributo GX_DIRECT para indicar que recibe dato directo GX_INDEX8 para indicar que el dato está indexado en una tabla de hasta 256 elementos GX_INDEX16 para indicar que el dato está indexado en una tabla de hasta 65536 elementos


Gráficos 3D de Wii El descriptor de formato de vértice Además de decirle que tipo de datos acompañan a la lista de vértices, hay que especificarle obviamente, el formato del dato Eso se hace con la siguiente función: GX_SetVtxAttrFmt(u8 vtxfmt,u32 vtxattr,u32 comptype,u32 compsize,u32 frac); vtxfmt→ índice a la lista de formatos de vértices, usualmente GX_VTXFMT0 aunque puede ser desde GX_VTXFMT0 a GX_VTXFMT7, con el fin de poder seleccionar distintas combinaciones de formato de dato sin tener que recargarla de continuo

vtxattr→ elemento al que se aplica el formato desde GX_VA_POS a GX_VA_TEX7 usualmente comptype→ GX_POS_XY,GX_POS_XYZ,GX_NRM_XYZ, GX_NRM_NBT, GX_NRM_NBT3, GX_CLR_RGB, GX_CLR_RGBA, GX_TEX_S, GX_TEX_ST como tipos (evidentemente, cada tipo va con su atributo) compsize→ tamaño del dato: GX_U8, GX_S8, GX_U16, GX_S16, GX_F32 para colores se usan estos: GX_RGB565, GX_RGB8, GX_RGBX8, GX_RGBA4, GX_RGBA6, GX_RGBA8 frac→ Normalmente 0. Este dato es un desplazamiento a la derecha que se puede aplicar a los datos de posición, textura, normales... de forma que si especificamos 8, el valor del dato será dividido entre 256 (dato>>8). Ciertos datos como la texturas, trabajan con números muy pequeños donde 0.0 sería un extremo y 1.0 el otro. Asi por ejemplo, si especificáramos frac=10, 1024 en un entero representaría 1.0 (y 0 , 0.0 como es obvio)

Algunos ejemplos: GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_S16, 2); GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_NRM, GX_NRM_XYZ, GX_F32, 0);


Gráficos 3D de Wii Dibujado de polígonos Todo empieza en GX_Begin() una lista de atributos de vértices y por último GX_End(); void GX_Begin(u8 primitve, u8 vtxfmt, u16 vtxcnt); primitve → No, no se refieren a la TVE primitiva, si no a una de las posibles primitivas desde GX_POINT, pasando por GX_TRIANGLES, hasta GX_QUADS

vtxfmt → selecciona la lista de formatos. Usualmente GX_VTXFMT0 vtxcnt → número de vértices totales. Recuerda que deben ser tres por cada triángulo en caso de ser GX_TRIANGLES, o 4 por cada QUAD y que debe haber la cantidad exacta, con el número de atributos por vértice (que estén activos) correcto como posición, normal, color... lo que proceda.

void GX_End(); Técnicamente, es una función decorativa que indica que ya hemos terminado de hacer el gili... digo de enviar vértices (al menos de forma visual, porque internamente, no hace nada, como digo) Funciones para fijar atributos al vértice Posición void GX_Position3f32(f32 x,f32 y,f32 z); //X,Y y Z como flotantes void GX_Position3u16(u16 x,u16 y,u16 z); // X,Y,Z como enteros U16 void GX_Position3s16(s16 x,s16 y,s16 z); // X,Y,Z como enteros S16 (con signo) void GX_Position3u8(u8 x,u8 y,u8 z); void GX_Position3s8(s8 x,s8 y,s8 z); // X,Y,Z como enteros S8 (con signo) void GX_Position2f32(f32 x,f32 y); // Versiones para 2D: mandan X,Y void GX_Position2u16(u16 x,u16 y); void GX_Position2s16(s16 x,s16 y); void GX_Position2u8(u8 x,u8 y); void GX_Position2s8(s8 x,s8 y); // X e Y como signo de 8 bits void GX_Position1x8(u8 index); // Utiliza un índice de 8 bits sobre el Array de posiciones void GX_Position1x16(u16 index); // Utiliza un índice de 16 bits sobre el Array de posiciones


Gráficos 3D de Wii Normal void GX_Normal3f32(f32 nx,f32 ny,f32 nz); // x,y,z como floats void GX_Normal3s16(s16 nx,s16 ny,s16 nz); // x,yz como enteros: fijar frac en GX_SetVtxAttrFmt void GX_Normal3s8(s8 nx,s8 ny,s8 nz); void GX_Normal1x8(u8 index); // Utiliza un índice de 8 bits sobre el Array de normales void GX_Normal1x16(u16 index); // Utiliza un índice de 16 bits sobre el Array de normales

Color void GX_Color4u8(u8 r,u8 g,u8 b,u8 a); // color R, G, B, A 8bits void GX_Color3u8(u8 r,u8 g,u8 b); // color R, G, B 8 bits void GX_Color3f32(f32 r, f32 g, f32 b); // color R,G, B como floats (0.0 a 1.0) void GX_Color1u32(u32 clr); // color RGBA (A menos peso) void GX_Color1u16(u16 clr); // color 16 bits RGB565 o RBG5A3 void GX_Color1x8(u8 index); // Utiliza un índice de 8 bits sobre el Array de colores void GX_Color1x16(u16 index); // Utiliza un índice de 16 bits sobre el Array de colores

Coordenadas de textura void GX_TexCoord2f32(f32 s,f32 t); // coordenadas S,T, como floats void GX_TexCoord2u16(u16 s,u16 t); // S,T como enteros u16: fijar frac en GX_SetVtxAttrFmt void GX_TexCoord2s16(s16 s,s16 t); void GX_TexCoord2u8(u8 s,u8 t); // S,T como enteros u8: fijar frac en GX_SetVtxAttrFmt void GX_TexCoord2s8(s8 s,s8 t); void GX_TexCoord1f32(f32 s); // texturas 1D. Elemento S como float void GX_TexCoord1u16(u16 s); void GX_TexCoord1s16(s16 s); void GX_TexCoord1u8(u8 s); void GX_TexCoord1s8(s8 s); void GX_TexCoord1x8(u8 index); // Utiliza un índice de 8 bits sobre el Array void GX_TexCoord1x16(u16 index); // Utiliza un índice de 16 bits sobre el Array


Gráficos 3D de Wii Ejemplo de dibujo de polígonos GX_ClearVtxDesc(); GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ,GX_F32, 0); GX_SetVtxDesc(GX_VA_CLR0,GX_DIRECT); GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); // dibuja un QUAD (4 vértices) GX_Begin(GX_QUADS, GX_VTXFMT0, 4); // observa el orden de los vértices GX_Position3f32(-1.0f, 1.0f, -1.0f); // Arriba-izquierda GX_Color1u32(0xff0000ff); // Rojo GX_Position3f32(1.0f, 1.0f, -1.0f); // Arriba-derecha GX_Color1u32(0x00ff00ff); // Verde GX_Position3f32(1.0f, -1.0f, -1.0f); // Abajo-derecha GX_Color1u32(0x0000ffff); // Azul GX_Position3f32(-1.0f, -1.0f, -1.0f); // Abajo-Izquierda GX_Color1u32(0xffffffff); // blanco GX_End();

Ejemplos tienes algunos mas en Devkitpro y otros fuentes publicados (como los que acompañan a este documento), pero yo me he centrado mas en cosas especiales como sprites y mapeado de sombra y reflexion, que considero mas utiles tanto para los que controlan menos (o no se quieren complicar), como para los que ya estais mas avanzados (que seguramente encontreis mas provechoso el uso de las luces y los mapeados de textura éste tipo, asi como el uso mas directo del TEV en mi ejemplo.


Gráficos 3D de Wii

Vectores y Matrices Los vectores se utilizan para posicionar o direccionar (en éste caso, se usan vectores unitarios o 'normalizado'). Las matrices se emplean para proyectar, posicionar, escalar o rotar los vértices. Las matrices poseen una característica que hace que si se multiplican, se 'hereden' los cambios de forma que un objeto referenciado a (0,0,0) pueda ser rotado, posicionado o escalado aplicando los diferentes cambios en forma de matrices que se concatenan para obtener una matriz llamada 'mundial' que será la que finalmente, posicionará el objeto (polígonos, vértices) en pantalla.

Tipos de matrices: - Matriz de proyección: es una matriz de 4x4 que establece como se dibujarán los polígonos en el viewport (tipo Mtx44 en gx.h) - Matriz de vista: es una matriz que contiene la posición del observador (o cámara) y que se concatena con la matriz mundial (tipo Mtx en gx.h) - Matriz Mundial: Es la que finalmente, posiciona nuestro objeto en el mundo virtual, que a su vez, será combinada de forma interna con la Matriz vista para proyectar el objeto en el Viewport (tipo Mtx en gx.h). En gx.h, la matriz de proyección se podría cargar así:

Mtx44 projection; // fovy aspect znear zfar guPerspective(projection, 90, 1.333f, 1.0F, 1000.0F); GX_LoadProjectionMtx(projection, GX_PERSPECTIVE);


Gráficos 3D de Wii

Aplicación de matrices en Wii La matriz vista, se podría fijar así:

Mtx View; Vector pos = {0.0, 0.0, 0.0}, // posiciona cámara aquí up = {0.0f, 1.0f, 0.0f}, // vector que señala 'arriba' look = {0.0f, 0.0f, -1.0f}; // punto al que se mira guLookAt(View, &pos, &up, &look); // calcula matriz de vista

La matriz mundial se aplicaría asi:

Mtx World; guMtxIdentity(World); // uso esto debido a un bug en guMtxTrans guMtxTrans(World, 0.0f, 0.0f , -1.0f); // posiciona obj en (0,0,-1) guMtxConcat(View, World, modelView); // calcula matriz final GX_LoadPosMtxImm(modelView, GX_PNMTX0); // carga matriz GX_SetCurrentMtx(GX_PNMTX0); // selecciona la matriz

Como se puede observar, la matriz vista y mundial se 'mezclan' con guMtxConcat y de esa misma forma, se pueden aplicar otros cambios en la matriz mundial (World) (pero tened en cuenta que el orden, altera el producto)


Gráficos 3D de Wii

Configurando la proyección para gráficos 2D Ya que estamos metidos con las matrices, voy a mostrar como utilizar la proyección Ortográfica para manipular gráficos en 2D: Mtx44 projection; // ancho // alto GX_SetViewport(0,0,screenMode->fbWidth, screenMode->efbHeight, 0.0f,1.0f); // fija el viewport al ancho y alto de la pantalla guOrtho(projection, 0.0f ,(f32) (screenMode->efbHeight), 0.0f ,(f32) (screenMode->fbWidth-1), 0.0f, 1000.0f); // z desde 0 a 1000.0f GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC);

De esta forma ya podemos dibujar sprites (Quads texturados) utilizando un sistema de coordenadas basado en el ancho y alto de la pantalla, en éste caso con una ordenacion Z entre 0 y 1000 e incluso rotarlos mediante la matriz mundial (que se puede fijar con guMtxIdentity(World); y GX_LoadPosMtxImm(World, GX_PNMTX0) por defecto). Obviamente, en éste caso no se utilizarían cámaras, ni matriz de vista


Gráficos 3D de Wii Matrices de Normal y de Textura Existen otras matrices que controlan la aplicación de las normales y las coordenadas de textura. Las normales, representan ángulos de incidencia de la luz en relación al plano, y éste ángulo no se ve alterado cuando posicionamos un objeto en diferentes localizaciones, pero sí cuando rotamos alrededor de alguno de sus ejes. La forma de ajustar su matriz partiendo de la matriz Mundial es la siguiente: Mtx Norm; guMtxInverse(modelView, Norm); guMtxTranspose(Norm, Norm); Por su parte, la aplicación de texturas también se controla por una matriz. Como muestra, éste ejemplo pensado para aplicar una sombra: Proyección desde fuente de luz /* Esto ajusta la matriz TEXMTX1 como matriz de transformacion 3x4 para la Textura 1 que utiliza las coordenadas de Textura 1*/ GX_SetTexCoordGen(GX_TEXCOORD1,GX_TG_MTX3x4,GX_TG_T EX1, GX_TEXMTX1) /* Esto Cambia el origen de las coordenadas de textura 1, para que tome en su lugar la posición del objeto. Muy util para mapear texturas como sombras, reflejos, etc */ GX_SetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX3x4, GX_TG_POS, GX_TEXMTX1); /* Esto cargaría una matriz que contendría la proyección desde un punto de luz para aplicar una sombra sobre la textura */ GX_LoadTexMtxImm(lightProj, GX_TEXMTX1, GX_MTX3x4);


Gráficos 3D de Wii Para restablecer la matriz de textura original, sería: GX_SetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX1, GX_IDENTITY); Tienes junto a éste documento un código fuente que muestra todo esto en acción. Pero te explico los pasos para capturar el framebuffer a textura desde la fuente de luz para utilizarlo como Stencil buffer y aplicar sombra: Para ajustar la proyección de luz sería: Mtx lightProj, lightView; Vector light_pos, // posicion de la luz en el espacio light_up, // vector de verticalidad light_look; // posicion a donde se mira en el espacio // fija la cámara para la fuente de luz guLookAt(lightView, &light_pos, &light_up, &light_look);

Ahora deberías proyectar los objetos usando lightView como cámara, después debes capturar la textura como stencil como se explica en el procedimiento Capturar Textura Desde el Framebuffer. También puedes ver el capítulo El sistema de iluminación de Wii que te muestra como posicionar la luz hardware.

En la segunda pasada ajustas la cámara de tu vista y usas éste código para crear la proyección de la luz float znear=256.0f; // 256 -> fovy=90, 442 -> fovy= 60 , 617 -> fovy=45 en guPerspective() guLightFrustum(lightProj, -256.0f, 256.0f, (-1.33333f)*256.0f, (1.33333f)*256.0f, znear, 0.5f, 0.5f, 0.5f, 0.5f); guMtxConcat(lightProj, lightView, lightProj);


Gráficos 3D de Wii

Esto ajustaría la proyección a cada objeto, partiendo de su matriz mundial (una vez aplicada la matriz de cámara sobre world) Mtx mt; guMtxConcat(lightProj, world, mt); GX_LoadTexMtxImm( mt, GX_TEXMTX1, GX_MTX3x4); Podrías ajustarlo todo de una pasada usando ésta función que ajusta posición, normales y texturas para un objeto: // world: Matriz con todas las transformaciones del objeto // camera: Matriz con la cámara actual // lightproj: Matriz de proyeccion de luz en textura void AdjustMatrix(Mtx world) { Mtx mt; Mtx normal_matrix; guMtxConcat(camera, world, mt); GX_LoadPosMtxImm( mt, GX_PNMTX0); // matriz mundial guMtxInverse(mt, normal_matrix); guMtxTranspose(normal_matrix, normal_matrix); GX_LoadNrmMtxImm(normal_matrix, GX_PNMTX0); guMtxConcat(lightProj, world, mt); GX_LoadTexMtxImm( mt, GX_TEXMTX1, GX_MTX3x4); }


Gráficos 3D de Wii

Iluminación Existen tres tipos de luz que se emplean con frecuencia en gráficos 3D: - Luz Ambiental: La cantidad de luz mínima que se le aplica a los vértices de forma constante. Se puede tomar como una fuente de luz lejana cuyos rayos inciden en todas las direcciones de igual forma. - Luz Difusa: La cantidad de luz que refleja un objeto de su mismo color o mejor dicho, con su mismo patrón de color. Por ejemplo, imagina un objeto de color verde que es iluminado con una luz blanca. Eso haría que hubiera partes mas brillantes de color verde que otras, pero sin embargo, los componentes azul y rojo serían ignorados. - Luz Especular: La cantidad de luz que refleja un objeto del color de la fuente de luz. Es decir, si el objeto fuera verde y la luz que incide blanca, eso haría que se sumaran sus componentes de forma que parte de él se vería blanco Generalmente cuando los objetos se iluminan con fuentes de luz, se especifican unas propiedades 'materiales' que valen para todo el objeto y no se envía información de color para el vértice, si no que se fijan niveles de luz ambiental, difusa y especular (ademas de texturas)


Gráficos 3D de Wii El sistema de iluminación de Wii Wii cuenta con un soporte de hasta 8 luces por hardware (de GX_LIGHT0 a GX_LIGHT7) que se pueden combinar formando una máscara en cada uno de los dos canales de iluminación que disponemos donde se le puede especificar de que tipo de luces se trata (se pueden especificar luces posicionales, luces especulares y luces de punto (spot) ) Antes de nada, hay que saber que el mezclado de la iluminación se realiza por el Texture Environment (TEV) y que por tanto, que especifiquemos una luz como especular, no significa que se comporte tal y como yo defino arriba a la iluminación especular, si no que eso dependerá de las propiedades materiales y la fórmula de aplicación que se establezca en el el TEV. Luz Posicional Una luz posicional, es como su nombre implica, una luz que se posiciona en el espacio y en función de su posición con respecto a los objetos y la normal de sus caras, lo ilumina. Debido a sus características, tiende a iluminar todas las caras del objeto que se encuentre frente a el, lo que hace que no sea precisa y mas bien es adecuada para llevar a cabo iluminación difusa. Para aplicar éste tipo de luz se le debe pasar GX_AF_NONE al canal de iluminación, como veremos luego. Definición del objeto de una luz posicional GXLightObj light; // objeto luz GXColor clight = {255, 255, 255, 255}; // color de la luz (blanca) Vector light_pos; // vector con la posición de la luz en el espacio Vector light_world; // vector calculado a la posición mundial /*-----------------------------------------------------------------------------------------------------*/ // multiplica la matriz cámara para obtener la posición de la luz en relacion a ella guVecMultiply(camera, &light_pos, &light_world); GX_InitLightPosv(&light,&light_world); GX_InitLightColor(&light,clight);


Gráficos 3D de Wii

Luz Especular Una luz especular es un tipo de luz direccional (no se especifica su posición en el espacio) pero que puede controlar el ángulo de incidencia de forma que puede concentrar sus haces en una región relativamente pequeña del objeto si se quiere, para conseguir el efecto que produce la iluminación especular. Definición del objeto de una luz especular GXLightObj light; // objeto luz GXColor clight = {255, 255, 255, 255}; // color de la luz (blanca) Vector light_pos; // vector con la posición de la luz en el espacio Vector light_look; // vector con la posición a la que mira en el espacio /*---------------------------------------------------------------------------------------*/ Vector light_dir; // vector calculado a la posición mundial Vector pos,look; // temporales de pos y look // posiciona los vectores de posición y donde se mira con respecto a la camara guVecMultiply(camera, &light_pos, &pos); guVecMultiply(camera, &light_look, &look); // obtiene la dirección a la que apunta (recordemos que es una luz direccional) guVecSub(&look, &pos, &light_dir); GX_InitSpecularDirv(&light,&light_dir); GX_InitLightColor(&light,clight); GX_InitLightShininess(&light, 1.0f); // establece una angulo relativamente abierto

La apertura de la luz se fija de dos formas GX_InitLightShinines o pero es mas sencillo con ésta:

GX_InitLightAttn

#define GX_InitLightShininess(lobj, shininess) Realmente, se trata de GX_InitLightAttn con una determinada curva de atenuación fijada. Para shininess emplea valores como 1.0, 4.0 o 256.0 para ver sus efectos (no se puede explicar, hay que verlo con tus propios ojos :p)


Gráficos 3D de Wii

Luz de Punto (Spot) Podríamos decir que combina las propiedades de las otras dos, permitiendo especificar la posición en el espacio y la dirección a la que 'mira', además del ángulo de incidencia y el nivel de atenuación. Definición del objeto de Luz Spot GXLightObj light; // objeto luz GXColor clight = {255, 255, 255, 255}; // color de la luz (blanca) Vector light_pos; // vector con la posición de la luz en el espacio Vector light_look; // vector con la posición a la que mira en el espacio /*---------------------------------------------------------------------------------------*/ Vector light_dir; // vector calculado a la posición mundial Vector pos,look; // temporales de pos y look // posiciona los vectores de posición y donde se mira con respecto a la camara guVecMultiply(camera, &light_pos, &pos); guVecMultiply(camera, &light_look, &look); // obtiene la dirección a la que apunta la luz guVecSub(&look, &pos, &light_dir); guVecNormalize(&light_dir); // normaliza el vector GX_InitLightPosv(&light,&pos); GX_InitLightDirv(&light,&light_dir); GX_InitLightColor(&light,clight); GX_InitLightSpot(&light,70,GX_SP_SHARP); // 70 grados, curva GX_SP_SHARP GX_InitLightDistAttn(&light,100.0f, 1.0f, GX_DA_MEDIUM); //distancia 100.0f

void GX_InitLightSpot(GXLightObj *lit_obj,f32 cut_off,u8 spotfn); lit_obj→ dirección del objeto de luz cut_off→ ángulo de 0 a 90 en grados spotfn→ función de la curva spot GX_SP_OFF, GX_SP_FLAT, GX_SP_COS, GX_SP_COS2, GX_SP_SHARP, GX_SP_RING1, GX_SP_RING2


Gráficos 3D de Wii

void GX_InitLightDistAttn(GXLightObj *lit_obj,f32 ref_dist,f32 ref_brite,u8 dist_fn); lit_obj→ dirección del objeto de luz ref_dist→ distancia de referencia. ref_brite→ brillo de referencia (de 0.0 a 1.0) para esa distancia dist_fn → función de la curva de distancia: GX_DA_OFF, GX_DA_GENTLE, GX_DA_MEDIUM, GX_DA_STEEP Función InitLightAttn que puede ser empleada en lugar de InitLightShininess en las luces Especulares o de InitLightSpot/ InitLightDistAttn en las Spot

void GX_InitLightAttn(GXLightObj *lit_obj,f32 a0,f32 a1,f32 a2,f32 k0,f32 k1,f32 k2); Esta función se emplea tanto con luces especulares, como con luces tipo punto o spot a0 a a2, son coeficientes de atenuación angular, donde a0 es una cantidad constante aditiva, a1 multiplica el grado de atenuación (interno) y a2 multiplica el cuadrado del grado de atenuación. La atenuación interna sale del coseno del angulo que forma la dirección de la luz con respecto a la normal del vértice. k0 a k2, son coeficientes de atenuación de distancia, donde k0 es una cantidad constante aditiva, k1 se multiplica por la distancia (interno) y k2 se multiplica por el cuadrado de dicha distancia. La distancia interna es la distancia que hay entre la fuente de luz y el vértice, pero esto sólo tiene sentido real con luces Spot (aunque las luces especulares, "simulan" dicha distancia) El coeficiente de atenuación final sale de a/k siendo a la suma de todas las operaciones de los coeficientes a0 a a2, limitando los valores entre 0.0 y 1.0 ( es un coseno de iluminación) y k la suma de las operaciones con los coeficientes k0 a k2. Lo normal es que se combine a0, a1, k0, k1 para basarse en la distancia o a0, a2, k0, k2 para basarse en el cuadrado de la distancia


Gráficos 3D de Wii Conectando el objeto de Luz con el hardware GX_LoadLightObj(&light,GX_LIGHT0); // simple y rápido Recuerda que puedes usar de GX_LIGHT0 a GX_LIGHT7

Canales de iluminación La función GX_SetNumChans(); es la encargada de seleccionar el numero de canales activos (maximo dos). Por ejemplo GX_SetNumChans(1); conectaría el canal 0, mientras que GX_SetNumChans(2); los dos. El canal 0 está representado por GX_COLOR0, GX_ALPHA0, GX_COLOR0A0 mientras que el 1 por GX_COLOR1, GX_ALPHA1, GX_COLOR1A1 La función GX_SetChanCtrl() es la que define como opera el canal void GX_SetChanCtrl(s32 channel,u8 enable,u8 ambsrc,u8 matsrc,u8 litmask,u8 diff_fn,u8 attn_fn); channel→ GX_COLOR0, GX_ALPHA0, GX_COLOR0A0 para canal 0 GX_COLOR1, GX_ALPHA1, GX_COLOR1A1 para canal 1 enable→ GX_ENABLE activa las luces hardware, GX_DISABLE las desactiva ambsrc→ GX_SRC_REG para luz ambiental desde el registro o GX_SRC_VTX para luz ambiental desde vértice matsrc→ GX_SRC_REG para luz del material desde el registro o GX_SRC_VTX para luz del material desde vértice litmask→ mascara de luces, por ejemplo GX_LIGHTNULL

GX_LIGHT0, (GX_LIGHT0 | GX_LIGHT1)

o

para desactivar

diff_fn→ generalmente GX_DF_CLAMP attn_fn→ GX_AF_NONE, para las luces de posicionales, GX_AF_SPEC para las especulares y GX_AF_SPOT luces spot. Si se quieren combinar varios tipos de luz, para eso hay dos canales En concreto, fija la función de atenuación.


Gráficos 3D de Wii Color Ambiente y Color Material El color ambiente de un objeto se puede fijar mediante un registro que se carga con la función GX_SetChanAmbColor(), por ejemplo así: GXColor ambiente={0x40,0x40,0x40,0xff}; // R G B A // fija color ambiental canal 0 GX_SetChanAmbColor(GX_COLOR0A0, ambiente);

El color material de un objeto se puede fijar mediante un registro que se carga con la función GX_SetChanMatColor(), por ejemplo así: GXColor material={0xff,0xff,0xff,0xff}; // R G B A // fija color ambiental canal 0 GX_SetChanMatColor(GX_COLOR0A0, material); Para seleccionarlas como fuentes, se requiere especificar en GX_SetChansCtrl() ambsrc y/o matsrc como GX_SRC_REG Los canales conectan con el Texture Environment (TEV) mediante la función GX_SetTevOrder() cuyo último parámetro recibe el numero del canal sobre el que opera esa unidad.


Gráficos 3D de Wii

Transparencia y Translucidez Existen ciertos objetos que poseen características que hacen que la luz pase a través de ellos, permitiendo ver lo que hay detrás de forma mas o menos claro. Por ejemplo, una botella de vidrio de color verde, mostraría lo que hay detrás con un verde acusado, mientras que el cristal de una ventana dejaría pasar la luz con una mínima interferencia. Estas propiedades de los objetos se reflejan mediante el uso del canal Alpha de color, donde tomando como referencia un valor de 8 bits, 0 representaría transparencia total, mientras que 255 representaría un objeto completamente opaco. Sin embargo, los objetos completamente transparentes, se suelen usar con otros fines en los gráficos 3D: para delimitar que parte de los objetos se deben dibujar y cuales no. Para entender esto, basta pensar que dibujáramos una pared completamente transparente en pantalla: sería invisible al ojo, pero si pintáramos un objeto detrás de dicha pared inmediatamente después, no se vería!. La razón por la que ocurre ésto es debido al buffer Z: si, virtualmente hablando el objeto es transparente, pero se ha actualizado el buffer Z y debido a ésto, el píxel mas cercano es el transparente. Así que las partes del nuevo objeto que queden ocluidas, no serán visibles. Sin embargo, se puede modificar el tratamiento del canal Alpha para que permita que un determinado rango de valores hagan que el pixel no se dibuje y de esa forma, poder hacer 'agujeros' que no solo permitan pasar la luz, si no que permitan dibujar objetos que queden detrás de forma correcta. Obviamente, éste truco solo vale para objetos que consideremos, completamente transparentes (por ejemplo, imagina que quieres dibujar una textura de una valla metálica: ésto permitiría que el alambre de acero se dibujase, mientras que los huecos se ignorasen).


Gráficos 3D de Wii

Los objetos translúcidos no tienen tanta suerte y debido al efecto de oclusión por el buffer Z, la forma correcta de dibujarlos es en una Lista separada ordenada y en último lugar, ya que así el buffer Z ya contendrá la información correcta de los objetos opacos y si el objeto translucido queda detrás, no se dibujará, pero si queda delante, se dibujarán correctamente 'mezclándose' según el modo de operación seleccionado. La translucidez se controla mediante la función de AlphaBlending, mientras la función que permite que un píxel se dibuje o no en funcion de su valor Alpha recibe el nombre de AlphaCompare o AlphaFunction, o similares. En Wii se pueden activar o desactivar así: Activar la translucidez: GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); Desactivar translucidez: GX_SetBlendMode(GX_BM_NONE,GX_BL_SRCALPHA,GX_BL_IN VSRCALPHA,GX_LO_CLEAR);

Activar transparencia: GX_SetAlphaCompare(GX_GEQUAL, 8, GX_AOP_AND, GX_ALWAYS, 0); // esto deja pasar solo valores entre 8 y 255 GX_PixModeSync(); // función de sincronización. GX_SetZCompLoc(GX_FALSE); /* esto hace que no se actualize el buffer Z hasta después de texturizar */


Grรกficos 3D de Wii

Desactivar transparencia: GX_SetAlphaCompare(GX_ALWAYS,0,GX_AOP_OR,GX_ALWAYS, 0);


Gráficos 3D de Wii

Texturas La textura no es mas que un mapa de bits que suele representar colores, aunque también se utilizan otras especiales que almacenan un mapa de normales para generar un efecto llamado Bump Mapping (que permite mostrar relieves en los objetos) corregiendo la normal de iluminación y conseguir un iluminación mas realista marcando mejor los relieves. A los píxeles de esa textura, se les suele llamar Texels y cuando se especifican sus coordenadas como S, T, los valores varían desde 0.0f a 1.0f para recorrer sus límites, de forma independiente a la altura/ anchura del bitmap. Para cuando se transpasa los límites de la textura al especificar las coordenadas, existen una serie de operaciones. Por ejemplo en Wii: Envoltura: - GX_CLAMP: Se recorta la coordenada pasada al valor mínimo o máximo , si procede (0.0-1.0f) - GX_REPEAT: Esto provoca que se vaya repitiendo la textura como si fuera un mosaico según se van superando los límites. - GX_MIRROR: Esto provoca que la textura se vea reflejada como en un espejo al superar los limites además de repetirse cómo un mosaico y así un valor -1.0 sería lo mismo que 1.0 Filtros: También se pueden activar una serie de filtros para representar las texturas con diferentes calidades. Puesto que el área de los polígonos puede ser mayor o menor que el bitmap de textura, se hace necesario especificar como proceder ante estas eventualidades y por ejemplo, un filtro GX_NEAR, mostrará las texturas como 'cuadraditos' si se las


Gráficos 3D de Wii aumenta mucho, mientras que GX_LINEAR interpolará el color entre los texeles mas próximos para suavizar la textura. También existe un tipo de texturas llamadas Mip Maps que básicamente, son una serie de bitmaps anidados a diferente resolución de forma que en función de la distancia, la GPU decida si es mejor utilizar la versión a 256x256 texels de la textura o la versión 64x64 texel de la 'misma' textura. En Wii también se puede ajustar el nivel de filtrado anisotrópico que es un filtro que mejora la calidad de la textura en función de la distancia o en ángulos oblicuos a nuestra perspectiva. En confidencia: con que sepas lo que es una textura NEAR o LINEAR y juguetear un poco con el filtro anisotrópico para comparar, vas sobrado para hacer cosillas en Wii XD También se puede aplicar múltiples texturas mezclandolas mediante el Texture Environment (TEV), como veremos luego. Texturas formando tiles Una cuestión que debes saber es que la GPU organiza las texturas por tiles para trabajar de forma más optimizada: Yo suelo trabajar con texturas en formato RGB5A3 pero preparé una función en los ejemplos, llamada tiling4x4 que es capaz de transformar texturas de 16 y 32 bits de hasta 1024x1024 texeles que es el tamaño máximo que admite Wii. También tiene la posibilidad de intercambiar los colores Rojo y Azul, dado que en Wii el Azul es el color de menos peso y yo estoy mas acostumbrado a que el Rojo sea el color de menos peso. También he añadido otra función para convertir la paleta de color para CI4 y CI8. La funciones se declaran así. (ver página siguiente)


Gráficos 3D de Wii void tiling4x4(void *mem,int format,int tx,int ty); mem→ dirección de la textura (alineada a 32 bytes) format→ Puede ser uno de estos modos: TILE_CI4 para índice de color de 4 bits TILE_CI8 para índice de color de 8 bits TILE_RGB5A3 para RGB5A3 o RGB5A1 16 bits TILE_RGB5A1 equivalente a RGB5A3 TILE_RGB565 para RGB565 16 bits TILE_SRGB565 para RGB565 16 bits con R y B intercambiados TILE_SRGB5A1 para RGB5A1 16 bits con R y B intercambiados TILE_RGBA8 para RGBA8 32 bits con R y B intercambiados TILE_SRGBA8 para RGBA8 32 bits con R y B intercambiados TILE_LITTLE_ENDIAN Cambia el orden de los bytes tx→ ancho de la textura ty→ alto de la textura

void ctlut(void *mem,int format,int entries); mem→ dirección de la paleta (alineada a 32 bytes) format→ Puede ser uno de estos modos: TLUT_RGB5A3 para RGB5A3 o RGB5A1 16 bits TLUT_RGB5A1 equivalente a RGB5A3 TLUT_RGB565 para RGB565 16 bits TLUT_SRGB565 para RGB565 16 bits con R y B intercambiados TLUT_SRGB5A1 para RGB5A1 16 bits con R y B intercambiados TLUT_LITTLE_ENDIAN Cambia el orden de los bytes entries→ número de entradas a la paleta


Gráficos 3D de Wii Cargando una textura en Wii GXTexObj texObj; // objeto textura // memoria de la textura de 256x256 texels (debe alinearse a 32 bytes) u16 texture[256*256] ATTRIBUTE_ALIGN(32); // rellena con color blanco memset((void ) texture, 255, 256*256*2); tiling4x4(texture, TILE_RGB5A3, 256, 256); /* convierte una textura lineal de 256x256 texeles en formato RBG5A3 a tiles */ /* se debe hacer un DCFlushRange() a la memoria de textura pero tiling4x4 ya lo hace de forma interna*/ // inicializa el objeto GX_InitTexObj(&texObj, // objeto textura a inicializar texture, // direccion de memoria de la textura 256, // ancho 256, // alto GX_TF_RGB5A3, // formato de color GX_CLAMP, // Wrap Mode para S GX_CLAMP, // Wrap Mode para T GX_FALSE // No MipMap ); // ajusta los filtros según el LOD GX_InitTexObjLOD(&texObj, // objeto de textura GX_LINEAR, // filtro Linear para cerca GX_LINEAR, // filtro Linear para lejos 0, 0, 0, 0, 0, GX_ANISO_1); // .......... // .......... // carga la textura para trabajar como Mapa de textura 0 GX_LoadTexObj(&texObj, GX_TEXMAP0); // puedes usar desde GX_TEXMAP0 a GX_TEXMAP7

En éste caso la textura es generada por la propia Wii, luego no necesita corregir el orden al crear los tiles de 4 por 4 . Si por el contrario la textura


Gráficos 3D de Wii hubiese sido exportada desde un ordenador PC como una lista de números tipo 'byte', necesitarás invertir el orden, pues Wii es big endian. Pero si la lista es pasada como unsigned short, no hay problema, solo deberás cuidar el orden del color Rojo y del Azul a la hora de especificar el formato para tiling4x4 y cuidar que el ancho y el alto, sean un múltiplo de cuatro (por el tile) Recuerda que es muy importante que la textura esté alineada a 32 bytes y si necesitas alojar memoria, usa memalign(32,size); en vez de malloc(size); Orden de colores en un tile 4x4 en memoria en relacion a su ancho-alto para 16 bits tile 1 0 4 8 12

1 5 9 13

tile2 2 6 10 14

3 7 11 15

16 20 24 28

17 21 25 29

18 22 26 30

19 23 27 31

Para RGBA8 (32 bits), tile 1 representaría AR y tile 2 GB pues se divide en fragmentos de 16 bits y dos tiles Orden de colores en un tile 8x4 para el formato CI8 (8 bits con paleta) tile 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

32 40 48 56

33 41 49 57

34 42 50 58

tile2 35 36 43 44 51 52 59 60

37 45 53 61

38 46 54 62

39 47 55 63


Gráficos 3D de Wii Cargando Texturas con Paleta GXTexObj texObj; // objeto Textura GXTlutObj paletteObj; // objeto Paleta #define n_palette_entries 2 // paleta de color (debe alinearse a 32 bytes) u16 palette[16] ATTRIBUTE_ALIGN(32) ={0,0xffff}; // memoria de la textura de 256x256 texels (debe alinearse a 32 bytes) u8 texture[256*256] ATTRIBUTE_ALIGN(32); // rellena textura con un patron de indices de rejilla int n; for(n=0;n<256*256;n++) { if((n & 2) ^ ((n>>8) & 2)) texture[n]=0; else texture[n]=1; // los indices a paleta pueden ir de 0 a 255 } // usa CI4 para indices de 4 bits // organiza la paleta y flushea ctlut(palette, TLUT_SRGB5A1, n_palette_entries ); // inicializa el objeto de paleta GX_InitTlutObj(&paletteObj, spalette, GX_TL_RGB5A3, n_palette_entries ); // carga la paleta (importante hacerlo primero) GX_LoadTlut(&paletteObj, GX_TLUT0); tiling4x4(texture, TILE_CI8 ,256,256); /* convierte una textura lineal de 256x256 texeles en formato Indice de Color 8 bits a tiles */ // inicializa el objeto de color indirecto GX_InitTexObjCI(&texObj, // objeto textura a inicializar texture, // direccion de memoria de la textura 256, // ancho 256, // alto GX_TF_CI8, // formato de color GX_CLAMP, // Wrap Mode para S GX_CLAMP, // Wrap Mode para T GX_FALSE, // No MipMap GX_TLUT0 // asigna la paleta ); // ajusta los filtros según el LOD GX_InitTexObjLOD(&texObj, // objeto de textura GX_LINEAR, // filtro Linear para cerca GX_LINEAR, // filtro Linear para lejos 0, 0, 0, 0, 0, GX_ANISO_1); // .......... // .......... // carga la textura para trabajar como Mapa de textura 0 GX_LoadTexObj(&texObj, GX_TEXMAP0); // puedes usar desde GX_TEXMAP0 a GX_TEXMAP7


Gráficos 3D de Wii Capturar Textura Desde el Framebuffer Capturar el frame buffer es algo que requiere de cinco fases: - Primera fase: Asignación de memoria para la textura. Eso se puede conseguir con el siguiente procedimiento: Preparando memoria para capturar en formato RGB5A3 int screen_sx=screenMode->fbWidth; int screen_sy=screenMode->efbHeight; int size_scr_texture=GX_GetTexBufferSize(screen_sx, screen_sy, GX_TF_RGB5A3, GX_FALSE,0); /* esta funcion devuelve el tamaño de una textura a partir de su tipo de color y ancho/alto*/ void * scr_texture_mem=memalign(32,size_scr_texture); Preparando memoria para simular un Stencil buffer usando 8 bits de muestra int screen_sx=screenMode->fbWidth; int screen_sy=screenMode->efbHeight; int size_scr_stencil=GX_GetTexBufferSize(screen_sx, screen_sy, GX_TF_I8, GX_FALSE,0); // funcion que devuelve el tamaño necesario de la textura void * scr_stencil_mem=memalign(32,size_scr_texture);

-Segunda fase: Ajuste del Viewport. Antes de comenzar a dibujar, conviene ajustar el Viewport al tamaño que queremos capturar: GX_SetViewport(0, 0, screen_sx, screen_sy, 0, 1.0f); GX_SetScissor(0, 0, screen_sx, screen_sy);

Si se quiere crear un reborde con el color de fondo en la captura, emplea éste truco (para cuando se rebasan los límites de la textura) GX_SetScissor(1, 1, screen_sx-2, screen_sy-2);


Gráficos 3D de Wii

-Tercera fase: Dibujas todos los objetos. Si se trata del 'Stencil' dibuja todos los objetos utilizando el color Rojo como un índice o valor de 8 bits de referencia. -Cuarta fase: Captura del Viewport. Captura en formato RGB5A3 GX_SetCopyFilter(GX_FALSE, NULL, GX_FALSE, NULL); // captura del color Rojo en indices de 8 bits GX_SetTexCopySrc(0, 0, screen_sx, screen_sy); GX_SetTexCopyDst(screen_sx, screen_sy, GX_TF_RGB5A3 , GX_FALSE); GX_CopyTex(size_scr_texture, GX_TRUE); // captura la textura y borra el framebuffer GX_PixModeSync(); // se necesita sincronizacion // restaura el viewport GX_SetCopyFilter(screenMode->aa,screenMode->sample_pattern, GX_TRUE, screenMode->vfilter); GX_SetViewport(0, 0, screenMode->fbWidth, screenMode->efbHeight, 0, 1.0f); GX_SetScissor(0, 0, screenMode->fbWidth, screenMode->efbHeight);

Capturar el 'Stencil' buffer usando 8 bits del componente Rojo GX_SetCopyFilter(GX_FALSE, NULL, GX_FALSE, NULL); // captura del color Rojo en indices de 8 bits GX_SetTexCopySrc(0, 0, screen_sx, screen_sy); GX_SetTexCopyDst(screen_sx, screen_sy, GX_CTF_R8 , GX_FALSE); GX_CopyTex(scr_stencil_mem, GX_TRUE); // captura la textura y borra el framebuffer GX_PixModeSync(); // se necesita sincronizacion // restaura el viewport GX_SetCopyFilter(screenMode->aa,screenMode->sample_pattern, GX_TRUE, screenMode->vfilter); GX_SetViewport(0, 0, screenMode->fbWidth, screenMode->efbHeight, 0, 1.0f); GX_SetScissor(0, 0, screenMode->fbWidth, screenMode->efbHeight);


Gráficos 3D de Wii -Quinta fase: Cargar la textura para su utilización Carga textura en formato RGB5A3 GXTexObj textureObj; GX_InitTexObj(&textureObj,shadowmem,screen_sx,screen_sy, GX_TF_RGB5A3, GX_CLAMP,GX_CLAMP,GX_FALSE ); GX_InitTexObjLOD(&textureObj, GX_NEAR, GX_NEAR, 0, 0, 0, 0, 0, GX_ANISO_1); GX_LoadTexObj(&textureObj, GX_TEXMAP0); // asigna al mapa de textura 0 Carga textura 'Stencil' en formato de Intensidad de 8 bits /* carga la textura como niveles de intensidad (eso significa que el color será visible en todos los componentes de color RGBA) */ GXTexObj stencilObj; GX_InitTexObj(&stencilObj,shadowmem,screen_sx,screen_sy, GX_TF_I8, GX_CLAMP,GX_CLAMP,GX_FALSE ); GX_InitTexObjLOD(&stencilObj, GX_NEAR, GX_NEAR, 0, 0, 0, 0, 0, GX_ANISO_1); GX_LoadTexObj(&stencilObj, GX_TEXMAP0); // asigna al mapa de textura 0

A partir de aquí podemos dibujar de nuevo con la pantalla limpia y aplicar las texturas convenientemente. El 'stencil' se puede trabajar desde el Texture Environment (TEV) y por ejemplo, aquí se puede ver como comparar si un valor de Alpha de la textura (que recordemos almacena Intensidades de Rojo), es igual al pasado en el TEVREG1 y en caso de serlo, asigna Alpha=1.0f o Alpha=0.0f si no es igual:


Gráficos 3D de Wii

int tevstage=GX_TEVSTAGE0; // SHADOW TEXTURE // usamos coordenadas de textura 1 y mapa de textura 0 GX_SetTevOrder(tevstage, GX_TEXCOORD1, GX_TEXMAP0, GX_COLOR0A0); GX_SetTevKAlphaSel(tevstage, GX_TEV_KASEL_1); // ajusta KONST=1.0 GX_SetTevColorIn(tevstage, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO); // color no se usa GX_SetTevAlphaIn(tevstage, GX_CA_TEXA, GX_CA_A1, GX_CA_ZERO); // si ATEX==A1 ? 1.0 : 0.0

GX_CA_KONST,

GX_SetTevColorOp(tevstage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV ); // operación suma GX_SetTevAlphaOp(tevstage, GX_TEV_COMP_A8_EQ, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE,GX_TEVPREV); // operación Alpha Igual tevstage++;

TEXCOORD1 puede ser obtenido desde la posición de los objetos al ser transformados utilizando éste código para la proyección de texturas Proyección desde fuente de luz


Gráficos 3D de Wii

Texture Environment (TEV) Sin duda la parte más difícil de comprender y la que más partido proporcionará a nuestros gráficos es el texture environment, que es la parte encargada de operar las distintas fuentes de color y mezclarlas para obtener el píxel definitivo. Hay tres funciones a tener en cuenta a la hora de trabajar con el TEV: - GX_SetChansCtrl(num) : que como vimos en el capítulo anterior,

establecía el numero de canales de iluminación al que se le hacía caso, - GX_SetNumTexGens(num): que indica el número de mapas de textura que se tendrán en cuenta (recuerda que cuando empleábamos GX_LoadTexObj() para cargar texturas, le pasábamos como ultimo parámetro el mapa de textura asociado) y como es evidente, si se le pasa 1 a la función, solo hará caso a GX_TEXMAP0, mientras que si le pasáramos 2, entonces haría caso a GX_TEXMAP0 y GX_TEXMAP1. - GX_SetNumTevStages(num): Establece el número de etapas del TEV que se tendrán en cuenta, lo cual nos puede permitir por ejemplo, controlar la aplicación de texturas simplemente, especificando una etapa más o una etapa menos El TEV se puede trabajar de dos formas, una relativamente fácil de entender y que oculta el 'mecanismo' interior proporcionando una serie de funciones ya preparadas, y otra mas compleja donde nosotros configuramos directamente, las operaciones de color y alfa que efectúan las etapas del TEV. Mi intención es tratar de explicar ambas, tratando de entrar en detalle todo lo necesario para que podáis aprovecharlo al máximo.


Gráficos 3D de Wii

El TEV desde un punto de vista sencillo Las etapas en el TEV se definen con la función GX_SetTevOrder() que es común ambos usos, el sencillo que vamos a ver aquí y el 'complejo'. Después va seguida GX_SetTevOp() para establecer la operación a realizar. Ambas son descritas aquí debajo:

void GX_SetTevOrder(u8 tevstage,u8 texcoord,u32 texmap,u8 color); Función que establece el origen de las coordenadas de textura, el mapa de textura y el canal de color que se tendrá en cuenta. tevstage→ Numero de etapa del TEV. Puede ser desde GX_TEVSTAGE0 a GX_TEVSTAGE15

texcoord→ Establece el origen de las coordenadas de textura: Puede ser GX_TEXCOORDNULL si se ignora o desde GX_TEXCOORD0 a GX_TEXCOORD7

color→ Canal de color: puede ser GX_COLORNULL si no se usa el color del Rasterizador (proveniente de las fuentes de luz), GX_COLORZERO si queremos que el TEV reciba 0 en la etapa de color. GX_COLOR0A0 o GX_COLOR1A1 para recibir el color del rasterizador para el canal de color. También puede recibir valores de Bump Mapping, pero yo no conozco como funciona el mecanismo y creo que se escapa a la labor homebrew la realización de videojuegos profesionales: esto es solo un bonito hobby y siempre puedes investigar tu mismo esas cosas.


Gráficos 3D de Wii

void GX_SetTevOp(u8 tevstage,u8 mode) Establece el modo de operación de la etapa. tevstage→Numero de etapa del TEV. Puede ser desde GX_TEVSTAGE0 a GX_TEVSTAGE15

mode→ Uno de estos modos: GX_MODULATE: Multiplica el color/alfa de la etapa anterior y el de la textura para obtener el color definitivo. Es la forma normal de mezclar una textura con un color para obtener una textura tirando a dicho color GX_DECAL: Utiliza el Alfa de la textura para atenuar su color (mediante multiplicación) y usa Alfa del registro previo para mostrar el color. Es la forma de hacer que sólo se tenga en cuenta el color de la textura, pero que se use el Alfa del rasterizador o de otras etapas, para efectos de translucidez. GX_BLEND: Produce una mezcla diferencial entre el color de la textura y el color previo, de forma que si el color de textura de un canal (por ejemplo, Rojo) es débil, se obtendrá una mezcla mas rica en el color previo, mientras que si el color de textura es fuerte, se impondrá al color previo. El alfa resultantes se obtiene de multiplicar el de la textura con el color previo. GX_REPLACE: Simplemente, deja pasar el color y alfa de la textura, ignorando el resto de fuentes. GX_PASSCLR: Deja pasar el color previo ignorando el de textura. Ideal para cuando no se utilizan texturas en los polígonos. El alfa es sumado extrañamente con el del Registro 2 de color en una operacion asi: A2*(1.0-A2)+A2*A2+AlfaIn. (ni idea del propósito real de esto XD) Como se definen estas funciones a bajo nivel, lo podéis ver en gx.c


Gráficos 3D de Wii

El TEV desde un punto de vista menos sencillo Las etapas del TEV utilizan 5 funciones para operar, realmente: GX_SetTevOrder()-> que hemos visto justo arriba GX_SetTevColorIn()-> que pasa los 4 operandos para Color GX_SetTevAlphaIn()-> que pasa los 4 operandos para Alfa GX_SetTevColorOp()-> que fija la operación para obtener Color GX_SetTevAlphaOp()-> que fija la operación para obtener Alfa Antes de nada, tengo que explicar el tema de los registros como operandos. El TEV tiene 4 registros: uno que dedica para almacenar el resultado de la etapa anterior y otros 3 que puedes asignar mediante la función GX_SetTevColor() pero que también puedes utilizarlos para almacenar el resultado de una operación en las funciones GX_SetTevColorOp() y GX_SetTevAlphaOp() con el fin de utilizar ese resultado en varias etapas. Además de estos registros, se pueden asignar como operandos el color/alfa del rasterizador, de la textura (siempre que estén activos desde GX_SetTevOrder()) ZERO, ONE (solo en Color) y KONST. Las constantes (KONST) se pueden fijar de forma independiente para cada etapa del TEV: hay una serie de valores fijos que se pueden indicar con la funciones GX_SetTevKColorSel() y GX_SetTevKAlphaSel() y cuatro registros definibles por el usuario mediante la función GX_SetTevKColor(), como veremos luego.


Gráficos 3D de Wii

Operandos TEV de Color y Alfa void GX_SetTevColorIn(u8 tevstage,u8 a,u8 b,u8 c,u8 d); tevstage→Numero de etapa del TEV. Puede ser desde GX_TEVSTAGE0 a GX_TEVSTAGE15

a,b,c→ Operandos con valor de 8 bits (si algún valor usara 10 bits se recortaría) d→ Operando con valor de 10 bits (con signo) A menudo se hace una equivalencia del valor de estos operandos de forma que 0=0.0f y 255=1.0f con el fin de entender mejor las operaciones Estos son los operandos que puedes utilizar aquí y su significado: NOTA: los valores Alfa se expanden para operar con todos los componentes de Color.

GX_CC_CPREV → Color del registro TEV previo GX_CC_APREV → Alfa del registro TEV previo GX_CC_C0 → Color del Registro 0 GX_CC_A0 → Alfa del Registro 0 GX_CC_C1 → Color del Registro 1 GX_CC_A1 → Alfa del Registro 1 GX_CC_C2 → Color del Registro 2 GX_CC_A2 → Alfa del Registro 2 GX_CC_TEXC → Color de la Textura GX_CC_TEXA → Alfa de la Textura GX_CC_RASC → Color del Rasterizador (aplicación de luces/color) GX_CC_RASA → Alfa del Rasterizador (aplicación de luces/color) GX_CC_ONE → Constante que equivale a 1.0f (255) GX_CC_HALF → Constante que equivale a la mitad 0.5f (127) GX_CC_KONST→ Valor de Color desde la constante (definible) GX_CC_ZERO → Constante que equivale a 0.0f (0)


Gráficos 3D de Wii

void GX_SetTevAlphaIn(u8 tevstage,u8 a,u8 b,u8 c,u8 d); tevstage→Numero de etapa del TEV. Puede ser desde GX_TEVSTAGE0 a GX_TEVSTAGE15

a,b,c→ Operandos con valor de 8 bits (si algún valor usara 10 bits se recortaría) d→ Operando con valor de 10 bits (con signo) A menudo se hace una equivalencia del valor de estos operandos de forma que 0=0.0f y 255=1.0f con el fin de entender mejor las operaciones Estos son los operandos que puedes utilizar aquí y su significado: GX_CA_APREV → Alfa del registro TEV previo GX_CA_A0 → Alfa del Registro 0 GX_CA_A1 → Alfa del Registro 1 GX_CA_A2 → Alfa del Registro 2 GX_CC_TEXA → Alfa de la Textura GX_CC_RASA → Alfa del Rasterizador (aplicación de luces/color) GX_CC_KONST→ Valor Alfa desde la constante (definible) GX_CC_ZERO → Constante que equivale a 0.0f (0)


Gráficos 3D de Wii Operaciones TEV de Color y Alfa void GX_SetTevColorOp(u8 tevstage,u8 tevop,u8 tevbias,u8 tevscale,u8 clamp,u8 tevregid); tevstage→Numero de etapa del TEV. Puede ser desde GX_TEVSTAGE0 a GX_TEVSTAGE15

tevop→ Una de : (Los argumentos desde GX_SetTevColorIn()) GX_TEV_ADD → tevscale*(a*(1.0-c) + b*c + d+ tevbias) GX_TEV_SUB → tevscale*(-(a*(1.0-c) + b*c) + d + tevbias) GX_TEV_COMP_R8_EQ → a[R]==b[R] ? c+d : d (compara solo Rojo) GX_TEV_COMP_R8_GT → a[R]>b[R] ? c+d : d (compara solo Rojo) GX_TEV_COMP_GR16_EQ → a[GR]==b[GR] ? c+d : d (compara GR) GX_TEV_COMP_GR16_GT → a[GR]>b[GR] ? c+d : d (compara GR) GX_TEV_COMP_BGR24_EQ → a[BGR]==b[BGR] ? c+d : d (compara BGR) GX_TEV_COMP_BGR24_GT → a[BGR]==b[BGR] ? c+d : d (compara BGR) GX_TEV_COMP_RGB8_EQ → Rojo Verde Azul

GX_TEV_COMP_RGB8_GT → Rojo Verde Azul

= = = = = =

a[R]==b[R] ? c[R]+d[R] : d[R] a[G]==b[G] ? c[G]+d[G] : d[G] a[B]==b[B] ? c[B]+d[B] : d[B] a[R]>b[R] ? c[R]+d[R] : d[R] a[G]>b[G] ? c[G]+d[G] : d[G] a[B]>b[B] ? c[B]+d[B] : d[B]

tevbias→ GX_TB_ZERO , GX_TB_ADDHALF o GX_TB_SUBHALF Añade 0.0 , la mitad (0.5) o resta 0.5 al resultado tevscale→ GX_CS_SCALE_1, GX_CS_SCALE_2, GX_CS_SCALE_4, GX_CS_DIVIDE_2. Multiplica x1, x2, x4 o divide entre 2


Gráficos 3D de Wii clamp→ GX_TRUE or GX_FALSE. Normalmente GX_TRUE tevregid→ Registro de salida. Puede ser GX_TEVPREV, GX_TEVREG0, GX_TEVREG1, GX_TEVREG2, GX_TEVREG3. Usualmente GX_TEVPREV para encadenar etapas. NOTA: tevbias y tevscale solo se aplican con GX_TEV_ADD y GX_TEV_SUB, como se aprecian en las operaciones

void GX_SetTevAlphaOp(u8 tevstage,u8 tevop,u8 tevbias,u8 tevscale,u8 clamp,u8 tevregid); tevstage→Numero de etapa del TEV. Puede ser desde GX_TEVSTAGE0 a GX_TEVSTAGE15

tevop→ Una de : (Los argumentos desde GX_SetTevColorIn()) GX_TEV_ADD → tevscale*(a*(1.0-c) + b*c + d+ tevbias) GX_TEV_SUB → tevscale*(-(a*(1.0-c) + b*c) + d + tevbias) GX_TEV_COMP_R8_EQ → a[R]==b[R] ? c+d : d (compara solo Rojo) GX_TEV_COMP_R8_GT → a[R]>b[R] ? c+d : d (compara solo Rojo) GX_TEV_COMP_GR16_EQ → a[GR]==b[GR] ? c+d : d (compara GR) GX_TEV_COMP_GR16_GT → a[GR]>b[GR] ? c+d : d (compara GR) GX_TEV_COMP_BGR24_EQ → a[BGR]==b[BGR] ? c+d : d (compara BGR) GX_TEV_COMP_BGR24_GT → a[BGR]==b[BGR] ? c+d : d (compara BGR) GX_TEV_COMP_A8_EQ → a[A]==b[A] ? c+d : d (compara Alfa) GX_TEV_COMP_A8_GT → a[A]>b[A] ? c+d : d (compara Alfa)

tevbias→ GX_TB_ZERO , GX_TB_ADDHALF o GX_TB_SUBHALF Añade 0.0 , la mitad (0.5) o resta 0.5 al resultado


Gráficos 3D de Wii tevscale→ GX_CS_SCALE_1, GX_CS_SCALE_2, GX_CS_SCALE_4, GX_CS_DIVIDE_2. Multiplica x1, x2, x4 o divide entre 2

clamp→ GX_TRUE or GX_FALSE. Normalmente GX_TRUE tevregid→ Registro de salida. Puede ser GX_TEVPREV, GX_TEVREG0, GX_TEVREG1, GX_TEVREG2, GX_TEVREG3. Usualmente GX_TEVPREV para encadenar etapas. NOTA: tevbias y tevscale solo se aplican con GX_TEV_ADD y GX_TEV_SUB, como se aprecian en las operaciones


Gráficos 3D de Wii

Cargando los registros de color del TEV void GX_SetTevColor(u8 tev_regid, GXColor color) Función que carga uno de los registros de color con el valor pasado en la estructura GXColor. tev_regid→ GX_TEVREG0 o GX_TEVREG1 o GX_TEVREG2 color→

Color RGBA8 pasado en una estructura GXColor

void GX_SetTevColorS10(u8 tev_regid,GXColorS10 color); Función que carga uno de los registros de color con el valor pasado en la estructura GXColorS10. (cada componente de color es una constante de 10 bits con signo, lo que equivale a un rango de -1024 a 1023 o en numero flotante de -4.0f a 4.0f que solo tiene efectividad en el operando d porque en los otros se recorta) tev_regid→ GX_TEVREG0 o GX_TEVREG1 o GX_TEVREG2 color→

Constantes de color pasadas en una estructura GXColorS10


Gráficos 3D de Wii Las Constantes del TEV void GX_SetTevKColorSel(u8 tevstage,u8 sel); Función que conecta el tipo de constante con la etapa de Color del TEV.

tevstage→Numero de etapa del TEV. Puede ser desde GX_TEVSTAGE0 a GX_TEVSTAGE15

sel→Una de estas constantes: GX_TEV_KCSEL_1 → 1 GX_TEV_KCSEL_7_8 → 7/8 GX_TEV_KCSEL_3_4 → 3/4 GX_TEV_KCSEL_5_8 → 5/8 GX_TEV_KCSEL_1_2 → 1/2 GX_TEV_KCSEL_3_8 → 3/8 GX_TEV_KCSEL_1_4 → 1/4 GX_TEV_KCSEL_1_8 → 1/8 GX_TEV_KCSEL_K0 → Registro 0 de Constantes GX_TEV_KCSEL_K1 → Registro 1 de Constantes GX_TEV_KCSEL_K2 → Registro 2 de Constantes GX_TEV_KCSEL_K3 → Registro 3 de Constantes GX_TEV_KCSEL_K0_R → Valor de Rojo en Registro 0 de Constantes GX_TEV_KCSEL_K1_R → Valor de Rojo en Registro 1 de Constantes GX_TEV_KCSEL_K2_R → Valor de Rojo en Registro 2 de Constantes GX_TEV_KCSEL_K3_R → Valor de Rojo en Registro 3 de Constantes GX_TEV_KCSEL_K0_G → Valor de Verde en Registro 0 de Constantes GX_TEV_KCSEL_K1_G → Valor de Verde en Registro 1 de Constantes GX_TEV_KCSEL_K2_G → Valor de Verde en Registro 2 de Constantes GX_TEV_KCSEL_K3_G → Valor de Verde en Registro 3 de Constantes GX_TEV_KCSEL_K0_B → Valor de Azul en Registro 0 de Constantes GX_TEV_KCSEL_K1_B → Valor de Azul en Registro 1 de Constantes GX_TEV_KCSEL_K2_B → Valor de Azul en Registro 2 de Constantes GX_TEV_KCSEL_K3_B → Valor de Azul en Registro 3 de Constantes GX_TEV_KCSEL_K0_A → Valor de Alfa en Registro 0 de Constantes GX_TEV_KCSEL_K1_A → Valor de Alfa en Registro 1 de Constantes GX_TEV_KCSEL_K2_A → Valor de Alfa en Registro 2 de Constantes GX_TEV_KCSEL_K3_A → Valor de Alfa en Registro 3 de Constantes


Gráficos 3D de Wii

void GX_SetTevKAlphaSel(u8 tevstage,u8 sel); Función que conecta el tipo de constante con la etapa de Alfa del TEV. tevstage→Numero de etapa del TEV. Puede ser desde GX_TEVSTAGE0 a GX_TEVSTAGE15

sel→Una de estas constantes: GX_TEV_KASEL_1 GX_TEV_KASEL_7_8 GX_TEV_KASEL_3_4 GX_TEV_KASEL_5_8 GX_TEV_KASEL_1_2 GX_TEV_KASEL_3_8 GX_TEV_KASEL_1_4 GX_TEV_KASEL_1_8 GX_TEV_KASEL_K0_R GX_TEV_KASEL_K1_R GX_TEV_KASEL_K2_R GX_TEV_KASEL_K3_R GX_TEV_KASEL_K0_G GX_TEV_KASEL_K1_G GX_TEV_KASEL_K2_G GX_TEV_KASEL_K3_G GX_TEV_KASEL_K0_B GX_TEV_KASEL_K1_B GX_TEV_KASEL_K2_B GX_TEV_KASEL_K3_B GX_TEV_KASEL_K0_A GX_TEV_KASEL_K1_A GX_TEV_KASEL_K2_A GX_TEV_KASEL_K3_A

→ 1 → 7/8 → 3/4 → 5/8 → 1/2 → 3/8 → 1/4 → 1/8 → Valor de Rojo en Registro 0 de Constantes → Valor de Rojo en Registro 1 de Constantes → Valor de Rojo en Registro 2 de Constantes → Valor de Rojo en Registro 3 de Constantes → Valor de Verde en Registro 0 de Constantes → Valor de Verde en Registro 1 de Constantes → Valor de Verde en Registro 2 de Constantes → Valor de Verde en Registro 3 de Constantes → Valor de Azul en Registro 0 de Constantes → Valor de Azul en Registro 1 de Constantes → Valor de Azul en Registro 2 de Constantes → Valor de Azul en Registro 3 de Constantes → Valor de Alfa en Registro 0 de Constantes → Valor de Alfa en Registro 1 de Constantes → Valor de Alfa en Registro 2 de Constantes → Valor de Alfa en Registro 3 de Constantes


Gráficos 3D de Wii

void GX_SetTevKColor(u8 sel, GXColor col); Función que carga uno de los registros de constantes con el valor pasado en la estructura GXColor. tev_regid→ Especifica 0 a 3 para el registro color→

Color RGBA8 pasado en una estructura GXColor

void GX_SetTevKColorS10(u8 sel, GXColorS10 col); Función que carga uno de los registros de color con el valor pasado en la estructura GXColorS10. (cada componente de color es una constante de 10 bits con signo, lo que equivale a un rango de -1024 a 1023 o en numero flotante de -4.0f a 4.0f que solo tiene efectividad en el operando d) tev_regid→ GX_TEVREG0 o GX_TEVREG1 o GX_TEVREG2 color→

Constantes de color pasadas en una estructura GXColorS10


Grรกficos 3D de Wii

(pagina intencionadamente en blanco)


Gráficos 3D de Wii Funciones de gu.h Aquí voy a describir el uso de alguna de las funciones en gu.h que considero convenientes explicar. Lo primero que debéis saber es que utilizan 4 tipos de de datos fundamentales: Vector, Mtx, Mtx44 y Quaternion (que son un tipo de vectores de 4 dimensiones, sobre los que no voy a entrar en materia) void guPerspective(Mtx44 mt,f32 fovy,f32 aspect,f32 n,f32 f); Se usa para crear la matriz de proyección en perspectiva, como ya pudimos ver en el capitulo de Vectores y Matrices mt→ matriz 4x4 que será ajustada fovy→ Angulo de perspectiva (Comúnmente 45, 60, 90) aspect→ esto es la relacion de aspecto de la pantalla: 1.333f para 4:3 n→ Z cercana, usualmente 1.0f f→ Z lejana, a mi me gusta poner 1000.0f Con ésta perspectiva y usando GX_LEQUAL en GX_SetZMode(), la Z mas cercana a la pantalla y visible, sería -1.0f y la más alejada, -1000.0f

void guOrtho(Mtx44 mt,f32 t,f32 b,f32 l,f32 r,f32 n,f32 f); Se usa para crear la matriz de proyección ortográfica, como ya pudimos ver en el capitulo de Configurando la proyección para gráficos 2D mt→ matriz 4x4 que será ajustada t→ límite superior (para 2D, pon 0.0f) b→ límite inferior (para 2D, pon el alto de la pantalla -1) l→ límite a la izquierda (para 2D, pon 0.0f) r→ límite a la derecha (para 2D, pon el ancho de la pantalla -1) n→ Z cercana, usualmente 0.0f f→ Z lejana, a mi me gusta poner 1000.0f


Gráficos 3D de Wii void guLightFrustum(Mtx mt,f32 t,f32 b,f32 l,f32 r,f32 n,f32 scaleS,f32 scaleT,f32 transS,f32 transT); Establece el Frustum (volumen visual) para un punto de luz. Esto se puede usar para ajustar la proyección en una Matriz de Textura con el fin de proyectar sombras, reflejos, etc. mt→ matriz 3x4 que será ajustada t→ límite superior (yo uso -256.0f) b→ límite inferior (yo uso 256.0f) l→ límite a la izquierda (yo uso -256.0f *1.33333f) r→ límite a la derecha (yo uso -256.0f *1.33333f) n→ Z cercana, (yo uso 256 para fovy 90 y 442 para fovy 60 en guPerspective) scaleS→ factor de escala S (usualmente, 0.5f) scaleT→ factor de escala T (usualmente, 0.5f) transS→ desplazamiento en S (usualmente, 0.5f) transT→ desplazamiento en T (usualmente, 0.5f)

void guLookAt(Mtx mt,Vector *camPos,Vector *camUp,Vector *target); Ajusta la posición de la cámara mt→ matriz 3x4 que será ajustada camPos→ Posición de la cámara en el espacio camUp→ Vector que señala la dirección arriba con respecto al plano de la cámara target→ Punto del espacio al que mira la cámara Nota: camUp necesita lógica adicional para ajustarlo correctamente para que el vector sea perpendicular al que forman camPos y target

void guVecNormalize(Vector *v); Normaliza el vector pasado como argumento (o sea, lo vuelve un vector unitario)


Gráficos 3D de Wii void guVecCross(Vector *a,Vector *b,Vector *axb); Calcula el producto cruzado de dos vértices a y b devolviendo el resultado en axb. Esto se puede utilizar para calcular la normal de un triángulo partiendo de sus vértices. Supongamos que los vértices son tres vectores v1,v2,v3 en sentido horario: Vector a,b,norm; guVecSub(&v2, &v1, &a); // restamos vértices 2 y 1 para el primer vector guVecSub(&v3, &v1, &b); // restamos vértices 3 y 1 para el segundo vector guVecCross(&a, &b, &norm); // calculamos producto cruzado guVecNormalize(&norm); // obtenemos vector normal (unitario)

void guMtxIdentity(Mtx mt); Carga matriz identidad. Esta matriz no tiene efecto si se concatena con otras

void guMtxConcat(Mtx a,Mtx b,Mtx ab); Función de concatenación de matrices: aplica primero los cambios de a y luego los cambios de b sobre la matriz de destino ab


Gráficos 3D de Wii void guMtxScale(Mtx mt,f32 xS,f32 yS,f32 zS); Inicializa la matriz con el factor de escala para X,Y, Z. Si por ejemplo X=Y=Z=2.0f el objeto tendría el doble de tamaño.

void guMtxScaleApply(Mtx src,Mtx dst,f32 xS,f32 yS,f32 zS); Aplica el factor de escala para X,Y,Z en la matriz src y devuelve en dst el resultado. De esta forma, no es necesario usar guMtxConcat(); para escalar

void guMtxTrans(Mtx mt,f32 xT,f32 yT,f32 zT); Inicializa la matriz posicionando en X,Y,Z. De esta forma, podemos posicionar un objeto en el espacio. Nota: esta función tiene un bug llamar antes

a guMtxIdentity(mt) o modificar la función en

gu_psasm.S con las siguientes lineas: ps_guMtxTrans: lis addi lfs lfs stfs stfs .......................

r9,Unit01@ha r9,r9,Unit01@l fr4,0(r9) fr5,4(r9) fr4,16(r3) // esta es la nueva linea a añadir fr1,12(r3)

void guMtxTransApply(Mtx src,Mtx dst,f32 xT,f32 yT,f32 zT); Aplica la nueva posición X,Y,Z en la matriz src y devuelve en dst el resultado. De esta forma, no es necesario usar guMtxConcat(); para mover


Gráficos 3D de Wii u32 guMtxInverse(Mtx src,Mtx inv); Obtiene matriz inversa (usada para ajustar la matriz de normales, como puedes ver en Matrices de Normal y de Textura) Devuelve 0 si falla y 1 si OK

void guMtxTranspose(Mtx src,Mtx xPose); Funcion que intercambia filas y columnas en la matriz, excepto las casillas posicionales que las rellena con 0. Se usa para ajustar la matriz de normales (ver Matrices de Normal y de Textura)

void guMtxRotRad(Mtx mt,const char axis,f32 rad); Función que devuelve una matriz de rotación sobre el eje axis, girando rad radianes. Axis puede tener los siguientes valores: 'X' → rotación sobre el eje X 'Y' → rotación sobre el eje Y 'Z' → rotación sobre el eje Z Función que devuelve una matriz de rotación sobre el eje axis, girando rad radianes.

void guMtxRotAxisRad(Mtx mt,Vector *axis,f32 rad); Función que devuelve una matriz de rotación sobre el eje marcado por el vector axis, girando rad radianes.


Grรกficos 3D de Wii

void guMtxCopy(Mtx src,Mtx dst); Simplemente, copia la matriz fuente en la de destino. void guVecMultiply(Mtx mt,Vector *src,Vector *dst); Multiplica el vector src por la matriz y lo devuelve en dst. Esto lo puedes utilizar para posicionar puntos en el espacio con el fin de detectar colisiones, por ejemplo, partiendo de las matrices que posiciona el objeto en el espacio

void guVecAdd(Vector *a,Vector *b,Vector *ab); Simplemente, suma el vector a y b y lo devuelve en ab

void guVecSub(Vector *a,Vector *b,Vector *ab); Resta al vector a el b y lo devuelve en ab


Gráficos 3D de Wii Funciones de gx.h extras void GX_Flush(); Fuerza la limpieza del FIFO de la CPU asegurando que todos los comandos retenidos le llegan a la GPU void GX_DrawDone(); Fuerza el el envío de los comandos retenidos en el FIFO y aguarda hasta que todos los comandos se ejecutan en la GPU y ésta termina con las operaciones de escritura en el front buffer. void GX_SetZMode(u8 enable,u8 func,u8 update_enable); Función que ajusta el modo de operación del buffer Z enable → GX_TRUE permite la comparación de la Z procedente de la primitiva, con el Buffer Z. GX_FALSE la desconecta y no se realiza el test. func → Función de comparación: GX_NEVER → Nunca se pasa el test (siempre falla el pixel) GX_LESS → Pasa si Z es menor GX_EQUAL → Pasa si Z es igual GX_LEQUAL → Pasa si Z es Menor o Igual GX_GREATER → Pasa si Z es Mayor GX_NEQUAL → Pasa si Z no es igual GX_GEQUAL → Pasa si Z es Mayor o Igual GX_ALWAYS → Pasa siempre (siempre se escribe el pixel) update_enable → Habilita las escrituras del Buffer Z con GX_TRUE y las deshabilita con GX_FALSE. Se puede utilizar esto para escribir píxeles usando la función de comparación sin modificar el contenido del Buffer Z


Gráficos 3D de Wii

void GX_SetCullMode(u8 mode); Esta función se utiliza para seleccionar que polígonos se dibujan en base a su geometría. Los polígonos se definen de forma que sus vértices en relación al observador, están ordenados siguiendo un sentido horario. De esta forma, la función SetCullMode puede conseguir que no se dibujen los polígonos que quedan detrás o delante del objeto en función a que desde el punto de vista del observador, su vértices se vean en sentido horario o en sentido antihorario. mode → GX_CULL_NONE No se oculta ningún polígono GX_CULL_FRONT Se ocultan los polígonos frontales GX_CULL_BACK Se ocultan los polígonos traseros GX_CULL_ALL No se dibuja ninguno (sirve para putear XDDD)

void GX_SetClipMode(u8 mode); Conecta o desconecta el recorte de geometría (definido por GX_SetScissor()). Admite GX_CLIP_DISABLE y GX_CLIP_ENABLE


Gráficos 3D de Wii void GX_CallDispList(void *list,u32 nbytes); Esta función es tremendamente potente y versátil ya que permite incluir una llamada en el FIFO que permite ejecutar una lista de comandos /datos de forma externa. Se supone que se debería utilizar en conjunción GX_BeginDispList y GX_EndDispList que se utilizan para desviar la atención del FIFO y hacer que rellene nuestra lista sin dibujarla, con el fin de prepararla, pero según mis pruebas, éstas dos ultimas funciones no trabajan bien. Aún así es posible hacer la lista a mano, siendo cuidadoso con el tema de la alineación de datos. La lista debe estar alineada a 32 bytes y nbytes debe ser divisible entre 32 tambien, rellenado el hueco que quede entre el último comando y el final, con ceros. También hay que entender que la lista no se puede modificar hasta que finalice de dibujarse el frame, pues una vez ejecutada la función CallDispList, la cosa queda a la espera de que la GPU procese la lista. El uso mas frecuente que se le puede dar, es formar listas de vértices y polígonos y en ese sentido he estado trabajando, por lo que es cuestión de tiempo que publique dicho trabajo (que está bastante avanzado, pero ahora mismo, entre ésta documentación y los ejemplos, no doy abasto)


Gráficos 3D de Wii Conclusión y Agradecimientos Cuando emprendí la tarea de escribir éste documento, tenía muy claro que iba a ser una tarea que iba a requerir bastante trabajo, pues la idea no era aportar una simple documentación de las funciones, si no mostrar como utilizarlas de forma practica y en funcionamiento, no solo porque los mas novatos en 3D puedan tener dificultades para entender el uso de las funciones sueltas (que en éste caso, la falta de entendimiento se suple por un código ya formado y explicado hasta donde me es posible, pues obviamente, el lector tendrá que hacer un esfuerzo por su parte), si no que al mismo tiempo, sirviera como referencia rápida para los que trabajemos un poco más a fondo éstas cuestiones. También ha supuesto un esfuerzo extra no solo para tratar de trasladaros lo que yo se, si no lo que dejo de saber. Así por ejemplo, ha resultado útil aprender como se trabaja con paletas o explorar algunas cosas un poco mas allá de lo que uno sabe ( o necesita saber) simplemente, para poder documentaros a vosotros. Todo eso aderezado con la construcción de algunos ejemplos que muestren algunas de estas cosillas tan chulas que se pueden hacer o que os interese hacer. Evidentemente, este trabajo no lo cubre todo. Se podría decir que el objetivo no era cubrirlo todo, si no cubrir lo fundamental y lo suficiente, pero también es cierto que cansa preparar tantas páginas y que un documento con 70 páginas ya es un documento respetable. Siento no haber añadido algún gráfico extra que quizá hubiera venido bien, pero el trabajo se ha acumulado y hay que acabarlo algún día XD. Ah si! Agradecimientos: pues a ese 'bicho malo' llamando Marcan y a la gente que ha preparado libogc y todo lo demás: algunas cosas la he aprendido estudiando el código fuente de las librerías, y ejemplos antiguos para Gamecube, de cosas que hay colgadas por la red, entre ellas documentación del hardware y pequeñas informaciones desperdigadas sobre el formato de las texturas, etc. Y otras las se de cuando las


Gráficos 3D de Wii aceleradoras 3D solo existian en el terreno profesional y habia que hacerlo todo a base de CPU en el PC. Otras, tienen conexión con OpenGL o de trabajar con DirectX, que ya te dan una idea vaga de por donde pueden ir los tiros en ocasiones. Espero de veras que la información que proporciono os sea útil y no se os haga demasiado pesada: a mi al menos, me ha costado bastante esfuerzo empaparme de como funciona la consola que considero la sucesora de la Playstation 2 a nivel Homebrew (opinión personal de alguien que brilló en la scene de PS2) y desde hace un tiempo, Wii es mi base de operaciones y el foco de mi interés. Nunca tuve GameCube y eso ha hecho que haya partido desde mucho mas atrás que otras personas. Quizá gracias a eso, ahora mismo cuentas con una documentación que en otro momento, no hubiera preparado para vosotros pues estaría liado en otras cuestiones o me hubiera dado pereza prepararla XDDD En fin un fuerte abrazo y espero disfrutar de vuestros trabajos y haberos facilitado un poco la vida. Saludos y al tajo.


Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.