mieki256's diary



2014/06/24(火) [n年前の日記]

#1 [dxruby] DXRubyで円柱っぽいBG描画をしてみたり

円柱と言うか、パイプと言うか、スペースコロニー内部と言うか、そういう感じのBG描画を DXRuby の Shader を使って試してみたり。

こんな感じになりました。
pipe_bg_ss.gif

ソースは以下。

_pipebg.rb
require 'dxruby'

#
# パイプ内のような背景描画を行う
#
class PipeBg
  @@hlsl = <<EOS
  float sz;
  float r;
  float angle_max;
  float start_x;
  float start_y;
  texture tex0;

  sampler Samp = sampler_state {
    Texture =<tex0>;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    MipFilter = LINEAR;
    // MinFilter = POINT;
    // MagFilter = POINT;
    // MipFilter = POINT;
    AddressU = BORDER;
    AddressV = WRAP;
  };

  float4 PS(float2 input : TEXCOORD0) : COLOR0
  {
    input.x = (input.x - 0.5) * (r * cos(atan2(input.y - 0.5, sz))) / sz + start_x + 0.5;
    input.y = (atan2(input.y - 0.5, sz) / angle_max) + start_y;
    return tex2D( Samp, input );
  }

  technique {
    pass {
      PixelShader = compile ps_2_0 PS();
    }
  }
EOS

  attr_accessor :sz
  attr_accessor :r
  attr_accessor :angle_max
  attr_accessor :start_x
  attr_accessor :start_y
  attr_accessor :shader

  #
  # コンストラクタ
  #
  # @param [Number] sz スクリーンまでの距離
  # @param [Number] r 壁までの距離
  # @param [Number] start_x 初期位置 x
  # @param [Number] start_y 初期位置 y
  #
  def initialize(sz = 160, r = 280, start_x = 0.0, start_y = 0.0)
    self.sz = sz
    self.r = r
    self.start_x = start_x
    self.start_y = start_y

    core = Shader::Core.new(@@hlsl,{
                              :sz=>:float,
                              :r=>:float,
                              :angle_max=>:float,
                              :start_x=>:float,
                              :start_y=>:float
                            })
    self.shader = Shader.new(core)
    self.shader.sz = self.sz.to_f / (Window.height / 2)
    self.shader.r = self.r.to_f / (Window.width / 2)
    self.shader.start_x = self.start_x
    self.shader.start_y = self.start_y
    self.angle_max = Math.atan2(Window.height / 2, sz)
    # self.angle_max = 90.0 * Math::PI / 180.0
    self.shader.angle_max = self.angle_max
  end

  #
  # 描画開始位置を更新
  #
  # @param [Number] dx テクスチャ取得位置 x 増分(1.0より小さい値が望ましい)
  # @param [Number] dy テクスチャ取得位置 y 増分(1.0より小さい値が望ましい)
  #
  def update(dx, dy)
    self.start_x += dx
    self.start_y += dy
    self.shader.start_x = self.start_x
    self.shader.start_y = self.start_y
  end

  #
  # 描画
  #
  # @param [Number] x 描画位置 x
  # @param [Number] y 描画位置 y
  #
  def draw(x, y, img)
    Window.drawEx(x, y, img, :shader => self.shader)
  end
end


if $0 == __FILE__
  # ----------------------------------------
  # 使用例

  font = Font.new(14)

  img = Image.load("bg_960x480.png")
  bg = PipeBg.new(200, 360, 0.0, 0.0)

  Window.loop do
    break if Input.keyPush?(K_ESCAPE)
    bg.update(0.0, -0.005)
    bg.draw(-Window.width / 4, 0, img)

    s = "#{Window.real_fps.to_i} FPS CPU: #{Window.getLoad.to_i} % x = #{bg.start_x.to_i}"
    Window.drawFont(4, 4, s, font)
  end
end

使用画像も置いときます。画像サイズは 960x480ドット。

_bg_960x480.png

他のスクリプトからも呼び出せるようにしておきました。以下は使用例。

_pipebg_test_main.rb
#
# pipebg.rb の呼び出しテスト
#

require 'dxruby'
require_relative 'pipebg'

