three.js入門中

2年くらい前にもWebGLがんばろうと思ってthree.jsをちょっと触ってたんだけどなんやかやで時間が取れなくて中断してました。幸い最近ちょっと時間が取れる感じになってきたので再入門。

http://goo.gl/SJshg8

とりあえず木。

ホントはここにあるようなかっこいい木になることを期待してたんだけどなかなか思い通りにはいかない・・・。修行あるのみすね。

AngularDartすごい!マジすごい!

1. Googleの本気がすごい!

AngularDart去年の6月くらいに開発中であることが公になり、11月にわりとよさ気な感じになってきたかもということでリリース間近を感じさせるバージョン番号0.9.0が振られました。そしてその3ヶ月後に世界各地で行われた初めてのDart言語の大規模イベント「Dart Flight School」にはすでにイベントタイトルにwith AngularDartと振られています。Googleのスピード感とAngularDartにかける本気度が感じられます。

個人的にもAngularDartとDartRailsRubyみたいな関係になれるかもと思える程度にはAngularDartいい出来だと感じているので、Googleのこの扱いはわりと納得。

しかしですよ。

2. 開発者のアジャイル精神がすごい!

「変化ヲ抱擁セヨ」

2月一杯かけて行われたDart Flight Schoolで初心者を大量にDart界に呼び込んだ(はず)にも関わらず、4月半ばに入った修正がこれ。

https://github.com/angular/angular.dart/commit/f055ab6f7c4fadfdbb6a46d8bc547b304586d95c

fix: More consistent type naming
Closes #902

BREAKING CHANGE:
  - Concepts:
    - Filter                        -> Formatter

  - import:
    - angular/directive/ng_a.dart   -> angular/directive/a_href.dart
    - angular/filter/currency.dart  -> angular/formatter/currency.dart
    - angular/filter/date.dart      -> angular/formatter/date.dart
    - angular/filter/filter.dart    -> angular/formatter/filter.dart
    - angular/filter/json.dart      -> angular/formatter/json.dart
    - angular/filter/limit_to.dart  -> angular/formatter/limit_to.dart
    - angular/filter/lowercase.dart -> angular/formatter/lowercase.dart
    - angular/filter/module.dart    -> angular/formatter/module.dart
    - angular/filter/number.dart    -> angular/formatter/number.dart
    - angular/filter/order_by.dart  -> angular/formatter/order_by.dart
    - angular/filter/stringify.dart -> angular/formatter/stringify.dart
    - angular/filter/uppercase.dart -> angular/formatter/uppercase.dart

  - Types:
    - NgA                           -> AHref
    - NgAttachAware                 -> AttachAware
    - NgDetachAware                 -> DetachAware
    - NgShadowRootAware             -> ShadowRootAware
    - NgFilter                      -> Formatter
    - NgInjectableService           -> Injectable
    - AbstractNgAnnotation          -> Directive
    - AbstractNgFieldAnnotation     -> DirectiveAnnotation
    - NgComponent                   -> Component
    - NgController                  -> Controller
    - NgDirective                   -> Decorator
    - NgAnimate                     -> Animate
    - NgZone                        -> VmTurnZone
    - NgAnimationModule             -> AnimationModule
    - NgCoreModule                  -> CoreModule
    - NgCoreDomModule               -> CoreDomModule
    - NgAnimationDirective          -> NgAnimation
    - NgAnimationChildrenDirective  -> NgAnimationChildren
    - FilterMap                     -> FormatterMap
    - NgAttrMustacheDirective       -> AttrMustache
    - NgTextMustacheDirective       -> TextMustache

  - Constants
    - NgDirective.LOCAL_VISIBILITY           -> Directive.LOCAL_VISIBILITY
    - NgDirective.CHILDREN_VISIBILITY        -> Directive.CHILDREN_VISIBILITY
    - NgDirective.DIRECT_CHILDREN_VISIBILITY -> Directive.DIRECT_CHILDREN_VISIBILITY


クラス名の大規模改名

まぁ修正としては冗長なプレフィクスとかサフィックスとかが除かれたということですっきりしてよかったとは思うものの、とりあえずこれ以前に作られた資料は全部ゴミになりました。Dart Flight Schoolに伴って世界中でボランティアが頑張って資料作ってただろうに・・・。

