2012-04-24 18 views
8

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:

GPU Gems 3 Chapter 25

Curvy Blues

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.

+0

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

+0

@ananthonline ¡increíble! ¡Por favor, hazlo! – Roliga

+0

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

Respuesta

7

Estaba mirando el papel y su código, y parece que le falta la multiplicación a la matriz M3.

Las coordenadas p1, p2, p3 y p4 deben colocarse en una matriz y multiplicarse por la matriz M3, antes de usarlo para calcular los determinantes. por ej.

Matrix M3 = Matrix(
    1, 0, 0, 0, 
    -3, 3, 0, 0, 
    3, -6, 3, 0, 
    -1, 3, -3, 1); 
Matrix B = Matrix(
    p1.X, p1.Y, 0, 1, 
    p2.X, p2.Y, 0, 1, 
    p3.X, p3.Y, 0, 1, 
    p4.X, p4.Y, 0, 1); 
Matrix C = M3*B; 

Luego usa cada fila de la matriz C como las coordenadas para las matrices m1 a m4 en su código. Donde el primer y el segundo valor de la fila son las coordenadas x, y, y la última es la coordenada w.

Finalmente, la matriz de coordenadas de textura debe ser multiplicada por el inverso de M3 , por ejemplo.

Matrix invM3 = Matrix(
    1, 0, 0, 0, 
    1, 0.3333333, 0, 0, 
    1, 0.6666667, 0.333333, 0, 
    1, 1, 1, 1); 
Matrix F = Matrix(
    TexCoord1, 
    TexCoord2, 
    TexCoord3, 
    TexCoord4); 
Matrix result = invM3*F; 

Cada fila de la matriz resultante corresponde a las coordenadas de textura necesarias para el sombreador.

Todavía no lo he implementado, por lo que no puedo garantizar que eso resuelva su problema. Simplemente es lo que noté que falta en su implementación después de leer el documento.

Espero que esto ayude, si me equivoco, por favor díganme porque voy a probar esto pronto.

+0

¡Hombre impresionante! No puedo creer que lo haya estropeado tan mal: D El problema es sin embargo, det1 siempre parece ser 0, lo que definitivamente no es correcto. Voy a hacer más pruebas mañana y ver si puedo encontrar el problema. Dime si tienes alguna idea. – Roliga

+1

En el artículo de la sección Cubículos integrales (4.4) del primer párrafo, sí dice que para los cubics integrales tendrá el primer determinante igual a cero. Lo cual reduce la ecuación del código de sombreado a k^3-lm – rdsgalvao

+0

Oh, a la derecha. La razón por la que pensé que algo andaba mal fue porque solo pensé que las curvas que tienen un ciclo se supone que usan la ecuación de bucle, pero ese no parecía ser el caso, así que después de implementar la ecuación de bucle todo funcionó. Ahora a estructurar esto para que pueda ser usado: D Dejaré caer otro comentario si encuentro más problemas. – Roliga