9

数日前、ベジェ曲線を効率的に描画する方法を調べ始めたところ、Charles Loop と Jim Blinn によって開発された非常に興味深い方法に出会いました。しかし、彼らのアルゴリズムで多くの実験を行った後、3 次曲線をレンダリングできるようには思えません。二次方程式は問題ありません。問題ありません。

これまでに見つけた唯一のリソースは次のとおりです。

GPU Gems 3 チャプター 25

カービー・ブルース

プログラマブル グラフィックス ハードウェアを使用した解像度に依存しない曲線レンダリング

テストを迅速に開始して実行するために、XNA でこれを行っています。基本的に、頂点を含むテクスチャ座標を GPU に渡し、透視変換を適用し、ピクセル シェーダーのすべての記事に記載されている式を使用して、最終結果をレンダリングします。ただし、問題は (私が思うに) テクスチャ座標の計算方法にあります。このコードをチェックしてください:

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 になります。それはあまり役に立たないわけではないのに、なぜ彼らは記事でそれについて言及するのでしょうか?

もちろん、これは discr を不正確にし、それがどのタイプの曲線であるかを判断することさえできません。

そのコードをしばらくいじった後、Loop と 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 は今では少なくとももう少し正確なようです。少なくとも正しい符号があり、制御点がカスプを形成するように配置されている場合はゼロです。ただし、曲線がしばらくの間ランダムに消え (ピクセル シェーダーの式は常に 0 より大きい)、制御点をより正方形の形状に戻すと元に戻ることを除いて、同じ視覚的結果が得られます。ちなみに、ピクセルシェーダーコードは次のとおりです。

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

それは私が今考えることができるすべての有用な情報についてです. 何が起こっているのか誰にも分かりませんか?私はそれらを使い果たしているからです。

4

1 に答える 1

7

私は論文とあなたのコードを見ていましたが、M3 行列への乗算が欠けているようです。

p1、p2、p3、および p4 座標を行列に配置し、行列式を計算するために使用する前に、M3 行列を乗算する必要があります。例えば。

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;

次に、C マトリックスの各行を、コード内の m1 から m4 マトリックスの座標として使用します。行の最初と 2 番目の値は x、y 座標で、最後の値は w 座標です。

最後に、テクスチャ座標のマトリックスに M3 の逆数を掛ける必要があります。

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;

結果のマトリックスの各行は、シェーダーに必要なテクスチャ座標に対応します。

私はまだそれを自分で実装していないので、それがあなたの問題を解決することを保証することはできません. 論文を読んだ後、あなたの実装に欠けていることに気付いたのは単純です。

これが役に立てば幸いです。間違っている場合は、すぐに試してみるので教えてください。

于 2012-04-26T15:43:29.583 に答える