研究開発日誌

CG研究・開発のちょっとしたメモ書き

Sprite Lamp

2015-01-13 NPR

ドット絵をリライティングするソフトウェアSprite Lampの紹介です.

デモページにいくと分かりますが,このソフトウェアでは,ドット絵を立体的にライティングすることができます. システム構成としては, 1. 上下左右のライティングを基に法線画像を推定する部分 2. Diffuse Map(拡散反射マップ)を基にライティングする部分

の2ステップに分かれます.

法線推定の方法

上記のソフトウェアでは,予め決められたライティングの陰影結果を入力とすることで,法線推定を簡略化しているものと思われます.基本的なライティングの式は,拡散反射の場合,

$I = \lfloor \mathbf{L} \cdot \mathbf{N} \rfloor$

と表すことができます.$\lfloor x \rfloor$はxが0以上の部分を取り出す関数です(ここでは,議論を簡単にするため,拡散反射係数,環境反射成分は省略しています).

右: $I_r = \lfloor \mathbf{(1, 0, 0)} \cdot \mathbf{N} \rfloor$

上: $I_t = \lfloor \mathbf{(0, 1, 0)} \cdot \mathbf{N} \rfloor$

左: $I_l = \lfloor \mathbf{(1, 0, 0)} \cdot \mathbf{N} \rfloor$

下: $I_b = \lfloor \mathbf{(0, -1, 0)} \cdot \mathbf{N} \rfloor$

から,

$N_x = I_r - I_l$

$N_y = I_t - I_b$

$N_z = \sqrt{ 1 - N_x^2 - N_y^2 }$

を計算できます.

以下が,OpenCVを使って実装したソースコードになります.

void normalFromIlluminations(const cv::Mat& I_r, const cv::Mat& I_t, const cv::Mat& I_l, const cv::Mat& I_b, cv::Mat& N)
{
    N = cv::Mat( I_r.size(), CV_32FC3 );

    int height = N.rows;
    int width = N.cols;

    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {
            float Nx = I_r.at<float>(y,x) - I_l.at<float>(y,x);
            float Ny = I_t.at<float>(y,x) - I_b.at<float>(y,x);

            float Nz = sqrt( std::max( 0.0f, 1.0f - ( Nx * Nx + Ny * Ny ) ));

            N.at<cv::Vec3f>(y,x) = cv::Vec3f( Nx, Ny, Nz);
        }
    }
}

今回は触れませんが,少し設定を変えれば任意の方向のライティングを取り扱ったり,入力とするライティングモデルを調整することも可能だと思います.

参考文献

[1] Sprite Lamp: http://www.snakehillgames.com/spritelamp/