Hace un par de días comencé a buscar curvas de bezier con mayor eficiencia, y encontré un método desarrollado por Charles Loop y Jim Blinn que me pareció muy interesante. Sin embargo, después de una gran cantidad de experimentos con su algoritmo, parece que no puedo obtener las curvas cúbicas. Los cuadráticos están bien, no hay problema allí.Bézier curves, Loop and Blinn style
Los únicos recursos que he encontrado hasta el momento son los siguientes:
Resolution Independent Curve Rendering using Programmable Graphics Hardware
Para obtener las pruebas en funcionamiento rápidamente, estoy haciendo esto en XNA. Básicamente estoy pasando coordenadas de textura con mis vértices a la GPU, aplicando una transformación de perspectiva y uso la fórmula mencionada en todos los artículos en un sombreador de píxeles para obtener el resultado final. Sin embargo, el problema (creo) radica en cómo calculo las coordenadas de la textura. Verifique este código:
public void Update()
{
float a1 = Vector3.Dot(p1, Vector3.Cross(p4, p3));
float a2 = Vector3.Dot(p2, Vector3.Cross(p1, p4));
float a3 = Vector3.Dot(p3, Vector3.Cross(p2, p2));
float d1 = a1 - 2 * a2 + 3 * a3;
float d2 = -a2 + 3 * a3;
float d3 = 3 * a3;
float discr = d1 * d1 * (3 * d2 * d2 - 4 * d1 * d3);
if (discr > 0)
{
Type = CurveTypes.Serpentine;
float ls = 3 * d2 - (float)Math.Sqrt(9 * d2 * d2 - 12 * d1 * d3);
float lt = 6 * d1;
float ms = 3 * d2 + (float)Math.Sqrt(9 * d2 * d2 - 12 * d1 * d3);
float mt = 6 * d1;
TexCoord1 = new Vector3(ls * ms, (float)Math.Pow(ls, 3), (float)Math.Pow(ms, 3));
TexCoord2 = new Vector3((3 * ls * ms - ls * mt - lt * ms)/3, ls * ls * (ls - lt), ms * ms * (ms - mt));
TexCoord3 = new Vector3((lt * (mt - 2 * ms) + ls * (3 * ms - 2 * mt))/3, (float)Math.Pow(lt - ls, 2) * ls, (float)Math.Pow(mt - ms, 2) * ms);
TexCoord4 = new Vector3((lt - ls) * (mt - ms), -(float)Math.Pow(lt - ls, 3), -(float)Math.Pow(mt - ms, 3));
}
else if (discr == 0)
{
Type = CurveTypes.Cusp;
}
else if (discr < 0)
{
Type = CurveTypes.Loop;
}
}
Disculpe el desastre, es solo un código de prueba. p1 ... p4 son los puntos de control en el espacio mundial, y TexCoord1 ... TexCoord4 son las coordenadas de textura correspondientes. Esta es una réplica de lo que se dice en el artículo de GPU Gems.
Aquí hay algunos problemas, primero cuando calculamos a3, usamos p2 para ambos parámetros, lo que por supuesto siempre resulta en un vector (0,0,0), y tomar el producto de puntos de eso y p3 siempre danos 0. Eso es bastante inútil, ¿por qué lo mencionarían en el artículo?
Esto, por supuesto, hará que el disco sea incorrecto, y ni siquiera podremos determinar qué tipo de curva es.
Después de jugar con ese código por un tiempo, decidí tratar de hacerlo exactamente por qué lo hicieron en el papel de Loop and Blinn. De eso obtengo algo como esto:
public void Update()
{
Matrix m1 = new Matrix(
p4.X, p4.Y, 1, 0,
p3.X, p3.Y, 1, 0,
p2.X, p2.Y, 1, 0,
0, 0, 0, 1);
Matrix m2 = new Matrix(
p4.X, p4.Y, 1, 0,
p3.X, p3.Y, 1, 0,
p1.X, p1.Y, 1, 0,
0, 0, 0, 1);
Matrix m3 = new Matrix(
p4.X, p4.Y, 1, 0,
p2.X, p2.Y, 1, 0,
p1.X, p1.Y, 1, 0,
0, 0, 0, 1);
Matrix m4 = new Matrix(
p3.X, p3.Y, 1, 0,
p2.X, p2.Y, 1, 0,
p1.X, p1.Y, 1, 0,
0, 0, 0, 1);
float det1 = m1.Determinant();
float det2 = -m2.Determinant();
float det3 = m3.Determinant();
float det4 = -m4.Determinant();
float tet1 = det1 * det3 - det2 * det2;
float tet2 = det2 * det3 - det1 * det4;
float tet3 = det2 * det4 - det3 * det3;
float discr = 4 * tet1 * tet3 - tet2 * tet2;
if (discr > 0)
{
Type = CurveTypes.Serpentine;
float ls = 2 * det2;
float lt = det3 + (float)((1/Math.Sqrt(3)) * Math.Sqrt(3 * det3 * det3 - 4 * det2 * det4));
float ms = 2 * det2;
float mt = det3 - (float)((1/Math.Sqrt(3)) * Math.Sqrt(3 * det3 * det3 - 4 * det2 * det4));
TexCoord1 = new Vector3(lt * mt, (float)Math.Pow(lt, 3), (float)Math.Pow(mt, 3));
TexCoord2 = new Vector3(-ms * lt - ls * mt, -3 * ls * lt * lt, -3 * ms * mt * mt);
TexCoord3 = new Vector3(ls * ms, 3 * ls * ls * lt, 3 * ms * ms * mt);
TexCoord4 = new Vector3(0, -ls * ls * ls, -ms * ms * ms);
}
else if (discr == 0)
{
Type = CurveTypes.Cusp;
}
else if (discr < 0)
{
Type = CurveTypes.Loop;
}
}
Adivina qué, eso tampoco funcionó. Sin embargo, el discr parece ser al menos un poco más correcto ahora. Al menos tiene el signo correcto, y es cero cuando los puntos de control están dispuestos para formar una cúspide. Todavía obtengo el mismo resultado visual, excepto que la curva desaparece al azar por un tiempo (la fórmula del sombreador de píxeles siempre es mayor que cero) y regresa después de mover el punto de control a una forma más cuadrada. Aquí está el código de sombreado de píxeles por cierto:
PixelToFrame PixelShader(VertexToPixel PSIn)
{
PixelToFrame Output = (PixelToFrame)0;
if(pow(PSIn.TexCoords.x, 3) - PSIn.TexCoords.y * PSIn.TexCoords.z > 0)
{
Output.Color = float4(0,0,0,0.1);
}
else
{
Output.Color = float4(0,1,0,1);
}
return Output;
}
que se trata de cualquier tipo de información que se me ocurre en este momento. ¿Alguien tiene alguna idea de lo que está pasando? Porque me estoy quedando sin ellos.
Empecé a implementar este método yo mismo y lo publicaré cuando tenga algo funcionando. Solo quería que supiera que esta pregunta no está abandonada :) – Ani
@ananthonline ¡increíble! ¡Por favor, hazlo! – Roliga
También estoy teniendo problemas con esto. hiciste que funcionara? ¿Puedes responder mi pregunta? http://stackoverflow.com/questions/15519142/resolution-independent-cubic-bezier-drawing-on-gpu-blinn-loop – scippie