対策についてもNgプレフィクスなくすだけなら一括置換で何とかなりそうな気配もあるけど

- AbstractNgAnnotation          -> Directive
- NgDirective                   -> Decorator

これとか結構な罠。あと

- NgAnimationDirective          -> NgAnimation
- NgAnimationChildrenDirective  -> NgAnimationChildren

全てのNgプレフィクスが消えてるわけじゃないのもレベル高い(罠として)。

ついでにいつのまにかアプリケーションの開始方法も変わってたり。

ngBootstrap(module: myModule);   -> applicationFactory().addModule(myModule).run();

なんだろ、C言語で言えば「これからはmain関数ではなくてstart関数をエントリポイントにします」って言われた感じ?単純に置き換えればいい話とはいえそこ変えんの?みたいな。すごい!

3. バージョニングがすごい!

で、誰が見ても分かる通りこれはいろいろ変わりすぎだろうということでバージョンは0.9.10から0.10.0に・・・え?いいのそれ?

0.9.9の次に0.9.10が出た時「これ0.9.99まで行っちゃうんじゃないの?」って思ったのを覚えてるけど、0.10.0ってそれもう0.99.99まで視野に入っちゃう・・・すごい!

4. 地獄はこれからだ感がすごい!

いまmasterに入ってる変更。

使ってない人にはピンと来ないかもしれないけど、DIに使うメソッド総とっかえ。いや、まぁ、新しいのも悪くないとは思うけども。むしろよくなってると思うけども。でもこれAngularDartの全ユーザーに間違いなく影響あるよね?

これから入りそうな変更

@Controllerアノテーションなくすて!今このタイミングで!すごい!

5. そんなコミッタ陣にBreaking Changeを指摘されるオレすごい!

まぁ指摘したのはバシバシBreaking Change入れてる人とは違う人なんだけど。

https://github.com/angular/angular.dart/commit/3ee8740204d635c4d046fc1afdacdf18685e2d8b#commitcomment-6177893

でもこれFilterをFormatterにするっていう方針の追従漏れをちまちま直しただけのパッチだし、私に言われましても・・・。まぁそれはそれとしてAngularDartにBreaking Changeぶっこむオレすごい!

使おう!AngularDart!

色々書いたけど、変化が激しいのは開発が活発な証拠。むしろポジティブに捉えましょう。AngularDartはこのユーザーお構いなしなアグレッシブさも含めて、これまでなかった可能性を感じられるいいフレームワークだと思いますし、この不安定さダイナミックさは今しか味わえないものです。みなさんもこの機会にぜひ試してみませんか?

WebGLを簡単に使うためのzero.jsを作成しました

WebGLの登場で我らがウェブ業界にも徐々に3Dの波が押し寄せており、スプライトを利用した2Dゲームがいつのまにか3Dに駆逐されたように、3Dのできないウェブプログラマが駆逐されるのも時間の問題のようにさえ感じられます。

しかしいざWebGLを始めようと思っても、たとえば画面にドットを1つ表示しようと思っただけで

