Blur

今回はブラーの記事を載せます。

画面をぼかす処理はいろいろと使い道があるので知っておいたほうが良いです。

HDR系の処理や、被写界深度を表現するとき、シャギーが目立つときに軽くかけたりなど…
使い道はさまざまです。

ブラーをかけるには、一度シーンをレンダーターゲットに描画しなければなりません。
高度なシェーダーエフェクトをかけるときは、複数のレンダーターゲットを使うこともあるので
今回は管理が楽だったりします。

ブラーをかける前の描画には、ブリンフォンを使用しているので
ブリンフォンシェーダーはそちらから持ってきてください。

前回から、シェーダー管理クラスを使うようになったので、ブリンフォンシェーダーの使い方が違うので
メインプログラムのほうも載せておきます。

以下実装。

ブラーシェーダープログラム
//グローバル変数宣言
float u;
float v;

sampler2D target;

//入力ピクセル構造体
struct PS_INPUT
{
	float4 color : COLOR0;
	float2 texcoord  : TEXCOORD0; //テクスチャ座標
};

//ピクセルシェーダー
float4 ps_main_u( 
	PS_INPUT input,
	uniform float u,
	uniform sampler2D target
) : COLOR0
{
	float2 texel[10];
	
	for( int i = 0 ; i < 5; i++ ){
		texel[i] = input.texcoord + float2( u*(i+1), 0 );
		texel[i+5] = input.texcoord + float2( -u*(i+1), 0 );
	}
	
	float4 color1 = tex2D( target, input.texcoord ) * 0.2f;
	color1 += tex2D( target, texel[0] ) * 0.12f;
	color1 += tex2D( target, texel[1] ) * 0.10f;
	color1 += tex2D( target, texel[2] ) * 0.08f;
	color1 += tex2D( target, texel[3] ) * 0.06f;
	color1 += tex2D( target, texel[4] ) * 0.04f;
	color1 += tex2D( target, texel[5] ) * 0.12f;
	color1 += tex2D( target, texel[6] ) * 0.10f;
	color1 += tex2D( target, texel[7] ) * 0.08f;
	color1 += tex2D( target, texel[8] ) * 0.06f;
	color1 += tex2D( target, texel[9] ) * 0.04f;

	return color1 * input.color;
}

float4 ps_main_v( 
	PS_INPUT input,
	uniform float v,
	uniform sampler2D target
) : COLOR0
{
	float2 texel[10];
		
	for( int i = 0 ; i < 5; i++ ){
		texel[i] = input.texcoord + float2( 0, v*(i+1) );
		texel[i+5] = input.texcoord + float2( 0, -v*(i+1) );
	}
		
	float4 color = tex2D( target, input.texcoord ) * 0.2f;
	color += tex2D( target, texel[0] ) * 0.12f;
	color += tex2D( target, texel[1] ) * 0.10f;
	color += tex2D( target, texel[2] ) * 0.08f;
	color += tex2D( target, texel[3] ) * 0.06f;
	color += tex2D( target, texel[4] ) * 0.04f;
	color += tex2D( target, texel[5] ) * 0.12f;
	color += tex2D( target, texel[6] ) * 0.10f;
	color += tex2D( target, texel[7] ) * 0.08f;
	color += tex2D( target, texel[8] ) * 0.06f;
	color += tex2D( target, texel[9] ) * 0.04f;
		
	return color * input.color;
}

//テクニックの指定
technique blur
{	
	//パスの指定
	pass Pass_0
	{
		FragmentProgram = compile arbfp1 ps_main_u( u, target );
	}
	//パスの指定
	pass Pass_1
	{
		FragmentProgram = compile arbfp1 ps_main_v( v, target );
	}	
}

メインプログラム
#include "Matrix4.h"
#include "Color4.h"
#include "Vector2.h"
#include "Vector3.h"
#include "MathUtility.h"
#include "TGATexture.h"
#include "Shader.h"
#include <GL/glew.h>
#include <GL/glut.h>
#include <iostream>
#include <string>

