Entrega 3 - Computacion-Visual-Interactiva/proyecto GitHub Wiki
-
Cómo se hizo: Se accedió al Edit Mode, se seleccionaron las caras correspondientes a cada ala y al cuerpo, y se utilizó la opción
P → Selection
para separarlos como objetos independientes. -
Por qué: La animación de aleteo requiere que cada ala tenga sus propios vértices para rotar de forma independiente, sin afectar el cuerpo.
-
Los objetos se renombraron como
Body
,LeftWing
yRightWing
. -
Esto permite que al exportar el modelo en formato OBJ, los nombres se conserven en las cabeceras (
o Body
,o LeftWing
,o RightWing
) y el script Python los utilice para determinar a qué parte pertenece cada triángulo.
-
Se activaron las opciones Objects as OBJ Objects y Objects as OBJ Groups al exportar.
-
Esto introduce líneas
o ...
yg ...
en el archivo, facilitando que el conversor Python asigne correctamente un flag a cada vértice:-1
para el ala izquierda,+1
para la derecha,0
para el cuerpo.
pythonCopiarEditarkey = (vi, ti, wingFlag) # vi = índice posición, ti = índice UV
-
Por qué: Si no se incluye el
wingFlag
como parte de la clave, vértices compartidos entre el cuerpo y un ala se fusionarían, lo que provoca que el cuerpo también se anime como ala.
cppCopiarEditarstruct Vertex { float3 Pos; float2 UV; float Wing; };
-
Se genera un array interleaved de 6
floats
por vértice:(x, y, z)
,(u, v)
yflag
.
-
Se utiliza un
OrderedDict
para mapear por primera vez cada tupla(vi, ti, flag)
a un índice. En llamadas siguientes, se reutiliza ese índice, evitando duplicados.
cppCopiarEditar{0, 0, 3, VT_FLOAT32, false}, // Pos → ATTRIB0 {1, 0, 2, VT_FLOAT32, false}, // UV → ATTRIB1 {2, 0, 1, VT_FLOAT32, false}, // Flag → ATTRIB2
-
Por qué: Se configuran los tres atributos esperados por el shader. El layout usa un único
slot
constride = sizeof(Vertex)
(24 bytes).
cppCopiarEditarstruct VSConstants { float4x4 WorldViewProj; float WingAngle; };
-
Por qué: Al pasar
WingAngle
como constante al vertex shader, se evita recalcular senos y cosenos en cada vértice. La frecuencia del batido queda bajo control del CPU.
cppCopiarEditarfloat angularRevPS = kSpeed / (2*PI_F); float wingFreq = kWingFactor * angularRevPS; CB->WingAngle = sin(m_PathTime * 2*PI_F * wingFreq) * kWingAmp;
-
Por qué: La frecuencia del aleteo está ligada a la velocidad de vuelo. Reducir
kSpeed
ralentiza también el batido.
cppCopiarEditarfloat theta = m_PathTime * kSpeed; pos = { kRadius*cosθ, y(t), kRadius*sinθ };
cppCopiarEditary(t) = kBaseHeight + kBobAmp * sin(2π kBobFreq t)
-
Se agrega un armónico suave para romper la simetría perfecta del movimiento.
cppCopiarEditarforward = normalize({ -kRadius*sinθ, 0, kRadius*cosθ }); World = MakeWorld(pos, forward, {0, 1, 0});
-
Por qué: El cuerpo sólo rota en yaw (alrededor del eje Y), lo que evita movimientos bruscos y mantiene la estabilidad visual.
hlslCopiarEditarif (abs(IN.WingFlg) > 0.5) { float pivot = PIVOT_X * IN.WingFlg; p.x -= pivot;
float a = g_WingAngle * IN.WingFlg; float2 xy = mul(float2x2(cos(a), -sin(a), sin(a), cos(a)), p.xy); p.x = xy.x; p.y = xy.y; p.x += pivot;
}
-
Traslación a la bisagra: evita que el ala se desprenda visualmente.
-
Rotación en XY (eje Z): produce un aleteo vertical puro.
-
Signo opuesto: hace que ambas alas suban y bajen en sincronía.
-
Exclusión del cuerpo: como
WingFlg == 0
, el cuerpo no entra al bloque.
Parámetro | Valor | Justificación |
---|---|---|
kRadius | 6 u | Circulación amplia y legible. |
kSpeed | 0.20 rad/s | Vuelo pausado (31s por vuelta). |
kBobAmp | 0.25 u | Ondulación perceptible. |
kBobFreq | 0.8 Hz | 3 bobs por vuelta. |
kWingFactor | 12 | ~3 batidos por bob. |
kWingAmp | 0.35 rad | Aleteo de ~20°. |
PIVOT_X | 0.02 u | Bisagra realista visualmente. |
-
Movimiento: La mariposa describe un vuelo circular fluido, sube y baja suavemente, y se orienta tangencialmente sin cabecear.
-
Animación: Ambas alas se abren y cierran de forma sincronizada, con una amplitud y frecuencia que se ajusta dinámicamente a la velocidad.
-
Modularidad: Cambiar radio, velocidad o amplitud solo requiere ajustar constantes; no es necesario modificar geometría ni shaders.
-
Rendimiento: Solo se realiza un
draw-call
, con un buffer y un vertex shader simples. La animación esO(1)
por vértice.