タグに「WebGL」を持つ
1〜10件目 / 42件
1 2 3 4 5 次へ

 JSceneKitでobjファイル表示対応した / magicien 

image Polyなるサービスが始まったらしいと聞いて、JSceneKitで作りかけだったobjファイルの表示機能を実装した。例によって必要最低限の機能しか実装していないけれど、Polyからダウンロードできるobjファイルは概ね表示できるはず。


Polyにアップロードされているモデルを眺めていると、明らかに毛色の違うものがちらほら。どうやらVRで作られたものらしい。3Dモデルというと、直方体やカプセルのような形から始めて、ポリゴンを分割したり、凹凸を付けたりしていくのが一般的だと思うのだけれど、VRで作られたモデルは、まるで油絵のように、色の付いたポリゴンをいくつも重ねて作られていた。ポリゴン同士が連結していないので、隙間から向こうが見える。

露骨な三角形や四角形の組み合わせも折り紙のようで嫌いではないけれど、輪郭の曖昧なモデルも味があって良い。もちろん、VRを使わなくても同じ表現はできるのだろうが、VRがあるからこそ生まれた表現だろう。

2017/11/11(Sat) 04:59:09

 JSceneKitでMMDモデル・モーション表示デモ公開 / magicien 

紹介動画をアップしました。

デモページはこちら。
ニコニ立体ちゃん(アリシア・ソリッド)のモデルを初めて表示したとき、パンツ丸見えじゃん...どこがどうバグったらこんなことになるんだ...と思って、公式サイトを確認しに行ったら、元から丸見えだったよ!せっかく縦回転の角度に制限付けたのに台無しだよ!

2017/08/13(Sun) 09:58:25

 JSceneKit:SceneKitをJavaScriptで動かすライブラリ / magicien 

思うところあって、JavaScriptでSceneKitを動かそうと悪戦苦闘中です。

JSceneKitについての動画を2つほどアップしました。





ソースはこちら、サンプルページはこちら
(SafariはWebGL2未対応なので動きません。WebGL2.0の仕様のドラフトの第一著者がAppleの人なのに...)

実際、Appleの初期のサンプル(Fox)については、ほぼ動くようになりましたが、物理演算が大きな壁として立ちはだかっています。
本家SceneKitでは、BulletPhysicsに少し(かなり?)手を加えたものを使っているようです。
Concaveの剛体の衝突判定が何であんなに速いのかわけがわからん...
ammo.jsを試してみましたが、メモリ不足で落ちてしまい、途方に暮れております。

そこへSceneKitの新たなAPI登場で追い討ちをかけられている状況なわけです。
WWDC2017ではFoxの続編、Fox 2のデモが行われ、Foxと比べてすごくゲームっぽく(クラッシュバンディクーっぽく)なったな、という印象です。ジャンプと攻撃のアクションが増えたことで、UIも前作より良さげですね。
(動画はこちら。Fox 2のデモは1:40〜)

被写界深度、モーションブラー、SSAOは頑張って実装するとして、GLES3.0でテセレーションはハードル高い...時が解決してくれるのを待つべきでしょう。そういえば、GameplayKitも使われていましたね...こちらは何とか実現したいところ。

2017/06/15(Thu) 07:25:02

 3Dライブラリの更新状況 / magicien 

3Dライブラリの更新状況について諸々。

JavaScript向け、Swift向け(macOS、iOS、tvOS、watchOS)の3Dライブラリをちまちまと更新していました。
最近の状況をニコニコ動画にアップしたので、まとめて紹介します。

・JavaScript向け(DH3DLibrary)


以前エイプリルフールネタで公開したIQRevengeを一応最終ステージまで遊べるようにしました。
iPhoneでも遊べるようにタッチ操作対応したので、よければ遊んで見てください。
IQRevenge

・Swift向け(MMDSceneKit)


SceneKitが物理エンジン(BulletPhysics)に対応しているので、それを使って街中を歩かせてみました。
テクスチャの繰り返し設定誤りが痛恨のミス。なぜ修正しないまま動画にしてしまったんだ...


