WebGL(2D) - 入門編 - 9日目: 線の2次ベジェの先端を丸くする


# 目標

線の2次ベジェの先端を丸くする

<img width="432" alt="スクリーンショット 2016-08-01 18.54.17.png" src="https://qiita-image-store.s3.amazonaws.com/0/80502/c718d14a-a476-695e-afc1-4586171f09c5.png">



# WebGL開始。

```js:study.js
var w = 400; // 幅
var h = 400; // 高さ
var canvas = document.getElementById("canvas");
canvas.width = w;
canvas.height = h;
var gl = canvas.getContext("webgl");
```

# 座標セット

```js:study.js
// 座標セット
// 始点
var x = 50; // x座標
var y = 300; // y座標

// 放物線座標
var cx = 200; // x座標
var cy = 100; // y座標

// 終点
var dx = 350; // x座標
var dy = 300; // y座標

// 線太さ
var lineWidth = 100;
```

# 始点と放物線座標、終点と放物線座標の角度を取得

```js:study.js
var angle1 = Math.atan2(y - cy, x - cx) / (Math.PI / 180) * -1;
var angle2 = Math.atan2(dy - cy, dx - cx) / (Math.PI / 180) * -1;

// 角度
var a1 = 270;
var a2 = 90;
if (angle1 > 0) {
    a1 = 90;
    a2 = 270;
}
```


# 始点と終点から4点の座標をセット

