シェーダー
目次
はじめに
今回はhgimg4のシェーダーに手を出してみました。 シェーディング言語、前からやってみたいと思ってたんですよね。
シェーダーと言うのは、物体がどのように画面上に表示されるかを記述したプログラムです。 シェーダー次第で物体の色や質感が変わってきます。 このシェーダーと呼ばれるプログラムは「シェーディング言語(シェーダー言語)」で記述されていて、レンダリングする際にGPUで実行されます。
シェーディング言語は、言語の分類を示すもので具体的な言語名を持つものは複数あります。例えばDirectXのHSLS、OpenGLのGLSL、PlayStationのPSSLなどがあります。 いずれもUnityで使えるらしいですね。
hgimg4では、OpenGLシェーディング言語「GLSL」が採用されています。 hgimg4はGameplay3Dをベースに、Gameplay3DはOpenGLをベースに作られているためこの選択になったのだろうと思います。 GLSLの言語文法はC言語がベースとなっていて、C言語がわかる人ならあまり違和感なく読むことができます。 ポインタ型が存在しないので簡単そうです。 といはいえHSP3だけしか使ったことがないユーザーには見慣れない書き方があるので、ハードルが高そうですね。
C言語に似ているとは言え、初めて触れる言語なので基本的な部分を調べてみました。 なお、ここには調査した際のメモを記載しています。間違った解釈を記載している可能性があります。 情報を鵜呑みにせず、リンク先や実際にプログラムを動かしてみてご確認ください。
参考になりそうな資料
関連する資料の場所を調べてみました。ざっと目を通すとGLSLの雰囲気がわかってくると思います。
GamePlay
- GamePlay
- GamePlay 2D/3Dのサイト
- GamePlay Wiki
- Gameplay3Dのwiki。基本的な説明やチュートリアルなど。
- gameplay3d GitHub
- gameplay3dのGitHub。ダウンロードはこちらから。
- gameplay3d - Game Framework
- gameplay3dのクラスのリファレンス。
OpenGL
- シェーディング言語 - Wikipedia
- とりあえず「シェーディング言語」についてWikipedia。
- OpenGLシェーディング言語 - Wikipedia
- 日本語版Wikipediaは歴史や概要に重点が置かれているが、英語版は言語の仕様について説明がされているので学習者向き。
- OpenGL Wiki
- 用語の説明などが詳細。
- クロノス・グループ、クロノス・グループ(日本)
- OpenGLなどの開発元。日本語サイトはニュースと本家へのリンクだけっぽい。本家だとマニュアルとか置いてるようです。(但し探すのが大変)
GLSL
- GLSL - Wikipedia
- GLSLに付いて、基本的な文法の説明やシェーダーの例も記載されています。
- WebGL 1.0 API Quick Reference Card
- GLSL公式チートシート。英語なので、ダウンロードしたPDFをファイルごとGoogle翻訳に突っ込んで置くと読みやすくなります。
- GLSL (OpenGL ES2.0)リファレンス
- データ型、スウィズル演算子、ビルトイン関数、ビルトイン変数と定数
GLSL 解説サイト
- 第一回 WebGLスクール 「WebGLの概念」
- wgld.orgの管理人であるdoxasさん主催のWebGLスクールの内容。全12回の講座。
- [連載]やってみれば超簡単! WebGL と GLSL で始める、はじめてのシェーダコーディング(1)
- 全10回の講座。サンプルを自分で専用のエディタを使って実行しながら確認できる。GLSL Editor
- WebGL 学習に役立つサイト 2016 年度版
- 人によってはここ見とけば十分なのかもしれない。
- GLSL で暖を取るための準備をしよう! GLSL お役立ちマニュアル
- Webでの利用例が紹介されています。
- GLSL Shaderを扱う際の基本的な用語のメモ
- 用語について。
- 7日間でマスターするUnityシェーダ入門
- Unityで学ぶシェーダー。
- wgld.org WebGL contents
- 前述したサイトは入門や概要的な位置づけ、部分的に掘り下げたものが多いですが、ここは広範囲にわたって詳細に解説してくれているので、かゆいところに手が届きそう。
バーテックスシェーダーとフラグメントシェーダー
hgimg4で任意に指定できるシェーダーは、2種類で構成されています。 バーテックスシェーダーとフラグメントシェーダーです。 (.materialファイルについては今回触れません。)
- バーテックスシェーダー(*.vert)
- 頂点シェーダーとも言われるもの。 立体物を構成する面の頂点の位置を計算する。 上手に使うと、3Dモデルの頂点を移動したりすることもできます。
- フラグメントシェーダー(*.frag)
- テクスチャや光源など「様々な情報を元に実際の色を決定する」もの。 上手に使うと、テクスチャを移動したりできます。
3Dモデルが画面上に表示されるまでのおおまかな流れは次の図のようになります。 3Dモデルデータは、各シェーダーに定義された計算を経て画面上に表示されています。
3Dモデル → Vertex → Frag → 出力 頂点 色
頂点の情報が無いと色は決められない、色が決まらないと画面にも出せない、なんとなく納得できますね。
なおこれらをhgimg4でシェーダーを指定する場合は、gpusershader
命令を使用します。
変数
文法はC言語に似ていると言っておきながら、変数ではvec3
やビルトイン変数、uniform
など独特のものがいくつかあるのでなれるまで大変です。
代表的なものだけを簡単に整理してみました。
記述・用語 | 意味 |
---|---|
const | 定数 例: const vec3 POSITION = vec3(0.0, 0.0, 0.0); |
#define | マクロ 例: #define white vec4(1.0) |
attribute | アプリケーションから送らてきたデータを参照するための変数。
頂点ごとに異なる情報を渡すのに使う。 例: attribute vec4 color; |
uniform | アプリケーションから値を受け取るための変数に使用する。
全ての頂点に対して一律に処理される情報を渡す際に使う。
HSP3からgpmatprm 系命令で値を渡すことができます。
サンプルでは、変数名の頭に「u_」がついている。(u_xxxxxx)例: uniform mat4 mvpMatrix; |
varying | シェーダ間(頂点シェーダとフラグメントシェーダ)でデータをやり取りするための変数
サンプルでは、変数名の頭に「v_」がついている。(v_xxxxxx) 例: varying vec4 vColor; |
精度修飾子 | どのくらいの精度でデータを扱うかと指定する。 低 < low < mediump < highp < 高 とりあえず、これはあまり気にする必要はなさそうです。 |
precision宣言 | 精度修飾子を使用するために頭につける宣言です。 例: precision mediump float; |
この他にも、宣言無しで使用できる組み込み変数「ビルトイン変数」というものもあります。 ビルトイン変数は接頭辞がついていて「gl_xxxxxxxx」のような変数名です。 シェーダーの外で宣言されたグローバル変数のような感じのものです。 代表例を書き出してみます。
gl_Position | vec4 | [入力] | 頂点シェーダで、頂点の座標を代入するための変数(必須) |
gl_PointSize | float | [入力] | 頂点シェーダで描く点の大きさを指定するのに使う |
gl_FragColor | vec4 | [入力] | フラグメントシェーダで色を出力するための変数 |
gl_FragCoord | vec4 | [出力] | フラグメントシェーダで処理ピクセルの座標を読み取るのに使う |
他にもありますが、上記のどれかのリンク先を参照してください。
付属のシェーダー
sample\hgimg4\res\shaders フォルダには、たくさんのシェーダー(vert/frag)が保存されいます。 シェーダーの動作結果は、サンプルから確認することができます。
customshader.hsp
tamane3.hsp
posteffect.hsp(mod_posteffect.asから呼び出し)
light_test5eff.hsp(mod_posteffect.asから呼び出し)
どのシェーダーがどんな機能があるのかわかりにくいので、表にしてみました。 サンプルにないものについては、ファイル名から組み合わせを予想して書いています。 すぐに調査が面倒になってきたので空欄ご容赦を。
名称 | バーテックスシェーダー | フラグメントシェーダー |
---|---|---|
ぼかしフィルター | sprite.vert | p_blur.frag |
ガウスぼかしフィルター(高品質) | p_blur2.vert | p_blur2.frag |
グローフィルター | sprite.vert | p_bright.frag |
コントラストフィルター | sprite.vert | p_contrast.frag |
ブラウン管フィルター | sprite.vert | p_crtmonitor.frag |
ブラウン管? | sprite.vert | p_crtmonitor2.frag |
カットオフフィルター | sprite.vert | p_cutoff.frag |
白黒フィルター | sprite.vert | p_grayscale.frag |
モザイクフィルター | sprite.vert | p_mosaic.frag |
古いフィルムフィルター | sprite.vert | p_oldfilm.frag |
セピアフィルター | sprite.vert | p_sepia.frag |
輪郭抽出フィルター | sprite.vert | p_sobel.frag |
colored.vert ├skinning-none.vert └lighting.vert | colored.frag └lighting.frag | |
font.vert | font.frag | |
simpletex.vert | simpletex.frag | |
skybox.vert | skybox.frag | |
sprite.vert | sprite.frag | |
spritecol.vert | spritecol.frag | |
terrain.vert └lighting.vert | terrain.frag └lighting.frag | |
textured.vert └lighting.vert | textured.frag └lighting.frag | |
テクスチャーを白黒に変換 | textured.vert | textured_gray.frag └lighting.frag |
ファイル名が p_….vert/.frag となっているものについてはhgimg4で準備されたシェーダーで、その他は gameplay3d 付属のものっぽいです。 機能は調べきれていません。(.materialファイルについては今回は触れません。)
フラグメントシェーダー
少しわかってきたので、試しにフラグメントシェーダーを読んでみます。
sprite.frag
#if defined(OPENGL_ES) || defined(GL_ES)
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#endif
///////////////////////////////////////////////////////////
// Uniforms
uniform sampler2D u_texture;
///////////////////////////////////////////////////////////
// Varyings
varying vec2 v_texCoord;
varying vec4 v_color;
void main()
{
gl_FragColor = v_color * texture2D(u_texture, v_texCoord);
}
フラグメントシェーダーの最初に書かれていることが多いこの部分について見てみます。
#if defined(OPENGL_ES) || defined(GL_ES)
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#endif
実行環境がOpenGL ESだった場合のときのための処理です。 OpenGL ESは、スマホなど組込みシステムで使用されている3D-CG用のAPIです。 デバイスによっては高精度highpのデータに対応していないことがあるため、環境が変わっても動作するように最初にこのように記述されているようです。
フラグメントシェーダーでhighpがサポートされている場合は、floatはhighp(高精度)。 そうでない場合は、floatはmediump(中精度)の精度を使用します。
低スペックスマホでの動作を考えなくていいなら書かなくても大丈夫ですね。
// Uniforms
…
// Varyings
…
v_~は、バーテックスシェーダーから引き継いだ変数。
u_~は、HSP3側から入力された変数。
void main()
{
…
}
ここがメインプログラム。実行されるのはこの部分です。 例では使用していませんが、関数を使う場合は、この行より上に書く必要があります。
gl_FragColor = v_color * texture2D(u_texture, v_texCoord);
gl_FragColorに色情報を書き込んでいます。 テクスチャルックアップ関数 texture2D にテクスチャ(u_texture)とテクスチャ座標(v_texCoord)を与えて取り出した色の情報と、頂点の色情報とをかけ合わせて出力する色を決めているようです。
u_textureはシェーダー内で宣言されていますが、値が代入されていません。 値の代入はhgimg4が勝手にやってくれているらしいので何もせず使用することができます。 実はこのような「定義済値」がいくつも出てくるのですが、すべてを説明している資料が見つからずサンプルの解読が捗っていません。 どこかに良い資料無いものですかね。
さて次は…このサンプル以外でよく見かけるヘッダ部分。
#ifndef DIRECTIONAL_LIGHT_COUNT
#define DIRECTIONAL_LIGHT_COUNT 0
#endif
#ifndef SPOT_LIGHT_COUNT
#define SPOT_LIGHT_COUNT 0
#endif
#ifndef POINT_LIGHT_COUNT
#define POINT_LIGHT_COUNT 0
#endif
#if (DIRECTIONAL_LIGHT_COUNT > 0) || (POINT_LIGHT_COUNT > 0) || (SPOT_LIGHT_COUNT > 0)
#define LIGHTING
#endif
ディクショナルライト、スポットライト、ポイントライトの個数が定義されていない場合は、0個として定義。 という感じで、ライト関係の宣言みたいですね。
定義済みの変数
u_worldViewProjectionMatrix
とかどこにも説明が見つけられなくて困りました。
(マテリアルファイル側でWORLD_VIEW_PROJECTION_MATRIX
を代入してあるっぽいのですが、そもそもWORLD_VIEW_PROJECTION_MATRIX
の資料が見つからない。今回は.materialファイルについては触れません。)
最終手段として、別のものを説明する資料から引っ張ってくることにしました。
よく使われる変数名は同じ意味で利用されるだろうというわけです。
正しくない記述があるかもしれませんが、メモ書きということでご容赦を。
参考資料
https://www.materialx.org/docs/api/_hw_shader_generator_8h_source.html
Vertex input variables :
変数名 | 型 | 説明 |
---|---|---|
i_position | vec3 | オブジェクト空間での頂点の位置 |
i_normal | vec3 | オブジェクト空間における頂点の法線 |
i_tangent | vec3 | オブジェクト空間における頂点の接線 |
i_bitangent | vec3 | オブジェクト空間における頂点の2接線 |
i_texcoord_N | vec2 | N:番目のUVセットの頂点テクスチャ座標 |
i_color_N | vec4 | N番目のカラーセット(RGBA)の頂点カラー |
Uniform variables :
変数名 | 型 | 説明 |
---|---|---|
u_worldMatrix | mat4 | ワールド変換ワールド変換 |
u_worldInverseMatrix | mat4 | ワールド変換、反転ワールド変換、反 |
u_worldTransposeMatrix | mat4 | ワールド変換、転置ワールド変換、転 |
u_worldInverseTransposeMatrix | mat4 | ワールド変換、反転および転置ワールド変換、反 |
u_viewMatrix | mat4 | ビュー変換ビュー変換 |
u_viewInverseMatrix | mat4 | ビュー変換、反転ビュー変換、反転 |
u_viewTransposeMatrix | mat4 | ビュー変換、転置ビュー変換、転置 |
u_viewInverseTransposeMatrix | mat4 | ビュー変換、反転および転置ビュー変換、反転 |
u_projectionMatrix | mat4 | 投影変換投影変換 |
u_projectionInverseMatrix | mat4 | 投影変換、反転投影変換、反転 |
u_projectionTransposeMatrix | mat4 | 投影変換、転置投影変換、転置 |
u_projectionInverseTransposeMatrix | mat4 | 投影変換、反転および転置投影変換、反転お |
u_worldViewMatrix | mat4 | ワールドビュー変換ワールドビュー変 |
u_viewProjectionMatrix | mat4 | ビュー投影変換ビュー投影変換 |
u_worldViewProjectionMatrix | mat4 | ワールドビュー投影変換ワールドビュー投 |
u_viewPosition | vec3 | 視点(カメラ)のワールド空間座標視点(カメラ)の |
u_viewDirection | vec3 | 視点(カメラ)のワールド空間方向視点(カメラ)の |
u_frame | float | ホストアプリケーションによって定義された現在のフレーム番号ホストアプリケー |
u_time | float | 現在の時刻(秒単位)現在の時刻(秒単 |
u_geomprop_<name> | <type> | 指定された<type>のプロパティで、<name>はジオメトリ上の変数名。指定された<ty |
u_numActiveLightSources | int | 現在アクティブな光源の数。 シェーダーでは、これは光源の最大許容数に対してクランプされていることに注意してください。 最大数は、生成オプションGenOptions.hwMaxActiveLightSourcesによって現在アクティブな |
u_lightData[] | struct | アクティブな光源用のパラメータを保持する LightData 構造体の配列。LightData 構造体は、バウンド ライト シェーダーの要件に応じて動的に構築されます。アクティブな光源 |
u_envMatrix | mat4 | 環境の回転行列。環境の回転行列。 |
u_envIrradiance | sampler2D | ディフューズ光 (アンビエントライト・環境光)に使用されるテクスチャのサンプラーです。ディフューズ光 ( |
u_envRadiance | sampler2D | スペキュラ光(鏡面光)に使用されるテクスチャのサンプラーです。スペキュラ光(鏡 |
u_envRadianceMips | int | スペキュラー環境テクスチャで使用されるミップマップの数。 スペキュラー環境 |
u_envRadianceSamples | int | スペキュラー環境照明にFiltered Importance Sampling(フィルタリングされた重要なサンプリング)を使用する場合に使用するサンプル。スペキュラー環境 |
まとめ
以上、仕入れた情報を整理したメモ書きでした。 検証不十分な情報なので、あてにしないよう十分ご注意ください。 GLSLはこれから学習していきます!(多分!)
もう少し整理してから、もう少しちゃんと理解してから公開しようと思ったんですが、情報を整理できなくなってきたので一旦書き出すことにしました。
列挙子
追記。(2022/07/04)
GLSLでシェーダーを書く際に使用できる列挙子の情報を見つけたので掲載。
マテリアルパラメータ用の組み込みの即時更新定数。いや定数ではないか。列挙子?
WORLD_MATRIX | mat4 | ノードのワールドマトリックスをバインドします。 |
VIEW_MATRIX | mat4 | ノードのシーンのアクティブなカメラのビュー変換行列をバインドします。 |
PROJECTION_MATRIX | mat4 | ノードのシーンのアクティブなカメラの投影行列をバインドします。 |
WORLD_VIEW_MATRIX | mat4 | ノードのWorldView変換行列をバインドします。 |
VIEW_PROJECTION_MATRIX | mat4 | ノードのシーンのアクティブなカメラのViewProjection変換行列をバインドします。 |
WORLD_VIEW_PROJECTION_MATRIX | mat4 | ノードのWorldViewProjection変換行列をバインドします。 |
INVERSE_TRANSPOSE_WORLD_MATRIX | mat4 | ノードのInverseTransposeWorld変換行列をバインドします。 |
INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX | mat4 | ノードのInverseTransposeWorldView変換行列をバインドします。 |
CAMERA_WORLD_POSITION | vec3 | ノードのシーンのアクティブなカメラの位置(Vector3)をバインドします。 |
CAMERA_VIEW_POSITION | vec3 | ノードのシーンのアクティブなカメラのビュースペース位置(Vector3)をバインドします。 |
MATRIX_PALETTE | ? | ノードのモデルにアタッチされたMeshSkinのマトリックスパレットをバインドします。 |
SCENE_AMBIENT_COLOR | vec3 | 現在のシーンのアンビエントカラー(Vector3)をバインドします。 |
変換行列
変換行列とにうのは、頂点の座標データに掛けて座標変換に使うやつの事です。 調べてみると整理された情報が見つかりました。(HLSLですが)
World | ワールド変換行列 | モデル座標系→ワールド座標系 |
View | ビュー変換行列 | ワールド座標系→ビュー座標系 |
Projection(射影) | プロジェクション変換行列 | ビュー座標系→クリップ座標系 |
WorldView | ワールド変換行列×ビュー変換行列 | モデル座標系→ワールド座標系→ビュー座標系 |
ViewProjection | ビュー変換行列×プロジェクション変換行列 | ワールド座標系→ビュー座標系→クリップ座標系 |
WorldViewProjection | ワールド変換行列×ビュー変換行列×プロジェクション変換行列 | モデル座標系→ワールド座標系→ビュー座標系→クリップ座標系 |
InverseTransposeWorld | ワールド変換行列の逆行列の転置行列 | |
InverseTransposeWorldView | ワールドビュー変換行列の逆行列の転置行列 | |
Inverse~ | 逆行列 変換前の状態に戻す行列 | |
Transpose~ | 転置行列 | |
InverseTranspose~ | 逆行列の転置行列 |
これらの変換を理解するには、座標系や空間に関する言葉の意味を知っている必要があります。
モデル座標系 | 3Dモデルのローカル座標を原点とした座標系のこと。 |
ワールド座標系 | 3Dモデルを配置したりする際に使っているような、原点中心の座標のこと。 |
ビュー座標系(カメラ座標系) | カメラを中心とした座標系。カメラとPC画面は通常は固定されているのでほぼ同じようなもの。奥行きがあるのが違いか。hgimg4の場合、カメラが向いている方向が-Z方向、右手方向が+X方向、上方向が+Y方向です。 |
プロジェクション座標系 | クリップ座標系。クリップ空間にある物体だけがカメラに写ったものとして表示されます。ここで平行投影(遠近感が出ない投影方法)や透視投影変換(遠近感が出る投影方法)が決定され、またそのパラメータが反映されます。 |
クリップ座標系 | クリップ空間の座標系。プロジェクション座標系。 |
スクリーン座標系 | カメラの前にある仮想の平面で…(検索すると絵付きで解説しているところが見つかるので自分で調べてください。)…要するにPC画面の座標系です。 |
materialファイルで使用されている定数
hgimg4のサンプルに含まれるmaterialファイルから定数というか、大文字+アンダースコアを抽出してみました。 サンプルを読み解く際に必要になりそうなのでまとめ。
まずは前述したものです。
WORLD_VIEW_PROJECTION_MATRIX INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX CAMERA_WORLD_POSITION MATRIX_PALETTE
どうやらテクスチャの品質に関する指定らしいもの。リンク先参照。
テクスチャの品質を指定する
https://wgld.org/d/webgl/w028.html
LINEAR LINEAR_MIPMAP_LINEAR
資料を見つけることが出来なかったので、そのうちなんとかしたい一覧です。 名前からなんとなく想像できそうな感じもします。
CLAMP DIRECTIONAL_LIGHT_COUNT REPEAT SKINNING SKINNING_JOINT_COUNT SPECULAR VERTEX_COLOR
materialファイルで使用されているuniform変数
materialファイル内でuniform変数に代入が行われています。 おそらくここで代入された変数は、バーテックスシェーダーやフラグメントシェーダーで利用できるようになるのだと思います。 どんな変数が使用されているのか調べてみました。
samplerオブジェクト。テクスチャをどのようにサンプリングするかを定義しているようです。
u_diffuseTexture
数値を値を直接代入しているもの。
u_ambientColor u_diffuseColor u_specularExponent
列挙子を代入しているもの。 これらの命名規則は、大文字のスネークケースからキャメルケースに変えて、頭に「u_」を付けるようになっているようですね。
u_cameraPosition | CAMERA_WORLD_POSITION |
u_inverseTransposeWorldViewMatrix | INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX |
u_matrixPalette | MATRIX_PALETTE |
u_worldViewProjectionMatrix | WORLD_VIEW_PROJECTION_MATRIX |
追記まとめ
materialファイルまで捜索範囲を広げて資料を集めてみました。全てを把握できたわけではありませんが、なんとなく見えてきた気がします。
しかし、適当に資料眺めれば分かるかなといろいろと調べていたのですが、そろそろ真面目に手を動かさないと前に進まないみたいです。