Three.js로 제작하는 3D에셋 뷰어를 위한 3D Asset V-Ray 쉐이더 속성 Excel 파일 저장하기 v1.1


Three.js로 제작하는 3D에셋 뷰어를 위한 3D Asset V-Ray 쉐이더 속성 Excel 파일 저장하기

 

들어가며

Three.js 뷰어에 적용할 3D Asset의 V-Ray 쉐이더 속성을 Excel 파일로 저장하는 과정을 초보자를 위해 단계별로 설명합니다.

 

필요한 준비물

이 프로젝트를 시작하기 전에 Python과 필요한 패키지들을 설치해야 합니다. 다음 명령어들을 터미널에서 실행하여 필요한 패키지를 설치하세요:

pip install openpyxl

openpyxl을 설치한 후 마야가 해당 패키지를 인식하기 위해
‘C:\Users\사용자이름\AppData\Local\Programs\Python\파이썬 버전\Lib\site-packages’ 경로의
아래 4개 폴더를 복사 후 문서 폴더의 ‘maya\마야 버전\scripts\site-packages\’ 에 복사한 후 마야를 실행하거나 재실행 합니다.(패키지의 버전은 변경될 수 있습니다.)

et_xmlfile,
et_xmlfile-1.1.0.dist-info,
openpyxl,
openpyxl-3.1.2.dist-info

 

1단계: 필요한 모듈 임포트

먼저 필요한 Python 모듈들을 임포트합니다. 이 모듈들은 파일 작업, 색상 변환 및 Excel 파일 작업을 위해 필요합니다.

# 필요한 모듈 임포트
import maya.cmds as cmds  # Maya 명령어를 위한 모듈
import os  # 파일 시스템 경로와 상호작용을 위한 모듈
import colorsys  # 색상 변환을 위한 모듈
from openpyxl import load_workbook, Workbook  # Excel 파일 작업을 위한 모듈
from maya.OpenMaya import MGlobal  # Maya 전역 메시지 출력을 위한 모듈

 

2단계: 색상 변환 및 연결 확인 함수 정의

HSV 색상을 RGB로 변환하고, 어트리뷰트가 다른 어트리뷰트에 연결되어 있는지 확인하는 함수를 정의합니다.

# HSV 색상을 RGB로 변환하는 함수
def hsv_to_rgb(h, s, v):
    return colorsys.hsv_to_rgb(h, s, v)

# 어트리뷰트가 다른 어트리뷰트에 연결되어 있는지 확인하는 함수
def is_connected(attr):
    return cmds.connectionInfo(attr, isDestination=True) and cmds.listConnections(attr)

 

3단계: RGB 색상을 16진수로 변환하는 함수 정의

RGB 색상을 16진수 형식으로 변환하는 함수를 정의합니다. 이 함수는 쉐이더 속성에서 RGB 값을 16진수로 변환하는 데 사용됩니다.

# RGB 색상을 16진수 형식으로 변환하는 함수
def rgb_to_hex(rgb):
    return '0x{:02x}{:02x}{:02x}'.format(int(rgb[0]*255), int(rgb[1]*255), int(rgb[2]*255))

 

4단계: 쉐이더 속성 추출 및 Excel 파일로 저장

V-Ray 쉐이더의 속성을 추출하고 이를 Excel 파일로 저장하는 메인 함수를 정의합니다. 이 함수는 Maya의 쉐이더 목록을 순회하면서 각 쉐이더의 속성을 Excel 파일에 기록합니다.