<html>
<head>
  <script src="CanvasMatrix.js"></script>
  <script>
  function main() {
    var domElement = document.createElement('canvas');
    domElement.width = 500;
    domElement.height = 500;
    document.body.appendChild(domElement);

    var gl = domElement.getContext('webgl') || 
      domElement.getContext('experimental-webgl');
    if (!gl) throw 'WebGL is not supported.';

    var positions = [
      -0.5,  0.5, 0.0,
       0.5,  0.5, 0.0,
      -0.5, -0.5, 0.0,
       0.5, -0.5, 0.0
    ];
    var indices = [
      0, 1, 2,
      2, 1, 3
    ];
    var numIndices = indices.length;

    var vbuffers = [positions, positions];
    for (var i = 0; i < vbuffers.length; i++) {
      var data = vbuffers[i];
      var vbuffer = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
      vbuffers[i] = vbuffer;
    }
    gl.bindBuffer(gl.ARRAY_BUFFER, null);

    var ibuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Int16Array(indices), gl.STATIC_DRAW);
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

    var vshader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vshader, 
      "#ifdef GL_ES\n" +
      "precision highp float;\n" +
      "#endif\n" +
      "uniform mat4 mvpMatrix;\n" +
      "uniform mat4 normalMatrix;\n" +
      "uniform vec4 lightVec;\n" +
      "uniform vec4 lightColor;\n" +
      "uniform vec4 materialColor;\n" +
      "attribute vec3 position;\n" +
      "attribute vec3 normal;\n" +
      "varying vec4 color;\n" +
      "void main() {\n" +
      "  float light = clamp(dot(vec3(0.0, 0.0, 1.0), lightVec.xyz), 0.0, 1.0) * 0.8 + 0.2;\n" +
      "  color       = min(min(materialColor, lightColor), vec4(light, light, light, 1.0));\n" +
      "  gl_Position = mvpMatrix * vec4(position, 1.0);\n" +
      "}"
    );
    gl.compileShader(vshader);
    if (!gl.getShaderParameter(vshader, gl.COMPILE_STATUS)) {
      throw gl.getShaderInfoLog(vshader);
    }

    var fshader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fshader, 
      "#ifdef GL_ES\n" +
      "precision highp float;\n" +
      "#endif\n" +
      "varying vec4 color;\n" +
      "void main() {\n" +
      "  gl_FragColor = color;\n" +
      "}"
    );
    gl.compileShader(fshader);
    if (!gl.getShaderParameter(fshader, gl.COMPILE_STATUS)) {
      throw gl.getShaderInfoLog(fshader);
    }

    var program = gl.createProgram();
    gl.attachShader(program, vshader);
    gl.attachShader(program, fshader);

    gl.bindAttribLocation(program, 0, 'position');
    gl.bindAttribLocation(program, 1, 'normal');

    gl.linkProgram(program);
    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
      throw gl.getProgramInfoLog(program);
    }

    var uniformVars = [
      gl.getUniformLocation(program, 'mvpMatrix'),
      gl.getUniformLocation(program, 'normalMatrix'),
      gl.getUniformLocation(program, 'lightVec'),
      gl.getUniformLocation(program, 'lightColor'),
      gl.getUniformLocation(program, 'materialColor')
    ];

    gl.clearColor(0, 0, 0, 1);
    gl.clearDepth(1000);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    gl.enable(gl.DEPTH_TEST);
    gl.useProgram(program);

    var lightVec = [0.0, 0.0, 1.0, 0.0];
    var lightColor = [1.0, 1.0, 1.0, 1.0];

    var modelMatrix = new CanvasMatrix4();

    var mvpMatrix = new CanvasMatrix4(modelMatrix);
    mvpMatrix.translate(0, 0, -500);
    mvpMatrix.perspective(30, 1.0, 0.1, 1000);

    var normalMatrix = new CanvasMatrix4(modelMatrix);
    normalMatrix.invert();
    normalMatrix.transpose();

    var materialColor = [1.0, 1.0, 1.0, 1.0];

    var values = [mvpMatrix, normalMatrix, lightVec, lightColor, materialColor];
    for (var i = 0; i < values.length; i++) {
      var value = values[i];
      if (value instanceof CanvasMatrix4) {
        gl.uniformMatrix4fv(uniformVars[i], false, value.getAsWebGLFloatArray());
      }
      else {
        gl.uniform4fv(uniformVars[i], new Float32Array(value));
      }
    }

    var strides = [3, 3];
    for (var i = 0; i < strides.length; i++) {
      var stride = strides[i];
      gl.enableVertexAttribArray(i);
      gl.bindBuffer(gl.ARRAY_BUFFER, vbuffers[i]);
      gl.vertexAttribPointer(i, stride, gl.FLOAT, false, 0, 0);
    }

    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibuffer);

    gl.drawElements(gl.TRIANGLES, numIndices, gl.UNSIGNED_SHORT, 0);
    gl.flush();
  }
  document.addEventListener('DOMContentLoaded', main, false);
  </script>
</head>
<body>
</body>
</html>

信じがたいことにこのようなコードが要求されます*1

