2013/08/27

[three.js] Lightの使い方 - DirectionalLight編


前回、AmbientLight の使い方について書いた。今回は同じくライトの中で、DirectionalLight について纏めます。

DirectionalLight

DirectionalLightは、「指向性ライト」とでもいうのか。このライトに照らされるオブジェクトの素材(Material)によって、効果が変わる。ドキュメントには、

Affects objects using MeshLambertMaterial or MeshPhongMaterial.

とある。つまり、MeshLambertMeterialか、MeshPhongMaterialを使ったオブジェクトに作用する、と。
実際に、 をシーンに置いて、DirectionalLightで照らしてみたところ、ドキュメント通り動作した。
このライトによって、影が描画されるのかな、と思って、

  • shadowCameraNear
  • shadowCameraFar
  • shadowCameraLeft
  • shadowCameraRight
  • shadowCameraTop
  • shadowCameraBottom
を色々変えてみたが、影は描画できていない。その際に、もちろん、オブジェクト側のcastShadow属性や、 rendererのshadowMapEnabledはtrueで試した。
今のところの結論として、
  • オブジェクトはMeshLambertMaterialかMeshPhongMaterialであれば、光が当たっていることが表現される。
  • 影は描画できない。
を得た。 
Directional Light. - simalabsで実験版を動かしています。ご覧ください。


2013/08/26

[three.js] Lightの使い方 - AmbientLight編


Three.jsのライト

Three.js には、ライトを表現するオブジェクトとして、
がある。シーンに、ライトを適切に配置しないと、オブジェクトが見えない。
それぞれのライトがどのような性質があって、どう使えばいいのかが(3D初心者の僕には)わからないので、 一つずつ実験してみよう、ということで、AmbientLightを使ってみました。

Light

Lightは、上記の各ライトの基底となるオブジェクトで、属性として「color」を持っている。 それ以外に別段コレという属性も操作も持っていない。

AmbientLight

AmbientLightは、直訳すると「周辺光」という意味で、どうやら3D空間の描画において、自然に発生する普遍的な光を表現するための概念と思われた。
AmbientLightには、他のライトが持つような光源の座標や、影に関する属性がなく、
  • color
を属性に持っている。 それと、他のライトは、概ねintensityという光の強度を属性に持つが、AmbientLightはそれももっていない。
じゃあ、どうしたらライトのON/OFFができるのか、といえば、Lightの基底であるObject3Dから継承している、
  • visible
属性をfalseにすることで、AmbientLightをOFFにできた。
さらに、AmbientLightのドキュメントには、次のような記述がある。
This light's color gets applied to all the objects in the scene globally.

なので、このライトに照らされた全てのオブジェクトは、このライトが持つcolorの影響を受ける、ということだ。
一方で、3Dオブジェクトを表現するThree.jsのオブジェクト MeshLambertMaterialは、ambientという属性を持っていて、これは、
Ambient color of the material, multiplied by the color of the AmbientLight. Default is white.

なので、この値を変えれば、AmbientLightに照らされた時の色が表現できる、ということになる。

an experiment.

ということで、実際に試してみたのが、こちらになります。

2013/08/15

node(express.js)なアプリをcapistranoでdeployする。

Railsアプリをサーバーにデプロイする際に、Capistranoを使ってきたけど、これをNode.js+express.jsなアプリでも使えればなぁ、と思っていて、@d_akatsukaさんの、
Node.js アプリをデプロイして Upstart で起動させる Capistrano レシピを書いた
を大いに参考にさせて頂いて、やっと自分の環境でも動かすことができたので、まとめてみる。

Capistrano

Capistranoは、rubygemsとしてインストールする。

  $ gem install capistrano
  $ cap --version
  Capistrano v2.15.4
ちなみに、僕の環境のrubyはrbenv+ruby_buildでインストールしたruby2.0.0p0。

Capfileとdeploy.rb

Capistranoでデプロするには、一般的に、Capfileとdeploy.rbという2つのRubyスクリプトが必要になる。 今回は、express.jsアプリケーションの標準的なディレクトリ構成にこの2つのファイルを追加した。

project_root
├── Capfile
├── app.js
├── config
│   └── deploy.rb
├── node_modules
├── package.json
├── public
├── routes
└── views

それぞれ、次のような内容を書く。
#
# Capfile
#
load 'deploy'
# Uncomment if you are using Rails' asset pipeline
    # load 'deploy/assets'
load 'config/deploy' # remove this line to skip loading any of the default tasks
#
# deploy.rb
#
set :application,        'application_name'
set :host,               'your.host.com' # or ip address.
set :user,               'username'
set :password,           'password'
set :deploy_to,          "/var/www/apps/#{application}"
set :shared_children,    %w(log node_modules)

