ぽんこつメモ

https://github.com/kanorimon

Amazon EC2を使って、3Dレンダリングをしてみたメモ

米国東部ヴァージニアリージョンにEC2コンピュータクラスタを作成

プレイスメントグループにインスタンス(マスター・スレーブ)を作成

  • AMIにWindows Server 2008 R2を選択
  • Administratorパスワードを取得
  • リモートデスクトップ接続する
  • ファイアウォールの設定で、ポート8000(blenderの通信ポート)を開放する
  • blenderをインストールする(NetworkRenderアドインを設定しておくこと)

blenderで処理

  • マスター用サーバーでblenderを起動し、マスターの開始ボタンを押す
  • マスター用サーバーのIPアドレス(プライベートアドレス)を確認する
  • スレーブ用サーバーでblenderを起動し、マスター用サーバーのアドレスを設定し、スレーブの開始ボタンを押す
  • クライアント用サーバーでblenderを起動し、処理するファイルを開いた後、マスター用サーバーのアドレスを設定する
  • クライアントの「Send job」ボタンを押す
  • サーバーモニターでFinishedになったら「Get Animation」ボタンを押す
  • レンダリング結果は、クライアントで設定したPathに格納される

注意事項

bake処理はコア数が増えても並列処理できないので、マイクロインスタンスで十分。

クラスタコンピュートの場合(Twitterから転載)

  • クラスタコンピュート エイトエクストララージ
  • レンダリングエンジン=Blender内部レンダラ
  • 画像サイズ=640*360
  • HDR使用
  • サンプル数8
  • ミラーマテリアルなし

約30sec/1フレーム。

30fpsで5minの動画だと9000枚必要なので、9000*0.5minで4500min=75h。

75h*$3=$225。

 

$240/$3=80台を並列に並べれば約1hでレンダリング完了。

逆に1台でがんばっても、レンダリング時間がのびるだけで$225かかる。

ただし、ネットワーク転送に時間がかかる+分散処理に限界があるのでは?(未検証)

GPUインスタンスの場合(Twitterから転載)

  • クラスタコンピュート エイトエクストララージ
  • レンダリングエンジン=Cycles
  • 画像サイズ=640*360
  • HDR使用
  • サンプル数128
  • ミラーマテリアルなし

13.94sec/1フレーム。

0.25min*9,000フレーム=2,250min、2,250min/60min≒38h、38h*$2.6=$98.8。

スポットインスタンスなら38h*$1=$38。

 

10インスタンス起動すれば4hでレンダリング完了。

ただし、ネットワーク転送に時間がかかる+インスタンス起動数に上限があるので注意。

結論

何度かベンチしてみたところ、AWSだと、どの方法でもだいたい$8/500フレーム程度が限界。

ユーロ建てだと高くつく、または、自前でサーバーを構築しないといけない場合は、GPUインスタンスのオンデマンド*1(マスター用)と、GPUインスタンスのスポット*n(スレーブ用)で処理する。

一般的なレンダリング処理だけなら、レンダーファーム(RenderFlowだと5ユーロ/500フレーム)を利用する。

 

Unityのtoon shaderでalpha抜き

Shader "Custom/NewShader 2" {

Properties {

_Color ("Main Color", Color) = (0.5,0.5,0.5,1)

_MainTex ("Base (RGB)", 2D) = "white" {}

_Ramp ("Toon Ramp (RGB)", 2D) = "gray" {} 

}

 SubShader {

       Tags {"Queue"="Transparent" "RenderType"="Transparent"}

       Blend SrcAlpha OneMinusSrcAlpha

       LOD 200

 

CGPROGRAM

#pragma surface surf ToonRamp

 

sampler2D _Ramp;

 

// custom lighting function that uses a texture ramp based

// on angle between light direction and normal

#pragma lighting ToonRamp exclude_path:prepass

inline half4 LightingToonRamp (SurfaceOutput s, half3 lightDir, half atten)

{

    #ifndef USING_DIRECTIONAL_LIGHT

    lightDir = normalize(lightDir);

    #endif

 

    half d = dot (s.Normal, lightDir)*0.5 + 0.5;

    half3 ramp = tex2D (_Ramp, float2(d,d)).rgb;

 

    half4 c;

    c.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2);

    c.a = s.Alpha;

    return c;

}

 

 

sampler2D _MainTex;

float4 _Color;

 

struct Input {

    float2 uv_MainTex : TEXCOORD0;

};

 

void surf (Input IN, inout SurfaceOutput o) {

    half4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;

    o.Albedo = c.rgb;

    o.Alpha = c.a;

}

ENDCG

 

    } 

 

    Fallback "Diffuse"

 

    

}

 