watchOSのバージョンが3になって、SceneKitに対応したので、AppleWatch上でもMMDモデルを表示してみました。
Swiftの文法を2.xから3に上げるのに一苦労しましたが、AppleWatchでフレームワークを動かすのは拍子抜けするほどすんなりできました。
watchOSはCAAnimationには未対応のようなので、アニメーション対応はできませんでしたが、一度 .scn ファイルに書き出せばアニメーションさせることもできるかも?ですが、いずれwatchOSもアニメーション対応することを信じてもう少し待ってみます。

2016/11/11(Fri) 02:47:59

 setInterval を requestAnimationFrame へ / magicien 

今日、とあるテストを受けようと必死に勉強していたところ、テストセンターがお休みだということに気付き、がっくり。
仕方が無いので、WebGLのライブラリで使っている setInterval を requestAnimationFrame へ書き換えてみた。

実装の仕方はここを参考に。
これといった情報が無くて申し訳ないのですが、特にひっかかることなく変更できました。

が、手元のMacのSafariだと、webkitRequestAnimationFrame が無い。どうやら、Safari 6 以上じゃないと実装されていないらしい。そして、Safari 6 は、MacOS v10.7(Lion)以上しか対応していないらしい。手元のMacはv10.6(Snow Leopard)。ということで、Macでは動作確認できませんでした。

Chromeは問題無く動きました。前よりも心なしかぬるぬる動いているような気がする。FirefoxとOperaは相変わらず。

新しいMacを買うべきか、OSをバージョンアップすべきか、それが問題だ...

2013/01/14(Mon) 22:59:10

 JavaScriptでテキストデータの読み込み / magicien 

JavaScriptでXファイルを読み込んだときに使った方法。

