고민

프로젝트를 진행하면서 우리는 종종 옷 시뮬레이션의 문턱에 서곤한다.

이 과정에서 나타나는 고민들은 다양한데, 나는 그 중에서도

  1. ‘시물레이션 세팅의 값은 무엇인가?’,

  2. ‘어떻게 하면 가볍게 작업 할 수 있을까?’

두 가지의 주된 고민이었다.

오늘은 특히 가벼운 작업의 방법에 대해 작성해보려 한다.

당연히 이 방법이 모든 상황에 정답이 될 수는 없겠지만,

나만의 해결 방식을 기록해 보고자 한다.

Cloth Simulation

옷 시뮬레이션은 그 자체로 꽤나 무거운 작업이다.

이를 조금이라도 가벼워지게 하기 위해,

나는 low mesh에서 시물레이션을 하고,

high mesh는 warp 기능을 이용해 따라오게 하는 방식을 선호한다.

이 접근법은 high mesh에 직접 시물레이션을 적용하는 것보다 더 쾌적한 작업이 된다.

문제점

하지만 이 방법을 사용하면서도 한 가지 문제에 부딪힌다.

바로 warp 기능이 high mesh가 low mesh를 너무 정직하게 따라간다는 점이다.

딱딱한 high mesh


해결방안

이 문제를 해결하기 위해, 나는 warp이 적용된 high mesh에 직접 만든 smooth 디폼을 적용시켜봤다.

이 디폼은 메시의 모든 버텍스를 계산하기 때문에, 수축이나 디테일의 감소 같은 몇 가지 단점이 있다.

아래와 같은 기능을 조절해 단점을 극복하려 해봤다.

1. iterater를 이용한 Smooth 강도 조절

순차적으로 iterater 값을 3까지 올렸다.

2. Paint를 이용한 스무스 강도 조절

paint를 통해 smooth 노드의 전 값과 후 값을 0과 1 사이로 제어 할 수 있다.

모서리 수축을 제어 가능하다.


Deform 핵심 코드

def deform(self, data_block, geo_iter, matrix, multi_index):
    envelope = data_block.inputValue(self.envelope).asFloat()
    iterator = data_block.inputValue(self.iterator).asInt()

    if envelope == 0 or iterator == 0:
        return

    input_handle = data_block.outputArrayValue(self.input)
    input_handle.jumpToElement(multi_index)
    input_element_handle = input_handle.outputValue()

    input_geom = input_element_handle.child(self.inputGeom).asMesh()
    mesh_fn = om.MFnMesh(input_geom)
    prevIndex = om.MScriptUtil()
    prevIndex.createFromInt(0)

    vertices = om.MItMeshVertex(input_geom)
    new_pt_local = []
    geo_iter.reset()

    while not geo_iter.isDone():
        index = geo_iter.index()
        vertices.setIndex(index, prevIndex.asIntPtr())

        connectedVertices = om.MIntArray()
        vertices.getConnectedVertices(connectedVertices)

        new_pt_local.append(self.get_connected_vertices_position_avarge(mesh_fn, connectedVertices))
        geo_iter.next()

    new_pos = []
    sub_pos = []
    count = 0
    while count < iterator:
        # print(count)
        geo_iter.reset()
        while not geo_iter.isDone():
            index = geo_iter.index()
            if count == 0:
                source_pt = geo_iter.position()
                target_pt = new_pt_local[index]
            else:
                source_pt = new_pos[index]
                vertices.setIndex(index, prevIndex.asIntPtr())
                connectedVertices = om.MIntArray()
                vertices.getConnectedVertices(connectedVertices)
                length = connectedVertices.length()
                sum_point = om.MPoint(0, 0, 0, 0)
                for i in range(length):
                    point = new_pos[connectedVertices[i]]
                    sum_point.x += point.x
                    sum_point.y += point.y
                    sum_point.z += point.z

                target_pt = om.MPoint(sum_point[0] / length, sum_point[1] / length, sum_point[2] / length)

            source_weight = self.weightValue(data_block, multi_index, geo_iter.index())
            final_pt = source_pt + ((target_pt - source_pt) * envelope * source_weight)

            geo_iter.setPosition(final_pt)
            geo_iter.next()

            if count == 0:
                new_pos.append(final_pt)
                sub_pos.append(final_pt)
            else:
                sub_pos[index] = final_pt

        for i in range(len(sub_pos)):
            new_pos[i] = sub_pos[i]
        count += 1

def get_connected_vertices_position_avarge(self, meshFn, connectedVertices):
    length = connectedVertices.length()
    sum_point = om.MPoint(0, 0, 0, 0)
    connectedVerticesPosition = []
    for i in range(length):
        point = om.MPoint()
        meshFn.getPoint(connectedVertices[i], point, om.MSpace.kObject)
        connectedVerticesPosition.append(point)

        sum_point.x += point.x
        sum_point.y += point.y
        sum_point.z += point.z

    result = om.MPoint( sum_point[0] / length, sum_point[1] / length, sum_point[2] / length)

    return result

cmds와 pymel 위주로 스크립트만 만들어 사용하다가, 처음으로 openMaya를 이용해 제작한 플러그인이다.

그래픽스를 공부해보기 시작하며, 강의의 맛보기 부분에서 배울 수 있었던 블러효과가 있었다.

단순히 왼쪽 오른쪽, 위 아래 픽셀의 색상의 평균을 구하는 박스 블러를 마야의 포인트들에 적용 시켜봤으며, 원하지 않는 부분을 제어 할 수 있는 Paint 기능과 함께 Deformer를 구성하게 되었다.

지금의 형태는 작동 하는데에 의미를 가지고 있다. 추후 코드 최적화 및 GPU가속까지 적용해 볼 계획이다.

이러한 노드를 활용함으로써, warp을 사용할 때 이 방법이 향후에도 유용하게 사용될 수 있으리라 기대한다.

비록 복잡하고 새로운 시도였지만, 이 경험이 앞으로 생각 하는 방식을 유연하게 해줄 것 같다.

댓글남기기