#pragma comment( lib, "glew32.lib" )

static void display();
static void idle();
static void key( unsigned char state, int x, int y );
static void initializeGL();
static void initializeCg();
static void drawRect( float x, float y, float w, float h );

namespace {

	//ランバートシェーダー
	Shader* blinn = NULL;
	//ブラーシェーダー
	Shader* blur = NULL;

	//ワールド行列
	Matrix4 World;
	//透視変換行列
	Matrix4 Projection;
	//ビュー行列
	Matrix4 View;
	//回転角度
	float angle;
	//FBOID
	unsigned int fbo;
	//テクスチャID
	unsigned int texture;

	//バッファのサイズ
	static const float BUFFER_SIZE = 1024.0f;
}

//メイン関数
void main( int argc, char* argv[] )
{
	glutInit( &argc, argv );
	glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH );
	glutInitWindowPosition( 0, 0 );
	glutInitWindowSize( 640, 480 );

	glutCreateWindow( "Blur Shader Test" );

	int err = glewInit();
	if( err != GLEW_OK ){
		std::cout << glewGetErrorString( err ) << std::endl;
		exit(0);
	}

	//初期化
	initializeGL();
	initializeCg();

	//関数の登録
	glutDisplayFunc( display );
	glutIdleFunc( idle );
	glutKeyboardFunc( key );

	glutMainLoop();
}
//OpenGLの初期化
void initializeGL()
{
	glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );

	glViewport( 0, 0, 640, 480 );
	//OpenGLでは何もさせない
	//透視変換行列の設定
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();

	Projection.setIdentity();
	Projection.setPerspective( 45.0f, 640.0f/480.0f, 0.1f, 100.0f );

	//ビュー行列の設定
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();

	View.setIdentity();
	View.setLookAt( 
		Vector3( 0.0f, 0.0f, 5.0f ),
		Vector3( 0.0f, 0.0f, 0.0f ),
		Vector3( 0.0f, 1.0f, 0.0f )
	);

	//回転角度の初期化
	angle = 0.0f;

	//ワールド行列の設定
	Matrix4 translate, scale, rotate;
	translate.setIdentity();
	translate.setTranslate( Vector3( 0.0f, 0.0f, 0.0f ) );
	scale.setIdentity();
	scale.setScale( Vector3( 1.0f, 1.0f, 1.0f ) );
	rotate.setIdentity();
	rotate.setRotateY( angle );
	World = rotate * translate * scale;

	glDisable( GL_DEPTH_TEST );
	glDisable( GL_LIGHTING );
	glDisable( GL_CULL_FACE );

	//テクスチャのバインド
	glGenTextures( 1, &texture );
	glBindTexture( GL_TEXTURE_2D, texture );
	
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
	 
	glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, BUFFER_SIZE, BUFFER_SIZE, 0, GL_RGBA, GL_FLOAT, 0 );

	glBindTexture( GL_TEXTURE_2D, 0 );
	 
	glGenFramebuffersEXT( 1, &fbo );
	glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, fbo );
	glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texture, 0 );
	glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );

	TGATexture::load( 0, "back.tga" );
}

//Cgの初期化
void initializeCg()
{
	//シェーダーの生成
	blinn = new Shader( "BlinnPhong.cgfx" );
	blur = new Shader( "Blur.cgfx" );

	//テクニックの設定
	blinn->setTechnique( "blinnphong" );
	blur->setTechnique( "blur" );

	//パラメータの設定
	blinn->setParameter( "world", World );
	blinn->setParameter( "view", View );
	blinn->setParameter( "projection", Projection );

	blur->setParameter( "u", 1.0f/(BUFFER_SIZE/4.0f) );
	blur->setParameter( "v", 1.0f/(BUFFER_SIZE/4.0f) );
	blur->setTexture( "target", texture );
}

