椿の日記

たぶんプログラムの話をします

とりあえずレイトレース

f:id:tbk:20110507123228p:image

http://d.hatena.ne.jp/mokehehe/20090716/raytracer
こちらの記事を参考にしながら、レイトレースの実験を開始しました。記事に習って、レイが正しく飛ばせているかどうかの実験からです…。

そういえば、初めてDirect2D使った。意外に便利かも。ウィンドウ作ってHWNDを渡すだけなんで、こういう用途に使うなら凄く便利です。(RenderTarget作るところとかのソースは乗せてないが)

#include "stdafx.h"
#include "vector.h"

/*
    直線と球の交差判定。

    球面方程式 || p - c || = r
    直線方程式 p = p0 + vt
    pについて代入してtについて解くと得られる。
 */
bool Intersect_Line_Sphere( const Vector3& p0, const Vector3& v, const Sphere& s )
{
    Vector3 m = p0 - s.p;

    // tについての二次方程式
    float a = MathUtil::Dot( v, v );
    float b = MathUtil::Dot( v, m );
    float c = MathUtil::Dot( m, m ) - s.r * s.r;

    // 判別式を求める
    float D = b*b - a*c;

    // 交差してるなら解が2つ。接してるなら解が1つ。交差してなければ解0。
    return D > 0;
}

/*
    光線追跡
 */
uint32_t TraceRay( const Vector3& p, const Vector3& d )
{
    // シーンとして球を1つ置いてみる
    Sphere s( Vector3( 0, 0, 1 ), 1.0f );

    // 直線が衝突したら白、しなければ黒
    if( Intersect_Line_Sphere( p, d, s ) )
    {
        return 0xffffffff;
    }
    else
    {
        return 0x00000000;
    }
}

/*
    画像を作成する

    視点から原点方向を向き、垂直視野角alpha、アスペクト比aspectで視野を作る。
    そしてxy平面(z=0)に像を作る。
    像とピクセルの中心が対応する点に対して光線を追跡する。
 */
void RenderImage( uint32_t width, uint32_t height, uint32_t* image )
{
    float e = 1;
    Vector3 eye( 0, 0, -e );                        // 視点
    float aspect = (float)width / (float)height;    // 垂直方向に対する水平方向のアスペクト比
    float alpha = PI / 4;                           // 垂直視野角
    float h2 = sinf( alpha ) * e;                   // 縦方向の像の大きさ/2
    float w2 = h2 * aspect;                         // 横方向の像の大きさ/2

    // 矩形の中を(width,height)の大きさで分割します
    for( uint32_t j = 0; j < height; j++ )
    {
        for( uint32_t i = 0; i < width; i++ )
        {
            // 各ピクセルの中心への方向ベクトルを求める
            float px = ( ( (float)i + 0.5f ) / (float)width - 0.5f ) * w2 * 2;
            float py = -( ( (float)j + 0.5f ) / (float)height - 0.5f ) * h2 * 2;
            Vector3 p( px, py, 0 );
            Vector3 d = MathUtil::Normal( p - eye );

            // その方向に向けて光線追跡して色を回収
            image[j*width + i] = TraceRay( eye, d );
        }
    }
}

/*
    シーンの描画

    画像作ってビットマップオブジェクト作って全画面貼り付け
 */
void RenderScene( ID2D1HwndRenderTarget* pRenderTarget )
{
    HRESULT hr;

    D2D1_SIZE_U size = pRenderTarget->GetPixelSize();

    ID2D1Bitmap* pBitmap;
    uint32_t* image = new uint32_t[size.width * size.height];
    RenderImage( size.width, size.height, image );
    D2D1_PIXEL_FORMAT pixelFormat = D2D1::PixelFormat( DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE );

    hr = pRenderTarget->CreateBitmap( size, image, size.width * sizeof(uint32_t), D2D1::BitmapProperties( pixelFormat ), &pBitmap );
    if( FAILED(hr) )
    {
        pRenderTarget->Clear();
    }
    else
    {
        pRenderTarget->DrawBitmap( pBitmap );
    }

    delete [] image;
}

void Render( ID2D1HwndRenderTarget* pRenderTarget )
{
    pRenderTarget->BeginDraw();
    RenderScene( pRenderTarget );
    pRenderTarget->EndDraw();
}