# 背景マップデータ
mapdata = [
  [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 1, 1, 2, 2, 2, 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 2, 1, 1, 2, nil, nil, 2, 1, 2, 2, 2, 1, 1, 2, nil, nil, nil, 2, 1, 1, 1, 1, 1, 1, 1, 1, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 1, 2, nil, nil, 2, 1, nil, 2, 1, 2, nil, 1, 2, nil, nil, 2, 1, 2, nil, nil, 2, 1, 2, nil, nil, 2, 1, 2, nil, nil, nil, 2, 1, 3, 3, 3, 3, nil, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 2, nil, nil, 2, 1, nil, nil, 2, nil, nil, 1, 2, nil, nil, 2, 1, 2, nil, nil, 2, 1, 2, nil, 2, 1, 1, 1, 2, nil, 2, 1, 1, 3, 3, 3, 3, 3, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 2, nil, nil, 2, 1, nil, nil, 2, nil, nil, 1, 2, 2, 2, 1, 1, 2, nil, nil, 2, 1, 2, nil, nil, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 1, 2, nil, nil, 2, 1, nil, 2, 1, 2, nil, 1, 2, nil, 2, 1, 1, 2, nil, nil, 2, 1, 2, nil, nil, 2, 1, 1, 1, 2, 1, nil, nil, nil, nil, 1, 3, 3, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 1, 1, 2, 2, 2, 1, 1, 2, 1, 1, 1, 2, 1, 2, nil, nil, 2, 1, 1, 2, 2, 1, 1, 2, 2, 2, 1, 1, 1, 1, 2, 1, nil, nil, nil, 3, 3, 3, nil, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, nil]
]

font = Font.new(14)

bg = PipeBg.new(160, 280)

imgs = Image.loadTiles("bg_chip.png", 4, 1, true)
star_bg = Image.load("bg_star.png")
rt = RenderTarget.new(64 * 15, 64 * 8)

bg_x, bg_y = 0, 0

Window.loop do
  break if Input.keyPush?(K_ESCAPE)

  # 星BG描画
  Window.drawTile(0, 0, [[0, 0]], [star_bg], bg_x / 3, 0, 2, 1)

  # ReanderTargetにタイルBGを描画
  rt.drawTile(0, 0, mapdata, imgs, bg_x, bg_y, 15, 8).update

  # RenderTarget を円柱のように描画
  bg.draw(-Window.width / 4, -32, rt)

  bg_x += 3
  bg_y += 2

  s = "#{Window.real_fps.to_i} FPS CPU: #{Window.getLoad.to_i} %"
  Window.drawFont(4, 4, s, font)
end

使用画像。

_bg_chip.png
_bg_star.png

以下のような感じの画面になります。
pipe_bg_ss_2.gif


ソースや画像のライセンスは、Public Domain / CC0 ってことで。

一応、マップデータ(Tiled形式。.tmx) その他も含めて、一式置いときます。

_pipe_bg_20140624.zip (120KB)

処理内容について。 :

どうやって実現したのか自分自身が忘れそうなので、考え方をメモ。

Shader (HLSL) で、Input.x と Input.y の値を変化させてやれば、こういう円柱っぽい表示ができるわけですが…。

とりあえず、横から見た図で考えてみるとして。
pipe_side_proj2.png
  • 原点 (0,0) が、視点。
  • スクリーンまでの距離が、sz。テキトーに決めておく。
  • 青い線で描かれた円が、壁に相当するところ。半径 r の円。

点B、原点、点A でできる角度 a は、アークタンジェントを使えば求められます。この場合は、atan2(sy, sz) で得られるはず。

この、角度 a を、テクスチャを参照する際のuv値 ―― Input.y を変化させる値としてそのまま利用すれば、y方向に関しては円柱っぽい見た目になりそうだなと。Input.y は、0,0〜1.0 の値を取るので、角度 a も 0,0〜1.0 に収まるように変換してやればいいわけで。

さらに、角度 a が分かれば、点Aを通る直線と円の交点、のz値も、r * cos(a) で求められます。z値が得られたら、各ラスターを横方向にどれだけ拡大縮小すればいいのか計算可能になるわけで。

今度は上から見た図で考えるとして。
pipe_top_proj.png
  • z値は ―― 図で言うところの pz 値は、r * cos(a) で得られた。
  • sz はテキトーに決めてある。
  • sx も HLSL内では Input.x として得られると分かってる。
後は px を求めるだけ。
sx : sz = px : pz
sz * px = sx * pz
px = sx * pz / sz
px が求まったら、それを Input.x に反映してやれば、拡大縮小ができるわけで。

注意点としては…。Input.x も Input.y も、0.0〜1.0 として得られるので、0.5 を引くことで、画面の中心が 0.0 になるようにしてから計算に使ってます。

また、Shader は、与えられた画像 or RenderTarget を円柱っぽく描画する処理しかしていません。スクロール処理は…。
  • テクスチャuv値をちょっと弄る。
  • もしくは、RenderTarget にスクロールした状態の画を描画しておいて、その RenderTarget を Shader に渡す。
という仕組みでやってます。