//描画
void display()
{
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	//レンダーターゲットのバインド
	glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, fbo );
	 
	//レンダーターゲットに描画
	glPushAttrib( GL_VIEWPORT_BIT  );
	glViewport( 0, 0, BUFFER_SIZE, BUFFER_SIZE );
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	//背景の描画
	glEnable( GL_TEXTURE_2D );
	glBindTexture( GL_TEXTURE_2D, TGATexture::getID( 0 ) );
	drawRect( 0, 0, 640, 480 );
	glDisable( GL_TEXTURE_2D );

	//トーラスの描画
	blinn->begin();
	for( unsigned int pass = 0; pass < blinn->getPassNum(); pass++ ){

		blinn->setPass( pass );
		//トーラスの描画
		//glutSolidSphere( 0.5f, 32, 32 );
		glutSolidTorus( 0.5f, 1.0f, 3, 3 );
		//glutSolidCube( 0.5f );
	}
	blinn->end();

	glPopAttrib();
	glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );

	//通常の描画
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	//レンダーターゲットをシェーダーに渡す
	blur->setTexture( "target", texture );

	blur->begin();
	for( unsigned int pass = 0; pass < blur->getPassNum(); pass++ ){
		
		blur->setPass( pass );
		
		drawRect( 0, 0, 640, 480 );
		
	}
	blur->end();
 
	//Framebufferの表示
	glEnable( GL_TEXTURE_2D );

	glBindTexture( GL_TEXTURE_2D, texture );
	drawRect( 0, 0, 200, 200 );

	glDisable( GL_TEXTURE_2D );

	//バッファの切り替え
	glutSwapBuffers();
}

//更新
void idle()
{
	//回転角度の更新
	angle += 1.0f;

	//ワールド行列の更新
	Matrix4 translate, scale, rotate;
	translate.setTranslate( Vector3( 0.0f, 0.0f, 0.0f ) );
	scale.setScale( Vector3( 1.0f, 1.0f, 1.0f ) );
	rotate.setRotateY( angle );
	World = rotate * scale * translate;

	//パラメータの設定
	blinn->setParameter( "world", World );

	//再描画
	glutPostRedisplay();
}

//キー状態
void key( unsigned char state, int x, int y )
{
	switch( state ){
		//エスケープが押されたら終了
		case '\033':
			delete blinn;
			delete blur;
			exit( 0 );
			break;
	}
}

//矩形の描画
void drawRect( float x, float y, float w, float h )
{
	//レンダリングモードの退避
	glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT );

	glDisable( GL_LIGHTING );
	glDisable( GL_DEPTH_TEST );
	glDisable( GL_CULL_FACE );

	//透視変換行列の退避
	glMatrixMode( GL_PROJECTION );
	glPushMatrix();

	//透視変換行列の設定
	glLoadIdentity();
	gluOrtho2D( 0, 640, 480, 0 );

	//モデルビュー変換行列の退避
	glMatrixMode( GL_MODELVIEW );
	glPushMatrix();

	//変換行列の初期化
	glLoadIdentity();

	//四角形の描画
	glBegin( GL_QUADS );
		glTexCoord2f( 0, 0 );
		glVertex2f( x,  y);
		glTexCoord2f( 0, 1);
		glVertex2f(  x,  y+h);
		glTexCoord2f( 1, 1);
		glVertex2f(  x+w,  y+h);
		glTexCoord2f( 1, 0);
		glVertex2f(  x+w,  y );
	glEnd();

	//モデルビュー変換行列の復帰
	glPopMatrix();

	//透視変換行列の復帰
	glMatrixMode( GL_PROJECTION );
	glPopMatrix();

	//モデルビュー変換行列に設定
	glMatrixMode( GL_MODELVIEW );

	//レンダリングモードの復帰
	glPopAttrib();
}

実行結果は以下のようになると思います。

ブラーをかける前のシーンを一緒に表示しているので
その違いが良く分かると思います。
背景が無いとブラーのかかり具合が分かりにくかったので背景を表示してみました。

テクスチャの読み込みはトゥーンに載せてあるのでそちらを使ってください。
最終更新:2009年04月10日 15:19
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。