Math 如何在WebGL中绘制三次贝塞尔曲线

Math 如何在WebGL中绘制三次贝塞尔曲线,math,svg,webgl,Math,Svg,Webgl,我正在尝试在WebGL中制作SVG(和其他2D矢量图形)渲染器。 到目前为止,我已经知道了如何用三角形绘制二次贝塞尔曲线 这是代码 var createProgram=函数(vsSource,fsSource){ var vs=gl.createShader(gl.VERTEX_着色器); gl.shaderSource(vs,vsSource); 总帐编译主管(vs); var fs=gl.createShader(gl.FRAGMENT\u着色器); gl.shaderSource(fs、

我正在尝试在WebGL中制作SVG(和其他2D矢量图形)渲染器。
到目前为止,我已经知道了如何用三角形绘制二次贝塞尔曲线

这是代码

var createProgram=函数(vsSource,fsSource){
var vs=gl.createShader(gl.VERTEX_着色器);
gl.shaderSource(vs,vsSource);
总帐编译主管(vs);
var fs=gl.createShader(gl.FRAGMENT\u着色器);
gl.shaderSource(fs、fsSource);
总帐编辑主任(fs);
var program=gl.createProgram();
总分类:attachShader(项目,vs);
总承包商助理(项目,财政司);
总账链接程序(程序);
返回程序;
}
var vsSource=`
精密中泵浮子;
属性向量2顶点;
属性向量2属性;
可变vec2p;
真空总管(真空){
gl_位置=向量4(顶点,0.0,1.0);
p=attrib;
}
`;
变量fsSource=`
精密中泵浮子;
可变vec2p;
真空总管(真空){
如果(p.x*p.x-p.y>0.0){
//丢弃;
gl_FragColor=vec4(0.0,0.0,0.0,1.0);
}否则{
gl_FragColor=vec4(1.0,0.0,0.0,1.0);
}
}
`;
var canvas=document.querySelector('canvas');
var gl=canvas.getContext('webgl')||
getContext(‘实验性webgl’);
gl.clearColor(0.5,0.5,0.5,1.0);
变量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(程序);
var vertexLoc1=gl.getAttributeLocation(程序“顶点”);
var attribLoc1=gl.getAttribLocation(程序“attrib”);
总账清除(总账颜色\缓冲\位);
gl.useProgram(程序);
gl.EnableVertexAttributeArray(vertexLoc1);
gl.EnableVertexAttributeArray(attribLoc1);
var vertexBuffer1=gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer1);
gl.bufferData(gl.ARRAY\u BUFFER、新Float32Array(shapeData)、gl.STATIC\u DRAW);
gl.VertexAttribute指针(vertexLoc1,2,gl.FLOAT,false,0,0);
var vertexBuffer2=gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer2);
gl.bufferData(gl.ARRAY\u BUFFER、新Float32Array(curveAttr)、gl.STATIC\u DRAW);
gl.VertexAttributePointer(attribLoc1,2,gl.FLOAT,false,0,0);
gl.DrawArray(gl.TRIANGLES,0,shapeData.length/2)
为什么二次型有效 让我们首先了解为什么这对二次型有效。如你所知,二次Bézier曲线被描述为

(一)−t) 二,∙A+2 t(1−(t)∙B+t2∙C

现在,如果将曲线属性插入此公式,则

(一)−t) 二,∙(0, 0) + 2 (1−t) t∙(1/2,0)+t2∙(1, 1) = (0,0)+(t−t2,0)+(t2,t2)= (t,t2)

因此,通过对第一个坐标进行平方运算并减去第二个坐标,曲线上的一个点总是得到0

立方体更难 三角形特别容易。如果你有一个角为a、B和C的三角形,那么对于三角形内的任何点P(或者实际上是平面上的任何地方),有一种独特的方法可以将P写成αa+βB+γC,α+β+γ=1。这本质上只是不同2D坐标系之间的转换

对于三次Bézier曲线,有四个定义点。它们的凸包是四边形的。虽然曲线的参数化表示仍然根据这四个点的线性组合来定义它,但这一过程不再容易逆转:你不能在平面上取一个点并将其唯一地分解为线性组合的系数。即使采用齐次坐标(即参数的投影插值),如果要避免内部三角形边界处的接缝,也必须使角位于平面中。由于可以使三次Bézier曲线自相交,因此Bézier曲线上甚至可以有对应于t的多个值的点

解决立方体问题的一种方法 你可以做的是仔细观察隐式表示。当你有

Px=(1−t) 三,∙Ax+3 (1−t) 2t∙Bx+3 (1−t) t2∙Cx+t3∙Dx
Py=(1−t) 三,∙Ay+3 (1−t) 2t∙到+3 (1−t) t2∙Cy+t3∙Dy

您可以使用计算机代数系统(或手动合成计算)从这些方程中消除t,从而在所有其他变量中生成六次方程,该方程表征了点(Px,Py)位于曲线上的事实。为了简化,您可以选择仿射坐标系,以便
Ax=Ay=Bx=Dy=0, By=Dx=1
换句话说,使用A作为原点,AD作为x单位向量,AB作为y单位向量。然后,关于这个坐标系,点C有一些特定的坐标(Cx,Cy),你需要计算。如果将这些坐标用作顶点的属性,则该属性的线性插值将产生(Px,Py),即当前点相对于该坐标系的坐标

根据这些坐标,点位于曲线上的条件如下:

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
括号中的内容仅取决于控制点的坐标,因此它们可以成为着色器代码中的统一属性或每面属性。在片段着色器中,您需要从插值位置属性中插入
Px
Py
,并使用结果的符号来决定要使用的颜色

还有很大的改进空间。可能是选择坐标系的更聪明的方法导致了一个更简单的公式。可能是这样一个简单的公式,或者甚至是上面的公式,可以通过巧妙地使用分配定律来简化很多。但是我现在没有时间去寻找更好的配方,上面的内容应该足以让你开始

在具体情况下,我在选择坐标系时也存在一些问题。如果B位于直线AD上,您可能希望交换A和D以及B和C的角色。如果B和C都位于该直线上,则Bézier曲线本身就是一条直线,这是另一种特殊情况,尽管很容易实现