2026年8月9日日曜日

【Grasshopper_117】シボの形状を簡易的に再現してみました。


今月は、シボの形状をポリゴンメッシュで簡易的に再現してみた内容をまとめてみました。

断面形状等を精密に再現したものではなく、あくまでも画像の輝度値から似たような雰囲気に仕立てたものとなりますのでご了承願います。

近頃は生成Ai関連の業務が多く、Grasshopperによるモデリングの時間がかなり減ってしまいましたが、忘れないように細々と触っております。

ここ数年にわたりAiの進化が激しく、デザインの提案レベルであれば短時間の画像生成で代用できてしまい、わざわざデータを作る必要が無いため、今後もこのような傾向が続きそうですね。

少し話が脱線しましたが、今回はシボ形状の作り方を解説していきます。

・Rhino上で立体形状を作成します。シボ柄を配置する表面はエッジから1mmオフセット&フィレット処理をしています。

・XY平面上に、適当な大きさの正方形の中に点を配置します。
・ベタ塗りみたいに見えますが、実際には0.1mmピッチで点を作成しています。
・シボをそれっぽく再現するには点の密度を細かくする必要があるためデータサイズがかなり大きくなってしまいます。

・作成した点群をギリギリカバーする範囲のサーフェスを作成します。

・次に、シボのサンプル画像を用意します。上の画像はChat GPTを使って生成しました。
・サイズが1254 x 1254で96dpiのpng画像です。

・サーフェスにおける点群の位置のUV座標を取り出し、Image Samplerのノードにつないで輝度値を取り出します。
・取り出した輝度値をRemapノードにつないで0~最大深さの範囲に変換し、Z座標軸にかけ合わせてベクトルデータに変換します。

・Image Samplerの設定は、XとYの範囲が0 To 1、Channelはbrightnessを選択。

・計算したベクトルに沿って点を移動させたあと、Delaunay Mesh(ドロネーメッシュ)コンポーネントにつないでポリゴンメッシュを生成します。

・XY平面上のポリゴンメッシュを冒頭で作成した表面サーフェスにメッシュのモーフィングを行います。

・モーフィングの個所はノードで組む方法がなかなか思いつかなかったので、Chat GPTでPythonスクリプトを生成させる方法を取りました。スクリプトの中身は以下となります。

-----------------------------------------------------------------------------------------------------------------

"""GhPython (Rhino7)

Inputs
------
input  : Mesh
target : Surface または BrepFace

Output
------
object : Mesh
"""

import Rhino
import Rhino.Geometry as rg
import Grasshopper as gh


# ==========================================================
# Utility
# ==========================================================

def clamp(value, minimum, maximum):
    if value < minimum:
        return minimum
    if value > maximum:
        return maximum
    return value


def get_surface(obj):
    """Surface/BrepFaceどちらでも受け付ける"""

    if obj is None:
        return None

    if isinstance(obj, rg.Surface):
        return obj

    if isinstance(obj, rg.BrepFace):
        return obj.UnderlyingSurface()

    if hasattr(obj, "UnderlyingSurface"):
        return obj.UnderlyingSurface()

    return None


# ==========================================================
# Morph
# ==========================================================

def morph_mesh(mesh, surface):

    bbox = mesh.GetBoundingBox(True)

    xmin = bbox.Min.X
    ymin = bbox.Min.Y
    zmin = bbox.Min.Z

    xmax = bbox.Max.X
    ymax = bbox.Max.Y

    width = xmax - xmin
    height = ymax - ymin

    if width < Rhino.RhinoMath.ZeroTolerance:
        raise Exception("BoundingBoxの幅が0です。")

    if height < Rhino.RhinoMath.ZeroTolerance:
        raise Exception("BoundingBoxの高さが0です。")

    # Surface Domain
    u_domain = surface.Domain(0)
    v_domain = surface.Domain(1)

    result = mesh.DuplicateMesh()

    verts = result.Vertices

    for i in range(verts.Count):

        v = verts[i]

        # -------------------------
        # XY -> 0~1
        # -------------------------

        uu = (v.X - xmin) / width
        vv = (v.Y - ymin) / height

        uu = clamp(uu, 0.0, 1.0)
        vv = clamp(vv, 0.0, 1.0)

        # -------------------------
        # Surface UV
        # -------------------------

        u = u_domain.ParameterAt(uu)
        w = v_domain.ParameterAt(vv)

        pt = surface.PointAt(u, w)
        normal = surface.NormalAt(u, w)

        normal.Unitize()

        # 元メッシュの高さを保持
        offset = v.Z - zmin

        pt = pt + normal * offset

        verts.SetVertex(i, pt)

    result.Normals.ComputeNormals()
    result.Compact()

    return result


# ==========================================================
# Main
# ==========================================================

object = None

try:

    if input is None:
        raise Exception("Meshが入力されていません。")

    if target is None:
        raise Exception("Surfaceが入力されていません。")

    if not isinstance(input, rg.Mesh):
        raise Exception("inputはMeshを入力してください。")

    srf = get_surface(target)

    if srf is None:
        raise Exception("targetはSurfaceまたはBrepFaceである必要があります。")

    object = morph_mesh(input, srf)

except Exception as e:

    ghenv.Component.AddRuntimeMessage(
        gh.Kernel.GH_RuntimeMessageLevel.Error,
        str(e)
    )

    if isinstance(input, rg.Mesh):
        object = input.DuplicateMesh()
    else:
        object = None

----------------------------------------------------------------------------------------------------------------------

・シボ形状と相関を取る形状を作成します。
・冒頭で作成した形状のうち、シボを配置する表面のエッジ部分をZ方向に押し出しサーフェスを作成し、Mesh Brepノードにつないでメッシュに変換します。

・Mesh Brepノードの設定によってメッシュの細かさが変わります。今回は、Max Edgeの項目のみ変更しています。

・Mesh Splitノードにそれぞれつないで分割し、その後ろにメッシュの結合、ウェルド処理をつなげたら完成です。

・データのサイズが大きいため、小さなサンプル形状を作るぐらいが限界かもしれません。


0 件のコメント:

コメントを投稿

【Grasshopper_117】シボの形状を簡易的に再現してみました。

今月は、シボの形状をポリゴンメッシュで簡易的に再現してみた内容をまとめてみました。 断面形状等を精密に再現したものではなく、あくまでも画像の輝度値から似たような雰囲気に仕立てたものとなりますのでご了承願います。