ループしたコースティクスの作成

時間軸と境界でループしたコースティクスを作る必要があったので作ってみた。

まず波の実装。
波動方程式を解くタイプの波だと境界でループさせるのは簡単なのでとりあえず実装。
時間軸方向にループさせるとなるとちょっとオラには出来そうに無いので、
サイン波の合成で波を作ることにした。これなら境界ループも時間軸方向のループも簡単だしね。
で、実装したらこれがまた糞重い。
けど我慢。

昔のDirectXのサンプルで適当な計算してる奴があったんだけどどこにあるかわかんなかったので自分で作ることにした。まじめに計算する方向性で。


とりあえず2次元の正弦波を使ってプロットしてみる。
こんな感じで。
水の屈折率は1.3334なんだけど法線の逆に1/2をかけたものをレイに足して正規化してもほぼ同様の値になるのでそれで。



	float distance = 256.0f;
	// 接線と高さから計算.
	for ( int i = 0 ; i < 256 ; i++ )
	{
		int pi = i-1;
		int ni = i+1;
		if ( pi < 0 ) pi = 255;
		if ( ni >= 256 ) ni = 0;

		float dtl = line[i]-line[pi];
		float dtr = line[ni]-line[i];
		float dt = (dtr+dtl)/2.0f; // 変化量の平均.

		// 高さの変化量から法線を生成.
		D3DXVECTOR3 p(1.0f,dt,0.0f);
		D3DXVec3Cross( &n, &z, &p );
		D3DXVec3Normalize( &n, &n );

		// Y+方向に向くようにする.
		if ( n.y < 0.0f )
				D3DXVec3Scale( &n, &n, -1.0f );

		// 屈折した後のray
		D3DXVECTOR3 oray;

		// -Nの1/2をrayに加えると水の屈折率になる.
		D3DXVec3Scale( &oray, &n, -0.5f );
		D3DXVec3Add( &oray, &oray, &ray );
		D3DXVec3Normalize( &oray, &oray );

		// rayを2.0くらい先に落とす.
		float dy = D3DXVec3Dot( &oray, &mn );
		float l = (distance+line[i])/dy;
		l = oray.x * l;

		// indexの1がおよそ1としてずらして記録する.
		float offset = l;

		// 隣接するピクセルにも渡す.
		float cur = floorf(offset);
		float next = floorf(cur+1.0f);
		float ratio = 1.0f - (offset-cur);

		int icur = (int)cur + i;
		int inext = (int)next + i;

		offsets[i] = icur;

		icur &= 0xff;
		inext &= 0xff;
		pline[icur] += ratio*0.1f;
		pline[inext] += (1.0f-ratio)*0.1f;
	}

