20
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

HoudiniAdvent Calendar 2017

Day 22

ポリゴン面積を考慮したテクスチャアトラスに関して

Posted at

##テクスチャアトラスとは?
テクスチャアトラス(又はアトラステクスチャ)とは、下図のように複数の画像を1枚にまとめた画像をテクスチャアトラスと言います。おもにドローコールの軽減に使用します。
image.png
このように均等に配置する場合はMosaic COPを使用すること作成することができます。
image.png
ですが、使用する画像は3Dモデルにテクスチャマッピングする事を想定していますので、適切な画像解像度はモデルに依存します。そこで今回はモデルの面積を考慮したテクスチャアトラスの生成方法に関して紹介します。

##ポリゴン面積を考慮したテクスチャアトラスとは?

今回使用するモデルはこのように大きさの異なる複数のモデルです(1モデルあたりテクスチャは1枚、UVは0~1とします)。
image.png
これらのモデルの面積を考慮したテクスチャアトラスを作成するのが目的です。モデルの大きさが考慮された結果になっているのがわかると思います。ライトマップをアトラス化するなどを想定しています。
image.png

##実現にあたって

  • できるだけ小さい実装
  • できるだけコードを使用しない

を心がけて実装を行いたいと思います。

###ノードネットワーク
完成版のノードネットワークはこのようになっています。
image.png
※グレーのネットボックスはビジュアライズ用なので解説は省略します。

ノードネットワークは5つに分類されています。

  • メッシュのインポート
  • メッシュへアトラス処理のための情報を追加
  • アトラス化の配置情報の算出
  • パラメータ管理
  • アトラステクスチャ生成COPネットワーク

順追って解説します。

メッシュのインポート

今回は内臓さているテストジオメトリーを3種、それぞれサイズを2種の合計6モデルを用意しました。
image.png
image.png

メッシュへアトラス処理のための情報を追加

アトラス処理向けにアトリビュートを追加します。今回はテクスチャパスを追加し、処理しやすいようにパックします。
image.png

Wrangleを使用してDetailへテクスチャパスを追加します。
image.png

次にPack Primitiveへ変換し、その後マージして一つのノードにまとめます。
image.png

アトラス化の配置情報の算出

今回の重要な部分はここに集約されています。
image.png
面積を考慮したアトラステクスチャのレイアウトはUV Layoutを使用しています(※できるだけコードを使用しないで機能を実現するのが目的の一つですので、肝心な部分は既存のノードを使用します)。

まず普通にUV Layoutを実行するとこのような結果になります。これはこれで素晴らしい結果ですがUVが変更されているためアトラス化できません。 ※UV LayoutのCorrect Area Proportionsを有効にするとポリゴン面積を考慮してくれます。
image.png

で考えた結果、各モデルの面積を反映した四角に置き換える事にします。手順は以下になります。

  1. モデルを一つ取り出す
  2. Measure SOPで個々のポリゴンの面積を取得
  3. Attribute Promote SOPでポリゴンの総面積を算出
  4. 総面積を反映した四角をGrid SOPを使用して作成
  5. 1~4をメッシュ分繰り返してマージする
  6. UV Layoutでレイアウトする

結果、以下のUVが生成されます。
image.png

これでアトラス化のレイアウトは完了です。

パラメータ管理

アトラス化した画像を生成するまえにパラメータ管理を解説します。
パラメータはNullに必要なパラメータをまとめています。HDAであればパラメータを一元して扱う機能がありますが、いったんNullにまとめておけば、作業も早くなりますし、HDA化する際もUIの構築が楽になります。
image.png

アトラステクスチャ生成COPネットワーク

最後にアトラス情報を元にテクスチャを一枚にまとめる方法を解説します。HoudiniにはCOPと呼ばれる画像合成機能が搭載されていますので、COPを使用して複数画像を一枚にまとめます。
ここだけはPythonを使用して画像を一枚にまとめるネットワークを構築します。コードはできるだけシンプルにしたつもりですが、流れを解説します。アトラス化の配置情報は画像枚数分の四角メッシュで構成されていて、それぞれUVをもっていま。このUVがそのまま配置情報になっています。

  1. アトラス化の配置情報からUVを取得
  2. UV値からオフセット、スケール値を算出
  3. file、scale、compositeノードをcreateNode関数で作成し、parmでパラメータを設定し、setInputでノード間をつなぎます
  4. 2~3を繰り返す
  5. 合成画像完成
  6. ROPなどでファイルに書き出す(今回は追加していません)
  7. ついでにアトラス化情報をJSONに変換
import json

def CreateAtlas(parameters='/obj/ATLAS/PARAMETERS'):
    parms = hou.node(parameters)
    cop = hou.node(parms.parm('cop').eval())
    geo = hou.node(parms.parm('geom').eval())
    image_path = parms.parm('image_base').eval()
    canvas_size = parms.parm('size').eval()
    # delete exists cop nodes
    cop.deleteItems(cop.allItems())
    
    # build atlas
    
    canvas = cop.createNode('color')
    canvas.parm('overridesize').set(True)
    canvas.parm('size1').set(canvas_size)
    canvas.parm('size2').set(canvas_size)
    canvas.parm('colorr').set(0)
    canvas.parm('colorg').set(0)
    canvas.parm('colorb').set(0)
    
    src = canvas
    
    atlas_uv = []
    
    for p in geo.geometry().iterPrims():
        uvmin = [1.0, 1.0]
        uvmax = [0.0, 0.0]
        for v in p.vertices():
            uv = v.attribValue('uv')
            uvmin[0] = min(uvmin[0], uv[0])
            uvmin[1] = min(uvmin[1], uv[1])
            uvmax[0] = max(uvmax[0], uv[0])
            uvmax[1] = max(uvmax[1], uv[1])
        file = cop.createNode('file')
        file.parm('filename1').set(os.path.join(image_path, p.attribValue('tex')))
        w = file.parm('size1').eval()
        h = file.parm('size2').eval()
        scale = cop.createNode('scale')
        scale.setInput(0, file)
        sw = (canvas_size*(uvmax[0]-uvmin[0]))/w
        sh = (canvas_size*(uvmax[1]-uvmin[1]))/h
        scale.parm('imagefract1').set(sw)
        scale.parm('imagefract2').set(sh)
        comp = cop.createNode('composite')
        comp.parm('op').set(6)
        comp.parm('tx').set(uvmin[0])
        comp.parm('ty').set(uvmin[1])
        comp.setInput(0, scale)
        comp.setInput(1, src)
        src = comp
        # display uv scaling info
        atlas_uv.append({"offset":uvmin, "scale":[uvmax[0]-uvmin[0],uvmax[1]-uvmin[1]]})

    parms.parm('atlas_uv').set(json.dumps(atlas_uv, indent=2))
    src.setCurrent(True)
    src.setDisplayFlag(True)
    src.setRenderFlag(True)
    src.cook(force=True)    # for just in case.
    cop.layoutChildren()

Pythonコードはhou.sessionに設定しています。

おわり

もしかするとUV Layoutだけで実現できる方法があるのでは?、という疑念を拭いきれませんが、この機能でできそうなんだけど惜しいなぁという時は、工夫することで実現できてしまう事が多いのも、Houdiniの強みの一つだと思います。

HIPファイルはこちらから
https://www.dropbox.com/s/de672kg1u17wkh8/texture_atlas.zip?dl=0

20
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?