# 쉐이더 속성을 Excel 파일로 추출하는 함수
def get_shader_properties_xlsx():
    # 건너뛸 쉐이더 리스트
    skip_shaders = ['lambert1', 'particleCloud1', 'shaderGlow1', 'standardSurface1']
    current_file = cmds.file(query=True, sceneName=True)  # 현재 열려 있는 Maya 파일의 경로
    file_path = os.path.dirname(current_file)  # 파일의 디렉토리 경로
    output_file_path = os.path.join(file_path, "props.xlsx")  # 출력할 Excel 파일의 경로

    # 기존 파일이 존재하면 삭제
    if os.path.exists(output_file_path):
        os.remove(output_file_path)

    wb = Workbook()  # 새 Excel 워크북 생성
    
    shaders = sorted(cmds.ls(materials=True))  # 쉐이더 목록 불러오기
    for shader in shaders:
        if shader not in skip_shaders:
            try:
                # 쉐이더 이름으로 워크시트 생성
                ws = wb[shader] if shader in wb.sheetnames else wb.create_sheet(title=shader)
                attr_data = get_shader_attributes(shader)  # 쉐이더 속성 추출
                if attr_data:
                    for attribute, value in attr_data.items():
                        ws.append([attribute, value])  # 워크시트에 속성 추가
            except ValueError as ve:
                MGlobal.displayError(f"Error processing shader {shader}: {ve}")  # 에러 메시지 출력

    # 기본 생성된 Sheet 제거
    if 'Sheet' in wb.sheetnames:
        wb.remove(wb['Sheet'])

    wb.save(output_file_path)  # 워크북 저장
    MGlobal.displayInfo("props.xlsx creation is completed!!!")  # 완료 메시지 출력

 

5단계: 특정 쉐이더의 속성 추출 함수 정의

특정 쉐이더의 속성을 추출하는 get_shader_attributes 함수를 정의합니다. 이 함수는 특정 쉐이더의 VRayMtl 속성을 추출하고, 이를 해석하여 저장합니다.

# 특정 쉐이더의 속성을 추출하는 함수
def get_shader_attributes(shader):
    shader_type = cmds.nodeType(shader)  # 쉐이더 타입 확인
    attributes = {}

    # VRayMtl 속성 이름 매핑
    vray_attr_name_mapping = {
        "reflectionColorAmount": "reflectivity",
        "reflectionGlossiness": "roughness",
        "metalness": "metalness",
        "refractionIOR": "ior",
        "sheenColorAmount": "sheen",
        "sheenGlossiness": "sheenRoughness",
        "refractionColor": "transmission",
        "color": "baseColor",
        "sheenColor": "sheenColor"
    }
    
    # Lambert 속성 이름 매핑 (예시)
    lambert_attr_name_mapping = {
        "color": "baseColor",
        "translucence": "transmission",
        "refractiveIndex": "ior"
        # 여기에 추가적인 Lambert 쉐이더 속성 매핑을 추가하세요.
    }

    if shader_type == "VRayMtl":
        for internal_attr, display_attr in vray_attr_name_mapping.items():
            full_attr = f"{shader}.{internal_attr}"
            if not is_connected(full_attr):
                attr_value = cmds.getAttr(full_attr)
                if internal_attr == "refractionColor":  # refractionColor 속성 처리
                    # RGB를 HSV로 변환
                    hsv_value = colorsys.rgb_to_hsv(attr_value[0][0], attr_value[0][1], attr_value[0][2])
                    # V(밝기) 값을 추출하여 속성에 할당
                    attributes[display_attr] = hsv_value[2]
                elif isinstance(attr_value, list) and isinstance(attr_value[0], tuple):  # RGB 색상값 처리
                    hex_value = rgb_to_hex(attr_value[0])
                    attributes[display_attr] = hex_value
                elif isinstance(attr_value, float):  # 단일 float 값인 경우
                    attributes[display_attr] = round(attr_value, 3)
                else:  # 다른 타입의 경우 (예: 리스트)
                    attributes[display_attr] = str(attr_value)  # 문자열로 변환

        # 추가 속성 처리
        attributes['specularIntensity'] = 1.0

    elif shader_type == "lambert":
        for internal_attr, display_attr in lambert_attr_name_mapping.items():
            full_attr = f"{shader}.{internal_attr}"
            if not is_connected(full_attr):
                attr_value = cmds.getAttr(full_attr)
                if isinstance(attr_value, list) and isinstance(attr_value[0], tuple):  # RGB 색상값이 리스트 안의 튜플로 들어있는 경우
                    hex_value = rgb_to_hex(attr_value[0])
                    attributes[display_attr] = hex_value
                elif isinstance(attr_value, float):  # 단일 float 값인 경우
                    attributes[display_attr] = round(attr_value, 3)
                else:  # 다른 타입의 경우 (예: 리스트)
                    attributes[display_attr] = str(attr_value)  # 문자열로 변환

    return attributes  # 추출된 속성 반환