特にC言語様のGLSLコードを文字列として渡さないと画面に何も表示されず、そのGLSLのコードにJS側から値を与えるには「GLSLコード内で何番目に宣言された変数か」という情報が必要だったりするところなど、まるで何か悪い夢を見ているようですらあります。

WebGLの難しさはこのようなWebGL自体の複雑さに加えて、さらに3Dの知識までもが要求されるところにあるといえるでしょう。

さて、このように複雑過ぎる問題に直面した時に我々プログラマが取るべき手段とはなんでしょうか?そうです。分割統治です。そこで私はこの問題から3Dという問題を除き、まずは「WebGL自体の複雑さ」だけを解決するためのライブラリを開発しました。


http://technohippy.github.io/zero.js/

それがこのzero.jsです。zero.jsは名前の通りWebGLを使用して原点、すなわちゼロ次元を描画するためのライブラリです。このライブラリを使用すれば煩わしい3Dについて考えることなく、WebGLを利用することに集中できます。例えば最初に上げたコードと同様の表示を次のように非常にシンプルに記述することができます。

function main() {
  var scene = new ZERO.Scene();

  var geometry = new ZERO.PointGeometry();
  var material = new ZERO.MeshBasicMaterial({color: 0xffffff});
  var mesh = new ZERO.Mesh(geometry, material);
  scene.add(mesh);
  
  var width = 500;
  var height = 500;
  var fov = 30;
  var aspect = width / height;
  var near = 0.1;
  var far = 1000;
  var camera = new ZERO.PerspectiveCamera(fov, aspect, near, far);
  camera.position.set(500);

  var directionalLight = new ZERO.DirectionalLight(0xffffff);
  directionalLight.position.set(1.0);
  scene.add(directionalLight);

  var renderer = new ZERO.WebGLRenderer();
  renderer.setSize(width, height);
  document.getElementById('demo').appendChild(renderer.domElement);

  renderer.render(scene, camera);
}
document.addEventListener('DOMContentLoaded', main, false);

これ以上なくシンプルなコードで原点が描画できていることが分かるでしょう。

もちろんここでライブラリに存在する唯一のジオメトリであるZERO.PointGeometryは原点であるため一切の座標が指定できません。これは光速が観測者によらず一定であることを考えると分かりやすいのではないでしょうか。

またカメラ(視点)についても指定できるのは基本的に原点からの距離だけです。ただし原点はゼロ次元であるため当然"大きさ"という概念が存在せず、カメラの位置に関わらず視角つまり表示上のサイズは一定になります。

このようにzero.jsを使用すれば座標をほぼ意識せずにWebGLが利用できることが分かっていただけるでしょう。zero.jsが皆さんがWebGLを使い始める零歩目になることを願っています。

・・・

なお、姉妹品として1次元を描画するためのone.jsも合わせて作成しました。zero.jsに習熟した方の次の一歩としてよろしければこちらもご利用ください。

http://technohippy.github.io/one.js/

大人のプログラミング言語Dart

Dartの本を翻訳しました。今月28日発売です。

プログラミング言語Dart

プログラミング言語Dart

こんな記事を書いたり、こんな記事を勝手訳したりしておきながら、しれっとDart本の翻訳に参加し、あまつさえ訳者まえがきでみなさんにDartをオススメする。この変り身の早さ、このしたたかさこそが激動の現代を生き抜くのに最も必要とされているものと言えるでしょう。かのチャールズ・ダーウィンは言いました。

最も強い者が生き残るのではなく、最も賢い者が生き延びるでもない。唯一生き残るのは、変化できる者である

何一つ恥じ入るところなどない、これこそが正しい大人の姿です。この私の背中を見て人生に迷う若者たちがしなやかに強く生きる術を身につけてくれればと願ってやみません。

・・・

という照れ隠し的なエクスキューズはさておき。言語の本は一度書いてみたいなぁと思っていたので、翻訳書とはいえなかなか喜ばしいです。ものすごい大それた日本語タイトルがついていて初めて見た時は結構ビビリましたが(原題は「Dart in Action」)。

