glslはRen’Pyの独自言語ではなくグラフィック界隈ではよく使われているようで”glsl ゲームエフェクト sample”とか”glsl samle”とか検索すればいくらでもサンプルコードつきででてきます。このページではそれらから1つを選んでRen’Pyに移植してみます。
まずメジャーなglslのサンプル公開サイトであるglslsandboxを開いて一番最初のエフェクトである以下を移植してみます。
https://glslsandbox.com/e#370395.0
#ifdef GL_ES
precision mediump float;
#endif
// dashxdr was here 20120228
// whoops the rainbow colors weren't correct...
// ugh, can't stand the previous version... too dizzy
uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
//float rainbow(float x)
//{
// x=fract(0.16666 * abs(x));
// if(x>.5) x = 1.0-x;
// return smoothstep(.166666, .333333, 0.7);
//}
void main( void ) {
vec2 position = ( 2.0*gl_FragCoord.xy - resolution) / resolution.xx;
float ti=time*0.;
vec3 color = vec3(0.0);
float r = length(position);
float a = atan(position.y, position.x);
float b = a*3.0/3.14159;
color = vec3(0.0, 0.0, 1.0);
float t = .5*(1.0 + cos(a + 40.0 * r * (1.0 + sin(a*24.0)*.1) - ti) * (5.0 / (r+5.0)));
//t = (t<0.5) ? 0.0 : 1.0;
gl_FragColor.rgba = vec4(color*t, 1.0);
}
まずは変数を定義します。上記のサンプルでの使用している変数をRen’Pyでも定義します。 time, mouse については定義しているだけで実際には使っていないので取り除いています。 resolution は描画先の解像度を意図しているようなのですが、 Ren’Py の res0 はテクスチャの解像度なのでちょっと違いそうです。なので、 u_model_size でとりあえず代用します。フラグメントシェーダーで使用されている gl_FragCoord も描画先の座標(単位はピクセル)なのですが、そのまま使うと画面の中心がずれたので、とりあえず a_position で代用していみます。
variables="""
uniform vec2 u_model_size;
attribute vec4 a_position;
varying vec4 v_position;
""",
このコードでは頂点シェーダーでの処理をしていないのですが、 gl_FragCoord を a_position で代用するのでそこだけ処理します。
vertex_300="""
v_position = a_position;
""",
次にフラグメントシェーダーを定義します。優先度の数値は適当です。 vec2 型では(変数名).xx とすると vec2(第一要素, 第一要素)を表わし、 res0.xx の部分は両方共解像度の横幅の vec2 型を表しています。 gl_FragCoord は v_positionで置き換え、 resolution は u_model_size で置き換えます。
fragment_300="""
vec2 position = ( 2.0*gl_FragCoord.xy - u_model_size) / u_model_size.xx;
float ti=0.;
vec3 color = vec3(0.0);
float r = length(position);
float a = atan(position.y, position.x);
float b = a*3.0/3.14159;
color = vec3(0.0, 0.0, 1.0);
float t = .5*(1.0 + cos(a + 40.0 * r * (1.0 + sin(a*24.0)*.1) - ti) * (5.0 / (r+5.0)));
gl_FragColor.rgba = vec4(color*t, 1.0);
"""
全部合わせたシェーダーの定義は以下となります。
init python:
renpy.register_shader("example.test1", variables="""
uniform vec2 u_model_size;
attribute vec4 a_position;
varying vec4 v_position;
""", vertex_300="""
v_position = a_position;
""", fragment_300="""
vec2 position = ( 2.0*v_position.xy - u_model_size) / u_model_size.xx;
float ti=0.;
vec3 color = vec3(0.0);
float r = length(position);
float a = atan(position.y, position.x);
float b = a*3.0/3.14159;
color = vec3(0.0, 0.0, 1.0);
float t = .5*(1.0 + cos(a + 40.0 * r * (1.0 + sin(a*24.0)*.1) - ti) * (5.0 / (r+5.0)));
gl_FragColor.rgba = vec4(color*t, 1.0);
""")
次にこのシェーダーを使用したdisplayableを定義してみます。シェーダーだけではなにも表示されないので子として Model() を使用し、 child には Solid をダミーにわたしています。
image glsl_test:
shader "example.test1"
Model().child(Solid("#000"))
label start:
show glsl_test
"..."
結果が以下の通りでサンプルどおりの結果が出力されました。思いのほか簡単に既存のエフェクトの移植ができました。できることは増えそうですが、公開サンプルを使用する場合は著作権周りには気を付けましょう。