get_shader_properties_xlsx()  # 쉐이더 속성 추출 함수 실행

 







 

 

6단계: 완료 다이얼로그 표시

모든 과정이 끝나면 사용자에게 작업 완료를 알리는 다이얼로그를 표시합니다.

# 완료 다이얼로그 표시
cmds.confirmDialog(title='완료 다이얼로그', message='프로세스가 성공적으로 완료되었습니다!', button='확인')

 

7단계: 최종 코드

# 필요한 모듈 임포트
import maya.cmds as cmds  # Maya 명령어를 위한 모듈
import os  # 파일 시스템 경로와 상호작용을 위한 모듈
import colorsys  # 색상 변환을 위한 모듈
from openpyxl import load_workbook, Workbook  # Excel 파일 작업을 위한 모듈
from maya.OpenMaya import MGlobal  # Maya 전역 메시지 출력을 위한 모듈

# HSV 색상을 RGB로 변환하는 함수
def hsv_to_rgb(h, s, v):
    return colorsys.hsv_to_rgb(h, s, v)

# 어트리뷰트가 다른 어트리뷰트에 연결되어 있는지 확인하는 함수
def is_connected(attr):
    return cmds.connectionInfo(attr, isDestination=True) and cmds.listConnections(attr)

# RGB 색상을 16진수 형식으로 변환하는 함수
def rgb_to_hex(rgb):
    return '0x{:02x}{:02x}{:02x}'.format(int(rgb[0]*255), int(rgb[1]*255), int(rgb[2]*255))

# 쉐이더 속성을 Excel 파일로 추출하는 함수
def get_shader_properties_xlsx():
    # 건너뛸 쉐이더 리스트
    skip_shaders = ['lambert1', 'particleCloud1', 'shaderGlow1', 'standardSurface1']
    current_file = cmds.file(query=True, sceneName=True)  # 현재 열려 있는 Maya 파일의 경로
    file_path = os.path.dirname(current_file)  # 파일의 디렉토리 경로
    output_file_path = os.path.join(file_path, "props.xlsx")  # 출력할 Excel 파일의 경로

    # 기존 파일이 존재하면 삭제
    if os.path.exists(output_file_path):
        os.remove(output_file_path)

    wb = Workbook()  # 새 Excel 워크북 생성
    
    shaders = sorted(cmds.ls(materials=True))  # 쉐이더 목록 불러오기
    for shader in shaders:
        if shader not in skip_shaders:
            try:
                # 쉐이더 이름으로 워크시트 생성
                ws = wb[shader] if shader in wb.sheetnames else wb.create_sheet(title=shader)
                attr_data = get_shader_attributes(shader)  # 쉐이더 속성 추출
                if attr_data:
                    for attribute, value in attr_data.items():
                        ws.append([attribute, value])  # 워크시트에 속성 추가
            except ValueError as ve:
                MGlobal.displayError(f"Error processing shader {shader}: {ve}")  # 에러 메시지 출력

    # 기본 생성된 Sheet 제거
    if 'Sheet' in wb.sheetnames:
        wb.remove(wb['Sheet'])

    wb.save(output_file_path)  # 워크북 저장
    MGlobal.displayInfo("props.xlsx creation is completed!!!")  # 완료 메시지 출력

