ヘキサ日記 Blog

 

2016年11月10日

フォントのDistanceFieldTexture生成ツールをつくる その2

みなさんこんにちは。グリフォンです。

最近いっきに冷え込んできて、先週の土日は風邪で家から一歩も出れませんでした

 

さて今回は前回の続き「フォントのDistanceFieldTexture生成ツールをつくる」のその2です。

 

大まかな処理の流れは以下

フォントを1文字ずつ読み出してピクセル情報を取得

ピクセル情報からDistance Field情報を生成

CharacterInfoを生成

アトラステクスチャに書き込む

アトラステクスチャとFontSettingsを出力保存

 

 

今回はを説明していきます。

private bool GenerateDistanceFieldTexture()
{
    // 有効領域を切り出す
    if( !ClippingValidRect() ) {
        return false;
    }

    // 入力テクスチャ情報をバッファ
    var srcPixels = _BufferTexture.GetPixels32();
    var srcWidth  = _BufferTexture.width;
    var srcHeight = _BufferTexture.height;

    // 出力用のテクスチャを生成
    var dstWidth  = srcWidth / _Param_QualityScale;
    var dstHeight = srcHeight / _Param_QualityScale;
    var DFTexture = new Texture2D(dstWidth, dstHeight, TextureFormat.ARGB32, false, false);

    // 距離テクスチャを生成
    var dstPixels = DFTexture.GetPixels32();
    for( int y=0; y<dstHeight; ++y ) {
        for( int x=0; x<dstWidth; ++x ) {
            var srcCenterX = (x * _Param_QualityScale) + (_Param_QualityScale / 2);
            var srcCenterY = (y * _Param_QualityScale) + (_Param_QualityScale / 2);
            var distance   = FindSignedDistance(srcCenterX, srcCenterY, srcWidth, srcHeight, srcPixels);
            dstPixels[(y * dstWidth) + x] = new Color32(255, 255, 255, CalcDistanceToAlpha32(distance / _Param_SpreadDist));
        }
    }
    DFTexture.SetPixels32(dstPixels);
    DFTexture.Apply();

    // 生成した距離テクスチャでバッファに上書き
    _BufferTexture    = DFTexture;

    // 有効ピクセル情報追加
    _regularCharsWidthSum    += dstWidth;
    _regularCharsHeightSum    += dstHeight;

    return true;
}

 

4行目の ClippingValidRect() は、の処理のときに少し大きめなテクスチャにフォントをキャプチャしたので、ピクセル情報を調べて余計な余白を削除しています。

また、フォントに存在しない文字など全て空白の場合などは false としてスキップしています。

 

次に各種外部パラメータの説明です。

_Param_QualityScale は、より綺麗に距離テクスチャを生成するため、

『拡大してキャプチャ → 距離情報計算 → 縮小して保存 』 という手順を踏んでいるので、そのためのスケール値です。

_Param_SpreadDist は、境界からどの程度の距離を 0.0 ~ 1.0 にするかという閾値です。詳しくはこちらを見てください。

 

続いて、24行目の FindSignedDistance() では指定のピクセル位置から最近傍境界点までの距離を計算しています。

private float FindSignedDistance(int srcCenterX, int srcCenterY, int srcWidth, int srcHeight, Color32[] srcPixels)
{
    var delta   = _Param_SpreadDist * _Param_QualityScale;
    var srcMinX = Mathf.Max(0, srcCenterX - delta);
    var srcMinY = Mathf.Max(0, srcCenterY - delta);
    var srcMaxX = Mathf.Min(srcWidth - 1, srcCenterX + delta);
    var srcMaxY = Mathf.Min(srcHeight - 1, srcCenterY + delta);
    
    var curInside     = srcPixels[(srcCenterY * srcWidth) + srcCenterX].r;
    var closestDistSq = delta * delta;

    // 最近傍境界点までの距離を取得
    for( int y=srcMinY; y<srcMaxY; ++y ) {
        for( int x=srcMinX; x<srcMaxX; ++x ) {
            if( curInside != srcPixels[(y * srcWidth) + x].r ) {
                var distSq = CalcDistSq(srcCenterX, srcCenterY, x, y);
                if( distSq < closestDistSq ) {
                    closestDistSq = distSq;
                }
            }
        }
    }

    // 二乗距離を変換して符号判定する
    var closestDist = Mathf.Sqrt((float)closestDistSq) / _Param_QualityScale;
        closestDist = Mathf.Min(closestDist, _Param_SpreadDist);
    if( 0 == curInside ) {
        // 白で描画しているので、検索ピクセルが黒の場合は符号を反転
        closestDist = -closestDist;
    }

    return closestDist;
}

 

これらの処理で注意することは、ソーステクスチャより結果テクスチャのほうが _Param_QualityScale の分、小さくなるということです。

このスケール値が大きくなれば、より綺麗になりますが計算負荷が増し時間が掛かります。

スマホのゲームなどで使用する分には8倍くらいが費用対効果として妥協点かと思います。

※第2水準まで8000文字弱を処理したときは10分程度かかりました。

 

 

今回はここまで。以降はまた次回をお楽しみに


ヘキサブログ ピックアップ



過去の日記はこちら

2017年10月
« 9月    
 1
2345678
9101112131415
16171819202122
23242526272829
3031