2016-02-21 76 views
1

我試圖在WebGL中製作SVG(和其他2D矢量圖形)渲染器。
到目前爲止,我已經想出瞭如何繪製三角形的二次貝塞爾曲線。如何在WebGL中繪製Cubic Bezier

這是代碼。

var createProgram = function (vsSource, fsSource) { 
 

 
    var vs = gl.createShader(gl.VERTEX_SHADER); 
 
    gl.shaderSource(vs, vsSource); 
 
    gl.compileShader(vs); 
 

 
    var fs = gl.createShader(gl.FRAGMENT_SHADER); 
 
    gl.shaderSource(fs, fsSource); 
 
    gl.compileShader(fs); 
 

 
    var program = gl.createProgram(); 
 
    gl.attachShader(program, vs); 
 
    gl.attachShader(program, fs); 
 
    gl.linkProgram(program); 
 

 
    return program; 
 

 
} 
 

 
var vsSource = ` 
 
precision mediump float; 
 
attribute vec2 vertex; 
 
attribute vec2 attrib; 
 
varying vec2 p; 
 
void main(void) { 
 
    gl_Position = vec4(vertex, 0.0, 1.0); 
 
    p = attrib; 
 
} 
 
`; 
 

 
var fsSource = ` 
 
precision mediump float; 
 
varying vec2 p; 
 
void main(void) { 
 
    if (p.x*p.x - p.y > 0.0) { 
 
    // discard; 
 
    gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); 
 
    } else { 
 
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); 
 
    } 
 
} 
 
`; 
 

 

 
var canvas = document.querySelector('canvas'); 
 
var gl = canvas.getContext('webgl') || 
 
     canvas.getContext('experimental-webgl'); 
 

 
gl.clearColor(0.5, 0.5, 0.5, 1.0); 
 

 
var shapeData = [ 
 
    -0.5, 0, 
 
    0.5, 0, 
 
    0, 1 
 
]; 
 

 
var curveAttr = [ 
 
    0, 0, 
 
    1, 1, 
 
    0.5, 0 
 
]; 
 

 

 

 
var program = createProgram(vsSource, fsSource); 
 
gl.useProgram(program); 
 
var vertexLoc1 = gl.getAttribLocation(program, 'vertex'); 
 
var attribLoc1 = gl.getAttribLocation(program, 'attrib'); 
 

 
gl.clear(gl.COLOR_BUFFER_BIT); 
 

 

 

 
gl.useProgram(program); 
 
gl.enableVertexAttribArray(vertexLoc1); 
 
gl.enableVertexAttribArray(attribLoc1); 
 

 
var vertexBuffer1 = gl.createBuffer(); 
 
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer1); 
 
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(shapeData), gl.STATIC_DRAW); 
 
gl.vertexAttribPointer(vertexLoc1, 2, gl.FLOAT, false, 0, 0); 
 

 
var vertexBuffer2 = gl.createBuffer(); 
 
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer2); 
 
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(curveAttr), gl.STATIC_DRAW); 
 
gl.vertexAttribPointer(attribLoc1, 2, gl.FLOAT, false, 0,0); 
 

 
gl.drawArrays(gl.TRIANGLES, 0, shapeData.length/2);
<canvas></canvas>

我的問題是如何繪製三次Bezier,像上面。

我想應該用2個或幾個三角形來完成。 而且,我知道現在有必要將Cubic Bezier轉換爲Quadratic。

+0