課題。 :

y値に応じてラスター単位で明るさを変えたら、もうちょっと見た目がリアルっぽくなるのかもしれず。

実装してみたものの。 :

この手の処理はどうやって実現していたのか、個人的に気になっていたので、今回一応試してみたものの。例えば Unityあたりを使うなら、円柱っぽいモデルを配置・回転させて、それで終わりだろうなと予想するわけで。

3D描画が当たり前の現代においては、こういう処理ができたからと言って、何に使えるのか、どこで使えるのかと言う問題があるなと…。

でもまあ、プログラマーにとっての頭の体操、プログラマー向けのパズル、としてならアリかなと…。

#2 [dxruby] マップエディタ Tiled でエクスポートしたjsonに対して処理

マップエディタ _Tiled Map Editor は、json ファイルでエクスポートすることもできるのだけど。その json ファイルを Ruby で読み込んで、DXRuby で使える形に変換して出力できないかなと。

とりあえず、レイヤー情報だけでも取り出せないか、少し実験。

_convtmxjson.rb
# マップエディタ Tiled でエクスポートした json から
# DXRuby用のマップデータを取り出してみるテスト

require 'json'
require 'pp'

if ARGV.size == 0
  puts "usage: ruby #{$0} json_file"
  exit
end

infile = ARGV[0]
unless File.file?(infile)
  puts "#{infile} not found"
  exit 1
end

d = JSON.parse(File.open(infile).read)

# レイヤー枚数分ループ
d["layers"].each do |layer|
  name = layer["name"]
  w = layer["width"].to_i
  h = layer["height"].to_i

  ndt = layer["data"].map {|c| (c == 0 or c== 1)? nil : (c - 1)}
  a = ndt.each_slice(w).to_a

  puts "# name=#{name},w=#{w},h=#{h}"
  puts "mapdata = ["
  a.each { |aa| puts "  #{aa}," }
  puts "]"
end

exit

使い方。
ruby convtmxjson.rb hoge.json
ruby convtmxjson.rb hoge.json > map.txt

例えば、以下のような json ファイルを読みこませると。

_bg.json
{ "height":8,
 "layers":[
        {
         "data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 2, 2, 3, 2, 2, 2, 3, 2, 3, 3, 3, 2, 2, 3, 1, 1, 3, 2, 3, 3, 3, 2, 2, 3, 1, 1, 1, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 1, 1, 3, 2, 1, 3, 2, 3, 1, 2, 3, 1, 1, 3, 2, 3, 1, 1, 3, 2, 3, 1, 1, 3, 2, 3, 1, 1, 1, 3, 2, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 3, 2, 1, 1, 3, 1, 1, 2, 3, 1, 1, 3, 2, 3, 1, 1, 3, 2, 3, 1, 3, 2, 2, 2, 3, 1, 3, 2, 2, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 3, 2, 1, 1, 3, 1, 1, 2, 3, 3, 3, 2, 2, 3, 1, 1, 3, 2, 3, 1, 1, 3, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 1, 1, 3, 2, 1, 3, 2, 3, 1, 2, 3, 1, 3, 2, 2, 3, 1, 1, 3, 2, 3, 1, 1, 3, 2, 2, 2, 3, 2, 1, 1, 1, 1, 2, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 2, 2, 3, 2, 2, 2, 3, 2, 3, 1, 1, 3, 2, 2, 3, 3, 2, 2, 3, 3, 3, 2, 2, 2, 2, 3, 2, 1, 1, 1, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1],
         "height":8,
         "name":"layer1",
         "opacity":1,
         "type":"tilelayer",
         "visible":true,
         "width":60,
         "x":0,
         "y":0
        }],
 "orientation":"orthogonal",
 "properties":
    {

    },
 "tileheight":64,
 "tilesets":[
        {
         "firstgid":1,
         "image":"bg_chip.png",
         "imageheight":64,
         "imagewidth":256,
         "margin":0,
         "name":"bg_chip",
         "properties":
            {

            },
         "spacing":0,
         "tileheight":64,
         "tilewidth":64
        }],
 "tilewidth":64,
 "version":1,
 "width":60
}

