Skip to content

Instantly share code, notes, and snippets.

@edom18
Created February 14, 2013 00:13
Show Gist options
  • Save edom18/4949624 to your computer and use it in GitHub Desktop.
Save edom18/4949624 to your computer and use it in GitHub Desktop.
テクスチャマッピングを使って動的に背景を作るテスト
#テクスチャマッピングのテストから発展して、ちょっと立体的に見える背景の生成をやってみた。
動的に画像を生成し、それをパラパラ漫画風に表示するため、初期化処理が少し重いです。
html, body, div, p {
margin: 0;
padding: 0;
}
canvas {
border: solid 1px #333;
background: #000;
}
#container {
width: 500px;
position: relative;
margin: 15px;
}
<div id="container">
<canvas id="cv" width="500" height="166"></canvas>
<p><input type="button" id="btn" value="STOP" /></p>
<p>動的に画像を生成し、それをパラパラ漫画風に表示するため、初期化処理が少し重いです。<br />
その代わり、初期化後はiPhoneなどのモバイル機器でも高速に動きます。</p>
<!-- /#container --></div>
do (win = window, doc = window.document) ->
$ = (selector) -> doc.querySelector selector
$$ = (selector) -> doc.querySelectorAll selector
btn = $('#btn')
ci = null
flg = false
btn.addEventListener 'click', (e) ->
if (flg = !flg) is true
ci.stop()
else
ci.play()
, false
# --------------------------------------------------------------------
drawTriangles = (g, img, vertecies, indecies, uvData) ->
# move position from A(Ax, Ay) to _A(_Ax, _Ay)
# move position from B(Ax, Ay) to _B(_Bx', _By)
# A,Bのベクトルを、_A,_Bのベクトルに変換することが目的。
# 変換を達成するには、a, b, c, dそれぞれの係数を導き出す必要がある。
#
# ↓まずは公式。アフィン変換の移動以外を考える。
#
# _Ax = a * Ax + c * Ay
# _Ay = b * Ax + d * Ay
# _Bx = a * Bx + c * By
# _By = b * Bx + d * By
#
# ↓上記の公式を行列の計算で表すと以下に。
#
# |_Ax| = |Ax Ay||a|
# |_Bx| = |Bx By||c|
#
# ↓a, cについて求めたいのだから、左に掛けているものを「1」にする必要がある。
#  行列を1にするには、逆行列を左から掛ければいいので、両辺に逆行列を掛ける。(^-1は逆行列の意味)
#
# |Ax Ay|^-1 |_Ax| = |a|
# |Bx By| |_Bx| = |c|
#
# この式をプログラムで表すと以下になる。
cv = doc.createElement 'canvas'
_g = cv.getContext '2d'
w = img.width
h = img.height
cv.width = w
cv.height = h
for index, i in indecies by 3
i0 = indecies[i + 0]
i1 = indecies[i + 1]
i2 = indecies[i + 2]
v0 = vertecies[i0]
v1 = vertecies[i1]
v2 = vertecies[i2]
uv0 = uvData[i0]
uv1 = uvData[i1]
uv2 = uvData[i2]
v0x = v0.x
v0y = v0.y
v1x = v1.x
v1y = v1.y
v2x = v2.x
v2y = v2.y
uv0x = uv0.x
uv0y = uv0.y
uv1x = uv1.x
uv1y = uv1.y
uv2x = uv2.x
uv2y = uv2.y
_Ax = v1x - v0x
_Ay = v1y - v0y
_Bx = v2x - v0x
_By = v2y - v0y
Ax = (uv1x - uv0x) * w
Ay = (uv1y - uv0y) * h
Bx = (uv2x - uv0x) * w
By = (uv2y - uv0y) * h
m = new M22
m._11 = Ax
m._12 = Ay
m._21 = Bx
m._22 = By
mi = m.getInvert() #To invert
if mi is null then return
a = mi._11 * _Ax + mi._12 * _Bx
c = mi._21 * _Ax + mi._22 * _Bx
b = mi._11 * _Ay + mi._12 * _By
d = mi._21 * _Ay + mi._22 * _By
tx = v0x - (a * uv0x * w + c * uv0y * h)
ty = v0y - (b * uv0x * w + d * uv0y * h)
_g.save()
_g.beginPath()
_g.moveTo v0x, v0y
_g.lineTo v1x, v1y
_g.lineTo v2x, v2y
#TODO
#for debugging
#g.strokeStyle = 'red'
#g.stroke()
_g.clip()
_g.closePath()
_g.setTransform a, b, c, d, tx, ty
_g.drawImage img, 0, 0
_g.restore()
g.drawImage cv, 0, 0
# ---------------------------------------------------------
class CubeImage
mesh: 10
constructor: (@g, @url, @bgColor = '#000') ->
@img = new Image()
@img.onload = @onload
@img.src = url
@_cv = doc.createElement 'canvas'
@_ctx = @_cv.getContext '2d'
@frame = 0
@dx = 0
@delta = 3
onload: =>
@width = @_cv.width = @img.width
@height = @_cv.height = @img.height
@vertexes = @createVertex @g, @img
@addCurve()
@createMovie()
@play()
createMovie: ->
movie = @movie = []
drawTriangles = @drawTriangles
img = @_cv
w = @width
h = @height
g = @g
mm = @mm
vertecies = mm.getVertecies()
indecies = mm.getIndecies()
uvData = mm.getUvData()
for i in [0..@width / @delta]
cv = doc.createElement 'canvas'
cv.width = w
cv.height = h
ctx = cv.getContext '2d'
@drawTriangles ctx, img, vertecies, indecies, uvData
movie.push cv
@createNextImage()
createNextImage: ->
dx = @dx
w = @width
h = @height
img = @img
ctx = @_ctx
ctx.fillStyle = @bgColor
ctx.fillRect 0, 0, w, h
ctx.drawImage img, dx, 0
ctx.drawImage img, (w - dx), 0, dx, h, 0, 0, dx, h
@dx = ((@dx += @delta) % @width)
addCurve: ->
PI = Math.PI
TO_RAD = PI / 180
sin = Math.sin
img = @_cv
hh = @height / 2
strength = 30
deg = 0
mesh = @mesh
seg = mesh + 1
step = 180 / mesh
meshes = @mm.getMesh()
l = meshes.length
while (l > 0)
i = 0
l -= seg
while (i <= mesh)
m = meshes[l + i]
k = sin(step * i * TO_RAD)
f = (hh - m.oy) / hh
m.y = m.oy + ~~(strength * k * f)
i++
createVertex: ->
mesh = @mesh
img = @img
mm = new MeshMgr @g, img, mesh
w = img.width
h = img.height
wi = w / mesh
hi = h / mesh
_x = 0
_y = 0
len = mesh * mesh + mesh * 2 + 1
for i in [0...len]
x = wi * _x
y = hi * _y
if x >= w
_x = 0
_y++
corner = true
else
_x++
corner = false
if y >= h
corner = true
ux = x / w
uy = y / h
mm.addVertex(x, y, ux, uy, corner)
seg = mesh + 1
meshes = mm.getMesh()
for m, i in meshes
if m.corner is true
continue
i1 = (i + 0)
i2 = (i + 1)
i3 = (i + seg + 0)
i4 = (i + seg + 1)
mm.addIndex(i1, i2, i3)
mm.addIndex(i4, i3, i2)
@mm = mm
drawTriangles: drawTriangles
draw: ->
#[arguments list]
#transform(a, b, c, d, tx, ty)
# ↓ as result.
#|a c tx| |x| = ax + cy + tx
#|b d ty|x|y| = bx + dy + ty
#|0 0 1 | |1| = 0 + 0 + 1
@createNextImage()
g = @g
mm = @mm
g.clearRect 0, 0, @width, @height
vertecies = mm.getVertecies()
indecies = mm.getIndecies()
uvData = mm.getUvData()
@drawTriangles g, @_cv, vertecies, indecies, uvData
draw2: ->
frame = (++@frame % @movie.length)
img = @movie[frame]
@g.drawImage img, 0, 0
play: ->
do _loop = =>
@draw2()
@timer = setTimeout _loop, 32
stop: ->
clearTimeout @timer
class Vec3
constructor: (@x = 0, @y = 0, @z = 0) ->
zero: ->
@x = @y = @z = 0;
sub: (v) ->
@x -= v.x
@y -= v.y
@z -= v.z
return @
add: (v) ->
@x += v.x
@y += v.y
@z += v.z
return @
copyFrom: (v) ->
@x = v.x
@y = v.y
@z = v.z
return @
norm: ->
Math.sqrt(@x * @x + @y * @y + @z * @z)
normalize: ->
nrm = @norm()
if nrm isnt 0
@x /= nrm
@y /= nrm
@z /= nrm
return @
#scalar multiplication
smul: (k) ->
@x *= k
@y *= k
@z *= k
return @
#dot product
dpWith: (v) ->
@x * v.x + @y * v.y + @z * v.z
#cross product
cp: (v, w) ->
@x = (w.y * v.z) - (w.z * v.y)
@y = (w.z * v.x) - (w.x * v.z)
@z = (w.x * v.y) - (w.y * v.x)
return @
toString: ->
"#{@x},#{@y},#{@z}"
class M22
constructor: ->
@_11 = 1
@_12 = 0
@_21 = 0
@_22 = 1
getInvert: ->
out = new M22
det = @_11 * @_22 - @_12 * @_21
if 0.0001 > det > -0.0001
return null
out._11 = @_22 / det
out._22 = @_11 / det
out._12 = @_12 / det
out._21 = @_21 / det
return out
class MeshMgr
constructor: (@g, @img, @mesh) ->
@vertecies = []
@indecies = []
@uvData = []
addVertex: (x, y, ux, uy, corner) ->
@vertecies.push(new Vertex x, y, corner)
@uvData.push(new UvData ux, uy)
addIndex: (i1, i2, i3) ->
@indecies.push(i1, i2, i3);
getVertecies: ->
@vertecies
getIndecies: ->
@indecies
getUvData: ->
@uvData
getMesh: ->
@vertecies
class Vertex
constructor: (@x, @y, @corner) ->
@ox = x
@oy = y
class UvData
constructor: (@x, @y) ->
# ---------------------------------------------------------
init = ->
cv = $ 'canvas'
ctx = cv.getContext '2d'
ci = new CubeImage ctx, 'http://jsrun.it/assets/8/B/7/O/8B7OB.png'
win.addEventListener 'load', init, false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment