2017/12/10(日) [n年前の日記]
#1 [love2d] love2dのShaderについて試していたり
love2dにはShader機能があるらしい。興味が湧いたので使い方を調べているところ。
この場合のShaderというのは…。コアが少ないCPUではなく、コアがたくさん入ってるGPU側で処理をすることで、リアルタイム・高速に画像の色や形を変えたり、あるいは元画像が無くても計算式でリアルタイムに画像を生成して描画できる機能、という説明で合っているのかな。どうなんだろう。
使うときは、love.graphics.setShader(Shaderが入った変数) で使いたいShaderに切り替えて、描画が終わったら love.graphics.setShader() でデフォルトのShaderに戻す。
ShaderのGLSLは以下のような感じで書くけれど…。
vec4 pixel = Texel(texture, texture_coords) で、現在のスクリーン座標に描画されるはずのピクセル情報が得られる。pixel.r、pixel.g、pixel.b、pixel.a の形で利用できる。それぞれ 0.0〜1.0の範囲で値が入ってる。
Shaderに何か値を渡したいときは、 _Shader:send() を使う。あらかじめShader側に、値を受け取るための変数を列挙しておくこと。ちなみに、Shader側で、処理に使わない変数が書かれてるとエラーになる。
GLSL と、love2d の Shader で使うキーワードは一部違う、らしい。以下の置き換えが必要、かもしれない。
この場合のShaderというのは…。コアが少ないCPUではなく、コアがたくさん入ってるGPU側で処理をすることで、リアルタイム・高速に画像の色や形を変えたり、あるいは元画像が無くても計算式でリアルタイムに画像を生成して描画できる機能、という説明で合っているのかな。どうなんだろう。
◎ 参考ページ。 :
以下のページが参考になった。ありがたや。特に一番最初のページが分かりやすい。提示されてるサンプルソースを眺めてるだけでもぼんやり分かる、かなと。
_A Beginner's Guide to Shaders | LOVE - Community Blogs
_Tutorial:Introduction to Shaders (日本語) - LOVE
_love.graphics.newShader (日本語) - LOVE
_Shader (日本語) - LOVE
_Shader:send (日本語) - LOVE
_GLSLについてのメモ - Qiita
_GLSL (OpenGL ES2.0)リファレンス.md
_A Beginner's Guide to Shaders | LOVE - Community Blogs
_Tutorial:Introduction to Shaders (日本語) - LOVE
_love.graphics.newShader (日本語) - LOVE
_Shader (日本語) - LOVE
_Shader:send (日本語) - LOVE
_GLSLについてのメモ - Qiita
_GLSL (OpenGL ES2.0)リファレンス.md
◎ 分かった範囲でメモ。 :
- Shaderの処理は、GLSLという言語で書く。
- Shaderは、love.load() の中で、love.graphics.newShader() を使って作成する。
- love.graphics.newShader("shader.fs") という形で、別ファイルにGLSLを書いておいて、そのファイルを読み込んで作成できる。
- あるいは、love.graphics.newShader() に、GLSL を文字列としてずらずら書いて渡して作ることもできる。
- GLSLをずらずら書いて渡す際は、[[ 〜 ]] で囲んで、複数行文字列として書くのがフツーっぽい。
使うときは、love.graphics.setShader(Shaderが入った変数) で使いたいShaderに切り替えて、描画が終わったら love.graphics.setShader() でデフォルトのShaderに戻す。
function love.draw() love.graphics.setShader(myshader) -- 使うShaderに切り替え -- 何かしら描画 love.graphics.setShader() -- デフォルトのShaderに戻す end
ShaderのGLSLは以下のような感じで書くけれど…。
function love.load()
myShader = love.graphics.newShader[[
vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ){
vec4 pixel = Texel(texture, texture_coords );
return pixel * color;
}
]]
end
以下の4つの変数が渡された状態で処理をするようで。
- vec4 color : love.graphics.setColor(r, g, b, a) で指定された値が入っている。color.r、color.g、color.b、color.a で値を読み取れる。値の範囲は、0.0〜1.0 に変換されている。
- Image texture : 画像データを渡してるらしい。
- vec2 texture_coords : テクスチャのuv値が渡される。texture_coords.x、texture_coords.y の形で利用できる。値の範囲は、0.0〜1.0。
- vec2 screen_coords : スクリーン座標が入ってる。screen_coords.x、screen_coords.y の形で利用できる。それぞれドット単位で値が入る。例えば640x480の画面なら、0〜639、0〜479 の値が入ってくるし、800x600の画面なら、0〜799、0〜599の値が入ってくる。
vec4 pixel = Texel(texture, texture_coords) で、現在のスクリーン座標に描画されるはずのピクセル情報が得られる。pixel.r、pixel.g、pixel.b、pixel.a の形で利用できる。それぞれ 0.0〜1.0の範囲で値が入ってる。
Shaderに何か値を渡したいときは、 _Shader:send() を使う。あらかじめShader側に、値を受け取るための変数を列挙しておくこと。ちなみに、Shader側で、処理に使わない変数が書かれてるとエラーになる。
GLSL と、love2d の Shader で使うキーワードは一部違う、らしい。以下の置き換えが必要、かもしれない。
- GLSL で float → love2d では number と書く
- GLSL で uniform → love2d では extern (uniform のままでも一応動く?)
- GLSL で sampler2D → love2d では Image
- GLSL で texture2D(tex, uv) → love2d では Texel(tex, uv)
◎ ラスタースクロールのテスト。 :
ラスタースクロールっぽい処理を書いてみたり。こんな感じに。
使用画像は以下。
_bg_1024x1024.png
ソースは以下。
_conf.lua
使用画像は以下。
_bg_1024x1024.png
ソースは以下。
_conf.lua
function love.conf(t) t.window.title = "Shader test 03" 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 03
-- raster scroll modoki
function love.load()
love.graphics.setDefaultFilter("nearest", "nearest")
scr_w, scr_h = 640, 480
canvas = love.graphics.newCanvas(scr_w, scr_h)
-- load image
img = love.graphics.newImage("bg_1024x1024.png")
-- make shader
myshader = love.graphics.newShader(
[[
extern number ang;
extern vec2 scrSize;
extern vec2 r;
extern vec2 curve;
extern vec2 basePos;
vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ){
number factor_x = radians(r.x * (screen_coords.x / scrSize.x));
number factor_y = radians(r.y * (screen_coords.y / scrSize.y));
vec2 uv = texture_coords;
uv.x = mod(-basePos.x + uv.x - curve.x * sin(ang + factor_y), 1.0);
uv.y = mod(-basePos.y + uv.y - curve.y * sin(ang + factor_x), 1.0);
vec4 pixel = Texel(texture, uv);
return pixel * color;
}
]]
)
myshader:send("scrSize", {scr_w, scr_h})
myshader:send("r", {360.0 * 1.5, 360.0 * 1.5})
myshader:send("curve", {0.02, 0.02})
myshader:send("basePos", {0.0, 0.0})
angle = 0
bx = 0
by = 0
end
function love.update(dt)
bx = (bx + (64 / scr_w) * dt) % 1.0
by = (by + (64 / scr_h) * dt) % 1.0
myshader:send("basePos", {bx, by})
angle = (angle + 180 * dt) % 360.0
myshader:send("ang", math.rad(angle))
px = (scr_w - img:getWidth()) / 2
py = (scr_h - img:getHeight()) / 2
end
function love.draw()
love.graphics.setCanvas(canvas)
love.graphics.clear(0, 0, 0, 255)
love.graphics.setShader(myshader)
love.graphics.setColor(255, 255, 255)
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_ofsx = (wdw_w - (scr_w * scr_scale)) / 2
scr_ofsy = (wdw_h - (scr_h * scr_scale)) / 2
love.graphics.setColor(255, 255, 255)
love.graphics.draw(canvas, scr_ofsx, scr_ofsy, 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()
end
end
[ ツッコむ ]
以上です。
