LoginSignup
1
1

More than 5 years have passed since last update.

WebGL(2D) - 入門編 - 8日目: 滑らか(アンチエイリアス)な線の2次ベジェを描く

Last updated at Posted at 2016-08-01

目標

線の2次ベジェを描く

スクリーンショット 2016-08-01 18.10.49.png

WebGL開始。

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

座標セット

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;

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

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日目: 線の太さをコントロールする

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;

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

image1.jpg

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)
];

初期化

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

alphaを有効にする

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

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

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

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

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) - 入門編 - 5日目: 滑らか(アンチエイリアス)な線(矩形)を描く

参考資料

study.js
var rgba = [0.0, 0.0, 0.0, 1.0]; // Red, Green, Blue, Alpha

// 4点からzを算出
function edge (x0, y0, x1, y1, h)
{
    var x = x1 - x0;
    var y = y1 - y0;
    var _y0 = (h - y0);
    var _y1 = (h - y1);

    var edges = [];
    var scale = 1.0 / Math.sqrt(x * x + y * y);
    edges[edges.length] = (_y0 - _y1) * scale;
    edges[edges.length] = (x1 - x0) * scale;
    edges[edges.length] = (x0 * _y1 - x1 * _y0) * scale;
    return edges;
}

var e0 = edge(x1, y1, x4, y4, h);
var e1 = edge(x3, y3, x2, y2, h);

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;",
        "vec3 pos = vec3(xy, 1.0);",
        "float a0 = clamp(dot(vec3("+ e0.join(",") +"), pos), 0.0, 1.0);",
        "float a1 = clamp(dot(vec3("+ e1.join(",") +"), pos), 0.0, 1.0);",
        "float alpha = min(a0, a1);",
        "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))8;",
        "vec2 t = vec2("+ ((lineWidth/2)-0.5) +", 0.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);

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

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側で受け受け取る

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

備忘録

WebGLStudy

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1