シェーダ(Shader)




shader の対応について

DESC glview によれば ... Vertex Program extension found.( DX8 Vertex Shader ) Fragment Program extension found.( DX9 Pixel Shader ) No OpenGL Shading Language support( GLSL は不可 )


基本的なれ流

SAMPLE https://www.dropbox.com/s/de2uiu4xmakqzv6/simple_cg.exe( フラグメントシェーダ )
# include "Cg/cg.h" # include "Cg/cgGL.h" # pragma comment (lib, "cg.lib") # pragma comment (lib, "cgGL.lib") App::init() { CGprofile pfV = CG_PROFILE_ARBVP1; CGprofile pfF = CG_PROFILE_ARBFP1; CGprogram shdV, shdF; CGcontext ctx = cgCreateContext(); // Shader は使えるか確認 cgGLIsProfileSupported(); // shader code を読んで Program を作成 shdV = cgCreateProgramFromFile( ctx, CG_SOURCE, ".\\vertex.cg", pfV, NULL, NULL); shdF = cgCreateProgramFromFile( ctx, CG_SOURCE, ".\\fragment.cg", pfF, NULL, NULL); // または文字列バッファから作成 shdF = cgCreateProgram( ctx, CG_SOURCE, "...", pfF, NULL, NULL); // あげる cgGLLoadProgram(shdV); cgGLLoadProgram(shdF); // Shader 有効化 cgGLEnableProfile( pfV ); cgGLEnableProfile( pfF ); cgGLBindProgram( shdV ); cgGLBindProgram( shdF ); } App::draw() { // Shader の指定 // Texture Object の指定と同じ cgGLBindProgram(); // 描画 draw(); }
フラグメントの色を単色にするだけのフラグメントシェーダ
float4 main() : COLOR { return float4( 0, 0, 1, 1 ); }



cgGLEnableProfile

SYNTAX void cgGLEnableProfile( CGprofile profile ) DESC プロファイルを有効化する。 有効化した後の OpenGL の描画コールではシェーダが利用される。


cgGLDisableProfile

SYNTAX void cgGLDisableProfile( CGprofile profile )
// 固定機能の描画に戻す。 cgGLDisableProfile( pfV ); cgGLDisableProfile( pfF ); // 描画 drawCube();



頂点シェーダ