set :node_file,          'app.js'
set :node_version,       "v0.10.12"
set :application_binary, "/usr/local/nvm/#{node_version}/bin/node"
set :npm_binary,         "/usr/local/nvm/#{node_version}/bin/npm"

set :scm,          :git
set :repository,   'git://git.host/your/repository.git'
set :branch,       'master'
set :deploy_via,   :remote_cache

role :app,           host
set  :use_sudo,      false
set  :default_run_options, :pty => true

namespace :deploy do

  task :default do
    update
    start
  end

  task :cold do
    update
    start
  end

  task :setup, :expect => { :no_release => true } do
    dirs = [deploy_to, release_path, shared_path]
    dirs += shared_children.map { |d| File.join(shared_path, d) }
    run "mkdir -p #{dirs.join(' ')}"
    run "chmod g+w #{dirs.join(' ')}" if fetch(:group_writable, true)
  end

  task :finalize_update, :expect => { :no_release => true } do
    run "chmod -R g+x #{latest_release}" if fetch(:group_writable, true)
    run <<-CMD
      rm -rf #{latest_release}/log #{latest_release}/node_modules &&
      ln -s #{shared_path}/log #{latest_release}/log &&
      ln -s #{shared_path}/node_modules #{latest_release}/node_modules
    CMD
  end

  task :start, :roles => :app do
    run "#{sudo} restart #{application} || #{sudo} start #{application}"
  end

  task :stop, :roles => :app do
    run "#{sudo} stop #{application}"
  end

  task :restart, :roles => :app do
    start
  end

  task :npm, :roles => :app do
    run "cd #{latest_release} && #{npm_binary} install"
  end

  desc "writes the upstart script for running the daemon."
  task :write_upstart_script, :roles => :app do
    upstart_script = <<-UPSTART
description "#{application} upstart script"

start on (local-filesystem and net-device-up)
stop  on shutdown

respawn
respawn limit 5 60

script
  chdir #{current_path}
  exec sudo -u #{user} NODE_ENV="production" #{npm_binary} start >> log/production.log 2>&1
end script
    UPSTART

    put upstart_script, "/tmp/#{application}.conf"
    run "#{sudo} mv /tmp/#{application}.conf /etc/init"
  end

end

after 'deploy:setup',           'deploy:write_upstart_script'
after 'deploy:finalize_update', 'deploy:npm'
ほぼ、前述の@d_akatsukaさんの記事のdeploy.rbとかわらないけど、サーバーサイドでnvmをシステムワイドに適用していることで、 nvm経由のnodeやnpmの起動で、パスの指定がこのようになった。

デプロイする

初めてサーバーにデプロイする場合は、

 $ cap deploy:setup
を実行する。コレが成功すると、:deploy_toで指定したディレクトリがサーバーサイドで生成され、 さらに/etc/init配下にupstartのスクリプトが生成される。
通常は、
 $ cap deploy
を実行すると、リポジトリから最新モジュールを取得し、転送、配置、upstartでnodeの再起動をやってくれる。
上述のdeploy.rbで、
 $ cap deploy:start
 $ cap deploy:stop
 $ cap deploy:restart
 $ cap deploy:rollback
を試したけど、うまく動いた。

2013/08/10

モックhtmlはJade+LESS+CoffeeScript+Grunt.jsで。

WebサイトやWebアプリケーションの画面仕様やデザインの検討や確認のために、 モックhtmlを書くことが度々あった。 そのたびに、テキストエディタで、生のhtml、css、Javascriptをガシガシ書いていた。 が、ページ数が多くなるに連れて、DRYじゃないことに憤りを感じ、メンテナンスが面倒になり、 もっと効率良く、メンテナンスが楽にできないものなのか、と考えていた。

node.jsの勉強をしていて、express.jsに出会い、テンプレートエンジンJadeと メタCSS言語LESSに出会った。Railsを使った開発では、CoffeeScriptを使っていた。 そこで、これらを使って静的なページを作るとかなり効率がいいのでは?と思った。


モックをはやく作るための道具

JadeとLESS、CoffeeScriptで静的なHTMLを作れないか。
Jadeは単体で使える。
LESSも単体でコンパイルできる。
もちろんCoffeeSriptも。
で、全部Node.jsの上で動く。
全部一発でビルドできれば、そりゃあ楽ちんだわぁ。
そういえば、Grunt.jsっていうのがあったな、たしか。
Javaのapache antみたいなやつ。

ということで、使うのは、

node.jsとGrunt.jsのインストール

