ページ 11

DxLibで3Dの海を作成する方法について

Posted: 2013年12月31日(火) 01:05
by あかり
DxLibで3Dの戦艦や潜水艦などで艦隊戦を行うシミュレーションゲームを作ろうとしているのですが、どうしても海の処理がわかりません。
水中から海上を見た時や、波の作成などを教えてもらえませんか?
よろしくお願いします。

Re: DxLibで3Dの海を作成する方法について

Posted: 2013年12月31日(火) 09:35
by softya(ソフト屋)
水中から見上げるのと水上から見下ろすのさほど処理が変わると思えません。どの様な問題が起きているのでしょうか?
波の作成に関してはテクスチャのアニメーションで事足りませんでしょうか? 実際に波が上下するとゲームとしては逆に見づらくなる気がします。

Re: DxLibで3Dの海を作成する方法について

Posted: 2013年12月31日(火) 11:20
by あかり
現在、球体の背景と、海の透明テクスチャだけを貼った物と、船を表示しているのですが、
海のテクスチャの模様は出ているのですが、なぜか透明ではなく、船の底が見えない状態です。
画像

Re: DxLibで3Dの海を作成する方法について

Posted: 2013年12月31日(火) 11:25
by softya(ソフト屋)
Zソートを自分でしていないんじゃないでしょうか。半透明のポリゴンは描画順番で上手く行かないので描画順番を制御する必要があります。
海の場合はZソートしなくても船の後に描画すれば良い気がしますけど。
艦船→背景→海 と描画してみてください。

Re: DxLibで3Dの海を作成する方法について

Posted: 2013年12月31日(火) 12:10
by あかり
ありがとうございます、表示できました。
もう一つなんですが、海の中に視点を移したときや海の中と海上両方が映っている場合、海のほうを少し青く表示するにはどうすればいいでしょうか。

Re: DxLibで3Dの海を作成する方法について

Posted: 2013年12月31日(火) 12:29
by softya(ソフト屋)
高度な処理は大変だと思うので、海面下に半透明で青いフィルターを描画するぐらいでしょうか。
ただ、その計算もちょっとややこしいかな。私もやったこと無いんですが考えて見ます。

Re: DxLibで3Dの海を作成する方法について

Posted: 2013年12月31日(火) 15:54
by ISLe
海の中を青くするのは影の生成と同じだと思います。
海上からの光線が海面を通して当たる部分に青い半透明の影が合成されるという具合です。
まずは影の生成方法をネットで調べてみてはいかがでしょうか。

Re: DxLibで3Dの海を作成する方法について

Posted: 2013年12月31日(火) 21:43
by あかり
影の作成は調べたもので、頂点シェーダーやピクセルシェーダーを使用するものでしょうか?
頂点シェーダーとピクセルシェーダーは.psoや.vsoなど、いろいろわかっていないところが多く、どうすればいいのかわかっていません。

Re: DxLibで3Dの海を作成する方法について

Posted: 2013年12月31日(火) 22:59
by softya(ソフト屋)
とりあえず、やっつけで作ってみましたが誤差があります。
あと、カメラのZ回転には耐えられません。
それっぽくあるけど水面あたりの誤差が気になるそんな感じです。
どこか勘違いしていて誤差になっているかもとも思わないでもないです。

※ 影は色々とあるので、とりあえずこんな方法もあるよバージョンです。

【補足】 まじめに投影画面と水面にあるカメラに向かっていく線の交点を計算するのが正解かも知れませんね。これ書いといてから思いつきました。

