B& #233; кривые Зье, петля и стиль Блинна

Пару дней назад я начал заниматься эффективным рисованием кривых Безье и наткнулся на этот метод, разработанный Чарльзом Лупом и Джимом Блинном, который показался мне очень интересным. Однако, после долгих экспериментов с их алгоритмом, я никак не могу заставить его отображать кубические кривые. С квадратиками все в порядке, проблем нет.

Пока я нашел только следующие ресурсы::

GPU Gems 3 Chapter 25

Curvy Blues

Рендеринг независимой от разрешения кривой с использованием программируемого графического оборудования

Чтобы быстро запустить тестирование, Я делаю это в XNA. По сути, я передаю координаты текстуры с моими вершинами на графический процессор, применяю преобразование перспективы и использую формулу, упомянутую во всех статьях, в пиксельном шейдере для рендеринга конечного результата. Однако проблема (, я думаю, )заключается в том, как я вычисляю координаты текстуры. Проверьте этот код:

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;
    }
}

Извините за беспорядок, это всего лишь тестовый код.p1...p4 - контрольные точки в мировом пространстве, а TexCoord1...TexCoord4 - соответствующие координаты текстуры. Это повторение того, что сказано в статье о GPU Gems.

Здесь есть несколько проблем: во-первых, при вычислении a3 мы используем p2 для обоих параметров, что, конечно, всегда приводит к (0,0,0 )вектору, и взяв скалярное произведение этого и p3 всегда будет давать нам 0. Это не совсем бесполезно, так зачем же упоминать об этом в статье?

Это, конечно, сделает дискр некорректным, и мы даже не сможем определить, к какому типу относится эта кривая.

Повозившись с этим кодом некоторое время, я решил попытаться сделать это именно так, как это было сделано в статье Loop and Blinn. Отсюда я получаю что-то вроде этого:

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;
    }
}

Угадайте, что это тоже не сработало. Тем не менее, сейчас Discr кажется более корректным. По крайней мере, у него правильный знак, и он равен нулю, когда контрольные точки расположены так, что образуют точку возврата. Я по-прежнему получаю тот же визуальный результат, за исключением того, что кривая случайно исчезает на некоторое время (, формула пиксельного шейдера всегда больше нуля )и возвращается после того, как я возвращаю контрольную точку к более квадратной форме. Вот, кстати, код пиксельного шейдера :

 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;
}

. Это почти вся полезная информация, которую я могу сейчас придумать. Кто-нибудь знает, что происходит? Потому что они у меня заканчиваются.

9
задан Roliga 24 April 2012 в 14:49
поделиться