CANVASで網目模様を描く方法を教えて下さい.その4
面の明るさを計算できるようにした。
面の法線ベクトルを調べ、平行光源とのベクトルの角度を抽出して明るさを調べる。
もう高速化には限度がありそうなので、オブジェクト指向っぽく書き直すかな。
(プログラムが読みづらい)
<!DOCTYPE html> <meta charset="UTF-8"> <title>3D</title> <style> canvas { background : #aaa; } </style> <body> <canvas width="1024" height="700"></canvas> <script> function ball (r, n, n1) { var rst = [], a = [], b = [], c = []; var pi = Math.PI, sin = Math.sin, cos = Math.cos; var k = 2 * pi / n1, k2 = pi / n; var i, i_, j, s, r2, yb; var r2 = sin (k2) * r, h2 = cos (k2) * r, yr = r2, yt = h2; for (i = 0; i <= n1; i++) { s = k * i; a[i] = sin (s); b[i] = cos (s); } for (i = 0; i < n1; i++) { rst[i] = [ [0, r, 0], [a[i] * r2, h2, b[i] * r2], [a[i_= i +1 ] * r2, h2, b[i_] * r2] ]; c[i] = [ [a[i_ = i + 1] * r2, -h2, b[i_] * r2], [a[i] * r2, -h2, b[i] * r2], [0, -r, 0] ]; } for (i = 2; i < n; i++) { s = k2 * i; yr2 = sin (s) * r; yb = cos (s) * r; for (j = 0; j < n1; j++) { rst.push ([ [a[j] * yr, yt, b[j] * yr], [a[j] * yr2, yb, b[j] * yr2], [a[j+1] * yr2, yb, b[j+1] * yr2], [a[j+1] *yr, yt, b[j+1] * yr] ]); } yt = yb; yr = yr2; } return rst.concat (c); } function rotation3D (m, x, y, z) { var deg = Math.PI / 180, sin = Math.sin, cos = Math.cos; var xc = cos (x * deg), xs = sin (x * deg); var yc = cos (y * deg), ys = sin (y * deg); var zc = cos (z * deg), zs = sin (z * deg); var x0 = xc * yc, y0 = xc * ys * zs - xs * zc, z0 = xc * ys * zc + xs * zs; var x1 = xs * yc, y1 = xs * ys * zs + xc * zc, z1 = xs * ys * zc - xc * zs; var x2 = -ys, y2 = yc * zs, z2 = yc * zc; var i, j, a, b, c, d, e, s; var rst = []; for (i = 0; a = m[i]; rst[i++] = s) for (j = 0, s = []; b = a[j]; j++) s[j] = [ x0 * (c = b[0]) + y0 * (d = b[1]) + z0 * (e = b[2]), x1 * c + y1 * d + z1 * e, x2 * c + y2 * d + z2 * e ]; return rst; } //面の法線ベクトルを求める function crossProduct (ary) { var rst = []; var i, s; var p0, p1, p2; var x0, x1, x2, y0, y1, y2, z0, z1, z2; var px, py, pz, qx, qy, qz; for (i = 0; s = ary[i]; i += 1) { p0 = s[0]; p1 = s[1]; p2 = s[2]; x0 = p0[0]; x1 = p1[0]; x2 = p2[0]; y0 = p0[1]; y1 = p1[1]; y2 = p2[1]; z0 = p0[2]; z1 = p1[2]; z2 = p2[2]; px = x1 - x0; py = y1 - y0; pz = z1 - z0; //p qx = x2 - x1; qy = y2 - y1; qz = z2 - z1; //q rst[i] = [ py * qz - pz * qy, pz * qx - px * qz, px * qy - py * qx ]; } return rst; } function innerProduct (ary, v) { var x = v[0], y = v[1], z = v[2]; var rst = []; var i, a; var lv = Math.sqrt (x * x + y * y + z * z); var inp, tv, len; for (i = 0; a = ary[i]; i++) { tv = Math.sqrt (a[0] * a[0] + a[1] * a[1] + a[2] * a[2]); inp = a[0] * x + a[1] * y + a[2] * z; len = inp / (lv * tv); rst[i] = Math.acos (len) * 180 / Math.PI; } return rst; } //明るさを求める function brightness (ary, v, el) { var lx = v[0], ly = v[1], lz = v[2]; var lv = Math.sqrt (lx * lx + ly * ly + lz * lz); var i, a, x, y, z, rst = []; var c, b = 1 - el; for (i = 0; a = ary[i]; i++) { x = a[0]; y = a[1]; z = a[2]; tv = Math.sqrt (x * x + y * y + z * z); inp = x * lx + y * ly + z * lz; len = inp / (lv * tv); rst[i] = el + Math.cos (Math.acos (len)) * b; } return rst; } //3Dの物体を2Dの座標へと変換する function cov3to2 (m, zz, sc) { var rst = [], a, b, c, d, s, i, j, t; for (i = 0; d = m[i]; i++) { a = d[0]; for (c = [], j = 0; b = a[j]; j++) { c[j] = [b[0] / (t = (zz + b[2]) / sc), b[1] / t]; } rst[i] = [c, d[1]]; } return rst; } function canvas_draw_create (c, o) { var ctx = c.getContext ('2d'); var x = c.width / 2 + o[0]; var y = c.height / 2 + o[1]; var rgb = [50, 150,255]; var int = Math.floor; ctx.lineWidth = 1; return { draw: function (a, b) {//面を描く ctx.beginPath (); ctx.fillStyle = 'rgb(' + int (rgb[0] * b) + ',' + int (rgb[1] * b) + ',' + int (rgb[2] * b) + ')'; ctx.moveTo (x + a[0][0], y - a[0][1]); for (i = 1; b = a[i++]; ) ctx.lineTo (x + b[0], y - b[1]); ctx.lineTo (x + a[0][0], y - a[0][1]); ctx.closePath(); ctx.fill(); }, cls: function () { ctx.fillStyle = 'RGBA(160,160,160,1)'; ctx.fillRect (0,0, c.width, c.height); } }; } function draw (a, dw) { var i, b, c; dw.cls (); for (i = 0; c = a[i++]; ) {//面の3点だけでベクトルの外積の向きで判断 b = c[0]; if (0 >= ((b[2][0] - b[1][0]) * (b[0][1] - b[1][1]) - (b[2][1] - b[1][1]) * (b[0][0] - b[1][0])) ) dw.draw (b, c[1]); } } var hoge = (function _ () { var z = rotation3D (ball (18, 20, 40), 23.43,0,0); var v = canvas_draw_create (document.querySelector ('canvas'), [0,0]); var h = [[[1, 1, -1]]]; return function () { h = rotation3D (h, 1, 0, 0); z = rotation3D (z, 0, 1, 0); var bb = brightness (crossProduct (z), h[0][0], .5); for (var i = 0, I = z.length, d = []; i < I; i++) { d[i] = [z[i], bb[i]]; } draw (cov3to2 (d, 50, 600), v); }; }) (); setInterval (hoge, 1000/60); </script>