Hay pocas cosas tan útiles en programación como la interpolación, de hecho son tantos los casos en los que resulta útil (o incluso esencial) que ni me voy a molestar en mencionarlos.
Directamente dejo aqui un par de ejemplos, el primero es una función de interpolación lineal:
1 2 3 4
| float InterpLineal(float tiempo, float inicio, float fin)
{
return((1.0f - tiempo) * inicio + tiempo * fin);
} |
Esta es una función de interpolación cuadrática (“Easy In”):
1 2 3 4
| float InterpCuadIn(float tiempo, float inicio, float fin)
{
return(InterpLineal(tiempo * tiempo, incio, fin));
} |
Y por último una interpolación cuadrática (“Easy In-Out”):
1 2 3 4 5 6 7 8 9 10 11 12
| float InterpCuadInOut(float tiempo, float inicio, float fin)
{
float medio = (inicio + fin) * 0.5f;
tiempo *= 2.0f;
if(tiempo <= 1.0f)
{
return(InterpLineal(tiempo * tiempo, inicio, medio));
}
tiempo -= 1.0f;
return(InterpLineal(tiempo * tiempo, medio, fin));
} |
En los tres casos se fija el rango a interpolar con los parámetros inicio y fin, y con el parámetro tiempo se indica el punto de interpolación (por supuesto se asume que tiempo tendrá un valor comprendido entre 0.0 y 1.0).
En programación 3D hay pocas funciones tan necesarias como la raíz cuadrada. ¿Quién no recuerda el famoso teorema de Pitágoras? El cuadrado de la hipotenusa es igual a la suma de los cuadrados de los catetos
por lo tanto la hipotenusa será 
Esto lo podemos emplear con cualquier número de dimensiones, en 3D tendremos tres coordenas
que definen un punto o vector en un espacio tridimensional, aplicando el mismo teorema podemos saber el módulo (longitud) del vector
y a partir del módulo podemos, por ejemplo, normalizarlo (hacer que el vector sea de módulo 1).
Un ejemplo práctico:
1 2 3 4 5 6 7 8 9 10 11 12
| V3 &NormalizarVector(V3 &v)
{
float modulo = sqrt( v.x*v.x + v.y*v.y + v.z*v.z);
assert(modulo != 0.0f);
v.x /= modulo;
v.y /= modulo;
v.z /= modulo;
return(v);
} |
La función anterior para normalizar vectores es perfectamente correcta pero se puede optimizar bastante. En primer lugar calcular raíces cuadradas tiene un coste computacional bastante importante y en aplicaciones 3D como juegos se usa constantemente por lo que es interesante optimizarla todo lo posible. Además de la raíz cuadrada también podemos sustituir las tres divisiones por tres multiplicaciones, ¿cómo lo hacemos? usando la inversa de la raíz cuadrada
que nos permitirá multiplicar en lugar de dividir ya que la división está implícita en el cálculo.
¿Y cómo optimizamos la raíz cuadrada? Existen muchas formas de hacerlo pero vamos a ver la más rápida y sencilla, la raíz cuadrada inversa rápida (Fast InvSqrt):
1 2 3 4 5 6 7 8 9 10
| float InvSqrt (float x)
{
float x_div2 = x * 0.5f; // Dividimos x entre 2
int i = *(int*) &x; // Pasamos de float a int (ambos de 32 bits)
i = 0x5f3759df - (i >> 1); // Hacemos una primera aproximación del resultado
x = *(float*) &i; // Pasamos de int a float
x = x * (1.5f - x_div2 * x * x); // Recalculamos para aproximar más el resultado
return(x);
} |
Esta función se aprovecha de la forma interna de trabajo de la unidad de punto flotante (normativa IEEE 754) para calcular una buena aproximación del resultado exacto. No es más que una simple aproximación por el método Newton.
Este código se hizo muy popular por haber sido utilizado en el Quake 3 y aunque su origen no está nada claro parece que surgió de SGI (Silicon Graphics) hace bastantes años.
La nueva función de normalización vectorial quedaría de la siguiente manera:
1 2 3 4 5 6 7 8 9 10
| V3 &NormalizarVector(V3 &v)
{
float m = InvSqrt( v.x*v.x + v.y*v.y + v.z*v.z);
v.x *= m;
v.y *= m;
v.z *= m;
return(v);
} |
El incremento de velocidad conseguido es muy importante pero también hay que tener en cuenta que el resultado es una aproximación y que este código sólo puede usarse en procesadores cuyas unidades de punto flotante sigan la normativa IEEE 754.