座標の詳細はこちら
[WebGL(2D) - 入門編 - 2日目: 線の太さをコントロールする](http://qiita.com/ienaga/items/f0392e1aaf41651ace7a)


```js:study.js
// border
var lw = lineWidth + 1;

// 4点の座標をセット
var x1 = x + Math.cos((angle1 + a1) * Math.PI / 180) * lw / 2;
var y1 = y + Math.sin((angle1 + a1) * Math.PI / 180) * lw / 2 * -1;

var x2 = dx + Math.cos((angle2 - a1) * Math.PI / 180) * lw / 2;
var y2 = dy + Math.sin((angle2 - a1) * Math.PI / 180) * lw / 2 * -1;

var x3 = dx + Math.cos((angle2 - a2) * Math.PI / 180) * lw / 2;
var y3 = dy + Math.sin((angle2 - a2) * Math.PI / 180) * lw / 2 * -1;

var x4 = x + Math.cos((angle1 + a2) * Math.PI / 180) * lw / 2;
var y4 = y + Math.sin((angle1 + a2) * Math.PI / 180) * lw / 2 * -1;
```

# 弧の部分の頂点を外積から交差座標をセット

[WebGL(2D) - 入門編 - 8日目: 滑らか(アンチエイリアス)な線の2次ベジェを描く](http://qiita.com/ienaga/items/5c50476cb0b760d5a20c)


```js:study.js
// 外積から交差座標をセット
var xc1 = cx + Math.cos((angle1 + a1) * Math.PI / 180) * lw / 2;
var yc1 = cy + Math.sin((angle1 + a1) * Math.PI / 180) * lw / 2 * -1;
var xc2 = cx + Math.cos((angle2 - a1) * Math.PI / 180) * lw / 2;
var yc2 = cy + Math.sin((angle2 - a1) * Math.PI / 180) * lw / 2 * -1;

// 外積
var S1 = ((xc2 - x2) * (y1 - y2) - (yc2 - y2) * (x1 - x2)) / 2;
var S2 = ((xc2 - x2) * (y2 - yc1) - (yc2 - y2) * (x2 - xc1)) / 2;

var xc = x1 + (xc1 - x1) * (S1 / (S1 + S2));
var yc = y1 + (yc1 - y1) * (S1 / (S1 + S2));

var vertices = [
    (x1-(w/2))/(w/2), -(y1-(h/2))/(h/2),
    (xc-(w/2))/(w/2), -(yc-(h/2))/(h/2),
    (x2-(w/2))/(w/2), -(y2-(h/2))/(h/2),
    (x3-(w/2))/(w/2), -(y3-(h/2))/(h/2),
    (x4-(w/2))/(w/2), -(y4-(h/2))/(h/2)
];
```



# 初期化

```js:study.js
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
```


# alphaを有効にする

```js:study.js
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
```


# 空のバッファオブジェクトを生成

```js:study.js
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
```

# バーテックス(頂点)シェーダー

```js:study.js
var vSource = [
    "precision mediump float;",
    "attribute vec2 vertex;",
    "void main(void) {",
        "gl_Position = vec4(vertex, 0.0, 1.0);",
    "}"
].join("\n");

var vShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vShader, vSource);
gl.compileShader(vShader);
gl.getShaderParameter(vShader, gl.COMPILE_STATUS);
```

# フラグメントシェーダー

アンチエイリアスな円の詳細はこちら
[WebGL(2D) - 入門編 - 4日目: 滑らか(アンチエイリアス)な円を描く](http://qiita.com/ienaga/items/02e4b8d8c2a975ab86e1)


```js:study.js
var rgba = [0.0, 0.0, 0.0, 1.0]; // Red, Green, Blue, Alpha
var fSource = [
    "precision highp float;",

    // From: http://research.microsoft.com/en-us/um/people/hoppe/ravg.pdf
    "float det(vec2 a, vec2 b) { return a.x * b.y - b.x * a.y; }",
    "vec2 get_distance_vector(vec2 b0, vec2 b1, vec2 b2) {",
        "float a = det(b0, b2), b = 2.0 * det(b1, b0), d = 2.0 * det(b2, b1);",
        "float f = b * d - a * a;",
        "vec2 d21 = b2 - b1, d10 = b1 - b0, d20 = b2 - b0;",
        "vec2 gf = 2.0 * (b * d21 + d * d10 + a * d20);",
        "gf = vec2(gf.y, -gf.x);",
        "vec2 pp = -f * gf / dot(gf, gf);",
        "vec2 d0p = b0 - pp;",
        "float ap = det(d0p, d20), bp = 2.0 * det(d10, d0p);",
        "float t = clamp((ap + bp) / (2.0 * a + b + d), 0.0 ,1.0);",
        "return mix(mix(b0, b1, t), mix(b1, b2, t), t);",
    "}",

    "void main(void) {",
        "vec2 xy = gl_FragCoord.xy;",
        "vec2 p0 = vec2("+ x +", "+ (h-y) +") - xy;",
        "vec2 p1 = vec2("+ cx +", "+ (h-cy) +") - xy;",
        "vec2 p2 = vec2("+ dx +", "+ (h-dy) +") - xy;",
        "float d = length(get_distance_vector(p0, p1, p2));",
        "vec2 t = vec2("+ ((lineWidth/2)-0.5) +", 0.0);",
        "float alpha = 1.0;",
        "if(d > t.x) {",
            "alpha = clamp(1.0 - smoothstep(d, t.x, t.x + 0.25), 0.0, 1.0);",
        "}",
        "gl_FragColor = vec4("+ rgba.join(",") +") * alpha;",
    "}"
].join("\n");


var fShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fShader, fSource);
gl.compileShader(fShader);
gl.getShaderParameter(fShader, gl.COMPILE_STATUS);
```

# プログラムオブジェクトの生成

```js:study.js
var program = gl.createProgram();
gl.attachShader(program, vShader);
gl.attachShader(program, fShader);
gl.linkProgram(program);
gl.getProgramParameter(program, gl.LINK_STATUS);
gl.useProgram(program);
```

# シェーダー側の変数をjs側で受け受け取る

```js:study.js
var vertex = gl.getAttribLocation(program, "vertex");
gl.enableVertexAttribArray(vertex);
gl.vertexAttribPointer(vertex, 2, gl.FLOAT, false, 0, 0);
```


# 線の先端を丸くするの詳細はこちら
[WebGL(2D) - 入門編 - 6日目: 線の先端を丸くする(round)]
(http://qiita.com/ienaga/items/b9c6460d59078c4b705b)

# 先端の三角形の長さ

```js:study.js
var rl = (lineWidth / 2) / Math.cos(45 * Math.PI / 180);
```

# 先端 - 1

```js:study.js
var x1 = x + Math.cos(angle1 * Math.PI / 180) * rl;
var y1 = y + Math.sin(angle1 * Math.PI / 180) * rl * -1;

var x2 = x + Math.cos((angle1 - 90) * Math.PI / 180) * rl;
var y2 = y + Math.sin((angle1 - 90) * Math.PI / 180) * rl * -1;

var x3 = x + Math.cos((angle1 - 270) * Math.PI / 180) * rl;
var y3 = y + Math.sin((angle1 - 270) * Math.PI / 180) * rl * -1;
```

# 先端 - 2

```js:study.js
var x1 = dx + Math.cos(angle2 * Math.PI / 180) * rl;
var y1 = dy + Math.sin(angle2 * Math.PI / 180) * rl * -1;

var x2 = dx + Math.cos((angle2 - 90) * Math.PI / 180) * rl;
var y2 = dy + Math.sin((angle2 - 90) * Math.PI / 180) * rl * -1;

var x3 = dx + Math.cos((angle2 - 270) * Math.PI / 180) * rl;
var y3 = dy + Math.sin((angle2 - 270) * Math.PI / 180) * rl * -1;
```



# 備忘録

[WebGLStudy](https://github.com/ienaga/WebGLStudy)