SlideShare a Scribd company logo
シェーダーしよっ
  OpenGL ES, cocos2d, Unity
シェーダーしよっ
  OpenGL ES, cocos2d, Unity
自己紹介
                                       FREE!




•   xionchannel

•   @ajinotataki

•   Technical Artist
                       ElectroMaster   HungryMaster
                          18万本            5万本
シェーダーとは
描画パイプライン
ソフトウェア処理(CPU処理)

        ・頂点座標
        ・頂点番号リスト(ストリップ)
        ・法線
        ・頂点カラー
        ・テクスチャのUV座標
        など




ハードウェア処理(GPU処理)

 OpenGL ES 1.0 固定パイプライン        OpenGL ES 2.0 シェーダー

        ワールド変換

                                     Vertex Shader
        ビュー変換

                                 varying変数のラスタライズ
         射影変換

        ライティング                      Fragment Shader


     テクスチャマッピング




                          画面
シェーダーとは

• GPU用の色をつけるプログラム
• VertexShaderとFragmentShaderがある
• OpenGLではGLSL言語を使う
• シェーダーに与える引数もある
  Attribute, Uniform *後ほど説明します
よくあるシェーダーの処理順
1. VertexShader
  1. 頂点座標をProjection座標系に変換する
    →頂点座標を描画へ渡す

  2. 法線をView座標系に変換して、ライティン
    グを行う
    →頂点の輝度をFragmentShaderへ渡す

  3. UVをFragmentShaderへ渡す
よくあるシェーダーの処理順

2. FragmentShader
  1. VertexShaderから得られたUV値を元にテク
    スチャをフェッチしてピクセルの色を得る

  2. VertexShaderから得られた輝度を乗算してピ
    クセルの色を変化させる

  3. 得た色を描画へ渡す
VertexShaderの仕組み

 (0,1)    (1,1)   ポリゴンの頂点情報
                  *設定次第でこの情報量は変えられます



                  Position (x, y, z)
                  UV (u, v)
                  Color (r, g, b, a)


UV(0,0)   (1,0)
VertexShaderの仕組み

C(0,1,1,0.5) D(1,0,0,0.5)

                              頂点の色を左のように
                              変えるとグラデーショ
                              ンになる

A(0,1,1,0.5)   B(1,0,0,0.5)
VertexShaderの仕組み
                      UV値をずらすと絵が
 (0+a,1)    (1+a,1)   スクロールします




UV(0+a,0)   (1+a,0)
FragmentShaderの仕組み
   UV(u’, v’)   ピクセルの情報
                *設定次第でこの情報量は変えられます



                UV (u, v)
                Color (r, g, b, a)

                これらが頂点間の位置
                関係によって補間され
                て手に入る
FragmentShaderの仕組み
得られたUV値を使って手に入った
テクスチャカラーの色相を変える
とこうなります。
FragmentShaderの仕組み
得られたUV値を除算などで変化さ
せると絵が荒くなります。




スーパーファミコンでよくあった
場面転換に使えそうですね。
シェーダーで注意すべき点

•   staticな変数を持てない
    →その場限りで処理を考える

•   他の頂点情報を参照できない
    →座標やUV値から処理してみる?

•   他のピクセル情報を参照できない
    →引数として必要なテクスチャを用意する
    (フレームバッファを処理する場合など)
Attribute, Uniform
• AttributeはVertexShader用パラメータ
 ★ 頂点数分のデータが必要
   (頂点座標、法線、頂点カラーなど)


• Uniformは双方へのパラメータ
 ★ ピクセル分のデータではない
 ★ 全ピクセルに対して同じデータ
   (テクスチャ、演算用の値など)
シェーダーの実例
Gouraud, Phong




•   頂点毎に陰影を計算して色付け(Gouraud,
    Lambertとも言う)

•   ピクセル毎に陰影を計算して色付け(Phong)
Gouraud

• Vertex Shaderでライトベクトルと頂点法
 線ベクトルの内積を取って、光源に頂
 点が向いているかどうかの度合いを計
 算し、頂点の輝度とする

• 光源に向いているほど明るくする
Phong

• Gouraudと同様の計算をFragment Shader
 内で行うことで、ピクセル毎に光源に
 向いているかを計算する

• これによって頂点が荒くてもなめらか
 なシェーディングが計算できる
Bump mapping




•   法線情報をもたせたテクスチャを参照して、
    ピクセル毎に凹凸計算をして陰影をつける

•   Normal mappingとも言う、法線マップとか
Bump mapping

• テクスチャに面法線からの相対法線情
 報を描画したものを用意し、Fragment
 Shader内でピクセル毎に光源計算を行う

• これによって頂点以外の細かい凹凸表
 現ができるというもの
Toon shading




•   法線と光線の角度を元に陰影マップから陰影
    色をピックアップすることで陰影に特徴を出
    したものにしている(他の手法もある)
Toon shading



•   Vertex, Fragmentのどちらでもいいが、光源計
    算後の輝度をそのまま使わずに、段階的に変
    化するように加工する

