2014年04月25日

ソフトライト的なシェーダー

RenderMonkeyのHDRエフェクトをBGEに移植しようとした結果、何だかよく分からないシェーダーができました。

HDR.jpg

RenderMonkeyのHDRエフェクトをそのまま再現しようとしたのですが、BGEではフィルタをかけた画像と、かける前の画像を合成する処理ができませんでした。
で、代わりにシングルパスのブルームフィルタを使ってみたのですが、そしたらこうなりました。
何シェーダーかよく分からない感じになしましたが、ソフトライトっぽい効果に加えてトーンマッピングしたような効果もあって、なかなか気に入ってます。
これも一応、HDRになるんでしょうか。
HDRが何なのかもよく分かってませんが。


下の画像はこのシェーダーをかける前のシーンの明るさです。

moto.jpg

奥に見えている薄暗い巨大なスフィアが、最初の画像にあった発光体ですが、もちろんこの輝度では発光しません。水面シェーダーとの相乗効果で発光しています。
ややこしくて申し訳ない。

白飛び効果を強めに効かせようとするとシーン全体の明るさがかなり持ち上がってしまうため、シーンをここまで暗くしています。
いまのとこそういう仕様です。
使いにくいと思うのでソースコードの公開は控えますが、参考URLを以下で紹介しておきますので、興味がある方は参考にしてみてください。


▼出典

ブルームフィルタは、例のごとくBlenderArtistsのスレッドからお借りしました。

http://blenderartists.org/forum/showthread.php?239280-bloom-filter-for-2-6a

このフィルタの出力にRenderMonkeyのHDRのサンプルコードの最後のステップを付け足すと、今日紹介したソフトライトシェーダーになります。



posted by gency at 01:18| Comment(0) | エフェクト

2013年07月09日

輪郭線シェーダーを作ったよ

BGE用の輪郭線シェーダーを作ってみました。
ゲームで気軽に使えるポストエフェクト(CustomFilter)タイプです。

filter3.jpg

こちらは標準的な設定の輪郭線。
極端に太い線にしなければ、わりと綺麗な線が描画できているのではないかと思います。
輪郭線の太さや色などは、ゲームプロパティから変えられるようにしてます。
試しに太めの線で描画してみたのが↓

filter2.jpg

輪郭摘出には、ラプラシアンフィルタを使っています。
ラプラシアンフィルタのソースは、こちらから拝借しました。
見るからに軽そうな処理で、ゲームには適していそうです。

で、参考ソースをBGE的な雰囲気に改変し、さらに輪郭線になるようごにょごにょしたものが以下になります。
※追記:一部修正しました。

uniform sampler2D bgl_RenderedTexture;
uniform sampler2D bgl_DepthTexture;
uniform float bgl_RenderedTextureWidth;
uniform float bgl_RenderedTextureHeight;
uniform float edge_thickness;
uniform float edge_color;
uniform float edge_r_step;
uniform float edge_d_step;

vec2 texCoord = gl_TexCoord[0].st;
float w = bgl_RenderedTextureWidth*edge_thickness;
float h = bgl_RenderedTextureHeight*edge_thickness;

void main(void)
{
vec3 lf_color = vec3(0.5, 0.5, 0.5);
lf_color += texture2D(bgl_RenderedTexture, texCoord).rgb * -4.0;
lf_color += texture2D(bgl_RenderedTexture, texCoord + vec2(-1.0/w, 0.0)).rgb;
lf_color += texture2D(bgl_RenderedTexture, texCoord + vec2( 1.0/w, 0.0)).rgb;
lf_color += texture2D(bgl_RenderedTexture, texCoord + vec2(0.0, -1.0/h)).rgb;
lf_color += texture2D(bgl_RenderedTexture, texCoord + vec2(0.0, 1.0/h)).rgb;
lf_color = step(edge_r_step, lf_color);

vec3 dlf_color = vec3(0.5, 0.5, 0.5);
dlf_color += texture2D(bgl_DepthTexture, texCoord).rgb * -4.0;
dlf_color += texture2D(bgl_DepthTexture, texCoord + vec2(-1.0/w, 0.0)).rgb;
dlf_color += texture2D(bgl_DepthTexture, texCoord + vec2( 1.0/w, 0.0)).rgb;
dlf_color += texture2D(bgl_DepthTexture, texCoord + vec2(0.0, -1.0/h)).rgb;
dlf_color += texture2D(bgl_DepthTexture, texCoord + vec2(0.0, 1.0/h)).rgb;
dlf_color = step(0.5+edge_d_step*0.001, dlf_color);

vec3 g_color = mix(-lf_color, -dlf_color, 0.50);

float gc = g_color.r + g_color.g + g_color.b;
float gt = 0.333;
g_color = (gc*gt, gc*gt, gc*gt);

vec3 r_color = texture2D(bgl_RenderedTexture, texCoord).rgb;
gl_FragColor = vec4(r_color + (g_color*edge_color), 1.0);
//gl_FragColor = vec4(-g_color, 1.0);
}