コード:

 
#include "DxLib.h"
#include <stdio.h>
 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    VERTEX3D Vertex[ 4 ] ;
    WORD Index[ 6 ] ;
    
    ChangeWindowMode(TRUE);
    if( DxLib_Init() < 0 ) { return -1; }
    SetDrawScreen( DX_SCREEN_BACK ) ;


    // 4頂点分のデータをセット
    Vertex[ 0 ].pos  = VGet(-2000.0f,   0.0f,-2000.0f ) ;
    Vertex[ 0 ].norm = VGet(   0.0f,   0.0f, -1.0f ) ;
    Vertex[ 0 ].dif  = GetColorU8(   0,128,255,128 ) ;
    Vertex[ 0 ].spc  = GetColorU8(   0,  0,  0,  0 ) ;
    Vertex[ 0 ].u    = 0.0f ;
    Vertex[ 0 ].v    = 0.0f ;
    Vertex[ 0 ].su   = 0.0f ;
    Vertex[ 0 ].sv   = 0.0f ;

    Vertex[ 1 ].pos  = VGet( 2000.0f,   0.0f,-2000.0f) ;
    Vertex[ 1 ].norm = VGet(   0.0f,   0.0f, -1.0f ) ;
    Vertex[ 1 ].dif  = GetColorU8(   0,128,255,128 ) ;
    Vertex[ 1 ].spc  = GetColorU8(   0,  0,  0,  0 ) ;
    Vertex[ 1 ].u    = 0.0f ;
    Vertex[ 1 ].v    = 0.0f ;
    Vertex[ 1 ].su   = 0.0f ;
    Vertex[ 1 ].sv   = 0.0f ;

    Vertex[ 2 ].pos  = VGet( -2000.0f,  0.0f,2000.0f ) ;
    Vertex[ 2 ].norm = VGet(   0.0f,   0.0f, -1.0f ) ;
    Vertex[ 2 ].dif  = GetColorU8(   0,128,255,128 ) ;
    Vertex[ 2 ].spc  = GetColorU8(   0,  0,  0,  0 ) ;
    Vertex[ 2 ].u    = 0.0f ;
    Vertex[ 2 ].v    = 0.0f ;
    Vertex[ 2 ].su   = 0.0f ;
    Vertex[ 2 ].sv   = 0.0f ;

    Vertex[ 3 ].pos  = VGet( 2000.0f, 0.0f, 2000.0f ) ;
    Vertex[ 3 ].norm = VGet(   0.0f,   0.0f, -1.0f ) ;
    Vertex[ 3 ].dif  = GetColorU8(   0,128,255,128 ) ;
    Vertex[ 3 ].spc  = GetColorU8(   0,  0,  0,  0 ) ;
    Vertex[ 3 ].u    = 0.0f ;
    Vertex[ 3 ].v    = 0.0f ;
    Vertex[ 3 ].su   = 0.0f ;
    Vertex[ 3 ].sv   = 0.0f ;

    // 2ポリゴン分のインデックスデータをセット
    Index[ 0 ] = 0 ;
    Index[ 1 ] = 1 ;
    Index[ 2 ] = 2 ;
    Index[ 3 ] = 3 ;
    Index[ 4 ] = 2 ;
    Index[ 5 ] = 1 ;
    
    //	カメラの座標
    VECTOR CameraPos ;
    CameraPos.x = 0.0f ;
    CameraPos.y = 60.0f ;
    CameraPos.z = -800.0f ;
    
    //	ブレンドを有効に。
    SetDrawBlendMode( DX_BLENDMODE_ALPHA, 255 ) ;
    
    // Zバッファを有効にする
    SetUseZBuffer3D( TRUE ) ;
    // Zバッファへの書き込みを有効にする
    SetWriteZBuffer3D( TRUE ) ;

    while( ProcessMessage() == 0 ) {
        ClearDrawScreen() ;
		
        // 方向キーでカメラの座標を移動
        if( CheckHitKey( KEY_INPUT_UP ) == 1 )
        {
            CameraPos.y += 1.0f ;
        }
        if( CheckHitKey( KEY_INPUT_DOWN ) == 1 )
        {
            CameraPos.y -= 1.0f ;
        }
        if( CheckHitKey( KEY_INPUT_LEFT ) == 1 )
        {
            CameraPos.x -= 20.0f ;
        }
        if( CheckHitKey( KEY_INPUT_RIGHT ) == 1 )
        {
            CameraPos.x += 20.0f ;
        }
        
		//	カメラの設定
        SetCameraNearFar( 100.0f, 15000.0f ) ; 
        SetCameraPositionAndTarget_UpVecY( CameraPos, VGet( 0.0f, 0.0f, 0.0f ) );

		//	物体
		DrawSphere3D( VGet( 0.0f, 0.0f, 0.0f ), 100.0f, 10, GetColor( 255,255,255 ), GetColor( 255, 255, 255 ), TRUE ) ;

		//	仮定水面(部分)を描画
    	DrawPolygonIndexed3D( Vertex, 4, Index, 2, DX_NONE_GRAPH, TRUE );
		
        //  カメラのビューマトリクスを得る。
        MATRIX viewMtx = GetCameraViewMatrix();
        
        //  カメラの方向のZが100fとちょっと先の位置を得る。つまりカメラに映る水面の端
        MATRIX dirMtx = MInverse(viewMtx);
        VECTOR dirVec = VTransformSR( VGet(0.0f,0.0f,10000.0f), dirMtx );
        dirVec.y = 0;//y成分を消す
		dirVec = VNorm(dirVec);//単位ベクトル化
		dirVec = VScale(dirVec, 100.01f );//距離分拡大。少し奥。
        
		//	カメラの近くの仮想水面の座標
		VECTOR TargetSeaPos = CameraPos;
		TargetSeaPos.y = 0; //水面は高さ0
		TargetSeaPos = VAdd(TargetSeaPos,dirVec); //ベクトルの加算

		//	ワールドから画面座標に変換。カメラの位置の水面。
		VECTOR ScreenSeaPos = CameraPos;
		ScreenSeaPos.y = 0; //水面は高さ0
		VECTOR screen_pos = ConvWorldPosToScreenPos( ScreenSeaPos ) ;
		
		//	値の表示
		DrawFormatString( 0, 16, GetColor( 255,0,0 ), "camera (x,y,z)=(%7.1f,%7.1f,%7.1f)", CameraPos.x, CameraPos.y ,CameraPos.z ) ;
		DrawFormatString( 0, 32, GetColor( 255,0,0 ), "target sea (x,y,z)=(%7.1f,%7.1f,%7.1f)", TargetSeaPos.x, TargetSeaPos.y ,TargetSeaPos.z ) ;
		DrawFormatString( 0, 48, GetColor( 255,0,0 ), "screen_pos (x,y)=(%7.1f,%7.1f)", screen_pos.x, screen_pos.y ) ;
		
		//	カメラの近くの仮想水面の座標が画面内なら、海面下の描画を行う。
		BOOL bDrawUnderSea = false;
        if( CheckCameraViewClip( TargetSeaPos ) == FALSE ) {
			//	画面内
            DrawString( 0, 0, "Screen in", GetColor( 255,255,255 ) ) ;
            bDrawUnderSea = true;
        } else {
			//	画面外
            DrawString( 0, 0, "Screen out", GetColor( 255,255,255 ) ) ;
        }
		//	画面外でも座標がマイナスなら海面下の描画をを行う。
		if( screen_pos.y < 0 ) {
            bDrawUnderSea = true;
		}
		
		//	screen_pos.y を元に海面下の描画
		if( bDrawUnderSea ) {
			//	海面下を半透明のBOXで塗りつぶす。
			SetDrawBlendMode( DX_BLENDMODE_ALPHA, 64 ) ;
			DrawBox( 0, screen_pos.y, DEFAULT_SCREEN_SIZE_X, DEFAULT_SCREEN_SIZE_Y, GetColor( 0,0,255 ), TRUE ) ;
//			DrawBox( 0, screen_pos.y, DEFAULT_SCREEN_SIZE_X, DEFAULT_SCREEN_SIZE_Y, GetColor( 255,255,0 ), TRUE ) ;
			SetDrawBlendMode( DX_BLENDMODE_ALPHA, 255 ) ;
		}

        // 取得したスクリーン座標に四角形を描画
//        DrawBox( ScreenPos.x - 2, ScreenPos.y - 2, ScreenPos.x + 2, ScreenPos.y + 2, GetColor( 255,0,0 ), TRUE ) ;

        ScreenFlip() ;
    }

    DxLib_End() ;
    return 0 ;
}

