2021/09/18(土) [n年前の日記]
#2 [love2d] love2dでテクスチャ付きポリゴンを歪めずに描画したい
love2d 11.3 上で、テクスチャ付きポリゴンを描画したい。
Mesh を使えば描画できることは分った。ただ、そのまま描画すると、四角形等を描画した際に見た目がめっちゃ歪んでしまう。
_love2dでテクスチャ付きポリゴンを描画したい
解決策は無いものか。
とりあえず、四角形を細かく分割して描画すれば、見た目ではさほど歪みが気にならなくなるのではないか…と思ったので試してみた。
Mesh を使えば描画できることは分った。ただ、そのまま描画すると、四角形等を描画した際に見た目がめっちゃ歪んでしまう。
_love2dでテクスチャ付きポリゴンを描画したい
解決策は無いものか。
とりあえず、四角形を細かく分割して描画すれば、見た目ではさほど歪みが気にならなくなるのではないか…と思ったので試してみた。
◎ ソースと使用画像。 :
ソースと使用画像は以下。
_conf.lua
_main.lua
使用画像は以下。
_uvcheckermap01-512.png
以下で公開されてる画像を利用させてもらいました。ありがたや。
_Arahnoid/UVChecker-map: A collection of free images what can be used during unwrapping of 3D models
_conf.lua
function love.conf(t) t.window.title = "Love2d Mesh divide" t.window.width = 640 t.window.height = 480 t.window.vsync = true t.modules.joystick = false -- t.window.fullscreen = true -- t.window.fullscreentype = "exclusive" end
_main.lua
-- Love2d Mesh function makeVertices(src, xc, yc) xyuv = {} local xi, yi, u, v local yi = 0 for yi = 0, yc do v = yi / yc local sx0 = (src[4][1] - src[1][1]) * v + src[1][1] local sy0 = (src[4][2] - src[1][2]) * v + src[1][2] local sx1 = (src[3][1] - src[2][1]) * v + src[2][1] local sy1 = (src[3][2] - src[2][2]) * v + src[2][2] for xi = 0, xc do u = xi / xc local x = (sx1 - sx0) * u + sx0 local y = (sy1 - sy0) * u + sy0 table.insert(xyuv, {x, y, u, v}) end end vidx = {} for y = 1, yc do for x = 1, xc do local i0 = x + ((xc + 1) * (y - 1)) local i1 = i0 + 1 local i2 = i0 + (xc + 1) local i3 = i2 + 1 table.insert(vidx, i0) table.insert(vidx, i1) table.insert(vidx, i2) table.insert(vidx, i2) table.insert(vidx, i1) table.insert(vidx, i3) end end vt = {} for j = 1, #vidx do local i = vidx[j] local x, y, u, v x, y = xyuv[i][1], xyuv[i][2] u, v = xyuv[i][3], xyuv[i][4] table.insert(vt, {x, y, u, v, 1.0, 1.0, 1.0, 1.0}) end return vt, xyuv end -- init function love.load() -- get window width and height wdw_w, wdw_h = love.graphics.getDimensions() img = love.graphics.newImage("uvcheckermap01-512.png") divide = 3 -- src = { {270, 40}, {370, 40}, {620, 440}, {20, 440} } -- src = { {270, 20}, {370, 60}, {620, 350}, {20, 440} } src = { {270, 240}, {370, 240}, {630, 460}, {10, 460} } vert, xyuv = makeVertices(src, divide, divide) mesh = love.graphics.newMesh(vert, "triangles") mesh:setTexture(img) guide = true mesh_refresh = false ang = 0 end -- update function love.update(dt) if mesh_refresh then vert, xyuv = makeVertices(src, divide, divide) mesh = love.graphics.newMesh(vert, "triangles") mesh:setTexture(img) end ang = ang + 90 * dt local d = 140 * math.sin(math.rad(ang)) -- change x0, x1 src[1][1] = (wdw_w / 2) - 150 + d src[2][1] = (wdw_w / 2) + 150 - d vert, xyuv = makeVertices(src, divide, divide) mesh:setVertices(vert, 1) end -- draw function love.draw() -- fill BG color love.graphics.setColor(0.1, 0.2, 0.4) love.graphics.rectangle("fill", 0, 0, wdw_w, wdw_h) -- draw mesh love.graphics.setColor(1, 1, 1) love.graphics.draw(mesh, 0, 0) if guide then -- draw lines love.graphics.setColor(1, 1, 1, 0.5) for i=1, #vert, 3 do local vlst = {vert[i][1], vert[i][2], vert[i+1][1], vert[i+1][2], vert[i+2][1], vert[i+2][2]} love.graphics.polygon("line", vlst) end --draw points love.graphics.setColor(0, 1, 1, 0.5) for i = 1, #xyuv do local x = xyuv[i][1] local y = xyuv[i][2] love.graphics.ellipse("line", x, y, 3, 3) end end -- print FPS love.graphics.setColor(1, 1, 1) love.graphics.print("FPS: "..tostring(love.timer.getFPS()), 2, 2) love.graphics.print("Divide: "..tostring(divide), 2, 20) love.graphics.print("G key : Guide on/off", 2, 40) love.graphics.print("Up, Down : Divide +/-", 2, 60) end function love.keypressed(key, isrepeat) -- ESC to exit if key == "escape" then love.event.quit() end if key == "g" then guide = not guide end if key == "up" then if divide < 32 then divide = divide + 1 end mesh_refresh = true end if key == "down" then if divide > 2 then divide = divide - 1 end mesh_refresh = true end end
使用画像は以下。
_uvcheckermap01-512.png
以下で公開されてる画像を利用させてもらいました。ありがたや。
_Arahnoid/UVChecker-map: A collection of free images what can be used during unwrapping of 3D models
◎ 実行結果。 :
実行すると、Gキーで分割状態表示のON/OFFが切り替えられる。また、カーソルキーの上下で、分割数を 2 - 32 まで変更できる。
一応、 _LoVE Web Builder を利用させてもらって、Webブラウザ上でも動かせるようにしてみた。
_06_mesh_divide_c
Windows10 x64 21H1 + Firefox 92.0 64bit、Google Chrome 93.0.4577.82 64bit では動いているけど、どうだろう。
ちなみに、CPU は Ryzen 7 1700 (8コア、16スレッド、3.0GHz)、GPU(ビデオカード)は GeForce GTX 1060 6GB で動作確認した。このスペックなら 60FPS で動いてるように見える。ただ、それはローカルで動かした場合の話で、Webブラウザ上で動かすと11x11分割あたりから60FPSでは間に合わなくなってくる…。
一応、 _LoVE Web Builder を利用させてもらって、Webブラウザ上でも動かせるようにしてみた。
_06_mesh_divide_c
Windows10 x64 21H1 + Firefox 92.0 64bit、Google Chrome 93.0.4577.82 64bit では動いているけど、どうだろう。
ちなみに、CPU は Ryzen 7 1700 (8コア、16スレッド、3.0GHz)、GPU(ビデオカード)は GeForce GTX 1060 6GB で動作確認した。このスペックなら 60FPS で動いてるように見える。ただ、それはローカルで動かした場合の話で、Webブラウザ上で動かすと11x11分割あたりから60FPSでは間に合わなくなってくる…。
◎ 雑感。 :
4x4分割、もしくは、8x8分割ぐらいで、そこそこ歪みが気にならなくなった印象を受けた。16x16分割まで行けば、全然歪んでるようには見えない。まあ、描画面積も関係してくるのだろうけど…。今回、ウインドウサイズは 640x480 だけど、1280x720 や 1920x1080 なら歪みが感じられる可能性もありそう。
ただ、毎フレーム、四角形の頂点の描画位置が変わるとなると、分割のための再計算も毎フレーム必要になるわけで…。4x4分割なら25個の頂点を、8x8分割なら81個の頂点を再計算しないといけない。1〜2枚を処理するなら問題無いかもしれんけど、例えば何十枚も処理をしたらどうなるのかちょっと分からないなと。実際、前述したように、Webブラウザ上で動かしたら11x11分割あたりで処理落ちが始まった。
また、あくまで2D的に分割しているので、こういうソレを使って3D的な見せ方をしようとするなら、元テクスチャ自体に遠近感を盛り込む等の工夫が必要になるのかもしれない。
遠近感を意識した分割はできないのだろうか。やろうと思えばできるはず。ていうか、以前DXRubyを使ってやったことがあるし。
_DXRuby上で射影変換ができた
_DXRubyのShaderで射影変換
でも、使い道が…。そもそも 3Dっぽく見せたいなら Unity や Godot Engine を使ったほうが、という気分にもなるわけで。2Dゲームエンジンの love2d で無理してやらんでも、みたいな。
ただ、毎フレーム、四角形の頂点の描画位置が変わるとなると、分割のための再計算も毎フレーム必要になるわけで…。4x4分割なら25個の頂点を、8x8分割なら81個の頂点を再計算しないといけない。1〜2枚を処理するなら問題無いかもしれんけど、例えば何十枚も処理をしたらどうなるのかちょっと分からないなと。実際、前述したように、Webブラウザ上で動かしたら11x11分割あたりで処理落ちが始まった。
また、あくまで2D的に分割しているので、こういうソレを使って3D的な見せ方をしようとするなら、元テクスチャ自体に遠近感を盛り込む等の工夫が必要になるのかもしれない。
遠近感を意識した分割はできないのだろうか。やろうと思えばできるはず。ていうか、以前DXRubyを使ってやったことがあるし。
_DXRuby上で射影変換ができた
_DXRubyのShaderで射影変換
でも、使い道が…。そもそも 3Dっぽく見せたいなら Unity や Godot Engine を使ったほうが、という気分にもなるわけで。2Dゲームエンジンの love2d で無理してやらんでも、みたいな。
[ ツッコむ ]
以上です。