バイナリデータの読み込みについて書きかけの状態ですが、今回はテキストデータの読み込みについて。
実行速度よりも実装のしやすさを優先するなら、Ginがおすすめ。
実際の使い方としてはこんな感じ。
var xfileGrammar;
function start()
{
  xfileGrammar = new Gin.Grammar({
  XFile:         / XFileHeader:header (XObjectLong)* /,
  XFileHeader:   / 'xof ':xof VersionNumber FormatType:showValue FloatSize:showValue /,
  VersionNumber: / <\d\d>:showValue <\d\d>:showValue /,
  FormatType:    / 'txt ' | 'bin ' | 'tzip' | 'bzip' /,
  FloatSize:     / '0064' | '0032' /,
 
  XObject:       / Template | Animation | AnimationKey | AnimationOptions | AnimationSet | AnimTicksPerSecond | Boolean | Boolean2d | ColorRGB | ColorRGBA | Coords2d | DeclData | EffectDWorld | EffectFloats | EffectInstance | EffectParamDWorld | EffectParamFloats | EffectString | FaceAdjacency | FloatKeys | Frame | FrameTransformMatrix | FVFData | Header | IndexedColor | Material | Matrix4x4 | MeshFaceWraps | MeshFace | MeshMaterialList | MeshNormals | MeshTextureCoords | MeshVertexColors | Mesh | Patch | PatchMesh9 | PatchMesh | PMAttributeRange | PMInfo | PMVSplitRecord | SkinWeights | TextureFilename | TimedFloatKeys | Vector | VertexDuplicationIndices | VertexElement | XSkinMeshHeader /,
  XObjectLong:   / Template | AnimationLong | AnimationKeyLong | AnimationOptionsLong | AnimationSetLong | AnimTicksPerSecondLong | BooleanLong | Boolean2dLong | ColorRGBLong | ColorRGBALong | Coords2dLong | DeclDataLong | EffectDWorldLong | EffectFloatsLong | EffectInstanceLong | EffectParamDWorldLong | EffectParamFloatsLong | EffectStringLong | FaceAdjacencyLong | FloatKeysLong | FrameLong | FrameTransformMatrixLong | FVFDataLong | HeaderLong | IndexedColorLong | MaterialLong | Matrix4x4Long | MeshFaceWrapsLong | MeshFaceLong | MeshMaterialListLong | MeshNormalsLong | MeshTextureCoordsLong | MeshVertexColorsLong | MeshLong | PatchLong | PatchMeshLong | PatchMesh9Long | PMAttributeRangeLong | PMInfoLong | PMVSplitRecordLong | SkinWeightsLong | TextureFilenameLong | TimedFloatKeysLong | VectorLong | VertexDuplicationIndicesLong | VertexElementLong | XSkinMeshHeaderLong /,
 
  XString:       / <[0-9A-Za-z]*> /,
  Identifier:    / XString /,
  Name:          / XString /,
  UUID:          / '<' (<[0-9A-Za-z]|->)* '>' /,
  Member:        / ('array' TemplateType Name '[' ($INT | XString) ']') | (TemplateType Name) /,
  TemplateType:  / XString /,
  XArray:        / '[' $INT ']' /,
 
  XObjectTemplate:       / Identifier:identifier Name:name '{' UUID ('[...]' | ('[' Name ']') | (Member ';'))* '}' /,
  Template:             / 'template':template  Name '{' UUID ('[...]' | ('[' Name ']') | (Member ';'))* '}' /,
 
  IntValue:             / $INT ';' ::first /,
  IntArray:             / $INT (',' $INT)* (',')? ';' /,
 
  FloatValue:           / $REAL ';' ::first /,
  FloatArray:           / $REAL (',' $REAL)* (',')? ';' /,
 
  Header:               / HeaderLong | (HeaderValue ';') /,
  HeaderLong:           / 'Header' (Name)? '{' (UUID)? HeaderValue '}' /,
  HeaderValue:          / IntValue:major IntValue:minor IntValue:flags /,
 
  Animation:            / AnimationLong | ( AnimationValue ';') /,
  AnimationLong:        / 'Animation' (Name)? '{' (UUID)? AnimationValue '}' /,
  AnimationValue:       / $ANY /,
 
  AnimationKey:         / AnimationKeyLong | ( AnimationKeyValue ';') /,
  AnimationKeyLong:     / 'AnimationKey' (Name)? '{' (UUID)? AnimationKeyValue '}' /,
  AnimationKeyValue:    / $ANY /,
 
  AnimationOptions:      / AnimationOptionsLong | ( AnimationOptionsValue ';') /,
  AnimationOptionsLong:  / 'AnimationOptions' (Name)? '{' (UUID)? AnimationOptionsValue '}' /,
  AnimationOptionsValue: / $ANY /,
 
  AnimationSet:         / AnimationSetLong | ( AnimationSetValue ';') /,
  AnimationSetLong:     / 'AnimationSet' (Name)? '{' (UUID)? AnimationSetValue '}' /,
  AnimationSetValue:    / $ANY /,
 
  AnimTicksPerSecond:      / AnimTicksPerSecondLong | ( AnimTicksPerSecondValue ';') /,
  AnimTicksPerSecondLong:  / 'AnimTicksPerSecond' (Name)? '{' (UUID)? AnimTicksPerSecondValue '}' /,
  AnimTicksPerSecondValue: / $ANY /,
 
  Boolean:              / BooleanLong | ( BooleanValue ';') /,
  BooleanLong:          / 'Boolean' (Name)? '{' (UUID)? BooleanValue '}' /,
  BooleanValue:         / $ANY /,
 
  Boolean2d:            / Boolean2dLong | ( Boolean2dValue ';') /,
  Boolean2dLong:        / 'Boolean2d' (Name)? '{' (UUID)? Boolean2dValue '}' /,
  Boolean2dValue:       / $ANY /,
 
  ColorRGB:             / ColorRGBLong | (ColorRGBValue ';') /,
  ColorRGBLong:         / 'ColorRGB' (Name)? '{' (UUID)? ColorRGBValue '}' /,
  ColorRGBValue:        / FloatValue:red FloatValue:green FloatValue:blue /,
 
  ColorRGBA:            / ColorRGBALong | (ColorRGBAValue ';') /,
  ColorRGBALong:        / 'ColorRGBA' (Name)? '{' (UUID)? ColorRGBAValue '}' /,
  ColorRGBAValue:       / FloatValue:red FloatValue:green FloatValue:blue FloatValue:alpha /,
 
  Coords2d:             / Coords2dLong | (Coords2dValue ';') /,
  Coords2dLong:         / 'Coords2d' (Name)? '{' (UUID)? Coords2dValue '}' /,
  Coords2dArray:        / Coords2dValue (',' Coords2dValue)* (',')? ';' /,
  Coords2dValue:        / FloatValue:u FloatValue:v /,
(中略)
  XSkinMeshHeader:      / XSkinMeshHeaderLong | ( XSkinMeshHeaderValue ';') /,
  XSkinMeshHeaderLong:  / 'XSkinMeshHeader' (Name)? '{' (UUID)? XSkinMeshHeaderValue '}' /,
  XSkinMeshHeaderValue: / $ANY /,
}, "XFile", Gin.SPACE);

function XFileHandler() { this._s = [] };
XFileHandler.prototype = {
  first: function(v){ return v[0]; },
  header: function(v){ alert("version: " + v[1]); },
  identifier: function(v){ alert("identifier:" + v[0]); },
  xof: function(v){ alert("xof"); },
  showValue: function(v){ alert("value:" + v); },
  name: function(v){ alert("name:" + v); },
(中略)
  // Mesh
  mesh:      function(v){ alert("Mesh start"); },
  nVertices: function(v){ alert("nVertices: " + v); },
  vertices:  function(v){},
  nFaces:    function(v){ alert("nFaces: " + v); },
  faces:     function(v){},
(中略)
};
 
var match;
function parseXFile(request)
{
  var text = request.responseText;
  var handler = new XFileHandler();
  match = xfileGrammar.parse(text, handler);
  if(match && match.full) {
    alert("OK");
  }else{
     alert("NG");
  }
}

var url = "xfile.x";
var myAjax = new Ajax.Request(
  url,
  {
    method: 'get',
    onComplete: parseXFile
  });
}
パースしたいデータの構文が分かっているなら、それをそのまま書いてやれば簡単にパーサが実装できる。
速度を優先するなら、各ファイル形式に合わせて自分で実装すべし。
xファイルのパーサはこんな感じで作りました。
var XParser = Class.create({
  _text: null,
(中略)
  parse: function(text){
    this._text = text;
    if(!this._obj){
      this._obj = new XModel();
    }
    this._offset = 0;
    this._err = 0;

    if(!this.XFileHeader(this._obj)){
      alert("header format error");
      this._err = 1;
      return null;
    }
    while(this.XObjectLong(this._obj)){}
    if(this._err){
      alert("obj format error:" + this._err);
      return null;
    }
    this.splitFaceNormals();

    return this._obj;
  },

  moveIndex: function(len) {
    this._text = this._text.substring(len);
    this._offset += len;
  },

  getString: function(pattern) {
    this.skip();
    var str = this._text.match(pattern);
    if(str != null){
      this.moveIndex(str[0].length);
      return str[0];
    }
    return null;
  },

  /* matching patterns */
  skip: function() {
    var i=0;
    var code;
    while(1){
      code = this._text.charCodeAt(i);
      if(code != 32 && (code < 9 || code > 13)){
        break;
      }
      i++;
    }
    if(i>0){
      this.moveIndex(i);
    }
  },

  _integerPattern: new RegExp(/^(-|\+)?\d+;?/),
  getInteger: function() {
    var str = this.getString(this._integerPattern);
    var val = parseInt(str);
    return val;
  },

  _floatPattern: new RegExp(/^(-|\+)?(\d)*\.(\d)*;?/),
  getFloat: function() {
    var str = this.getString(this._floatPattern);
    var val = parseFloat(str);
    return val;
  },
(中略)
  XObjectLong: function(parent){
    var id = this.getWord();
    if(id == null){
      return null;
    }
    switch(id){
      case "template":
        return this.Template(parent);
      case "Header":
        return this.Header(parent);
      case "Mesh":
        return this.Mesh(parent);
      case "MeshMaterialList":
        return this.MeshMaterialList(parent);
      case "MeshNormals":
        return this.MeshNormals(parent);
      case "MeshTextureCoords":
        return this.MeshTextureCoords(parent);
      case "MeshVertexColors":
        return this.MeshVertexColors(parent);

      default:
        alert("unknown type:" + id);
        break;
    }
    return false;
  },
(中略)
  Mesh: function(parent) {
    this.getLeftBrace();

    // vertices
    var nVertices = this.getInteger();
    var skinArray = $A();
    for(var i=0; i<nVertices; i++){
      var skin = new Skin();
      var pos = new DHVector3();
      pos.x = this.getFloat();
      pos.y = this.getFloat();
      pos.z = -this.getFloat();
      skin.position = pos;

      skin.boneIndex[0] = 0;
      skin.boneIndex[1] = -1;
      skin.boneIndex[2] = -1;
      skin.boneIndex[3] = -1;

      skin.skinWeight[0] = 1;
      skin.skinWeight[1] = 0;
      skin.skinWeight[2] = 0;
      skin.skinWeight[3] = 0;

      skinArray.push(skin);

      this.getCommaOrSemicolon();
    }
    this._obj.skinArray = skinArray;
    this._obj.dynamicSkinOffset = -1;

    // faces
    var nFaces = this.getInteger();
    var faces = $A();
    for(var i=0; i<nFaces; i++){
      var face = this.getIntegerArray();
      faces.push(face);
    }
    this._indices = faces;
    this.getRightBrace();

    return true;
  },
(中略)
}

2012/10/05(Fri) 01:21:39

 【MMD】ブラウザで踊ってもらった(その7)【WebGL】 / magicien 

先日、こっそりサンプルを公開したら、本当に誰も遊びに来てくれなかったので、紹介動画を作りました。


奇しくも、あれからちょうど半年。嘘が真になる裏エイプリルフールというやつです。
年内にその8を公開したいけど、ネタが無いなぁ。

2012/10/01(Mon) 23:52:15

 JavaScriptでバイナリデータの読み込み / magicien 

JavaScriptでPMD、VMD等のバイナリデータを読み込んだときに使った方法。

HTML5のAPIの実装が進めば、こんなことする必要も無くなる気がしますが、今使っている方法は次のとおり。

まずはファイルをバイナリとして取得する必要がある。
読み込みの対象がFileオブジェクトの場合は、FileReaderを使えばOK。
function readData(data) {
 ...
}

function readBinaryFile(file) {
  var reader = new FileReader();
  reader.onloadend = function() {
    readData(reader.result);
  };
  reader.readAsBinaryString(file);
}
読み込み対象がサーバ上のファイルであれば、"charset=x-user-defined" として読み込む。
例によって prototype.js を使っているので、Ajax.Requestのサブクラスを作って次のようにした。
var BinaryRequest = Class.create(Ajax.Request,
  initialize: function($super, url, options) {
    $super(url, options);
  },

  request: function() {
    this.transport.overrideMimeType('text/plain; charset=x-user-defined');
    Ajax.Request.prototype.request.apply(this, argument);
  },
});

