2012-04-24 59 views
8

幾天前我開始高效地繪製貝塞爾曲線,並且我發現了由Charles Loop和Jim Blinn開發的這個方法,它看起來很有趣。在經過大量的算法實驗之後,我似乎無法讓它能夠渲染三次曲線。 Quadratics很好,沒有問題。Bézier曲線,Loop和Blinn風格

我發現的唯一的資源至今如下:

GPU Gems 3 Chapter 25

Curvy Blues

Resolution Independent Curve Rendering using Programmable Graphics Hardware

爲了讓測試快速啓動和運行,我在做這個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似乎至少有點正確。至少它有正確的標誌,當控制點排列成尖頭時,它是零。儘管我仍然得到相同的視覺效果,除了曲線隨機消失一段時間(像素着色器公式始終大於零)並在將控制點移回更多方形後返回。這裏是像素着色器代碼的方式:

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

這是關於我現在可以想到的所有有用的信息。有誰知道發生了什麼事?因爲我用完了。

+0

我開始自己實施這個方法,當我有一些工作時會回發。只是想讓你知道這個問題沒有被放棄:) – Ani 2012-04-25 16:31:00

+0

@ananthonline真棒!請做! – Roliga 2012-04-26 14:32:35

+0

我也有這個問題。你有沒有得到它的工作?你能回答我的問題嗎? http://stackoverflow.com/questions/15519142/resolution-independent-cubic-bezier-drawing-on-gpu-blinn-loop – scippie 2013-03-20 12:27:29

回答

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矩陣在你的代碼。行的第一個和第二個值是x,y座標,最後一個是w座標。

最後,紋理座標矩陣需要被M3的逆矩陣所代替,例如: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; 

結果矩陣的每一行對應於座標所需的着色器中的紋理。

我自己還沒有實現它,所以不能保證它會解決你的問題。這只是我在閱讀論文後注意到的在執行過程中丟失的內容。

我希望這會有所幫助,如果我錯了,請告訴我,因爲我會很快嘗試這個。

+0

太棒了!不能相信我把它弄糟了:D問題是,雖然det1總是好像是0,但這絕對是不對的。我將在明天做更多的測試,看看我能否找到問題。告訴我,如果你有任何想法。 – Roliga 2012-04-26 18:39:26

+1

在第一段的積分立方體部分(4.4)中的論文中,它確實表示,對於整數立方體,它將具有等於零的第一行列式。這將着色器代碼等式減少到k^3-lm – rdsgalvao 2012-04-27 08:47:20

+0

哦對。我之所以認爲是錯誤的原因是因爲我只認爲實際上有一個循環的曲線應該使用循環方程,但似乎並非如此,所以在實現循環方程之後,這一切都奏效了。現在結束這個事情,所以它可以實際上被使用:D如果我遇到更多的問題,我會再次發表評論。 – Roliga 2012-04-30 01:03:33