Re: DxLibで3Dの海を作成する方法について

Posted: 2013年12月31日(火) 23:12
by softya(ソフト屋)
アイデアだけですが、画面である仮想平面と線分の交点を求める方法。

「平面と線分の交点を求める方法」
http://www.sousakuba.com/Programming/gs ... rsect.html

カメラの100.0f先(Nearの距離)に仮想平面があるものとして、そこに向かって注視点からカメラの真下までの水面上の線分があると仮定して下さい。
これが交差するポイントの3D座標が水面下の開始座標に変換できると思います。誤差は少なくなるはず? 試してません。

Re: DxLibで3Dの海を作成する方法について

Posted: 2014年1月01日(水) 12:16
by softya(ソフト屋)
あれ? 昨日はなんかぼけてた模様。
マジックナンバーで調整したよろしく無いコードですが。
仮想平面板には未挑戦です。

コード:

 
#include "DxLib.h"
#include <stdio.h>
 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    VERTEX3D Vertex[ 4 ] ;
    WORD Index[ 6 ] ;
    
    ChangeWindowMode(TRUE);
    if( DxLib_Init() < 0 ) { return -1; }
    SetDrawScreen( DX_SCREEN_BACK ) ;


    // 4頂点分のデータをセット
    Vertex[ 0 ].pos  = VGet(-2000.0f,   0.0f,-2000.0f ) ;
    Vertex[ 0 ].norm = VGet(   0.0f,   0.0f, -1.0f ) ;
    Vertex[ 0 ].dif  = GetColorU8(   0,128,255,128 ) ;
    Vertex[ 0 ].spc  = GetColorU8(   0,  0,  0,  0 ) ;
    Vertex[ 0 ].u    = 0.0f ;
    Vertex[ 0 ].v    = 0.0f ;
    Vertex[ 0 ].su   = 0.0f ;
    Vertex[ 0 ].sv   = 0.0f ;

    Vertex[ 1 ].pos  = VGet( 2000.0f,   0.0f,-2000.0f) ;
    Vertex[ 1 ].norm = VGet(   0.0f,   0.0f, -1.0f ) ;
    Vertex[ 1 ].dif  = GetColorU8(   0,128,255,128 ) ;
    Vertex[ 1 ].spc  = GetColorU8(   0,  0,  0,  0 ) ;
    Vertex[ 1 ].u    = 0.0f ;
    Vertex[ 1 ].v    = 0.0f ;
    Vertex[ 1 ].su   = 0.0f ;
    Vertex[ 1 ].sv   = 0.0f ;

    Vertex[ 2 ].pos  = VGet( -2000.0f,  0.0f,2000.0f ) ;
    Vertex[ 2 ].norm = VGet(   0.0f,   0.0f, -1.0f ) ;
    Vertex[ 2 ].dif  = GetColorU8(   0,128,255,128 ) ;
    Vertex[ 2 ].spc  = GetColorU8(   0,  0,  0,  0 ) ;
    Vertex[ 2 ].u    = 0.0f ;
    Vertex[ 2 ].v    = 0.0f ;
    Vertex[ 2 ].su   = 0.0f ;
    Vertex[ 2 ].sv   = 0.0f ;

    Vertex[ 3 ].pos  = VGet( 2000.0f, 0.0f, 2000.0f ) ;
    Vertex[ 3 ].norm = VGet(   0.0f,   0.0f, -1.0f ) ;
    Vertex[ 3 ].dif  = GetColorU8(   0,128,255,128 ) ;
    Vertex[ 3 ].spc  = GetColorU8(   0,  0,  0,  0 ) ;
    Vertex[ 3 ].u    = 0.0f ;
    Vertex[ 3 ].v    = 0.0f ;
    Vertex[ 3 ].su   = 0.0f ;
    Vertex[ 3 ].sv   = 0.0f ;

    // 2ポリゴン分のインデックスデータをセット
    Index[ 0 ] = 0 ;
    Index[ 1 ] = 1 ;
    Index[ 2 ] = 2 ;
    Index[ 3 ] = 3 ;
    Index[ 4 ] = 2 ;
    Index[ 5 ] = 1 ;
    
    //	カメラの座標
    VECTOR CameraPos ;
    CameraPos.x = 0.0f ;
    CameraPos.y = 60.0f ;
    CameraPos.z = -800.0f ;
    
    //	ブレンドを有効に。
    SetDrawBlendMode( DX_BLENDMODE_ALPHA, 255 ) ;
    
    // Zバッファを有効にする
    SetUseZBuffer3D( TRUE ) ;
    // Zバッファへの書き込みを有効にする
    SetWriteZBuffer3D( TRUE ) ;

    while( ProcessMessage() == 0 ) {
        ClearDrawScreen() ;
		
        // 方向キーでカメラの座標を移動
        if( CheckHitKey( KEY_INPUT_UP ) == 1 )
        {
            CameraPos.y += 1.0f ;
        }
        if( CheckHitKey( KEY_INPUT_DOWN ) == 1 )
        {
            CameraPos.y -= 1.0f ;
        }
        if( CheckHitKey( KEY_INPUT_LEFT ) == 1 )
        {
            CameraPos.x -= 20.0f ;
        }
        if( CheckHitKey( KEY_INPUT_RIGHT ) == 1 )
        {
            CameraPos.x += 20.0f ;
        }
        
		//	カメラの設定
        SetCameraNearFar( 100.0f, 15000.0f ) ; 
        SetCameraPositionAndTarget_UpVecY( CameraPos, VGet( 0.0f, 0.0f, 0.0f ) );

		//	物体
		DrawSphere3D( VGet( 0.0f, 0.0f, 0.0f ), 100.0f, 10, GetColor( 255,255,255 ), GetColor( 255, 255, 255 ), TRUE ) ;

		//	仮定水面(部分)を描画
    	DrawPolygonIndexed3D( Vertex, 4, Index, 2, DX_NONE_GRAPH, TRUE );
		
        //  カメラのビューマトリクスを得る。
        MATRIX viewMtx = GetCameraViewMatrix();
        
        //  カメラの方向のZが100fとちょっと先の位置を得る。つまりカメラに映る水面の端
        MATRIX dirMtx = MInverse(viewMtx);
        VECTOR dirVec = VTransformSR( VGet(0.0f,0.0f,10000.0f), dirMtx );
        dirVec.y = 0;//y成分を消す
		dirVec = VNorm(dirVec);//単位ベクトル化
		dirVec = VScale(dirVec, 100.01f );//距離分拡大。少し奥。
        
		//	カメラの近くの仮想水面の座標
		VECTOR TargetSeaPos = CameraPos;
		TargetSeaPos.y = 0; //水面は高さ0
		TargetSeaPos = VAdd(TargetSeaPos,dirVec); //ベクトルの加算

		//	ワールドから画面座標に変換。こちらはマジックナンバーで調整した。
		VECTOR ScreenSeaPos = CameraPos;
		ScreenSeaPos.y = 0; //水面は高さ0
		dirVec = VNorm(dirVec);//単位ベクトル化
		dirVec = VScale(dirVec, 96.45f );//距離分拡大
		ScreenSeaPos = VAdd(ScreenSeaPos,dirVec); //ベクトルの加算
		VECTOR screen_pos = ConvWorldPosToScreenPos( ScreenSeaPos ) ;
		
		//	値の表示
		DrawFormatString( 0, 16, GetColor( 255,0,0 ), "camera (x,y,z)=(%7.1f,%7.1f,%7.1f)", CameraPos.x, CameraPos.y ,CameraPos.z ) ;
		DrawFormatString( 0, 32, GetColor( 255,0,0 ), "target sea (x,y,z)=(%7.1f,%7.1f,%7.1f)", TargetSeaPos.x, TargetSeaPos.y ,TargetSeaPos.z ) ;
		DrawFormatString( 0, 48, GetColor( 255,0,0 ), "screen_pos (x,y)=(%7.1f,%7.1f)", screen_pos.x, screen_pos.y ) ;
		
		//	カメラの近くの仮想水面の座標が画面内なら、海面下の描画を行う。
		BOOL bDrawUnderSea = false;
        if( CheckCameraViewClip( TargetSeaPos ) == FALSE ) {
			//	画面内
            DrawString( 0, 0, "Screen in", GetColor( 255,255,255 ) ) ;
            bDrawUnderSea = true;
        } else {
			//	画面外
            DrawString( 0, 0, "Screen out", GetColor( 255,255,255 ) ) ;
        }
		//	画面外でも座標がマイナスなら海面下の描画をを行う。
		if( screen_pos.y < 0 ) {
            bDrawUnderSea = true;
		}
		
		//	screen_pos.y を元に海面下の描画
		if( bDrawUnderSea ) {
			//	海面下を半透明のBOXで塗りつぶす。分かりやすいように黄色
			SetDrawBlendMode( DX_BLENDMODE_ALPHA, 64 ) ;
			DrawBox( 0, screen_pos.y, DEFAULT_SCREEN_SIZE_X, DEFAULT_SCREEN_SIZE_Y, GetColor( 0,0,255 ), TRUE ) ;
//			DrawBox( 0, screen_pos.y, DEFAULT_SCREEN_SIZE_X, DEFAULT_SCREEN_SIZE_Y, GetColor( 255,255,0 ), TRUE ) ;
			SetDrawBlendMode( DX_BLENDMODE_ALPHA, 255 ) ;
		}

        // 取得したスクリーン座標に四角形を描画
//        DrawBox( ScreenPos.x - 2, ScreenPos.y - 2, ScreenPos.x + 2, ScreenPos.y + 2, GetColor( 255,0,0 ), TRUE ) ;

        ScreenFlip() ;
    }

    DxLib_End() ;
    return 0 ;
}

Re: DxLibで3Dの海を作成する方法について

Posted: 2014年1月01日(水) 16:47
by あかり
ソースやいろいろな案を出して頂き、ありがとうございます!
出して頂いた案を作れるかわかりませんが、作ってみようと思います。

Re: DxLibで3Dの海を作成する方法について

Posted: 2014年1月01日(水) 17:07
by softya(ソフト屋)
あかり さんが書きました:ソースやいろいろな案を出して頂き、ありがとうございます!
出して頂いた案を作れるかわかりませんが、作ってみようと思います。
とりあえず、やってみたのは余り褒められた方法ではありません。
何日か考えれば良いアイデアも出そうですが、そこまで時間取れないので。

シャドウとか、Zバッファとか、そっちで解決できるのなら誤差もないのでそちらが一番です。