以下のような出力結果に。
# name=layer1,w=60,h=8
mapdata = [
  [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 1, 1, 2, 2, 2, 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 2, 1, 1, 2, nil, nil, 2, 1, 2, 2, 2, 1, 1, 2, nil, nil, nil, 2, 1, 1, 1, 1, 1, 1, 1, 1, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 1, 2, nil, nil, 2, 1, nil, 2, 1, 2, nil, 1, 2, nil, nil, 2, 1, 2, nil, nil, 2, 1, 2, nil, nil, 2, 1, 2, nil, nil, nil, 2, 1, 3, 3, 3, 3, nil, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 2, nil, nil, 2, 1, nil, nil, 2, nil, nil, 1, 2, nil, nil, 2, 1, 2, nil, nil, 2, 1, 2, nil, 2, 1, 1, 1, 2, nil, 2, 1, 1, 3, 3, 3, 3, 3, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 2, nil, nil, 2, 1, nil, nil, 2, nil, nil, 1, 2, 2, 2, 1, 1, 2, nil, nil, 2, 1, 2, nil, nil, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 1, 2, nil, nil, 2, 1, nil, 2, 1, 2, nil, 1, 2, nil, 2, 1, 1, 2, nil, nil, 2, 1, 2, nil, nil, 2, 1, 1, 1, 2, 1, nil, nil, nil, nil, 1, 3, 3, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 1, 1, 2, 2, 2, 1, 1, 2, 1, 1, 1, 2, 1, 2, nil, nil, 2, 1, 1, 2, 2, 1, 1, 2, 2, 2, 1, 1, 1, 1, 2, 1, nil, nil, nil, 3, 3, 3, nil, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, nil],
]

コピーして、DXRuby スクリプトに貼り付ければOK。

しかしコレ、jsonファイルをDXRubyスクリプトから直接読み込んで使ったほうがいいのかも…? Ruby 1.9 以降は、jsonを扱うためのライブラリが標準添付されているらしいので、新規にライブラリをインストールする必要もなさそうだし。ただ、Tiled で毎回、json のエクスポート操作をするのが、ちょっと面倒臭い気もする。

#3 [pc][neta] OneDrive(SkyDrive)とGoogleドライブの同期ソフトをアンインストールした

_米Microsoft、無料で使えるOneDrive容量を15GBに -INTERNET Watch という記事を見かけたのだけど。はてブのコメントを眺めてたら、「OneDriveはエロ画像を入れてるとアカウント削除されるから気をつけろ」という話を見かけて、なんだか気になったわけで。

ググってみたら、実際そうらしく。利用規約に違反する画像を入れてるだけで、問答無用で何の連絡も無くアカウント削除されるそうで。

_MicrosoftのOneDrive、検閲されている模様。エロ画像をアップロードすると垢停止
_クラウドにエロ画像を上げると非公開であってもBANされることが判明 不便すぎワロタ
_やはり画像が原因か - Aruyo
_Windows8.1に統合されたSkyDriveに間違ってもエロ絵を入れてはいけない - 仮想と現実
_ITライフハック |突然のアカウントBANを防ぐ その便利さの裏にある危険「Google+」の設定を確認すべし
_え、この写真がポルノ扱い? 非公開なのに? Googleアカウントを停止された とあるユーザーの悲劇 - エキサイトニュース(1/2)
_tappli blog: Googleアカウントを消されてしまった話

非公開フォルダに入れていてもアカウント削除・アカウント停止されるなんて、酷い話だなと。

自分はその手の画像なんか入れてないつもりだけど、しかし全然安心できないよなと。と言うのも、その手の画像かどうか判別する作業は、コンピュータ上の画像認識プログラムを使ってやってるらしいので。であれば、ガンガン誤認識するだろうし、バグも残ってるはずだよなと。

例えば、 _相撲取りが光学合成エフェクトを出しながら戦うアニメgif などを、面白いからと、うっかり OneDrive や Google ドライブに保存してしまったら、Microsoft や Google のプログラムは「肌色成分が多いからコイツはエロ画像だ! けしからん!」と誤認識してアカウント削除しやがるだろうなと予想できるわけで。

ということで、そんな酷い目には会いたくないので、OneDrive も Googleドライブも、今後は画像保存場所として使わないことにしました。OneDrive と Googleドライブの同期ソフトを、メインPC上からアンインストール。

そもそも、Windowsをログオフする際、その手のソフトだけが妙に終了処理を遅らせていたりもしたので、これはこれで問題が一つ解消できたような気もしてきたり。

zipを置いておくとか、そういう用途なら使えるのかしら。でも、実はzipの中身まで検閲して、しかも誤認識したら嫌だなあ…。一体何が置けるんだよ。

余談。

_孫が水浴びしている写真をパソコンの中に保存したおじいさん、児童ポルノ単純所持の罪で警察に捕まり起訴される - GIGAZINE

家庭内でそういう目に会う児童が多いから、という事情があるのだろうけど。それにしてもこの世界はもう滅茶苦茶だなと思います。

以上、1 日分です。

過去ログ表示

Prev - 2014/06 - Next
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project