このフィルタを実行するには、「edge_thickness」「edge_color」「edge_r_step」「edge_d_step」というFloat型のゲームプロパティを追加する必要があります。
値は、とりあえず全て0.8くらいにしておくと、無難な線がでるはずです。

edge_thickness:
線の太さを変更します。値は、1.0が標準サイズ、0.0に近づくほど線が太くなります。

edge_color:
線の色の濃さを変化させる値。
値が大きいほど濃く黒い線が出て、値を小さくすると半透明の線になり、最終的には線が消えます。

edge_r_step:
ラプラシアンフィルタの二値化関数のしきい値。レンダーテクスチャから摘出する輪郭線の性質を変化させます。0.5〜1.0くらいの範囲で使うと線がでますが、0.5以下からは描画がおかしくなるようです。

edge_d_step:
ラプラシアンフィルタの二値化関数のしきい値。デプステクスチャから摘出する輪郭線の性質を変化させます。0.0以上の値を指定します。輪郭線が出ないときや、出過ぎているときの調整用。

随所に改良の余地を残してはいますが、修正は追々。
今回は報告までに。

おまけ:
filter.jpg

スパッツ、ではまた。
posted by gency at 09:36| Comment(0) | エフェクト

2013年02月18日

パーティクルエフェクト

BGEでパーティクル(もどき)を飛ばしてみます。
(基本的なことの解説はしてません)

エミッター:
emtt.jpg

中心に見える明るいオレンジのPlainAxesがエミッターの親オブジェクト。
その周りに配置された4つのSingleArrowがその子オブジェクトで、エミッターの本体。ここからパーティクルが放射されます。
ちなみに、今回はエミッター用のメタオブジェクトをわざわざ用意しましたが、原点を持ったオブジェクトであれば何でもエミッターになります。

エミッターは「可視レイヤー」に配置します。
とりあえずカメラ前にでも置いときます。

パーティクル放出はスクリプトで行います。
スクリプトは親オブジェクトに実行させ、子オブジェクトは何もしません。
ソースコードは以下のとおり(稼働中のソースから抜粋したので動作確認してないけど・・たぶん動くよ!)


from bge import logic

co = logic.getCurrentController()
obj = co.owner

scene.addObject("particle","ParticleEmitter01")
scene.addObject("particle","ParticleEmitter02")
scene.addObject("particle","ParticleEmitter03")
scene.addObject("particle","ParticleEmitter04")

obj["radian"] += 0.2

matrix = mathutils.Matrix.Rotation(obj["radian"], 3, 'Z')
obj.orientation = matrix


addObject()の第一引数は次に紹介するパーティクルオブジェクトの名前です。
第二引数はエミッターの名前です。
いずれの引数も文字列の代わりに実体を渡すことができます。

obj["radian"]はゲームプロパティです。
LogiBrickの「Add Game Property」ボタンを押してプロパティを追加し、型を「Float」、名前を「radian」にしてください。


パーティクル:
partic.jpg

ここで扱うパーティクルは、普通のポリゴンオブジェクトです。
配置するレイヤーは「不可視レイヤー」です。可視レイヤーに置くとエライことになります。
CPUの負荷を減らすため、ある程度の大きさを持った粒子の塊を飛ばすのが、このやり方の基本になります。
さらに画像のは、1オブジェクトで3つの粒子塊を表現するという「嵩増し」もしています。
これくらいしないと、すぐに60fps割ってしまいます・・・けっこう重いのよ。


以下はパーティクルのソースコードです(たぶん動く系)


from bge import logic

cont = logic.getCurrentController()
obj = co.owner

if i<0.1: obj.endObject()

obj.position.z -= 0.1
i = 1.0 - obj["timer"] * 0.8
obj.scaling = (i, i, i)


obj["timer"]はゲームプロパティです。
Timer型のプロパティを追加して「timer」と命名してください。


結果:
effe.jpg

パーティクルでやる意味があるのかって感じですが・・
炎か煙のサンプルがよかったと、終わってから思う。

posted by gency at 11:25| Comment(0) | エフェクト