SAMPLE http://ooo.iiyudana.net/bin/gl/vertex_cg.exe( 頂点シェーダ ) とりあえず、固定機能と同じように描画してみる。
App::onDraw() { // 変換行列を設定。 glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glFrustum(); // シェーダから変数 "mvp" のハンドルを取得する。 CGparameter param = cgGetNamedParameter( shdV, "mvp"); // OpenGL のステートである ModelViewProjection 行列をセットする。 // これをコールした時点の ModelViewProjection 行列がセットされるため // 先に行列を設定しておく必要がある。 cgGLSetStateMatrixParameter( param, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY ); // この設定でプリミティブを描画 drawCube(); }
頂点シェーダ
struct app { float4 pos : POSITION; // 頂点データ }; struct v2f { float4 pos : POSITION; // 頂点データ }; v2f main( app IN, uniform float4x4 mvp ) { v2f OUT; // 頂点を変換してフラグメントシェーダへ渡す。 OUT.pos = mul( mvp, IN.pos ); return OUT; }
頂点シェーダ側では任意の頂点アトリビュートをフラグメントシェーダへレジスタ経由で渡す ことができる。 法線の値をフラグメントの色に指定してみる。
struct app { float4 pos : POSITION; // 頂点データ float4 nrm : NORMAL; // 法線データ }; struct v2f { float4 pos : POSITION; // 頂点データ float4 nrm : TEXCOORD0; // フラグメントシェーダへ法線の値を渡す。 }; v2f main( app IN, uniform float4x4 mvp ) { v2f OUT; // 頂点を変換してフラグメントシェーダへ渡す。 OUT.pos = mul( mvp, IN.pos ); OUT.nrm = IN.nrm; return OUT; }
フラグメントシェーダでは頂点シェーダから渡された法線の値を出力する。
float4 main( v2f IN ) : COLOR { return float4( 0.5f * IN.nrm.xyz + 0.5f, 1 ); }



フラグメントシェーダからテクスチャをサンプルする

SAMPLE https://www.dropbox.com/s/a6uy6limgatqtlb/texture_cg.exe( シェーダへテクスチャをわたす ) サンプラー変数のハンドルを取得して、 利用したいテクスチャオブジェクトの id を指定する。
App::onInit() { glGenTextures( 1, &id ); } App::onDraw() { CGparameter p = cgGetNamedParameter( shdF, "decal"); cgGLSetTextureParameter( p, id ); cgGLEnableTextureParameter( p ); }
フラグメントシェーダではサンプラー型の変数を宣言しておく
float4 main( v2f IN, uniform sampler2D decal ) : COLOR { return tex2D( decal, IN.tc.xy ); }



cgGLSetTextureParameter

SYNTAX void cgGLSetTextureParameter( CGparameter param, GLuint texobj ); DESC param で指定したテクスチャユニットにテクスチャオブジェクトをバインドする。 glBindTexture() に相当する処理。


cgGLEnableTextureParameter

SYNTAX cgGLEnableTextureParameter( CGparameter ) DESC 指定したテクスチャパラメータ(ユニット == サンプラー)を有効化する。 この処理は, テクスチャを利用して描画する前にコールしておく必要がある。 とあったが, あってもなくても動く? テクスチャの適用を解除するときは cgGLDisableTextureParameter() をコールする。


フラグメント単位のライティング

SAMPLE https://www.dropbox.com/s/5koghpnb80ha6q2/light_cg.exe( フラグメント単位のライティング ) アプリケーション側では視線の位置を頂点シェーダにわたす。
{ CGparameter param = cgGetNamedParameter( shdV, "eyepos"); cgGLSetStateMatrixParameter( param, CG_GL_MODELVIEW_MATRIX, CG_GL_MATRIX_INVERSE ); }
頂点シェーダ側ではフラグメントシェーダに渡す。
uniform float4x4 eyepos; OUT.nrm = IN.nrm; OUT.E = float4( eyepos[0][3],eyepos[1][3],eyepos[2][3],eyepos[3][3] ) - IN.pos;
頂点シェーダから視線ベクトルと法線ベクトルをフラグメントシェーダへ渡す。 フラグメントでのそれらの値は頂点間で線形補間されるため、 Normalize() が必要。
struct v2f { // 頂点データ float4 pos : POSITION; float4 nrm : TEXCOORD0; float4 E : TEXCOORD3; // 視線ベクトル }; float3 E = normalize( IN.E.xyz ); float3 N = normalize( IN.nrm.xyz ); // ライトベクトル float3 L = float3( 0, 0, 1 ); // ハーフベクトル float3 H = normalize( (L + E)/2 ); // ディフューズ成分 float d = max( dot( L, N ), 0 ); // スペキュラー成分 float s = max( 0, pow( dot( H, N ), 30 ) ); d += s; return float4( d, d, d, 1 );
ToonShader はライティング結果を階調化して表現してみる。
s = step( 0.5, s ); float n = 3; d = floor ( d * n ) / n; d += s; return float4( d, d, d, 1 );
WARNING 頂点シェーダで受け取る頂点属性は必ずアプリケーション側から渡すこと。
struct app { float4 pos : POSITION; float4 nrm : NORMAL; float2 tc : TEXCOORD0; }; glBegin( GL_TRIANGLES ); // この2つは必須 glNormal3f( 0, 1, 0 ); glTexCoord2f( 0, 0 ); glVertex3f(); glVertex3f(); glVertex3f(); glEnd();



ノーマルマップ(NormalMap)

SAMPLE https://www.dropbox.com/s/tzq13jhj35i2zzj/normalmap_cg.exe( ノーマルマップ ) https://www.dropbox.com/s/ry0i6gih6ko8s78/normalmap_lighting_cg.exe( ノーマルマップとライティング ) カラーのテクスチャからノーマルマップに変換する
float3 calcNormalMap( sampler2D decal, float2 tc) { // 1テクセルの大きさ // 1024×1024のテクスチャを利用する場合 float du = 1 / 1024.0f; float dv = 1 / 1024.0f; // 隣接テクセルの値 float2 tx = tc + float2( du, 0 ); float2 ty = tc + float2( 0, dv ); // 元のテクスチャがカラーのため、明度のみの色に変更 float cx = convMono( tex2D( decal, tx ).xyz ); float cy = convMono( tex2D( decal, ty ).xyz ); tx = tc - float2( du, 0 ); float cx2 = convMono( tex2D( decal, tx ).xyz ); ty = tc - float2( 0, dv ); float cy2 = convMono( tex2D( decal, ty ).xyz ); float scl = 1; // X, Y 方向の傾きを計算する float3 vu = normalize( float3( 1, 0, scl*(cx - cx2) ) ); float3 vv = normalize( float3( 0, 1, scl*(cy - cy2) ) ); // 2つの軸に直交するz軸を計算する float3 N = cross( vu, vv ); return N; }
float4 main( v2f IN, uniform sampler2D decal ) : COLOR { // -1 : 1 の範囲の値を 0 : 1 に変換する return float4( 0.5f * calcNormalMap( decal, IN.tc.xy) + 0.5f, 1 ); }