•   主に使われるものは、輝度をテクスチャのUV
    値として採用して、輝度変化を別のテクス
    チャに描いておくもの
Specular mask




•   スペキュラーによる艶部分を減じるためにマ
    スクテクスチャを用意して、そのテクスチャ
    の値を考慮して艶の出具合を調整するもの
Specular mask

•   Fragment Shader内で、マスクテクスチャを参
    照して、加算するスペキュラー値を調整して
    やることで、それぞれの箇所のスペキュラー
    の効果の強度が変化するようにする

•   たとえばマスクテクスチャの輝度をスペキュ
    ラー値に乗算するようにすれば、マスクテク
    スチャが暗い部分にはスペキュラーによる艶
    は当たらないことになる
その他



    Rim Light      Half Lambert




Texture Blending      Light Bloom
OpenGL ES 2.0 での流れ
 glCreateShader()

 glShaderSource()    glCreateProgram()

 glCompileShader()   glAttachShader()

                      glLinkProgram()



 破棄してOK

                                あとで使う
                      glUseProgram()
1. VertexShader, FragmentShaderのシェーダーオブ
  ジェクトを作成 glCreateShader()

2. シェーダーオブジェクトにプログラムを読み
  込む glShaderSource()

3. プログラムをコンパイル glCompileShader()
4. プログラムオブジェクトを作成
 glCreateProgram()

5. プログラムオブジェクトにシェーダーを登録
 glAttachShader() ※ここでシェーダーオブジェクトは破棄して良い

6. シェーダープログラムをリンク glLinkProgram()
 ※ここまで事前処理で良い




7. シェーダープログラムを適用 glUseProgram()
cocos2dではどういう
流れになっているか
cocos2d 2.0 での流れ
self.shaderProgram = [[CCShaderCache sharedShaderCache]
                      programForKey:kCCShader_PositionTextureColor];


            glCreateShader()

            glShaderSource()                     glCreateProgram()

            glCompileShader()                    glAttachShader()

                                                  glLinkProgram()


                  破棄                                           初期化メソッド



                            CC_NODE_DRAW_SETUP();

                                glUseProgram()             drawメソッド
初期化メソッドの流れ
self.shaderProgram = [[CCShaderCache sharedShaderCache]
                      programForKey:kCCShader_PositionTextureColor];

 [CCShaderCache sharedShaderCache]

  [[CCShaderCache alloc] init];

    [self loadDefaultShaders];

      [[CCGLProgram alloc]
      initWithVertexShaderByteArray:fragmentShaderByteArray:];

             glCreateShader()              glCreateProgram()
             glShaderSource()               glAttachShader()
            glCompileShader()           Attribute, Uniformの準備
                                            glLinkProgram()
                       破棄
初期化メソッドの流れ
1. [CCShaderCache sharedShaderCache]が呼ばれる

2. その中で[[CCShaderCache alloc] init];が呼ばれる

3. その中で[self loadDefaultShaders];が呼ばれる

4. その中で[[CCGLProgram alloc]
  initWithVertexShaderByteArray:
  fragmentShaderByteArray: ];が呼ばれシェーダーが
  コンパイルされ、配列に保持
5. さらにシェーダーのAttribute, Uniform指定用の
  識別子を準備 (updateUniformsメソッド)


6. シェーダーをリンクし、4で生成されたプログ
  ラムは破棄


7. 4で保持されたシェーダーをCCNodeで使用する
  ために識別子がCCNode側のself.shaderProgram
  を渡す
drawメソッドの流れ
CC_NODE_DRAW_SETUP();


                glUseProgram()

                  glUniform*()         動的Uniformを更新



               glBindTexture2d()       使用するテクスチャを指定

             glVertexAttribPointer()   Attributeメモリ座標を指定

                glDrawArrays()         ポリゴン描画
drawメソッドの流れ
1. CC_NODE_DRAW_SETUP()マクロ内で
 glUseProgram()を実行、シェーダーが使用可能に
 なる

2. 動的Uniformパラメータを更新

3. 使用テクスチャをBind

4. Attributeパラメータのメモリ座標をセット

5. ポリゴンを描画
cocos2dでオリジナル
シェーダーを使うには?
初期化メソッドを変更
self.shaderProgram = [[CCShaderCache sharedShaderCache]
                      programForKey:kCCShader_PositionTextureColor];

 [CCShaderCache sharedShaderCache]
                                            これを自前にして自前の
  [[CCShaderCache alloc] init];
                                              シェーダーを登録する
    [self loadDefaultShaders];

      [[CCGLProgram alloc]
      initWithVertexShaderByteArray:fragmentShaderByteArray:];

             glCreateShader()              glCreateProgram()
             glShaderSource()               glAttachShader()
            glCompileShader()           Attribute, Uniformの準備
                                            glLinkProgram()
                       破棄
drawメソッドの変更
CC_NODE_DRAW_SETUP();


                glUseProgram()

                  glUniform*()         動的Uniformを更新



                  glUniform*()         自前の動的Uniformを更新を追加