node.jsは予めインストールしておく必要がある。Grunt.jsは、grunt-cliというGrunt.jsのCLIパッケージをnpmでインストールする。

  $ npm install -g grunt-cli

ディレクトリとファイル

おそらく、こうしなければよい、という構造はない。 とりあえず、次のようなディレクトリ階層を作成し、2つのファイル、package.jsonとGruntfile.jsを作った。

├── Gruntfile.js
├── package.json
└── src
    ├── coffee
    │   └── jade-de-mock.coffee
    ├── jade
    │   ├── includes
    │   │   └── javascripts.jade
    │   ├── index.jade
    │   └── layout.jade
    └── less
        ├── jade-de-mock.less
        └── reset.less

Grunt.jsのプラグインとタスク

jadeのコンパイルや、lessのコンパイル、coffeeのコンパイルは、Grunt.jsのプラグインを利用する。 そのため、まず、Grunt.jsが動作する環境を整備しないと。

package.jsonを書く

  {
    "name": "jade-de-mock",
    "version": "0.1.0"
  }

gruntとgruntプラグインをnpmでインストールする。

jade、less、coffeeのgruntプラグインは、grunt.jsが提供してくれている。

それと、前回のビルド結果を相違するcleanタスクも欲しいので、これもインストール。

というわけで、上記4つのgruntプラグインを、npmコマンドでインストール。
  $ npm install grunt --save-dev
  $ npm install grunt-contrib-jade grunt-contrib-less grunt-contrib-coffee grunt-contrib-clean --save-dev
これを実行すると、上述のpackage.jsonにプラグインが追記され、結果、
  {
    "name": "jade-de-mock",
    "version": "0.1.0",
    "devDependencies": {
      "grunt": "~0.4.1",
      "grunt-contrib-coffee": "~0.7.0",
      "grunt-contrib-clean": "~0.5.0",
      "grunt-contrib-jade": "~0.8.0",
      "grunt-contrib-less": "~0.7.0"
    }
  }
となる。

Gruntfile.jsを書く

Gruntfile.jsは、apache-antでいうところのbuild.xmlのようなもの。gruntに実行させるタスクをJavascriptで書く(CoffeeScriptでも良い)。

module.exports = function(grunt) {

  // project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    jade: {
      compile: {
        options: {
          pretty: true
        },
        files: {
          "dst/index.html": ["src/jade/index.jade"]
        }
      }
    },
    less: {
      compile: {
        options: {
          compress: true,
          syncimport: true
        },
        files: {
          "dst/stylesheets/<%= pkg.name %>.min.css": "src/less/jade-de-mock.less"
        }
      }
    },
    coffee: {
      compile: {
        files: {
          "dst/javascripts/<%= pkg.name %>.js": "src/coffee/*.coffee"
        }
      }
    },
    clean: {
      build: ["dst"]
    }
  });

  // load npm tasks.
  grunt.loadNpmTasks('grunt-contrib-jade');
  grunt.loadNpmTasks('grunt-contrib-less');
  grunt.loadNpmTasks('grunt-contrib-coffee');
  grunt.loadNpmTasks('grunt-contrib-clean');

  // define default tasks.
  grunt.registerTask('default', ['clean', 'jade', 'less', 'coffee']);

};

これで準備完了。

モックを書く。

ひたすらモックを書く。Jadeを書く。LESSを書く。CoffeeScriptを書く。

ビルドする。

ビルドはGrundコマンドで実行。先ほどのGruntfile.jsにしたがって、タスクが実行される。

  $ grunt
これで、dstディレクトリが生成され、次のようなファイルが出力される。
  dst/
  ├── index.html
  ├── javascripts
  │   └── jade-de-mock.js
  └── stylesheets
       └── jade-de-mock.min.css

モックを作るたびに使えばいい

と思ってgithubに、

を作っちゃった。

2013/08/02

[three.js] PerspectiveCameraのレンズ

Three.jsのCameraには、レンズを設定するためのメソッド、

  THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) {
    if ( frameHeight === undefined ) frameHeight = 24;
      this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) );
      this.updateProjectionMatrix();
  }
があります。

このメソッドの引数は、

focalLength
焦点距離(mm)
frameHeight
フレーム高(mm)
となっていて、デフォルトだと、foculLength = 35, frameHeight = 24として動作します。

通常のカメラと同様に、foculLength(焦点距離)を大きくすると画角(fov)が小さくなるため、 描画対象が大きくなります(望遠レンズ)。
一方で、foculLengthを小さくすれば画角が大きくなるため、描画対象が小さくなります(広角レンズ)。

この振る舞いを確認するためのサンプルが、
let's play with cameras!
にあります。
WebGLに対応したブラウザで見ていただければ、と思います。