×

[PR]この広告は3ヶ月以上更新がないため表示されています。
ホームページを更新後24時間以内に表示されなくなります。






TOP

ABOUT

PROGRAM

DIALY







はじめてのジオメトリシェーダ

 1.はじめに…
 「そろそろジオメトリシェーダも触っておこうかな?」と思ったので,サンプルを作ってみました。
 ネタとしてはDirectXのSample Browserにある"Tutorial13 : GemotryShader"です。
実装にはOpenGLとCg言語使いました。ジオメトリシェーダを使う場合,ビデオカードがShaderModel 4.0に対応している必要があると思うので注意してください。


バグじゃないですよw。


 2.プログラム
 「ジオメトリシェーダとはなんぞや?」という話は今回パスさせてもらって,プログラムの組み方だけ触れることにします。
 まず,OpenGL側の処理ですが,Cgfxを用いている場合,普段とあまり変わらないです。エフェクトファイルを読み込んで,テクニック・パラメータを取得して…という流れなので問題ないでしょう。
 普段と変わるのはシェーダ側の処理です。
 今までは頂点シェーダ→ピクセルシェーダという流れだったのですが,頂点シェーダ→ジオメトリシェーダと間にジオメトリシェーダの処理が入るようになります。ジオメトリシェーダでは何をやるかというと,簡単にいうと頂点数を増加させる処理を行います。
 やり方としてはしたのような感じになります。
00011:  //
00012:  // Global Variables
00013:  //
00014:  float4x4 mMVP;
00015:  float explode;
00016:  float3 lightDir = { -0.577f, -0.577f, -0.577f };
00017:  float4 vAmbient;
00018:  float4 vDiffuse;
00019:  float4 vSpecular;
00020:  float3 vEye;
00021:  
00022:  struct VertexInput 
00023:  {
00024:  	float4 Position : POSITION;
00025:  	float3 Normal : NORMAL;
00026:  };
00027:  
00028:  struct VertexOutput
00029:  {
00030:  	float4 Position : POSITION;
00031:  	float4 Color : COLOR;
00032:  };
00033:  
00034:  //
00035:  // Vertex Shader
00036:  //
00037:  VertexOutput VS(VertexInput input)
00038:  {
00039:  	VertexOutput output;
00040:  	output.Position = input.Position;
00041:  	float3 N = input.Normal;
00042:  	float3 L = -lightDir;
00043:  	float3 E = vEye - input.Position.xyz;
00044:  	float3 R = reflect(-normalize(E), N);
00045:  
00046:  	output.Color = vAmbient + vDiffuse * max(dot(N, L), 0) + vSpecular * pow(max(dot(R, L), 0), 8);
00047:  
00048:  	return output;
00049:  }
00050:  
00051:  TRIANGLE void GS(AttribArray<VertexOutput> input)
00052:  {
00053:  	float3 faceEdgeA = input[1].Position.xyz - input[0].Position.xyz;
00054:  	float3 faceEdgeB = input[2].Position.xyz - input[1].Position.xyz;
00055:  	float3 faceNormal = normalize(cross(faceEdgeA, faceEdgeB));
00056:  	float3 explodeAmt = faceNormal * explode;
00057:  
00058:  	float3 centerPos = (input[0].Position.xyz + input[1].Position.xyz + input[2].Position.xyz)/3.0;
00059:  	centerPos += faceNormal * explode;
00060:  
00061:  	float4 Position : POSITION;
00062:  	float4 Color : COLOR;
00063:  	for ( int i=0; i<input.length; i++ )
00064:  	{
00065:  		Position = input[i].Position + float4(explodeAmt, 0);
00066:  		Position = mul(mMVP, Position);
00067:  		Color = input[i].Color;
00068:  		emitVertex(Position, Color);
00069:  
00070:  		int j = (i+1)%3;
00071:  		Position = input[j].Position + float4(explodeAmt, 0);
00072:  		Position = mul(mMVP, Position);
00073:  		Color = input[j].Color;
00074:  		emitVertex(Position, Color);
00075:  
00076:  		Position = float4(centerPos, 1) + float4(explodeAmt, 0);
00077:  		float3 N = faceNormal;
00078:  		float3 L = -lightDir;
00079:  		float3 E = vEye - Position.xyz;
00080:  		float3 R = reflect(-normalize(E), N);
00081:  		Color = vAmbient + vDiffuse * max(dot(N, L), 0) + vSpecular * pow(max(dot(R, L), 0), 8);
00082:  		Position = mul(mMVP, Position);
00083:  		emitVertex(Position, Color);		
00084:  	}
00085:  }
00086:  
00087:  float4 PS(float4 Color : COLOR) : COLOR
00088:  {
00089:  	return Color;
00090:  }
00091:  
00092:  technique Render
00093:  {
00094:  	pass P0
00095:  	{
00096:  		VertexProgram = compile gpu_vp VS();
00097:  		GeometryProgram = compile gpu_gp GS();
00098:  		FragmentProgram = compile gpu_fp PS();
00099:  	}
00100:  }
GS()という関数がジオメトリシェーダの処理に当たります。ここでは,頂点シェーダから送られてきたデータが面ごとにくるようです。たとえば三角形面ならば,頂点が3つ送られてくるということになります。で,面ごとにデータがおられてくるのでAttributeArrayという配列の形で入力を定義しておくみたいです。それで,あとはemitVertex()という関数を使って頂点数を増加させるみたいです。ここで注意してほしいのは,emitVertex()の関数の引数にはかならず,セマンティクスを指定しておかないとエラーが発生するということです。たとえば,emitVertex(Position, Color)という風に新しい頂点データに位置情報と色情報を持たせる場合は,Positionという変数を宣言する段階でセマンティクスを指定しておきます(0061行目)。Colorについても同様です。
気を付ける点としてはGS()関数の宣言のところで,
00051:  TRIANGLE void GS(AttribArray<VertexOutput> input)

という風にvoidの前にプリミティブを指定するキーワードのようなものをどうも入れるようです。ユーザーズマニュアルが更新されていなので,詳しいことがわかりませんが…
POINT
LINE
LINE_ADJ
TRIANGLE
TRIANGLE_ADJ
というのが,キーワードとしてあるっぽい感じで,おそらく"_ADJ"というのは「隣接付き」を表すのではないかと思います。あとQUADとかPOINT_OUT, LINE_OUT, TRIANGLE_OUTというのもあるみたいですけど,これらの使い方とか意味がよくわかんないので,わかったらあとで追記しておきます。
 とりあえず,はじめということで最初はこんなもんで許してください。


 Download
本ソースコードおよびプログラムを使用したことによる如何なる損害も製作者は責任を負いません。
本ソースコードおよびプログラムは自己責任でご使用ください。
プログラムの作成にはMicrosoft Visual Studio 2005 SP1 Professional, Cg Toolkit 2.0 Januaryを用いています。