あと普通のDeferred Renderingのデモとかはライトの数が多いと思うのですが,1個1個全部点灯を確認するのが面倒だったので,今回のサンプルのライト数は9個です。



1パス目=ジオメトリパス 2パス目=ライティングパスジオメトリパスで,シーン内のポリゴンのレンダリングを先に行い,ライティングを行う際にピクセルシェーダで必要となる計算中間値をGバッファと呼ばれる中間バッファに書き出します。このGバッファへの書き出しする際に使うのが,以前サンプルで書いたMRT(Multiple Rendering Target)です。次のライティングパスで,Gバッファに出力されたデータを用いてライティング処理を行います。
00175: bool DeferredRenderer::InitializeMRT( const int width, const int height )
00176: {
00177: // GLEWの初期化
00178: if ( glewInit() != GLEW_OK )
00179: {
00180: ELOG( "Error : glewInit() Failed." );
00181: return false;
00182: }
00183:
00184: // 浮動小数点テクスチャが使えるかチェック
00185: if ( !GLEW_ARB_texture_float )
00186: {
00187: ELOG( "Error : ARB_texture_float is not supported." );
00188: return false;
00189: }
00190:
00191: // テクスチャ3枚生成
00192: glGenTextures( NUM_GBUFFER, mTexture );
00193:
00194: // 3枚分の設定をしておく
00195: glEnable( GL_TEXTURE_2D );
00196: for( int i=0; i<NUM_GBUFFER; i++ )
00197: {
00198: glBindTexture( GL_TEXTURE_2D, mTexture[i] );
00199: glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
00200: glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
00201: glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
00202: glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
00203: glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0 );
00204: glBindTexture( GL_TEXTURE_2D, 0 );
00205: }
00206: glDisable( GL_TEXTURE_2D );
00207: assert( !GL_ERROR_CHECK );
00208:
00209: // レンダーバッファの設定
00210: glGenRenderbuffersEXT( 1, &mRenderBuffer );
00211: glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, mRenderBuffer );
00212: glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, width, height );
00213:
00214: // フレームバッファの設定
00215: glGenFramebuffersEXT( 1, &mFrameBuffer );
00216: glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, mFrameBuffer );
00217: glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, mTexture[0], 0 );
00218: glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, mTexture[1], 0 );
00219: glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT2_EXT, GL_TEXTURE_2D, mTexture[2], 0 );
00220: glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, mRenderBuffer );
00221: glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
00222: assert( !GL_ERROR_CHECK );
00223:
00224: return true;
00225: }
一応今回のサンプルでは,3枚分をGバッファとして用意してあります。あとテクスチャは浮動小数点テクスチャ使ってみました。00286: void DeferredRenderer::Render()
00287: {
00288: //========================================
00289: // pass1. ジオメトリ処理
00290: //========================================
00291:
00292: // ワールド行列計算
00293: Matrix world;
00294: world.Identity();
00295:
00296: // ビュー行列計算
00297: mCamera.Compute();
00298:
00299: // 射影行列計算
00300: Matrix proj = Math::CreatePerspectiveFieldOfView( Math::PiOver4, DemoApp::GetInstance()->GetAspectRatio(), 0.1f, 1000.0f );
00301:
00302: // パラメータ転送
00303: cgSetMatrixParameterfr( mParam.projection, proj );
00304: cgSetMatrixParameterfr( mParam.view, mCamera.GetView() );
00305: cgSetParameter3fv( mParam.cameraPosition, mCamera.GetPosition() );
00306:
00307: glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, mFrameBuffer );
00308: GLuint attachments[ 3 ] =
00309: {
00310: GL_COLOR_ATTACHMENT0_EXT,
00311: GL_COLOR_ATTACHMENT1_EXT,
00312: GL_COLOR_ATTACHMENT2_EXT
00313: };
00314: glDrawBuffers( 3, attachments );
00315:
00316: // バッファクリア
00317: glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
00318: glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
00319:
00320: // ビューポート設定
00321: glViewport( 0, 0, (GLsizei)FRAME_BUFFER_WIDTH, (GLsizei)FRAME_BUFFER_HEIGHT );
00322:
00323: assert( !GL_ERROR_CHECK );
00324:
00325: // ジオメトリパス
00326: {
00327: // パスステートを設定
00328: cgSetPassState( mpEffect->GetPass( 0, "Geometry" ) );
00329:
00330: // モデル描画
00331: cgSetMatrixParameterfr( mParam.world, world );
00332: mSaturn.Draw();
00333:
00334: world = Math::CreateScale( 5.0f ) * Math::CreateTranslation( 0.0f, -1.0f, 0.0f );
00335: cgSetMatrixParameterfr( mParam.world, world );
00336: mGround.Draw();
00337:
00338: // パスステートをリセット
00339: cgResetPassState( mpEffect->GetPass( 0, "Geometry" ) );
00340: }
00341:
00342: glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
00343:
00344: // 呼ばなくてもいいんだけど,念のため
00345: glFlush();
00346:
00347: // Gバッファ転送
00348: cgGLSetTextureParameter( mParam.positionSmp, mTexture[ GBUFFER_1 ] );
00349: cgGLSetTextureParameter( mParam.normalDepthSmp, mTexture[ GBUFFER_2 ] );
00350: cgGLSetTextureParameter( mParam.diffuseAlbedoSmp, mTexture[ GBUFFER_3 ] );
00351:
やっていることは大したことないですね。glDrawBuffers()で関連づけさせておいて,描画するだけです。00030: /////////////////////////////////////////////////////////////////////////
00031: // GeometryVSInput structure
00032: /////////////////////////////////////////////////////////////////////////
00033: struct GeometryVSInput
00034: {
00035: float4 Position : POSITION;
00036: float2 TexCoord : TEXCOORD0;
00037: float3 Normal : NORMAL;
00038: };
00039:
00040: /////////////////////////////////////////////////////////////////////////
00041: // GeometryVSOutput structure
00042: /////////////////////////////////////////////////////////////////////////
00043: struct GeometryVSOutput
00044: {
00045: float4 Position : POSITION;
00046: float2 TexCoord : TEXCOORD0;
00047: float4 WorldPosition : TEXCOORD1;
00048: float3 WorldNormal : TEXCOORD2;
00049: float4 DiffuseColor : TEXCOORD3;
00050: };
00051:
00052: /////////////////////////////////////////////////////////////////////////
00053: // GeometryPSOutput structure
00054: /////////////////////////////////////////////////////////////////////////
00055: struct GeometryPSOutput
00056: {
00057: float4 Position : COLOR0;
00058: float4 NormalDepth : COLOR1;
00059: float4 DiffuseAlbedo : COLOR2;
00060: };
00061:
00080: //-----------------------------------------------------------------------
00081: // Name : GeometryVSFunc()
00082: // Desc : 遅延レンダリング - ジオメトリパス頂点処理
00083: //-----------------------------------------------------------------------
00084: GeometryVSOutput GeometryVSFunc( const GeometryVSInput input )
00085: {
00086: GeometryVSOutput output = (GeometryVSOutput)0;
00087:
00088: float4 worldPos = mul( input.Position, World );
00089: float4 viewPos = mul( worldPos, View );
00090: float4 projPos = mul( viewPos, Projection );
00091:
00092: float3 worldNormal = mul( input.Normal, (float3x3)World );
00093:
00094: output.Position = projPos;
00095: output.TexCoord = input.TexCoord;
00096: output.WorldNormal = worldNormal;
00097: output.WorldPosition = worldPos;
00098: output.DiffuseColor = glstate.material.diffuse;
00099:
00100: return output;
00101: }
00102:
00103: //-----------------------------------------------------------------------
00104: // Name : GeometryPSFunc()
00105: // Desc : 遅延レンダリング - ジオメトリパスピクセル処理
00106: //-----------------------------------------------------------------------
00107: GeometryPSOutput GeometryPSFunc( const GeometryVSOutput input )
00108: {
00109: GeometryPSOutput output = (GeometryPSOutput)0;
00110:
00111: float depth = input.WorldPosition.z / input.WorldPosition.w;
00112:
00113: // -1~1までの範囲を0~1に変換
00114: float3 normal = normalize( input.WorldNormal ) * 0.5f + 0.5f;
00115:
00116: output.Position = input.WorldPosition;
00117: output.NormalDepth = float4( normal, depth );
00118: output.DiffuseAlbedo = input.DiffuseColor;
00119:
00120: return output;
00121: }
シェーダも大したことやっていないです。単に来たデータをそのまま出力しているだけですね。
00353: //========================================
00354: // pass2. ライティング処理
00355: //========================================
00356:
00357: // バッファクリア
00358: glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
00359:
00360: // ビューポート設定
00361: glViewport( 0, 0, DemoApp::GetInstance()->GetWidth(), DemoApp::GetInstance()->GetHeight() );
00362:
00363: // ライティングパス
00364: {
00365: cgSetPassState( mpEffect->GetPass( 0, "Lighting" ) );
00366:
00367: cgGLSetParameterArray4f( mParam.lightPostion, 0, NUM_LIGHT, &mLightPosition[0].x );
00368: cgGLSetParameterArray3f( mParam.lightColor, 0, NUM_LIGHT, &mLightColor[0].x );
00369:
00370: DrawRect();
00371:
00372: cgResetPassState( mpEffect->GetPass( 0, "Lighting" ) );
00373: }
00374:
00375: assert( !GL_ERROR_CHECK );
00376: }
00377:
3枚のテクスチャをシェーダに転送して,あとはライティングを行うためのライト位置・ライトカラーの配列をシェーダに転送してテクスチャを描画するために矩形を書いておくだけです。
00062: /////////////////////////////////////////////////////////////////////////
00063: // LightVSInput structure
00064: /////////////////////////////////////////////////////////////////////////
00065: struct LightVSInput
00066: {
00067: float4 Position : POSITION;
00068: float2 TexCoord : TEXCOORD0;
00069: };
00070:
00071: /////////////////////////////////////////////////////////////////////////
00072: // LightVSOutput structure
00073: /////////////////////////////////////////////////////////////////////////
00074: struct LightVSOutput
00075: {
00076: float4 Position : POSITION;
00077: float2 TexCoord : TEXCOORD0;
00078: };
00079:
00123: //-----------------------------------------------------------------------
00124: // Name : LightVSFunc()
00125: // Desc : 遅延レンダリング - ライティングパス頂点処理
00126: //-----------------------------------------------------------------------
00127: LightVSOutput LightVSFunc( const LightVSInput input )
00128: {
00129: LightVSOutput output = (LightVSOutput)0;
00130:
00131: output.Position = input.Position;
00132: output.TexCoord = input.TexCoord;
00133:
00134: return output;
00135: }
00136:
00137: //------------------------------------------------------------------------
00138: // Name : LightPSFunc()
00139: // Desc : 遅延レンダリング - ライティングパスピクセル処理
00140: //------------------------------------------------------------------------
00141: float4 LightPSFunc( const LightVSOutput input ) : COLOR0
00142: {
00143: float3 WorldPosition = tex2D( PositionSmp, input.TexCoord ).rgb;
00144: float3 WorldNormal = float3( 0, 0, 0 );
00145: float Depth = 0;
00146: {
00147: float4 normalDepth = tex2D( NormalDepthSmp, input.TexCoord );
00148: WorldNormal = normalDepth.rgb * 2.0f - 1.0f;
00149: Depth = normalDepth.a;
00150: }
00151:
00152: float4 Color = float4( 0, 0, 0, 0 );
00153: float3 EyeDir = normalize( CameraPosition - WorldPosition );
00154: float4 Diffuse = tex2D( DiffuseAlbedoSmp, input.TexCoord );
00155:
00156: for( int i=0; i<NUM_LIGHT; ++i )
00157: {
00158: float3 LightDir = LightPosition[ i ].xyz - WorldPosition;
00159: float3 L = normalize( LightDir );
00160: float attenution = 1.0f / length( LightDir );
00161: Color.rgb += attenution * LightColor[ i ] * Diffuse.rgb * max( dot( L, WorldNormal ), 0 );
00162: Color.a = 1.0f;
00163: }
00164:
00165: return Color;
00166: }
00167:
スポットライトみたいにしたかったので,減衰させる係数attenutionを入れてありますが,よく見るライティング処理なので複雑な処理もなく問題ないでしょう。