2017/12/19(火) [n年前の日記]
#1 [love2d] love2dのShaderで円柱っぽいBG描画を試したり
昔、
_DXRubyのShader機能で円柱だかパイプだかっぽいBG描画を試した
ことがあったのだけど。それを love2d でもやれないものかなと思えてきたので試したり。環境は Windows10 x64 + love2d 0.10.2。
こんな感じに。
何かビミョーにおかしいような気もするけど…。まあ、それっぽくなってるから、いいか…。でも、考え方、あるいは計算式が間違ってる可能性も否定できず。
こんな感じに。
何かビミョーにおかしいような気もするけど…。まあ、それっぽくなってるから、いいか…。でも、考え方、あるいは計算式が間違ってる可能性も否定できず。
◎ 画像とソース。 :
画像は以下。
_grid_bg_640x640.png
_scifi_bg_640x640.png
ソースは以下。
_conf.lua
_main.lua
画像もソースも、License : CC0 / Public Domain ってことで。
Shader部分の仕組みというか考え方は、 _DXRuby版 と同じなのでそちらを参照してもらえればと。
_grid_bg_640x640.png
_scifi_bg_640x640.png
ソースは以下。
_conf.lua
function love.conf(t) t.window.title = "Shader test 09 pipe" t.window.vsync = true t.window.resizable = true t.window.width = 640 t.window.height = 480 -- t.window.fullscreen = true -- t.window.fullscreentype = "exclusive" end
_main.lua
-- Shader test 09 pipe function love.load() love.graphics.setDefaultFilter("nearest", "nearest") scr_w, scr_h = 640, 480 canvas = love.graphics.newCanvas(scr_w, scr_h) img = love.graphics.newImage("grid_bg_640x640.png") -- img = love.graphics.newImage("scifi_bg_640x640.png") img:setFilter("linear", "linear") -- make shader local shadercode = [[ extern number scr_dist; extern number r; extern number angle_max; extern number start_x; extern number start_y; vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ) { float sx = texture_coords.x - 0.5; float sy = texture_coords.y - 0.5; float pz = r * cos(atan(sy, scr_dist)); float u = sx * pz / scr_dist; float v = atan(sy, scr_dist) / angle_max; texture_coords.x = mod(u + 0.5 + start_x, 1.0); texture_coords.y = mod(v + 0.5 + start_y, 1.0); vec4 texcolor = Texel(texture, texture_coords); return texcolor * color; } ]] myshader = love.graphics.newShader(shadercode) local scr_dist = 0.6 myshader:send("scr_dist", scr_dist) myshader:send("r", scr_dist + 0.3) myshader:send("start_x", 0.0) myshader:send("start_y", 0.0) myshader:send("angle_max", math.atan2(0.5, scr_dist)) v = 0.0 px = (scr_w - img:getWidth()) / 2 py = (scr_h - img:getHeight()) / 2 end function love.update(dt) v = v + dt myshader:send("start_x", v * 0.2) myshader:send("start_y", v * 0.4) end function love.draw() love.graphics.setCanvas(canvas) love.graphics.clear(0, 0, 0, 255) love.graphics.setColor(255, 255, 255) love.graphics.setShader(myshader) love.graphics.draw(img, px, py) love.graphics.setShader() love.graphics.setCanvas() -- draw canvas to window wdw_w, wdw_h = love.graphics.getDimensions() scr_scale = math.min((wdw_w / scr_w), (wdw_h / scr_h)) scr_ox = (wdw_w - (scr_w * scr_scale)) / 2 scr_oy = (wdw_h - (scr_h * scr_scale)) / 2 love.graphics.setColor(255, 255, 255) love.graphics.draw(canvas, scr_ox, scr_oy, 0, scr_scale, scr_scale) love.graphics.print("FPS: "..tostring(love.timer.getFPS()), 10, 10) end function love.keypressed(key, isrepeat) if key == "escape" then -- ESC to exit love.event.quit() elseif key == "f11" then -- toggle fullscreen if love.window.getFullscreen() then love.window.setFullscreen(false) else love.window.setFullscreen(true) end end end
画像もソースも、License : CC0 / Public Domain ってことで。
Shader部分の仕組みというか考え方は、 _DXRuby版 と同じなのでそちらを参照してもらえればと。
◎ 余談。 :
HLSL にはatan2() があるけれど GLSL には atan2() が無くてどうしようと思ったら、GLSL の場合、atan() が atan2() 相当らしくて。
_GLSLをHLSLに書き換える - Qiita
が参考になりました。ありがたや。ちなみに、件のページには atan(x,y) と書いてあるけど、たぶん atan(y,x) ではあるまいか。
今回、利用するテクスチャに linearフィルタを設定したら、見た目がちょっとマシになったように感じたり。処理内容によっては、その手のフィルタ設定が効果を発揮する場合もあるようだなと。
今回、利用するテクスチャに linearフィルタを設定したら、見た目がちょっとマシになったように感じたり。処理内容によっては、その手のフィルタ設定が効果を発揮する場合もあるようだなと。
[ ツッコむ ]
#2 [love2d] love2dのShaderでスカイドームっぽい描画を試したり
love2dのShaderで円柱だかパイプだかっぽい描画を試してるうちに、もうちょっとアレすれば某STGのスカイドームっぽい背景描画もできるんじゃないかと思えてきたので試したり。…スカイドームという呼び方で合ってるのかな。違うのかな。分からんけど。とりあえず、環境は Windows10 x64 + love2d 0.10.2。
こんな感じに。
ちょっと何をやってるか分かりづらいかな…。要は、1枚のテクスチャ画像を、以下のように歪めて描画する処理をしてるのだけど。
テクスチャをそのまま描画すると、2D絵を表示してますねー、てな印象しか受けないけれど。こういうことをすれば1枚絵でもビミョーに空間を感じられなくもない見た目にできる。かもしれない。みたいな。
ただ、考え方や計算式がこれで合ってるかどうかは自信無し…。この見た目で合ってるのかな…。どうなんだ…。
こんな感じに。
ちょっと何をやってるか分かりづらいかな…。要は、1枚のテクスチャ画像を、以下のように歪めて描画する処理をしてるのだけど。
テクスチャをそのまま描画すると、2D絵を表示してますねー、てな印象しか受けないけれど。こういうことをすれば1枚絵でもビミョーに空間を感じられなくもない見た目にできる。かもしれない。みたいな。
ただ、考え方や計算式がこれで合ってるかどうかは自信無し…。この見た目で合ってるのかな…。どうなんだ…。
◎ 画像とソース。 :
使用画像は以下。
_star_bg_1024x1024.png
ソースは以下。
_conf.lua
_main.lua
ソースも画像も、License : CC0 / Public Domain ってことで。
_star_bg_1024x1024.png
ソースは以下。
_conf.lua
function love.conf(t) t.window.title = "Shader test 10 sphere" t.window.vsync = true t.window.resizable = true t.window.width = 640 t.window.height = 480 -- t.window.fullscreen = true -- t.window.fullscreentype = "exclusive" end
_main.lua
-- Shader test 10 sphere function love.load() love.graphics.setDefaultFilter("nearest", "nearest") scr_w, scr_h = 640, 480 canvas = love.graphics.newCanvas(scr_w, scr_h) img = love.graphics.newImage("star_bg_1024x1024.png") -- img = love.graphics.newImage("uvcheckermap01-1024.png") img:setFilter("linear", "linear") -- make shader local shadercode = [[ extern number scr_dist; extern number r; extern number scale; extern number start_x; extern number start_y; vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ) { float sx = (screen_coords.x - (love_ScreenSize.x / 2)) / love_ScreenSize.x; float sy = (screen_coords.y - (love_ScreenSize.y / 2)) / love_ScreenSize.x; // float sx = texture_coords.x - 0.5; // float sy = texture_coords.y - 0.5; float ang_v = atan(sy, scr_dist); float ang_u = atan(sx, scr_dist); float pz = r * cos(ang_v); float qz = pz * cos(ang_u); float u = sx * qz / scr_dist; float v = sy * qz / scr_dist; texture_coords.x = mod((u + start_x) * scale + 0.5, 1.0); texture_coords.y = mod((v + start_y) * scale + 0.5, 1.0); vec4 texcolor = Texel(texture, texture_coords); return texcolor * color; } ]] myshader = love.graphics.newShader(shadercode) local angle_of_view = 60 local scr_dist = math.cos(math.rad(angle_of_view/2)) myshader:send("scr_dist", scr_dist) myshader:send("r", scr_dist + 0.1) myshader:send("scale", 0.4) myshader:send("start_x", 0.0) myshader:send("start_y", 0.0) ang_a = 0.0 ang_b = 0.0 px = (scr_w - img:getWidth()) / 2 py = (scr_h - img:getHeight()) / 2 end function love.update(dt) ang_a = ang_a + 10 * dt ang_b = ang_b + 16 * dt myshader:send("start_x", (1.0 - math.sin(math.rad(ang_a)))) myshader:send("start_y", (1.0 - math.sin(math.rad(ang_b)))) end function love.draw() love.graphics.setCanvas(canvas) love.graphics.clear(0, 0, 0, 255) love.graphics.setColor(255, 255, 255) love.graphics.setShader(myshader) love.graphics.draw(img, px, py) love.graphics.setShader() love.graphics.setCanvas() -- draw canvas to window wdw_w, wdw_h = love.graphics.getDimensions() scr_scale = math.min((wdw_w / scr_w), (wdw_h / scr_h)) scr_ox = (wdw_w - (scr_w * scr_scale)) / 2 scr_oy = (wdw_h - (scr_h * scr_scale)) / 2 love.graphics.setColor(255, 255, 255) love.graphics.draw(canvas, scr_ox, scr_oy, 0, scr_scale, scr_scale) love.graphics.print("FPS: "..tostring(love.timer.getFPS()), 10, 10) end function love.keypressed(key, isrepeat) if key == "escape" then -- ESC to exit love.event.quit() elseif key == "f11" then -- toggle fullscreen if love.window.getFullscreen() then love.window.setFullscreen(false) else love.window.setFullscreen(true) end end end
ソースも画像も、License : CC0 / Public Domain ってことで。
◎ 処理の考え方。 :
考え方としては、テクスチャが貼ってある球の中から外を覗いている、てな状況と仮定して。
視線の向きについて、縦方向(垂直方向)の角度が a、横方向(水平方向)の角度が b、として…。
まず、横方向(水平方向)の角度が0度の状態で考えてみる。横方向から見た時の図は以下になる。
上から見た図で考える。球と交差する点 P は、半径 pz の円の上にあるはずだから…。
z値 pz と、画面上のx,y座標 sx, sy と、画面までの距離 sz が分かっているので、読み取るべきテクスチャ座標を求めることができる。
てな感じで求めたのだけど。球と直線が交差した点のz値を取得するあたりで、もっと簡単な方法がありそうな気もする…。
視線の向きについて、縦方向(垂直方向)の角度が a、横方向(水平方向)の角度が b、として…。
まず、横方向(水平方向)の角度が0度の状態で考えてみる。横方向から見た時の図は以下になる。
- sz : 視点(0,0,0) から画面までの距離。
- sy : 画面を通る直線のy値。
- P : 球と交差する点。
- pz : 球と交差する点のz座標。
- a : 垂直方向の角度。
- r : 球の半径。
a = atan(sy, sz) pz = r * cos(a)
上から見た図で考える。球と交差する点 P は、半径 pz の円の上にあるはずだから…。
b = atan(sx, z) qz = pz * cos(b)これで、球と交差する点 P のz値、qz が得られた。
z値 pz と、画面上のx,y座標 sx, sy と、画面までの距離 sz が分かっているので、読み取るべきテクスチャ座標を求めることができる。
u = sx * qz / sz v = sy * qz / sz
てな感じで求めたのだけど。球と直線が交差した点のz値を取得するあたりで、もっと簡単な方法がありそうな気もする…。
◎ UVChecker-map画像について。 :
UVChecker-map画像は、以下で公開されてる画像を使わせてもらいました。ありがたや。この手の実験をする時はマジ助かるです。Public Domain 扱いになってる点もありがたい。
_Arahnoid/UVChecker-map: A collection of free images what can be used during unwrapping of 3D models
_Arahnoid/UVChecker-map: A collection of free images what can be used during unwrapping of 3D models
[ ツッコむ ]
以上、1 日分です。