function readBinaryFileFromURL(url) {
  new BinaryRequest(url, {
    method: 'GET',
    onComplete: function(response) {
      readData(response.responseText);
    },
  });
}
これで、データを読み込むところまでは出来たので、あとはデータの解析をするだけ。
とはいえ、そっちの方が大変ですが。データの解析はまた今度。
2012/09/29(Sat) 01:06:13

 JavaScriptでベクトル、行列計算の高速化 / magicien 

JavaScriptでベクトルや行列の計算を高速化しようとしてやったこと。

WebGLを始めた頃、まだライブラリもさほど世の中に無い状態だったので、ベクトルや行列計算のライブラリを自分で実装しました。
3Dのプログラミングというと、8割方ベクトルと行列の計算なんじゃないかと思うのですが、ということは、ベクトルや行列の計算を高速化することで、プログラム全体の高速化が望めるのではないかと考えました。
で、いろいろと実験した結果、割と効果があったのが、ループアンローリングという手法。
CPUのパイプラインが云々で分岐を無くすことで、コードの先読みを有効活用したりデータの依存関係をコンパイラに分かりやすくしたりとかなんとかでまぁあれなんですけど、JavaScriptみたいなスクリプト言語で効果があるのやら駄目もとでやってみたところ、思いのほか有効でした。
ループアンローリングをしない場合だと、4x4の行列計算は次のような書き方になるかと思います。
// 行列の定義
var matrix1 = [[11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34], [41, 42, 43, 44]];
var matrix2 = [[11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34], [41, 42, 43, 44]];
var matrix3 = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]];
// 行列のかけ算
for(var i=0; i<4; i++){
  for(var j=0; j<4; j++){
    for(var k=0; k<4; k++){
      matrix3[i][j] += matrix1[i][k] * matrix2[k][j];
    }
  }
}
ループアンローリングはループを全部展開してしまって、ループ毎に発生する分岐とindexの変数を無くします。
ついでに、配列も無くしてしまって変数を参照するときのオーバーヘッドを少なくしてやります。
すると、
// 行列の定義
var matrix1 = new Object();
matrix1.m11 = 11; matrix1.m12 = 12; matrix1.m13 = 13; matrix1.m14 = 14;
matrix1.m21 = 21; matrix1.m22 = 22; matrix1.m23 = 23; matrix1.m24 = 24;
matrix1.m31 = 31; matrix1.m32 = 32; matrix1.m33 = 33; matrix1.m34 = 34;
matrix1.m41 = 41; matrix1.m42 = 42; matrix1.m43 = 43; matrix1.m44 = 44;
var matrix2 = new Object();
matrix2.m11 = 11; matrix2.m12 = 12; matrix2.m13 = 13; matrix2.m14 = 14;
matrix2.m21 = 21; matrix2.m22 = 22; matrix2.m23 = 23; matrix2.m24 = 24;
matrix2.m31 = 31; matrix2.m32 = 32; matrix2.m33 = 33; matrix2.m34 = 34;
matrix2.m41 = 41; matrix2.m42 = 42; matrix2.m43 = 43; matrix2.m44 = 44;
var matrix3 = new Object();
matrix3.m11 = 0; matrix3.m12 = 0; matrix3.m13 = 0; matrix3.m14 = 0;
matrix3.m21 = 0; matrix3.m22 = 0; matrix3.m23 = 0; matrix3.m24 = 0;
matrix3.m31 = 0; matrix3.m32 = 0; matrix3.m33 = 0; matrix3.m34 = 0;
matrix3.m41 = 0; matrix3.m42 = 0; matrix3.m43 = 0; matrix3.m44 = 0;
// 行列のかけ算
matrix3.m11 = matrix1.m11 * matrix2.m11 + matrix1.m12 * matrix2.m21 + matrix1.m13 * matrix2.m31 + matrix1.m14 * matrix2.m41;
matrix3.m12 = matrix1.m11 * matrix2.m12 + matrix1.m12 * matrix2.m22 + matrix1.m13 * matrix2.m32 + matrix1.m14 * matrix2.m42;
matrix3.m13 = matrix1.m11 * matrix2.m13 + matrix1.m12 * matrix2.m23 + matrix1.m13 * matrix2.m33 + matrix1.m14 * matrix2.m43;
matrix3.m14 = matrix1.m11 * matrix2.m14 + matrix1.m12 * matrix2.m24 + matrix1.m13 * matrix2.m34 + matrix1.m14 * matrix2.m44;
matrix3.m21 = matrix1.m21 * matrix2.m11 + matrix1.m22 * matrix2.m21 + matrix1.m23 * matrix2.m31 + matrix1.m24 * matrix2.m41;
matrix3.m22 = matrix1.m21 * matrix2.m12 + matrix1.m22 * matrix2.m22 + matrix1.m23 * matrix2.m32 + matrix1.m24 * matrix2.m42;
matrix3.m23 = matrix1.m21 * matrix2.m13 + matrix1.m22 * matrix2.m23 + matrix1.m23 * matrix2.m33 + matrix1.m24 * matrix2.m43;
matrix3.m24 = matrix1.m21 * matrix2.m14 + matrix1.m22 * matrix2.m24 + matrix1.m23 * matrix2.m34 + matrix1.m24 * matrix2.m44;
matrix3.m31 = matrix1.m31 * matrix2.m11 + matrix1.m32 * matrix2.m21 + matrix1.m33 * matrix2.m31 + matrix1.m34 * matrix2.m41;
matrix3.m32 = matrix1.m31 * matrix2.m12 + matrix1.m32 * matrix2.m22 + matrix1.m33 * matrix2.m32 + matrix1.m34 * matrix2.m42;
matrix3.m33 = matrix1.m31 * matrix2.m13 + matrix1.m32 * matrix2.m23 + matrix1.m33 * matrix2.m33 + matrix1.m34 * matrix2.m43;
matrix3.m34 = matrix1.m31 * matrix2.m14 + matrix1.m32 * matrix2.m24 + matrix1.m33 * matrix2.m34 + matrix1.m34 * matrix2.m44;
matrix3.m41 = matrix1.m41 * matrix2.m11 + matrix1.m42 * matrix2.m21 + matrix1.m43 * matrix2.m31 + matrix1.m44 * matrix2.m41;
matrix3.m42 = matrix1.m41 * matrix2.m12 + matrix1.m42 * matrix2.m22 + matrix1.m43 * matrix2.m32 + matrix1.m44 * matrix2.m42;
matrix3.m43 = matrix1.m41 * matrix2.m13 + matrix1.m42 * matrix2.m23 + matrix1.m43 * matrix2.m33 + matrix1.m44 * matrix2.m43;
matrix3.m44 = matrix1.m41 * matrix2.m14 + matrix1.m42 * matrix2.m24 + matrix1.m43 * matrix2.m34 + matrix1.m44 * matrix2.m44;
うわぁ…となります。
一見するとすごく効率が悪そうですが、実際はこちらの方が格段に早いです。
あとは変数名の文字数を訳が分からなくならない程度に短くしてコード量を減らしてみたり、関数呼び出しを減らすように気をつけたりといった感じです。
配列を使わない方法で行列を定義した場合に一番苦労したのは、逆行列の計算。頭がおかしくなるかと思いました。まだバグが残ってるかもしれないという不安が付きまといます。
どなたかライブラリを使ってみてデバッグに協力してもらえないでしょうか。
といってもドキュメント皆無だからなぁ。早くドキュメントを作成せねば。
2012/09/26(Wed) 02:13:42

 canvasでWebフォントを使う / magicien 

canvasでWebフォントを使う方法。

まず、Webフォントを適当な場所にアップロードして、CSSにWebフォントを定義する。
@font-face {
  font-family: 'Expressway';
  src: url('./font/Expressway.woff') format('woff');
}

次に、JavaScriptでCanvasのコンテキストのfontにCSSで定義したfont-family(例ではExpressway)を指定する。Webフォント未対応の場合のために、代替となるフォントファミリー(例ではsans-serif)も指定しておく。
function drawString(context2D) {
  context2D.font = "24px bold Expressway,sans-serif";
  context2D.textAlign = "left";
  context2D.textBaseline = "middle";
  context2D.fillStyle = "#000000"; // black
  context2D.strokeStyle = "#FFFFFF#; // white
  context2D.fillText( "Hello, WebGL!" , 100, 100 );
}
CSSの"format"には、woff(Web Open Font Format)の他に、truetype、svg等が使えるらしい。
2012/09/25(Tue) 01:18:37