プログラミング言語Dart」というタイトルではありますが、実際のところ本書は言語仕様を説明するような本ではなく、「Dartを使って何かを作りたい」という人のための本です。そこさえ勘違いしなければとてもいい本だと思うので、本屋で見かけたらまずは著者紹介だけでも目を通していただけると嬉しいです。

A Perfect World

twitter@negipoさんが


https://twitter.com/negipo/status/439211041302392832

とか面白いことを言ってたので、なんとなく画像をRGBの各チャンネルごとにサイズの違うモザイクにして、それぞれのドットを円形にした上で合成したらおもしろい感じになるかなーとやってみたら

意外と面白くもなんともなく。あ、元画像はみんなの大好きなレナさんです。

で、この画像のモザイクのサイズの大小関係はいちおう比視感度を考慮してにしてたんですが、ふとこの順番変えてみたらどうなるんだろうかと思い立ちまして。

やってみたのが以下です。

解像度は同じはずなのに明らかにボヤけた感じになりました。

これがなにか定性的な話なのか、たまたまレナさんがこうなるだけなのか分からないけど、なんか面白かったのでメモ。

Dart Flight School Tokyo

Dart言語のバージョンが1.0を迎え、これを機に本格的に盛り上げていきたいということで2月に世界中でDart Flight Schoolというイベントが開かれています。今ちょっと公式サイトで数えてみたらイベントの数が119もあって、もうDartは世界を獲ったと言っても過言じゃない感じじゃないですか。

https://www.dartlang.org/events/2014/flight-school/

そんな世界を席巻しているDartのイベントがTokyo GTUGさん主催、Google Japanさんで開催されたので、AngularDartについてちょっと話をしてきました。話をしたというか、コードラボですが、資料はこちら。

http://goo.gl/aXyt2e

ということで、私としては京都と同じことを東京でもやったので、ブログエントリもコピペしてみました。


壇上からの写真は公開していいかよく分からなかったので公開しても問題なさそうな安生さんのご尊顔。

東京はコードラボ以外にセッションがいくつかありましたが、なかでもDartVMの話が白眉。

http://www.slideshare.net/nothingcosmos/2014-dart-flight-school-in-tokyo

Dartの資料読んでてモヤッとしてたところが、あーあれそういう意味だったのかー、みたいなaha!がとてもたくさん。とある書籍でイベントループ周りの説明読んでてもなんかピンと来ないなーと思ってたんだけど、おかげさまで分かった気になれました。すごく急だったけど発表をお願いしてよかった。

あとはLTでDartからSIMD使う話があったり、Scala.jsの話があったり、なかなか多彩で楽しい勉強会でした。

Dart Flight School Kyoto

Dart言語のバージョンが1.0を迎え、これを機に本格的に盛り上げていきたいということで2月に世界中でDart Flight Schoolというイベントが開かれています。今ちょっと公式サイトで数えてみたらイベントの数が119もあって、もうDartは世界を獲ったと言っても過言じゃない感じじゃないですか。

https://www.dartlang.org/events/2014/flight-school/

そんな世界を席巻しているDartのイベントがKyoto GTUGさん主催、京都リサーチパークさんで開催されたので、AngularDartについてちょっと話をしてきました。話をしたというか、コードラボですが、資料はこちら。

http://goo.gl/aXyt2e

公式チュートリアルはすでに終わらせてる人がいるかもしれないので資料は自作しましたが、コードラボ中にちょこちょこ不具合の指摘を受けて"TODO"とか追加してあります。すいません。

正直Dart単体だときっと便利な言語だけどいまいち面白みに欠ける感じですが、AngularDartはよく出来ててなかなか面白いです。AngularDartがRubyにおけるRailsみたいなポジションになってDart自体が盛り上がったりする可能性も完全にゼロではないと感じているので、少しでも気になる人は触ってみるといいんじゃないでしょうか。22日にはDart Flight School Tokyoも開催されるので、よろしければぜひ。

https://docs.google.com/forms/d/15RQ9U9i_TiSlGmRnpcgvnvOYaCv-w6ugGJAB2Bw9oEw/viewform

ちなみにFlight School Kyotoには日帰りで参加しましたが、よりにもよって大雪だったので、帰りは危うく遭難するところでした・・・。