この辺に自
前パラメー          glBindTexture2d()       使用するテクスチャを指定、自前のも追加
タを追加
             glVertexAttribPointer()   Attributeメモリ座標を指定、自前のも追加

                glDrawArrays()         ポリゴン描画
というわけで
実際にやってみた
法線マップを使用した
平行光源ライティングの例
法線マップを使用した
点光源ライティングの例
Unityでシェーダーを
使うにはどうするか?
ShaderLab
ShaderLab その1

• UnityはShaderLabという言語でシェー
 ダーを記述する

• ShaderLabは総合的な描画コントロール
 を行うためのもの
 (CgFXみたいな感じ)
ShaderLab その2

• ShaderLab内にVertexShader,
  FragmentShaderを記述することができる

• その他Alpha Blend、描画順序、ZTest等
  についても設定できる

• LOD値に応じて使用シェーダーを分け
  ることができる、複数パスも記述可
ShaderLab その3

• Unity上のパラメータとシェーダー側で
 使用するパラメータを結びつけること
 ができる

• Unity上のパラメータ編集のためのUIを
 構築できる
.shaderファイルのサンプル
パラメータの定義
Tag設定




•   Queue : 描画順序 cf. “Geometry+1”など
•   RenderType : 描画方法
•   LightMode : 指定すればライト値が更新される
•   これ以外にも機能はあります
Pass設定




•   Lighting : ライティングの有無
•   Cull : カリング指定
•   ZWrite : Zバッファへの描画有無
•   Blend : アルファブレンド指定
•   これ以外にも機能はあります
Vertex, Fragment Func
      (Cg, GLSL)




•   CGPROGRAM∼ENDCG内にCgが書ける
•   GLSLPROGRAM∼ENDGLSL内にGLSLが書ける
•   #pragma指定でshading関数名を定義
Fallback指定


•   Fallbackを指定することで定義したシェーダー
    が使用できない環境の場合に、Fallbackで指示
    されたシェーダーを代わりに使用して描画す
    ることができる

•   ここに特殊なシェーダーを指定すると検索し
    きれずにエラーになってしまうこともあるの
    で要注意
Surface Shader
Surface Shaderとは

• Surface ShaderとはShaderLabの一番美味
 しいところですが、ピーキーなシェー
 ダーを書こうと思う場合には不利かも

• Vertex, Fragmentという分け方ではな
 く、質感と光源処理に分けてシェー
 ダーを記述する機能
Surface Shaderのサンプル
短いです
Surface Func指定

•   surface [関数名] [ライティング名]

•   定義済みのライティングモデルが使用できる
    cf. Lambertなど
•   独自のライティング関数も定義可能

•   finalcolorは最終的に色変更する関数定義

•   vertexシェーダー関数も定義可能
ShaderLabの参考web
•   Surfaceシェーダーの参考web
    http://unity3d.com/support/documentation/
    Components/SL-SurfaceShaderExamples.html

•   Cg, GLSLを使用する場合の参考
    http://unity3d.com/support/documentation/
    Components/SL-ShaderPrograms.html

•   ShaderLab全体の参考web
    http://unity3d.com/support/documentation/
    Components/SL-Reference.html
ShaderLabの参考web 2


•   MMD for Unity
    http://sourceforge.jp/projects/mmd-for-unity/devel/

•   これに使われているシェーダーは機能と実装
    がどうなっているかの取っ掛かりに良いと思
    います。僕はこれで覚えました。
ShaderLabのTIPS
ライトカラーを参照するには


Tag {
  "LightMode" = "Vertex"
}


これを記述した上で、Vertex, Fragment関数内で
unity_LightColor[0].rgbをつかってライト値を参照できます。
ライトベクトルはunity_LightPosition[0].rgbでいけます。
Surface Shaderを使用する場合は特に特殊な指定をしなくても
そのあたりの値は引数からアクセスできます。
実機でGLSLコンパイルが通らない件

struct v2f {

  float4 pos : SV_POSITION;
  fixed2 uv : TEXCOORD0; <これとか
  fixed custom : TEXCOORD1; <これとか
  half4 custom2; <これはだめ
}

コロン後にセマンティック指定をしてやらないと実機でGLSLがコンパイル
される際にエラーになる可能性があります。
Unityのバグっぽいですけど、セマンティックが無いと同名の変数が定義さ
れてしまうので、正しいコードとして解釈されないようです。
実機だと描画結果が違う場合の対処

• 高速化のためにfixed変数を使いまくって
 いると、ものによっては演算精度が荒く
 なりすぎて求めている結果にならない

• 個別にhalfへ置き換えてなるべくfixedを残
 した形でコードを書くのが良い

• Mac等では現象が確認できないのが大変
GLSLの参考資料


•   床井研究室
    http://marina.sys.wakayama-u.ac.jp/~tokoi/?
    date=20051006

•   今回のソース(cocos2d)
    http://xionchannel.no-ip.org/
    cocos2d_shaderTest.zip

More Related Content

シェーダーしよっ☆ Let's play shaders!

Editor's Notes