最初傾きを計算するところがバグってて変なところで収束してました。
大体これでそれっぽい値になるので今度は3次元に拡張です。
なんかもう途中が最強に泥臭くなってるので恥ずかしい。
法線の計算がちょっと変なので修正[2006/10/24]

	void CalcCaustics()
	{
		std::vector<float>	&cur = m_fBuffer[1-m_iCur];
		std::vector<float>	&cau = m_fCaustics;

		int idx = 0;

		D3DXVECTOR3 ray(0.0f,-1.0f,0.0f);
		D3DXVECTOR3 n(0.0f,1.0f,0.0f);
		D3DXVECTOR3 nx(0.0f,1.0f,0.0f);
		D3DXVECTOR3 ny(0.0f,1.0f,0.0f);
		D3DXVECTOR3 mn(0.0f,-1.0f,0.0f);
		D3DXVECTOR3 z(0.0f,0.0f,1.0f);
		int h = m_param.m_iHeight;
		int w = m_param.m_iWidth;

		// 接線と高さから計算.
		for ( int y = 0 ; y < h ; y++ )
		{
			for ( int x = 0 ; x < w ; x++, idx++ )
			{
				int l = lap_low(x-1,w);
				int r = lap_hi(x+1,w);
				int u = lap_low(y-1,h);
				int d = lap_hi(y+1,h);

				int ic = y*w+x;
				int il = y*w+l;
				int ir = y*w+r;
				int iu = u*w+x;
				int id = d*w+x;

				// 法線を求める.
				float dtx = ((cur[ic]-cur[il]) + (cur[ir]-cur[ic]))/2.0f;
				float dty = ((cur[ic]-cur[iu]) + (cur[id]-cur[ic]))/2.0f;
				dtx *= 8.0f;
				dty *= 8.0f;

				// 高さの変化量から法線を生成.
				D3DXVECTOR3 px(1.0f,dtx,0.0f);
				D3DXVECTOR3 py(0.0f,dty,1.0f);
				D3DXVec3Cross( &n, &px, &py );
				D3DXVec3Normalize( &n, &n );
				/*
					D3DXVECTOR3 px(1.0f,dtx,0.0f);
					D3DXVECTOR3 py(1.0f,dty,0.0f);
					D3DXVec3Cross( &nx, &z, &px );
					D3DXVec3Cross( &ny, &z, &py );
					n.x = nx.x;
					n.y = 1.0f;
					n.z = ny.x;
					D3DXVec3Normalize( &n, &n );
				*/
				// Y+方向に向くようにする.
				if ( n.y < 0.0f ) D3DXVec3Scale( &n, &n, -1.0f );

				// 屈折した後のray
				D3DXVECTOR3 oray;

				// -Nの1/2をrayに加えると水の屈折率になる.
				D3DXVec3Scale( &oray, &n, -10.1f );
				D3DXVec3Add( &oray, &oray, &ray );
				D3DXVec3Normalize( &oray, &oray );

				// rayを先に落とす.
				float dy = D3DXVec3Dot( &oray, &mn );
				float ll = (256.0f+8.0f*cur[ic])/dy;
				D3DXVECTOR3 vl;
				D3DXVec3Scale( &vl, &oray, ll );

				// indexの1がおよそ1としてずらして記録する.
				D3DXVECTOR3 offset( vl.x, 0.0f, vl.z );

				// 隣接するピクセルにも渡す.
				
				D3DXVECTOR3 cr,next,ratio;
				cr.x = floorf(offset.x);
				cr.z = floorf(offset.z);
				next.x = floorf(cr.x+1.0f);
				next.z = floorf(cr.z+1.0f);
				ratio.x = 1.0f - (offset.x-cr.x);
				ratio.z = 1.0f - (offset.z-cr.z);

				// レイが落ちた先.
				int icurx = (int)cr.x + x;
				int icurz = (int)cr.z + y;
				int inextx = (int)next.x + x;
				int inextz = (int)next.z + y;
				icurx &= (w-1);
				icurz &= (h-1);
				inextx &= (w-1);
				inextz &= (h-1);

				cau[icurz*w+icurx] += ratio.x*0.1f + ratio.z*0.1f;
				cau[icurz*w+inextx] += (1.0f-ratio.x)*0.1f + ratio.z*0.1f;
				cau[inextz*w+icurx] += ratio.x*0.1f + (1.0f-ratio.z)*0.1f;
				cau[inextz*w+inextx] += (1.0f-ratio.x)*0.1f + (1.0f-ratio.z)*0.1f;
			}
		}

		D3DLOCKED_RECT lockrect;
		m_pTexCaustics->LockRect( 0, &lockrect, NULL, D3DLOCK_DISCARD );
		DWORD	*pBits = (DWORD *)lockrect.pBits;
		idx = 0;
		for ( int y = 0 ; y < h ; y++ )
		{
			for ( int x = 0 ; x < w ; x++, idx++ )
			{
				float fc = cau[idx]*128.0f;
				if ( fc > 255.0f ) fc = 255.0f;
				if ( fc <= 0.0f ) fc = 0.0f;
				DWORD c = (DWORD)fc;
				cau[idx] = 0.0f;
				pBits[idx] = D3DCOLOR_ARGB(255,c,c,c);
			}
		}

		m_pTexCaustics->UnlockRect( 0 );
	}

良い感じでコースティクスっぽくなります。
でもちょっとレイの量が足りなくてノイズ見たくなるから、フォトショップとかでガウスぼかしをかけてあげればOK?
あとは、波の大きさとかに合わせてスケーリングと投影する距離を調整しないとだめだね。
波動方程式で解いた奴とかに適用するとすんごいノイズが出る。