# 특정 쉐이더의 속성을 추출하는 함수
def get_shader_attributes(shader):
    shader_type = cmds.nodeType(shader)  # 쉐이더 타입 확인
    attributes = {}

    # VRayMtl 속성 이름 매핑
    vray_attr_name_mapping = {
        "reflectionColorAmount": "reflectivity",
        "reflectionGlossiness": "roughness",
        "metalness": "metalness",
        "refractionIOR": "ior",
        "sheenColorAmount": "sheen",
        "sheenGlossiness": "sheenRoughness",
        "refractionColor": "transmission",
        "color": "baseColor",
        "sheenColor": "sheenColor"
    }
    
    # Lambert 속성 이름 매핑 (예시)
    lambert_attr_name_mapping = {
        "color": "baseColor",
        "translucence": "transmission",
        "refractiveIndex": "ior"
        # 여기에 추가적인 Lambert 쉐이더 속성 매핑을 추가하세요.
    }

    if shader_type == "VRayMtl":
        for internal_attr, display_attr in vray_attr_name_mapping.items():
            full_attr = f"{shader}.{internal_attr}"
            if not is_connected(full_attr):
                attr_value = cmds.getAttr(full_attr)
                if internal_attr == "refractionColor":  # refractionColor 속성 처리
                    # RGB를 HSV로 변환
                    hsv_value = colorsys.rgb_to_hsv(attr_value[0][0], attr_value[0][1], attr_value[0][2])
                    # V(밝기) 값을 추출하여 속성에 할당
                    attributes[display_attr] = hsv_value[2]
                elif isinstance(attr_value, list) and isinstance(attr_value[0], tuple):  # RGB 색상값 처리
                    hex_value = rgb_to_hex(attr_value[0])
                    attributes[display_attr] = hex_value
                elif isinstance(attr_value, float):  # 단일 float 값인 경우
                    attributes[display_attr] = round(attr_value, 3)
                else:  # 다른 타입의 경우 (예: 리스트)
                    attributes[display_attr] = str(attr_value)  # 문자열로 변환

        # 추가 속성 처리
        attributes['specularIntensity'] = 1.0

    elif shader_type == "lambert":
        for internal_attr, display_attr in lambert_attr_name_mapping.items():
            full_attr = f"{shader}.{internal_attr}"
            if not is_connected(full_attr):
                attr_value = cmds.getAttr(full_attr)
                if isinstance(attr_value, list) and isinstance(attr_value[0], tuple):  # RGB 색상값이 리스트 안의 튜플로 들어있는 경우
                    hex_value = rgb_to_hex(attr_value[0])
                    attributes[display_attr] = hex_value
                elif isinstance(attr_value, float):  # 단일 float 값인 경우
                    attributes[display_attr] = round(attr_value, 3)
                else:  # 다른 타입의 경우 (예: 리스트)
                    attributes[display_attr] = str(attr_value)  # 문자열로 변환

    return attributes  # 추출된 속성 반환

get_shader_properties_xlsx()  # 쉐이더 속성 추출 함수 실행

# 완료 다이얼로그 표시
cmds.confirmDialog(title='완료 다이얼로그', message='프로세스가 성공적으로 완료되었습니다!', button='확인')

 

8단계: 실행

  • Maya 실행
  • test.ma(예시) 파일 불러오기
  • Script Editor 실행
  • Script Editor의 Python탭의 내용에 코드 붙여넣기 및 코드 전체 선택(ctrl+a)
  • 전체 선택된 코드를 마우스 가운데 버튼으로 드래그 하여 원하는 shelf(일반적으로 Custom shelf)에 드랍
  • 드랍 후 생기는 아이콘을 우클릭 유지한 채로 edit 선택
  • Shelf Editor 창의 Shelves 탭으로 이동
  • 하단의 Tolltip 내용을 모두 제거 후 원하는 팁 입력(비워둬도 됨)
  • Icon Label에 원하는 아이콘 이름 입력(VtoExcel 등)
  • 아이콘 이미지 변경을 원하는 경우 Icon Name 항목의 우측에 폴더나 M아이콘을 클릭하여 원하는 아이콘 이미지 선택
  • 원하는 편집이 끝났다면 하단의 Save All Shelves 버튼 클릭하여 편집 종료
  • VtoExcel버튼 클릭하여 실행
  • 정상실행이 되면 ‘프로세스가 성공적으로 완료되었습니다!’ 창이 나타납니다.
  • 확인 버튼 누른 후 test.ma가 저장된 경로로 가면 props.xlsx파일이 생성되어 있고, 내부에 test.ma의 쉐이더 목록(lambert1, particleCloud1, shaderGlow1, standardSurface1은 기본 쉐이더라 제외)과 그 속성들이 저장되어 있습니다.
  • 더 필요한 속성들이 있다면 ‘VRayMtl 속성 이름 매핑’ 부분에 원하는 속성과 속성이 표현될 이름을 추가하거나 변경하세요!