2016-02-21 36 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