すこしふしぎ.

VR/HI系院生による技術ブログ.まったりいきましょ.(友人ズとブログリレー中.さぼったら焼肉おごらなきゃいけない)

oFでshader勉強中「1.サンプルを読む」

最近,グラフィクスに興味がある.

昨年から始めたopenFrameworksにも慣れてきて,簡単なアニメーションならさくりと作れるようになってきた.実際,研究用のアプリとかもoFでさくさく作っている.この「まぁとりあえず作ってみようぜ」感がたまらない.

しかし,用意された図形描画だけではどうしても限界がある.GLつかってるはずなのに,結局使うのはプリミティブ図形にテクスチャ貼るくらいだからそりゃしょうがない."さくさくと"つくれる用には鳴ったので,どーせなら"もっとカッコいい"ものつくってみたい.

そこでだ.

プログラマブルシェーダ,勉強しちゃおうぜ.

と思った次第である.

経緯

そもそもshaderとはなんなのだ?「シェーダつかうとなんかかっこいいエフェクトできるんでしょ」程度の認識しかないぞ自分.そこでちょっと調べてみた.その結果,shaderを一言であらわすと

「CPUで扱いきれない処理をGPUで扱うためのもの」

という感じ.我ながら抽象的だなぁ.

CPUは論理構造には強い反面,あまり並列に処理を走らせない.せいぜい数個だろう.一方のGPUでは,「画面の各画素に対し**という色を描画する」みたいな感じで大量の単純作業を処理する.ここには複雑な論理構造はなく,シンプルに与えられた命令を処理すればよいため,実行が早くなるのだとか.

たとえば,CPUで画像の各画素に対しRGBを一個ずつ指定するのは明らかに重そうな処理である.しかし,もし各画素値を簡単な関数で機械的に指定するのであれば,GPU側に処理を任せることができ,結果処理が高速になる.

要は適材適所やったれば仕事早くなるよねってことだ.

さらに言うと,shaderの中でも頂点情報を扱うものだとか,色情報を扱うものだとかに細かく分けられるらしい.変形させたりグラデーションさせたりいろいろできるらしい.なんか勉強するべきことおおいなあ..



さて,OpenGLにはGLSLっていうShader Languageがあるらしい.存在自体は前々から知っていたが,「ごにょごにょ書いてあるけどよくわかんね.テクスチャはっとけばよくね?」と,きちんと見たことは無かった.

そんな中,別件でwebGLを調べているとこでもGLSLが出てきた.ここ参考.なんだよcanvas+createjsでみたいにさくさくできるのかと思ってたのに.そーいえばOpenGL ESとかではテクスチャ使うのにもshaderがでてきて云々みたいな話も聞いた気がする.

これ,iPhone/iPadでGLつかったりwebGL使いたいならそれくらいできないとダメってことなのでは..?

いやちがう.逆に考えれば,shader勉強したらいろいろ応用できるということだ!

そんなこんなで,ここらでひとつshaderの勉強をしようと思い立ったのである.なお,記事はshader勉強中の身で書いているので,正確性はあまり担保できない.むしろ積極的に突っ込んでくれるとうれしい.

環境準備

勉強したいなーと思ったはいいものの.新しいものを学ぶときあるある.

「まず何を準備すればいいんだ?」

となったわけ.そこで今回はoFからshaderを動かす段階までを準備しよう.

openFrameworksからshaderを使う

ぱっと調べた感じ,シェーダの勉強だけならGLSL Sandboxが良さげ.ブラウザ上でシェーダプログラム書いて,リアルタイムコンパイルしてくれるらしい.サイトにアクセスして新しくつくるを押せばテスト可能.そのうえプリセットでそれなりに面白いシェーダプログラムが書かれているので,値をいじって遊べる.

...けど、それだけではリレーメンバーに怒られそうなので、今回はofからシェーダを利用することを目指す.

といっても、専用のクラスがあるので呼び出しは簡単なんだけど.

ofの新しい機能を使うときは、まずはexampleを覗くのがいちばん.ちらと見るとシェーダを扱ってそーなサンプルとしてgl/shaderExampleってのがあった.

こんなん.

f:id:ism1000ch:20131007025926p:plain

画像だと分かりにくいけれど,テキストを画像として描画してうねうねさせてる.時間と共に色もちょっと変わる.そしてさらにマウスポインタ周りが強くうねってる.

こんなん普通にかいたら結構面倒な気がするが...これがshaderの力...

この力,ぜひ身に付けてやろうじゃないか.
さて,以下プログラム抜粋.

まずはheader.デフォルトの値以外.

ofTrueTypeFont font;
ofShader shader;
bool doShader;

文字描画のためのofTrueTypeFontと,shader使うためのofShaderオブジェクトを使ってるようだ.ofShaderはどんな使い方をするのだろうか.

では実装ファイル.
shaderに関するとこだけ抜粋してある.

まずはtestApp::setup()

#ifdef TARGET_OPENGLES
shader.load("shaders_gles/noise.vert","shaders_gles/noise.frag");
#else
if(ofGetGLProgrammableRenderer()){
    shader.load("shaders_gl3/noise.vert","shaders_gl3/noise.frag");
    }else{
    shader.load("shaders/noise.vert", "shaders/noise.frag");
}
#endif
doShader = true;	
}

TARGET_OPENGLESはスマホ向けの設定かな.ofShaderクラスのloadメソッドでshaderのスクリプトを読み込んでいるらしい.第一引数がvertex shader,第二引数がfragment shaderか.調べた感じ,参照する必要が無いときはパスに""を指定すればよいみたい.

shaderの読み込みは分かりやすいが,その前の条件分岐,else文のなかのofGetGLProgrammableRenderer()は何を見ているんだろうか?ここみたけどよくわかんね.レンダラの性能でも見てんのかなぁ.

testApp::updateは空だったので,次はtestApp::draw().

void testApp::draw(){
    if( doShader ){
	shader.begin();
	shader.setUniform1f("timeValX", ofGetElapsedTimef() * 0.1 );
	shader.setUniform1f("timeValY", -ofGetElapsedTimef() * 0.18 );
	shader.setUniform2f("mouse", mouseX - ofGetWidth()/2, ofGetHeight()/2-mouseY );
    }

    font.drawStringAsShapes("openFrameworks", 90, 260);
	
    if( doShader ){
	shader.end();
    }
}

なるほど,doShaderがtrueの時だけ,fontのdrawStringAsShapesをshader.begin()/end()が挟んでいるのか.
これはofFboの使い方と似てる.あれはfbo.begin()/end()間の描画がfboのテクスチャに格納されるやつだった.
しかしshader.beginの後に続くsetUniformとはなんなのだ?ofGetElapsedTime()やらmouseX/Yやらを使っているあたり,おそらくアニメーションとかインタラクティブ性を持たせることができるのだろうけども.shaderを読んでから戻ってこよう..

こんな感じで,of側はすげーシンプル。
setupでシェーダを読み込んで,drawの描画部をshader.begin()/end()で挟めば良いみたいだ.

というわけでいよいよshader部分を読む.

...と思ったが結構長くなってきたので,続きは次回に取っておこう.
次は2つのshaderを読み,それがどのようにoFと関わっているかを調べようと思う.