2021/06/29(火) [n年前の日記]
#2 [love2d] love2dで2つの動画を使って動画の一部を透明に見せかける
RGBA動画を元にして、RGB動画と、アルファチャンネルのみ取り出した白黒のRGB動画を作成することができた。この2つの動画を使って、love2d上で、動画の一部を透明にした感じの描画ができるかどうか試してみる。
念のために書いておくけど、love2d で扱える動画フォーマット ogv (Theora) はアルファチャンネルを含めることができないので、動画の一部を透明にしたい場合、何かしら頓智が必要になるわけで。
環境は、Windows10 x64 20H2 + love2d 11.3。
手順としては、昨日試した通り。
_ogvファイルとアルファチャンネル
再度、流れを書くけど…。
ソースは以下。
_main.lua
_conf.lua
必要な画像、動画は以下。
_bg.jpg
_mask.ogv
_rgb.ogv
念のために、ソース、画像、動画をまとめてzipにして置いときます。
_draw_video_alpha.zip
さて。実行してみたら、こうなった。
一見すると上手く行ってるように思えたけれど、よく観察してみると、どうも周辺に残像のようなものがチラチラと表示される…。
デスクトップを60fpsでキャプチャして、一部分をクロップして4fpsにしてみた。
アルファチャンネル担当の動画と、RGB動画の、同期が取れていないようだなと…。RGB動画のほうが1フレーム遅れて描画される瞬間が何度も発生しているように見える。それで輪郭がチラチラするというか、残像のようなものが見えてしまう模様。
ちなみに、元動画がそもそも1フレームずれているのではないかと疑って、AviUtl + 拡張編集プラグインを使って、2つの動画を重ねて確認してみたけれど、元動画はずれてるように見えなかった。というか、24fpsの動画で1フレームずれていたら、こんなもんじゃ済まない。もっと盛大なずれになる。つまりコレは、love2dで再生・描画した際に、love2dのフレームレートで、1フレームずれる時がある、ということだろう…。
解決策はあるのだろうか…。
そもそも love2dは、再生される動画は1つ、という前提で作られてそうな気もする。フツー、2つの動画を、両方一度に再生するとは思わないよな…。
念のために書いておくけど、love2d で扱える動画フォーマット ogv (Theora) はアルファチャンネルを含めることができないので、動画の一部を透明にしたい場合、何かしら頓智が必要になるわけで。
環境は、Windows10 x64 20H2 + love2d 11.3。
手順としては、昨日試した通り。
_ogvファイルとアルファチャンネル
再度、流れを書くけど…。
- 最初に、背景(BG)を、フツーのBlendMode (seBlendMode("alpha")) で描画。
- 次に、アルファチャンネルを元にした白黒画像を、setBlendMode("subtract") で ―― 減算で描画。これにより、白い部分が背景画像から引かれて真っ黒になる。
- 次に、RGB画像を settBlendMode("add") で ―― 加算で描画。描画されてほしい部分は、事前に減算描画したことで真っ黒になっているはずなので、そのまま加算描画すれば、描画色がそのまま出るはず。
ソースは以下。
_main.lua
function love.load() -- load bg image bgimg = love.graphics.newImage("bg.jpg") -- load video(.ogv) maskvideo = love.graphics.newVideo("mask.ogv") rgbvideo = love.graphics.newVideo("rgb.ogv") maskvideo:play() rgbvideo:play() end function love.update(dt) end function love.draw() -- loop video play if not rgbvideo:isPlaying() then maskvideo:rewind() rgbvideo:rewind() maskvideo:play() rgbvideo:play() end -- canvas clear love.graphics.clear(0, 0, 0, 1) -- draw bg love.graphics.setColor(1, 1, 1, 1) love.graphics.draw(bgimg, 0, 0) -- draw video love.graphics.setBlendMode("subtract") love.graphics.draw(maskvideo, 0, 0) love.graphics.setBlendMode("add") love.graphics.draw(rgbvideo, 0, 0) -- draw text love.graphics.setBlendMode("alpha") love.graphics.setColor(1, 1, 1, 1) love.graphics.print("ESC to exit.", 8, 4) end function love.keypressed(key, scancode, isrepeat) if key == "escape" then love.event.quit() end end
_conf.lua
function love.conf(t) t.window.width = 1280 t.window.height = 720 t.window.title = "Draw video alpha" t.window.fullscreen = false -- t.window.fullscreentype = "exclusive" end
必要な画像、動画は以下。
_bg.jpg
_mask.ogv
_rgb.ogv
念のために、ソース、画像、動画をまとめてzipにして置いときます。
_draw_video_alpha.zip
さて。実行してみたら、こうなった。
一見すると上手く行ってるように思えたけれど、よく観察してみると、どうも周辺に残像のようなものがチラチラと表示される…。
デスクトップを60fpsでキャプチャして、一部分をクロップして4fpsにしてみた。
アルファチャンネル担当の動画と、RGB動画の、同期が取れていないようだなと…。RGB動画のほうが1フレーム遅れて描画される瞬間が何度も発生しているように見える。それで輪郭がチラチラするというか、残像のようなものが見えてしまう模様。
ちなみに、元動画がそもそも1フレームずれているのではないかと疑って、AviUtl + 拡張編集プラグインを使って、2つの動画を重ねて確認してみたけれど、元動画はずれてるように見えなかった。というか、24fpsの動画で1フレームずれていたら、こんなもんじゃ済まない。もっと盛大なずれになる。つまりコレは、love2dで再生・描画した際に、love2dのフレームレートで、1フレームずれる時がある、ということだろう…。
解決策はあるのだろうか…。
そもそも love2dは、再生される動画は1つ、という前提で作られてそうな気もする。フツー、2つの動画を、両方一度に再生するとは思わないよな…。
◎ 1つの動画にしてみる。 :
2つの動画を同時に再生して同期が取れてないのが問題なら、1つの動画にまとめてしまえばいいのではないかと思いついた。
つまり、以下のような見た目になっている、1つの動画にしてしまえば…。
これを、以下のような仕組みで利用する。
これなら動画の同期云々なんて考えなくてもよいのではないか。
試してみた。ソースは以下。
_main.lua
_conf.lua
動作に必要な画像、動画は以下。
_bg.jpg
_comb.ogv
実行してみたら、こうなった。
一部をクロップして4fpsで再生してみる。
これで上手く行ったのではなかろうか。
ということで、love2d上で疑似的に、アルファチャンネルを持ってるかのような見た目で動画再生することも一応できる、と分かった。
考えてみたら、こういう見た目の素材をどこかで見たなと…。紙芝居ゲームの制作ツールで、こういう画像を用意せよと要求してくる事例があったような…。そのやり方を動画にも流用できるよ、という話になるのかな。
ちなみに、RGB動画とアルファチャンネル動画を上下に並べた動画を作成する作業には、AviUtl + 拡張編集プラグインを使った。ただ、その後ググってみたら、ffmpeg でも上下に繋げた動画を作成できたらしい。
_研究で使うFFmpegコマンドまとめ - Qiita
入力動画ファイルを2つ指定して、-filter_complex "vstack" を指定すればいいようだなと…。
つまり、以下のような見た目になっている、1つの動画にしてしまえば…。
これを、以下のような仕組みで利用する。
これなら動画の同期云々なんて考えなくてもよいのではないか。
試してみた。ソースは以下。
_main.lua
function love.load() videocanvas = love.graphics.newCanvas(1280, 1440) -- load bg image bgimg = love.graphics.newImage("bg.jpg") -- load video(.ogv) video = love.graphics.newVideo("comb.ogv") video:play() end function love.update(dt) end function love.draw() -- loop video play if not video:isPlaying() then video:rewind() video:play() end -- canvas clear love.graphics.clear(0, 0, 0, 1) -- draw bg love.graphics.setBlendMode("alpha") love.graphics.setColor(1, 1, 1, 1) love.graphics.draw(bgimg, 0, 0) -- draw video to video canvas love.graphics.setCanvas(videocanvas) love.graphics.draw(video, 0, 0) love.graphics.setCanvas() -- draw video canvas love.graphics.setBlendMode("subtract") love.graphics.draw(videocanvas, 0, 0) love.graphics.setBlendMode("add") love.graphics.draw(videocanvas, 0, -720) -- draw text love.graphics.setBlendMode("alpha") love.graphics.setColor(1, 1, 1, 1) love.graphics.print("ESC to exit.", 8, 4) end function love.keypressed(key, scancode, isrepeat) if key == "escape" then love.event.quit() end end
_conf.lua
function love.conf(t) t.window.width = 1280 t.window.height = 720 t.window.title = "Draw video alpha2" t.window.fullscreen = false -- t.window.fullscreentype = "exclusive" end
動作に必要な画像、動画は以下。
_bg.jpg
_comb.ogv
実行してみたら、こうなった。
一部をクロップして4fpsで再生してみる。
これで上手く行ったのではなかろうか。
ということで、love2d上で疑似的に、アルファチャンネルを持ってるかのような見た目で動画再生することも一応できる、と分かった。
考えてみたら、こういう見た目の素材をどこかで見たなと…。紙芝居ゲームの制作ツールで、こういう画像を用意せよと要求してくる事例があったような…。そのやり方を動画にも流用できるよ、という話になるのかな。
ちなみに、RGB動画とアルファチャンネル動画を上下に並べた動画を作成する作業には、AviUtl + 拡張編集プラグインを使った。ただ、その後ググってみたら、ffmpeg でも上下に繋げた動画を作成できたらしい。
_研究で使うFFmpegコマンドまとめ - Qiita
ffmpeg -i mask.avi -i rgb.avi -filter_complex "vstack" -vcodec utvideo out.avi
入力動画ファイルを2つ指定して、-filter_complex "vstack" を指定すればいいようだなと…。
[ ツッコむ ]
以上です。