椿の日記

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

複数のオブジェクトの表示

f:id:tbk:20110507164119p:image

複数のオブジェクトを表示するので、Sceneオブジェクトを作って、最近傍衝突点を返す関数を作る。

struct Scene
{
    std::vector<Sphere> spheres;
    std::vector<Plane> planes;
    std::vector<Vector3> directionalLights;
};

struct Contact
{
    Vector3 p;
    Vector3 n;
    float t;
};

Scene CreateScene()
{
    Sphere sphere( Vector3( 0, 0, 1 ), 1.0f );
    Plane plane( Vector3( 0, 1, 0 ), -0.1f );
    Vector3 directionalLight = MathUtil::Normal( Vector3( -1, -1, 1 ) );

    Scene scene;
    scene.spheres.push_back( sphere );
    scene.planes.push_back( plane );
    scene.directionalLights.push_back( directionalLight );

    return scene;
}

/*
    レイと平面の交差位置取得。

    平面方程式  p・n = d
    直線方程式  p = p0 + vt
    について代入してtについて解くと得られる。
*/
bool Test_Ray_Plane( const Vector3& p0, const Vector3& v, const Plane& plane, float* out_t )
{
    float t = ( plane.d - MathUtil::Dot( p0, plane.n ) ) / MathUtil::Dot( v, plane.n );

    if( t > 0 )
    {
        *out_t = t;
        return true;
    }
    else
    {
        return false;
    }
}

bool Test_Ray_Scene( const Vector3& p0, const Vector3& v, const Scene& scene, Contact* out_contact )
{
    Contact contact;
    contact.t = FLT_MAX;

    for( std::vector<Sphere>::const_iterator it = scene.spheres.begin(); it != scene.spheres.end(); ++it )
    {
        float t;
        if( Test_Ray_Sphere( p0, v, *it, &t ) )
        {
            if( t < contact.t )
            {
                contact.t = t;
                contact.p = p0 + t * v;
                contact.n = MathUtil::Normal( contact.p - it->p );
            }
        }
    }
    
    for( std::vector<Plane>::const_iterator it = scene.planes.begin(); it != scene.planes.end(); ++it )
    {
        float t;
        if( Test_Ray_Plane( p0, v, *it, &t ) )
        {
            if( t < contact.t )
            {
                contact.t = t;
                contact.p = p0 + t * v;
                contact.n = MathUtil::Normal( it->n );
            }
        }
    }

    if( contact.t < FLT_MAX )
    {
        *out_contact = contact;
        return true;
    }
    else
    {
        return false;
    }
}

/*
    光線追跡
 */
uint32_t TraceRay( const Vector3& p, const Vector3& d, const Scene& scene )
{
    // レイと衝突するか判定
    Contact contact;

    if( Test_Ray_Scene( p, d, scene, &contact ) )
    {
        float sum = 0;

        for( std::vector<Vector3>::const_iterator it = scene.directionalLights.begin(); it != scene.directionalLights.end(); ++it )
        {
            float diffuse = max( 0, MathUtil::Dot( contact.n, -*it ) );     // ディフューズライティング
            sum += diffuse;
        }

        return FRGBAToARGB32( sum, sum, sum, 1.0f );
    }
    else
    {
        return 0x00000080;
    }
}