您可能會發現有用的兩個鏈接。 (1)[使用可編程圖形硬件的分辨率獨立曲線渲染](https://www.microsoft.com/zh-cn/research/publication/resolution-independent-curve-rendering-using-programmable-graphics-hardware/)和(2)[Bézier曲線入門](https://pomax.github.io/bezierinfo/) – gman

回答

1

爲什麼二次工作

讓我們先理解爲什麼這適用於二次方。大家知道,二次貝塞爾曲線被描述爲

(1-)∙A + 2(1-)∙B ∙Ç 。

現在,如果堵塞曲線屬性插入這個公式,將得到

(1-)∙(0,0)+ 2(1-∙ (1/2,0) ∙(1,1)= (0,0)+( - ,0)+( )= (

所以通過平方所述第一座標和中減去第二,你總是對的一個點得到0曲線。

立方體更困難

三角形是特別容易的。如果你有一個角落一個三角形AÇ,那麼對於任意點在三角形內P(或事實上在飛機的任何地方)有寫P上獨特方式 asαABC其中α+β+γ= 1。這實質上只是不同的二維座標系之間的轉換。

對於三次Bézier曲線,您有四個定義點。這些凸包是一個四邊形。雖然曲線的參數化表示法仍然按照這四個點的線性組合來定義它,但這個過程不再容易可逆:您不能在平面中取一個點並將其分解爲線性係數中唯一的組合。即使您採用齊次座標(即參數的投影插值),如果要避免在內部三角形邊界處出現接縫,仍然必須在平面中放置角。由於可以得到三次Bézier曲線自相交,所以在Bézier曲線上甚至可以有點,它們對應於多於一個的值t

一種方法來解決立方米

你可以做的是有在隱式表示一探究竟。當你有

P X =(1-)∙ X + 3(1-)噸 (ttÇ Xd X
P Ŷ =(1-)∙ý + 3 (1- tý + 3(1-Çýd ý

您可以使用計算機代數系統(或手動結果計算)來消除t從這些方程得出所有其他變量中的第六級方程,其表徵點(曲線)上的點(P x,P 和)。爲簡單起見,可以選擇的仿射座標系統,使得
X = Ŷ = X = d Ŷ = 0, ÿ = D x = 1
換句話說您使用作爲原點,AD如作爲ÿ單位矢量的X單位矢量和AB。那麼相對於這個座標系中,點Ç有一些特殊的座標(ç X,C ÿ),你將不得不計算。如果使用這些座標作爲頂點的屬性,則該屬性的線性插值將導致(P x,P ),它們是當前點相對於該座標系的座標。

使用這些座標,該點的狀態到位於曲線上,根據my Sage computation,如下所示:

0 = (-27*Cy^3 + 81*Cy^2 - 81*Cy + 27)*Px^3 
    + (81*Cx*Cy^2 - 162*Cx*Cy - 27*Cy^2 + 81*Cx + 54*Cy - 27)*Px^2*Py 
    + (-81*Cx^2*Cy + 81*Cx^2 + 54*Cx*Cy - 54*Cx - 9*Cy + 9)*Px*Py^2 
    + (27*Cx^3 - 27*Cx^2 + 9*Cx - 1)*Py^3 
    + (27*Cy^3 + 81*Cx*Cy - 81*Cy^2 + 81*Cy - 54)*Px^2 
    + (-54*Cx*Cy^2 - 81*Cx^2 + 81*Cx*Cy + 81*Cx + 27*Cy - 54)*Px*Py 
    + (27*Cx^2*Cy - 9*Cx)*Py^2 
    + (-81*Cx*Cy + 27)*Px 

括號中的東西只依賴於控制點的座標,所以他們可能會成爲着色器代碼中的制服或每個面部屬性。在片段着色器中,您可以從插值位置屬性插入PxPy,並使用結果的符號來決定使用哪種顏色。

有很大的改進空間。這可能是選擇座標系更聰明的方式導致更簡單的公式。可能是這樣一個更簡單的公式,或者甚至上面的公式,可以通過巧妙地使用分配律來簡化。但我現在沒有時間去尋找更好的配方,上面的內容應該足以讓你走。

在特定情況下,我選擇座標系也存在一些問題。如果位於行AD,你可能要交換的角色一個dÇ。如果BC位於那條線上,那麼Bézier曲線本身就是一條線,這是另一種特殊情況,雖然它很容易實現。如果一個d是同一個點,你可以寫使用ABAC爲基礎載體不同的方程。區分所有這些特殊情況下,有一些數字錯誤的餘地,可能會非常痛苦。你可以通過例如只是使A的起源,本質上只是翻譯你的座標系統。resulting equation會更復雜,但也更一般,因爲它會同時涵蓋所有特殊情況。

+0

似乎我應該學習更多。我會花這個週末仔細閱讀並理解答案。這將是最大的暗示。非常感謝! – yomotsu