railsアプリ開発(もっと読む改善)

Ajaxでinsert,deleteすると、kaminariでは取得開始がずれます。

うまく回避する方法がわからなかったので、sessionを使って「もっと読む」を実装してみました。

index.html.erb

<% if @count_memos == 0 %>

  <span>もうありません</span>        

<% else %>

  <%= link_to 'more', load_more_path , :remote => true, :id => "load_more_link" %>

<% end %>

index.js.erb

$('#posts').append("<%= escape_javascript(render :partial => @memos)%>");

<% if @count_memos == 0 %>

    $('#load_more_link').hide();

<% end %>

memos_controller.rb

  def showlist

  # ユーザーのmemoを取得

    @memos = Memo.where("user_id =? and id < ?", current_user.id,last_memo.id).order('id desc').limit(5)

    # sessionに保存

    session[:last_memo_id] = @memos.last.id

   # 最終ページであるかの判断

    @count_memos = Memo.count(:conditions => ["user_id =? and id < ?", current_user.id,@memos.last.id])

    # 画面表示

    render 'contents/index.js.erb'

  end

application_controller.erb

  # 読み込み最終行設定

 helper_method ::last_memo

  private

  def last_memo

    @last_memo ||= Memo.find(session[:last_memo_id]) if session[:last_memo_id]

  end

railsアプリ開発(もっと読む)

kaminariで「もっと読む」を実装してみた。

Ajaxで書き換えた部分についてはhelperが効かないようなので、js.erbで最終ページか否かを判断しました。

index.html.erb

<div id="posts">

  <%= render @memos %>

</div>

<%= link_to_next_page @memos, 'more', :remote => true, :id => "load_more_link" %>

index.js.erb

$('#posts').append("<%= escape_javascript(render :partial => @memos)%>");

<% if @memos.last_page? %>

    $('#load_more_link').hide();

<% else %>

    $('#load_more_link').replaceWith("<%= escape_javascript(link_to_next_page @memos, 'more', :remote => true, :id => 'load_more_link' )%>");

<% end %>

railsアプリ開発(application.css)

requireの順番

bootstrap_responsiveは、最後に読み込む

以下のコードで読み込み順番を指定する

 *= require bootstrap.min

 *= require_self

 *= require default

 *= require bootstrap-responsive.min

 *= require_tree .

railsアプリ開発(paperclip)

imagemagickインストール

# yum install ImageMagick ImageMagick-devel

CentOS6系なのでyum、最新を使用する場合はソースからコンパイル

paperclipインストール

# vi Gemfile

gem "paperclip", "~> 3.0"

# vi config/enviroments/development.rb

Paperclip.options[:command_path] = "/usr/bin/"

model更新

attr_accessible :avatar

has_attached_file :avatar, :styles => { :thumb => "50x50>" }

migration追加

class AddAvatarToUsers < ActiveRecord::Migration

  def self.up

    add_attachment :users, :avatar

  end

  def self.down

    remove_attachment :users, :avatar

  end

end

URLからアップロード

require 'open-uri'

user.avatar = open(auth["info"]["image"])

アップロード時のファイル名指定

before_post_process :transliterate_file_name

def transliterate_file_name

  extension = 'jpg'

  filename = self.nickname

  self.avatar.instance_write(:file_name, "#{filename}.#{extension}")

end

メモ

has_attached_file :avatar,

  :path => ":rails_root/public/system/:attachment/:id/:style/:filename",

  :url => "/system/:attachment/:id/:style/:filename"