2017/12/21(木) [n年前の日記]
#1 [love2d] love2dのShaderで惑星をグルグル回す感じのソレを試したり
球を相手にアレコレ考えているうちに、某STGのどピンクな背景上でグルグル回ってたアレを love2d の Shader でもできるのではないかと思えてきたので試してみたり。
とりあえず、こんな感じに。
更に、向きを90度変えて(xとyを入れ替えて)、テクスチャを変えて、ついでに雲のテクスチャも別途回してみたらこんな感じに。
その手の地図(メルカトル図法?)を扱う時と同様に、南極・北極のあたりが怪しいことになってるけど…。でもまあ、例えば2Dゲームの背景として表示して雰囲気作りに使う分にはこれでもイケそうな。
とりあえず、こんな感じに。
更に、向きを90度変えて(xとyを入れ替えて)、テクスチャを変えて、ついでに雲のテクスチャも別途回してみたらこんな感じに。
その手の地図(メルカトル図法?)を扱う時と同様に、南極・北極のあたりが怪しいことになってるけど…。でもまあ、例えば2Dゲームの背景として表示して雰囲気作りに使う分にはこれでもイケそうな。
◎ 画像とソース。 :
使った画像は以下。
_grid_bg_480x480.png
_planet_tex_480x480.png
_planet_cloud_only_496x496.png
ソースは以下。
_conf.lua
_main.lua
向きを変えて、雲レイヤー(?)も追加した版のソースは以下。
_conf.lua
_main.lua
画像もソースも、License : CC0 / Public Domain ってことで。
_grid_bg_480x480.png
_planet_tex_480x480.png
_planet_cloud_only_496x496.png
ソースは以下。
_conf.lua
function love.conf(t) t.window.title = "Shader test 12 planet" 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 12 planet 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_480x480.png") img:setFilter("linear", "linear") -- make shader local shadercode = [[ extern number start_x; extern number start_y; const float PI = 3.14159265; vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ) { // float sx = (screen_coords.x - (love_ScreenSize.x / 2)) / love_ScreenSize.y; // float sy = (screen_coords.y - (love_ScreenSize.y / 2)) / love_ScreenSize.y; float tx = texture_coords.x - 0.5; float ty = texture_coords.y - 0.5; float a = asin(ty * 2); float v = a / PI; float r = cos(a); float b = asin(tx * 2 / r); float u = b / PI; texture_coords.x = mod((u + start_x) + 0.5, 1.0); texture_coords.y = mod((v + start_y) + 0.5, 1.0); vec4 texcolor = Texel(texture, texture_coords); texcolor.a = (tx * 2 > r)? 0.0 : texcolor.a; texcolor.a = (tx * 2 < -r)? 0.0 : texcolor.a; return texcolor * color; } ]] myshader = love.graphics.newShader(shadercode) myshader:send("start_x", 0.0) myshader:send("start_y", 0.0) ang = 0.0 px = (scr_w - img:getWidth()) / 2 py = (scr_h - img:getHeight()) / 2 end function love.update(dt) ang = ang + 0.1 * dt myshader:send("start_x", ang) end function love.draw() love.graphics.setCanvas(canvas) love.graphics.clear(24, 64, 176, 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
向きを変えて、雲レイヤー(?)も追加した版のソースは以下。
_conf.lua
_main.lua
画像もソースも、License : CC0 / Public Domain ってことで。
◎ 考え方。 :
処理の考え方をメモ。
そこに球があるとして、y方向の角度 a、x方向の角度 b を、テクスチャの v,u 値として利用することにする。
Shader の texture_coords.x、texture_coords.y には、0.0〜1.0 の値が入ってくるけど、それを -0.5 〜 +0.5 にした tx,ty があるとして。
y方向の角度 a は、
また、ty の高さには、上から見て cos(a) を半径とする円がそこにあるはずで。なので、x方向の角度 b は、
asin() が返してくる値はラジアン単位だから…。π(PI)で割ってやれば、-0.5 〜 0.5 の値になってくれるので、テクスチャの u,v値として使い易いはず。180度 = πラジアン、360度 = 2πラジアンだから。
このままだと、球の外側に相当する部分も描画されてしまうので…。tx と r = cos(a) を利用して、球の外側を処理してるなら描画する点のアルファ値を0に、球の内側を処理してるならアルファ値を本来のアルファ値にすることで、球だけを描画してるように見せかける。
こんな感じで合ってる…のかな。どうなんだ。自信無し。
そこに球があるとして、y方向の角度 a、x方向の角度 b を、テクスチャの v,u 値として利用することにする。
Shader の texture_coords.x、texture_coords.y には、0.0〜1.0 の値が入ってくるけど、それを -0.5 〜 +0.5 にした tx,ty があるとして。
y方向の角度 a は、
a = asin(ty * 2)で求められる。
また、ty の高さには、上から見て cos(a) を半径とする円がそこにあるはずで。なので、x方向の角度 b は、
b = asin(tx * 2 / r)で求められる…かなと。
asin() が返してくる値はラジアン単位だから…。π(PI)で割ってやれば、-0.5 〜 0.5 の値になってくれるので、テクスチャの u,v値として使い易いはず。180度 = πラジアン、360度 = 2πラジアンだから。
u = b / PI v = a / PI
このままだと、球の外側に相当する部分も描画されてしまうので…。tx と r = cos(a) を利用して、球の外側を処理してるなら描画する点のアルファ値を0に、球の内側を処理してるならアルファ値を本来のアルファ値にすることで、球だけを描画してるように見せかける。
texcolor.a = (tx * 2 > r)? 0.0 : texcolor.a; texcolor.a = (tx * 2 < -r)? 0.0 : texcolor.a;
こんな感じで合ってる…のかな。どうなんだ。自信無し。
◎ 元ネタは全然違うっぽい。 :
ここまでやってから
_元ネタ
を確認してみたら、見た目からして違う処理をしていた…。さて、これはどういう処理をすればいいんだろう? もうちょっと考えてみないと…。
待てよ。もしかしてコレ、テクスチャのスクロールする方向を横じゃなくて縦にすればいいのかな。
どうだろう。似てるような、ビミョーに何か違うような…。
使ったテクスチャは以下。
_planet_tex_480x480_3.png
このテクスチャ画像は、 _File:Full moon.jpeg - Wikimedia Commons で公開されてる画像を加工して作ったのだけど、Wikipediaのソレは Public Domain となってるように見えるから、この画像も Public Domain ってことで。
待てよ。もしかしてコレ、テクスチャのスクロールする方向を横じゃなくて縦にすればいいのかな。
どうだろう。似てるような、ビミョーに何か違うような…。
使ったテクスチャは以下。
_planet_tex_480x480_3.png
このテクスチャ画像は、 _File:Full moon.jpeg - Wikimedia Commons で公開されてる画像を加工して作ったのだけど、Wikipediaのソレは Public Domain となってるように見えるから、この画像も Public Domain ってことで。
[ ツッコむ ]
以上です。