Hola
Si empezamos más o menos por el principio, un cubemap es una estructura de datos que contiene 6 texturas en su interior formando un cubo.
Normalmente en un shader, accedemos a los pixeles de las texturas mediante dos coordenadas (u,v) que tienen un rango de valores de 0.0f a 1.0f.
Pero en un cubemap a diferencia de la metodología anterior accedemos a través de un vector. Es decir, nosotros damos un vector que tiene su origen en el centro de ese cubo que antes mencionábamos. Una vez tenemos ese vector colocado en el centro del cubo, la dirección del vector nos muestra el pixel de las 6 texturas que nos devolverá el sistema. Es como si de una colisión se tratara, el pixel que este tocando el vector será el elegido.
Toda esta introducción viene a raíz de que en muchos casos, las texturas de tipo cubemap, se utilizan para representar efectos de reflexión de la luz, representación de cielos, etc.
¿Pero y si necesitásemos el concepto de textura cubemap pero en vez de tener muchos pixeles por cada textura, solo buscásemos el hecho de tener un color asociado a cada uno de los lados?
Bien, alguien rápido de mente me podría decir: Usamos una textura cubemap de 1×1 píxeles por cada cara. Y sí, podría funcionar, pero no es muy recomendable por que los samplers de texturas en los shaders (es decir, los slots que tienes para poner texturas) están limitados a 16 y van muy buscados.
Otra respuesta podría ser una de esas representaciones de iluminación en un punto mediante esféricos armónicos (buscad PTR en el SDK de DirectX). Pero el problema de esta metodología es que es muy difícil actualizarla en tiempo real.
El tema está en recrear el mismo comportamiento que nos proporciona una cubemap o, más o menos, mediante interpolaciones entre 6 colores.
¿Lo bueno de este método? Que se puede actualizar en tiempo real y eso nos permite de una forma rápida el re-calculo de un volumen de ambiente en una zona determinada.
¿Lo malo de este método? Que pierdes cantidad de color dependiendo de cómo hagas los cálculos.
Voy a definir la función LERP(X, Y, n); tal y como se implementa en el lenguaje HLSL para que la podamos usar en C++.
//---------------------------------------------------------------------------------
// USE: Interpola entre X e Y
// IN: D3DXVECTOR3, D3DXVECTOR3, float - Hace la operacion: x + s (y − x)
// OUT: Void
//---------------------------------------------------------------------------------
D3DXVECTOR3 CDemo::Lerp(D3DXVECTOR3 &inX, D3DXVECTOR3 &inY, float inS)
{
D3DXVECTOR3 tmp = inX + (inS * (inY - inX));
return(tmp);
}
Y el codigo del Abs(x) que nos devuelve el absoluto de un valor
//---------------------------------------------------------------------------------
// USE: Devuelve valor absoluto
// IN: float
// OUT: Void
//---------------------------------------------------------------------------------
float CDemo::Abs(float inValue)
{
if(inValue >= 0.0f)
{
return(inValue);
}
else
{
return(inValue * -1.0f);
}
}
Este código lo utilizaremos más adelante para hacer las interpolaciones.
Para definir los 6 colores de este cubemap que vamos a hacer, es necesario que los coloquemos en unos lugares estratégicos en el espacio XYZ. Por lo tanto, pondremos imaginariamente un valor de color en la posición (1, 0, 0) como el color de +X. Luego pondremos otro color en la posición (-1, 0, 0) como el color –X y así sucesivamente para cada uno de los 3 ejes.
De este modo y sabiendo que, conceptualmente, tenemos los colores colocados en las coordenadas unitarias de los tres ejes, podemos interpolar el color resultante según la dirección de este y los 3 ejes.
Veamos el código:
D3DXVECTOR3 vDir = D3DXVECTOR3(GetCameraActive()->GetTarget());
D3DXVECTOR3 vNormDir;
D3DXVec3Normalize(&vNormDir,&vDir);
D3DXVECTOR3 vColX = Lerp(D3DXVECTOR3(m_vNegX.r,m_vNegX.g,m_vNegX.b),D3DXVECTOR3(m_vPosX.r,m_vPosX.g,m_vPosX.b),(vNormDir.x + 1.0f)*0.5f);// * Abs(vNormDir.x);
D3DXVECTOR3 vColY = Lerp(D3DXVECTOR3(m_vNegY.r,m_vNegY.g,m_vNegY.b),D3DXVECTOR3(m_vPosY.r,m_vPosY.g,m_vPosY.b),(vNormDir.y + 1.0f)*0.5f);// * Abs(vNormDir.y);
D3DXVECTOR3 vColZ = Lerp(D3DXVECTOR3(m_vNegZ.r,m_vNegZ.g,m_vNegZ.b),D3DXVECTOR3(m_vPosZ.r,m_vPosZ.g,m_vPosZ.b),(vNormDir.z + 1.0f)*0.5f);// * Abs(vNormDir.z);
D3DXVECTOR3 vColTmp = Lerp(vColY,vColX,Abs(vNormDir.x));
vColTmp = Lerp(vColTmp,vColZ,Abs(vNormDir.z));
Fijaros que se interpolan 3 colores según las componentes del vector que nos marca la dirección. Finalmente se hace una interpolación entre el eje ‘X’ y el eje ‘Y’ y el resultado de este con el eje ‘Z’, obteniendo el valor del color en esa dirección.
Es fácil de actualizar los 6 colores, y el cumulo de cálculos que hemos hecho solo implican sumas y multiplicaciones.
ERRATA: Añadido al código dos instrucciones Abs(…) necesarias para el cálculo correcto.
Un saludo