お絵かきプログラミングProcessingサンプルコード解説の第13弾です!
ProcessingをまだPCに導入されていない方は、こちらの記事を参考に導入してみてください!
今回は ToonShading というサンプルコードを見てみましょう。
難易度は ★★☆☆☆ くらい。
簡単な3Dオブジェクトとシェーダーの基本的な使い方を学びます。
わからない箇所があればコメントをください。
Table of Contents
もくじ
とりあえず動かしてみよう
Processnigを起動し、サンプルコードを開きましょう。
ファイル → サンプル と選択し現れたウィンドウで ライブラリ → Shaders → ToonShading と選択してください。
このようなスケッチが現れます。ウィンドウの左上にある再生ボタンをクリックして実行してみましょう。
色のついた球体が現れます。
コードを読む
とりあえずコードの全貌を確認しましょう。
あまり長くないですね。気軽に進みましょう。
いつものようにコメントから始まっていますね。
ざっくり訳すと、画像を「漫画っぽくする」シェーダーの使用例とのことです。(glslのチュートリアルを参考にしている旨も書かれていますが、ここの理解は必須ではないので、興味を持たれた方は読んでみてください。)
概要がわかったところで、先に進みましょう。
まずグローバル変数を定義しています。
- toon: PShaderという型で定義されていますが、初めて見られる方も多いのではないでしょうか。PShaderはProcessingでシェーダーを変数として扱う際に用いる型です。
- shaderEnabled: シェーダーの有効無効を表す変数と想像されますね。初期値はtrueなので有効になっていそうですね(現時点ではあくまで想像です)。
次にsetup()を読みましょう。
setup
初めの3行は、setup()でいつも呼ばれる定番関数ですね。
- size(): 描画ウィンドウのサイズを指定する関数。
- noStroke(): 描画時の図形の枠などの線が描画されないようにします。
- fill(): 図形の塗りの色を指定します。今回は灰色に設定しています。
今回のsize()については少し注意が必要です。よく見るsize()の引数は2つで、
size(横の長さ, 縦の長さ)
という形で呼んでいましたが、今回は引数が3つあり、
size(横の長さ, 縦の長さ、レンダリングモード)
という形で呼んでいます。(第3引数は“P3D”(文字列)ではなくP3Dであることに注意してください。参考: size())
レンダリングモードの指定は、3Dの描画を行う場合に必要になるおまじないと思ってください。詳細を知りたい方はこちらの公式の説明を読んでみてください→ https://processing.org/tutorials/p3d
4行目で呼ばれている関数はあまり見たことがないと思います。
toonに代入していますが、loadShader()という関数が呼ばれていますね。名前から分かる通りシェーダーを読み込みます。引数に拡張子がglslの二つのファイルが指定されていますが、これらは外部に用意しているシェーダー用のファイルです。
本サンプルでは事前にglslファイルが配置されているため動作していますが、自作のコードでシェーダーを使用したい場合は適切な場所にファイルを配置する必要があります。
(参考: loadShader())
これでsetup()は終わりですね。draw()に進みましょう。
draw
今回はdraw()もコンパクトですね。サクサク進みましょう!
分岐から始まっていますね。
shaderEnabledがtrueだった場合にshader()が呼ばれていますね。
shader()はシェーダーを適用する関数です。引数に取っているtoonをのシェーダー効果を反映します。
- noStroke(): 描画時の図形の枠などの線が描画されないようにします。
- background(): 描画ウィンドウ全体を塗りつぶす関数。今回は引数が0なので、黒で塗りつぶします。
次に変数を定義して使っていますね。
dirY, dirXがそれぞれmouseY, mouseXをもとに定義されていますね。
図に、マウスカーソルの位置ごとのdirX, dirYの値を示していています。灰色の長方形が描画ウィンドウだと思ってください。dirX, dirYとも-1.0~1.0の値を取ります。
directionalLight()は描画する物体に光をあてる関数です。引数を6つとる関数で、前半の3変数で光の色、後半の3変数で光の方向をそれぞれ指定します。
引数についてもう少し説明します。
前半3つの変数で順に光の色のRGBを指定します。今回は、白っぽい色になります。
後半3つの変数で、光の方向を表すベクトルのx,y,z座標の要素を指定するのですが、まず座標軸について確認しましょう。
x, y軸はいつもの描画でも意識するので向きはお分かりと思いますが、z軸は初登場かもしれません。
- x軸の正方向: 右向き
- y軸の正方向: 下向き
- z軸の正方向: 奥行き方向の手前向き
数学で右手系の座標系に慣れている方には違和感があるかもしれませんが、Processingでの座標系は左手系です
今回光の方向としては、(-dirX, -dirY, -1) を指定していますが、図の赤い矢印のようになります。(あくまで方向です。Oの位置を起点に光が放出されるわけではありません。)
ところで、マウスカーソルと光の方向の関係について考えてみましょう。
画像の中央からマウスカーソルまでのベクトルと、光の方向のベクトル(のz軸成分を0にしたベクトル)(図中の薄い赤のベクトル)は、ちょうど逆方向を向いています。
つまり直感的には、マウスカーソルから画像中央の方向に光を当てていると理解して問題ありません。
光の当て方について理解できたところで、次に進みましょう。
sphere()は球体を配置する関数ですが、配置する位置は指定できず、座標軸の原点の位置に配置されます。
このため先にtranslate()を呼ぶことで、sphere()を呼ぶ時点での原点を好きな位置に移動しています。
translate()の効果は、draw()のループが実行されるたびにリセットされます。
(参考: sphere(), translate())
translate()は引数2つで呼ぶ場合は、x軸方向, y軸方向の移動を行います。今回は描画ウィンドウの中央に原点が来るように座標が移動しています。これにより、以降sphere()を呼ぶと、新しい原点(描画ウィンドウの中央)に球体が配置されます。
これでdraw()は終了です。
mousePressed
mousePressed()はマウスボタンのクリック時に処理が走る関数です
分岐がありますが、ざっくり全体を読みます。
マウスボタンのクリックによってshaderEnabledのtrue/falseが切り替わります。
切り替える以外の処理もあるので確認しましょう。
resetShader()はシェーダーの効果をデフォルトの状態に戻します。
shaderEnabledがfalseの場合はresetShader()によりシェーダーがデフォルト状態に戻され、さらにdraw()の処理に戻ってもshader()が呼ばれず、シェーダーの効いていない球が表示されます。(光の効果はシェーダーとは関係ないので残ります)
(参考: resetShader())
これがToonShadingの全貌になります。お疲れ様でした!
おまけ
ToonShadingのサンプルについて理解を深めるための小さな問題を用意しました。
サンプルコードでは、マウスをクリックするとシェーダーの有効/無効が切り替わるようになっていましたが、mousuPressed()での処理を「shaderEnabledのtrue/falseを切り替える」と変更しても(以下のコードに差し替えても)問題ないように思われますが、実はサンプルコードとは挙動が異なってしまいます。挙動はどのように変化しますか?
サンプルコードでは3Dの物体として球を配置していましたが、Processingには他にも3Dの図形を描画する関数があるようです。
どのような図形が用意されているかを調べ、サンプルコードで描画する図形を変更してください。
解答例は、こちらにおいておきます。
問題1の解答例
マウスボタンをクリックしてもシェーダーが無効にならなくなってしまいます。
状態を変数で管理する場合は、その変数に基づいた処理への影響に注意しましょう。
問題の説明の通りにmousePressed()を更新した場合は、draw()の中で、shaderEnabledがfalseの場合にresetShader()を呼ぶようにすれば、元通りの挙動になります。
さいごに
今回は、シェーダーを学びました。
シェーダー自体の実装については今回は踏み込みませんでしたが、単体で多彩な表現が可能な技術になります。
興味を持たれた方は是非習得してみてください。
ではまた次回!