Three.js 매테리얼에 적용하기 위한 Maya standardSurface 쉐이더 속성 Excel 파일로 저장하기


Three.js 매테리얼에 적용하기 위한 Maya standardSurface 쉐이더 속성 Excel 파일로 저장하기

이번에는 Maya에서 작성된 standardSurface 쉐이더의 속성을 Excel 파일로 내보내고, 이를 Three.js와 같은 다른 플랫폼에서 사용할 수 있도록 하는 과정을 알아보겠습니다.

 

단계 1: 필요한 모듈 가져오기

먼저 필요한 모듈을 가져옵니다. 이 모듈들은 Excel 파일 생성 및 속성 정보 가져오기에 사용됩니다.

import maya.cmds as cmds
import os
from openpyxl import Workbook
from openpyxl.styles import Alignment
from maya.OpenMaya import MGlobal

 

단계 2: 속성 연결 확인 함수 작성하기

쉐이더의 속성이 다른 노드에 연결되어 있는지 확인하는 함수를 작성합니다.

def is_connected(attr):
    """속성이 다른 노드에 연결되어 있는지 확인합니다."""
    return cmds.connectionInfo(attr, isDestination=True) and cmds.listConnections(attr)

 

단계 3: 속성에 연결된 노드 가져오기 함수 작성하기

쉐이더의 속성에 연결된 노드를 가져오는 함수를 작성합니다.

def get_connected_node(attr):
    """속성에 연결된 노드를 가져옵니다."""
    connections = cmds.listConnections(attr, source=True, destination=False)
    if connections:
        return connections[0]
    else:
        return None

 

단계 4: RGB 값을 16진수 색상 코드로 변환하는 함수 작성하기

RGB 값을 16진수 색상 코드로 변환하는 함수를 작성합니다.

def rgb_to_hex(rgb):
    """RGB 값을 16진수 색상 코드로 변환합니다."""
    return '0x{:02x}{:02x}{:02x}'.format(int(rgb[0]*255), int(rgb[1]*255), int(rgb[2]*255))

 

단계 5: 반사율 계산 함수 작성하기

반사율을 계산하는 함수를 작성합니다.

def calculate_reflectivity(metalness_weight, specular_weight, specular_color):
    """반사율을 계산합니다."""
    if isinstance(specular_color, list) and len(specular_color) == 1 and isinstance(specular_color[0], tuple):
        specular_color = specular_color[0]
    specular_brightness = sum(specular_color) / 3.0
    if metalness_weight == 1:
        return 0.8 + (specular_weight * 0.2)
    else:
        return specular_weight * specular_brightness

 

단계 6: 쉐이더 속성 가져오기 함수 작성하기

쉐이더의 속성을 가져오는 함수를 작성합니다.

def get_shader_attributes(shader):
    """
    쉐이더의 속성을 가져옵니다. 특정 속성이 0이면 관련 속성들을 제외합니다.
    
    :param shader: 쉐이더의 이름
    :return: 쉐이더의 속성 딕셔너리
    """
    attributes = {}
    # 속성 검사 및 가져오기
    shader_props = {
        "baseColor": "baseColor",
        "metalness": "metalness",
        "specular": "specularIntensity",
        "specularRoughness": "roughness",
        "specularColor": "specularColor",
        "specularIOR": "ior",
        "transmission": "transmission",
        "transmissionColor": "transmissionColor",
        "coat": "clearcoat",
        "coatColor": "clearcoatColor",
        "coatRoughness": "clearcoatRoughness",
        "sheen": "sheen",
        "sheenColor": "sheenColor",
        "sheenRoughness": "sheenRoughness",
        "emission": "emissive"
    }

    # 속성 값을 먼저 검사합니다.
    excluded_props = []
    if cmds.getAttr(f"{shader}.transmission") == 0:
        excluded_props.extend(["transmission", "transmissionColor"])
    if cmds.getAttr(f"{shader}.coat") == 0:
        excluded_props.extend(["coat", "coatColor", "coatRoughness"])
    if cmds.getAttr(f"{shader}.sheen") == 0:
        excluded_props.extend(["sheen", "sheenColor", "sheenRoughness"])
    if cmds.getAttr(f"{shader}.emission") == 0:
        excluded_props.append("emission")

    for prop, attr_name in shader_props.items():
        if prop in excluded_props:
            continue  # 제외할 속성이면 건너뜁니다.
        
        prop_attr = f"{shader}.{prop}"
        if not is_connected(prop_attr):
            if prop in ["baseColor", "specularColor", "transmissionColor", "coatColor", "sheenColor"]:
                color_value = cmds.getAttr(prop_attr)[0]
                attributes[attr_name] = rgb_to_hex(color_value)
            else:
                value = cmds.getAttr(prop_attr)
                attributes[attr_name] = value
        else:
            print(f"{prop_attr} is connected to a texture or node, skipping.")

    if "specularColor" in attributes and "metalness" in attributes and "specularIntensity" in attributes:
        # 반사율 계산은 모든 필요한 속성이 있는 경우에만 수행합니다.
        metalness_weight = attributes["metalness"]
        specular_weight = attributes["specularIntensity"]
        specular_color = cmds.getAttr(f"{shader}.specularColor")[0] if not is_connected(f"{shader}.specularColor") else (1, 1, 1)
        reflectivity_value = calculate_reflectivity(metalness_weight, specular_weight, specular_color)
        attributes["reflectivity"] = reflectivity_value

    return attributes

 

단계 7: 쉐이더 속성을 Excel 파일로 내보내는 함수 작성하기

쉐이더의 속성을 Excel 파일로 내보내는 함수를 작성합니다.

def get_shader_properties_xlsx():
    """쉐이더 속성을 엑셀 파일로 내보냅니다. 특정 속성이 0이면 관련 속성들을 제외하고, 열 너비와 정렬을 설정합니다."""
    skip_shaders = ['lambert1', 'particleCloud1', 'shaderGlow1', 'standardSurface1']
    current_file = cmds.file(query=True, sceneName=True)
    file_path = os.path.dirname(current_file)
    output_file_path = os.path.join(file_path, "props.xlsx")

    if os.path.exists(output_file_path):
        os.remove(output_file_path)

    wb = Workbook()
    first_sheet = wb.active
    first_sheet.title = "StandardSurface Shaders"

    shaders = sorted(cmds.ls(materials=True))
    for shader in shaders:
        if shader not in skip_shaders and cmds.nodeType(shader) == "standardSurface":
            attr_data = get_shader_attributes(shader)
            if attr_data:
                ws = wb.create_sheet(title=shader)
                for attribute, value in attr_data.items():
                    ws.append([attribute, value])

                # A열 너비 설정, A열 왼쪽 정렬
                ws.column_dimensions['A'].width = 20
                for cell in ws['A']:
                    cell.alignment = Alignment(horizontal='left')

                # B열 너비 설정, B열 오른쪽 정렬
                ws.column_dimensions['B'].width = 10
                for cell in ws['B']:
                    cell.alignment = Alignment(horizontal='right')

    # 'Arnold Shaders' 시트가 비어있으면 삭제합니다.
    if 'StandardSurface Shaders' in wb.sheetnames and not list(first_sheet.rows):
        wb.remove(first_sheet)

    # 엑셀 파일 저장
    wb.save(output_file_path)
    MGlobal.displayInfo("props.xlsx 파일 생성이 완료되었습니다!!!")

 

단계 8: 함수 호출 및 완료 다이얼로그 표시

작성한 함수를 호출하고, 프로세스 완료를 알리는 다이얼로그를 표시합니다.

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

 

 







 

단계 9: 최종 코드

import maya.cmds as cmds
import os
from openpyxl import Workbook
from openpyxl.styles import Alignment
from maya.OpenMaya import MGlobal

def is_connected(attr):
    """속성이 다른 노드에 연결되어 있는지 확인합니다."""
    return cmds.connectionInfo(attr, isDestination=True) and cmds.listConnections(attr)

def get_connected_node(attr):
    """속성에 연결된 노드를 가져옵니다."""
    connections = cmds.listConnections(attr, source=True, destination=False)
    if connections:
        return connections[0]
    else:
        return None

def rgb_to_hex(rgb):
    """RGB 값을 16진수 색상 코드로 변환합니다."""
    return '0x{:02x}{:02x}{:02x}'.format(int(rgb[0]*255), int(rgb[1]*255), int(rgb[2]*255))

def calculate_reflectivity(metalness_weight, specular_weight, specular_color):
    """반사율을 계산합니다."""
    if isinstance(specular_color, list) and len(specular_color) == 1 and isinstance(specular_color[0], tuple):
        specular_color = specular_color[0]
    specular_brightness = sum(specular_color) / 3.0
    if metalness_weight == 1:
        return 0.8 + (specular_weight * 0.2)
    else:
        return specular_weight * specular_brightness

def get_shader_attributes(shader):
    """
    쉐이더의 속성을 가져옵니다. 특정 속성이 0이면 관련 속성들을 제외합니다.
    
    :param shader: 쉐이더의 이름
    :return: 쉐이더의 속성 딕셔너리
    """
    attributes = {}
    # 속성 검사 및 가져오기
    shader_props = {
        "baseColor": "baseColor",
        "metalness": "metalness",
        "specular": "specularIntensity",
        "specularRoughness": "roughness",
        "specularColor": "specularColor",
        "specularIOR": "ior",
        "transmission": "transmission",
        "transmissionColor": "transmissionColor",
        "coat": "clearcoat",
        "coatColor": "clearcoatColor",
        "coatRoughness": "clearcoatRoughness",
        "sheen": "sheen",
        "sheenColor": "sheenColor",
        "sheenRoughness": "sheenRoughness",
        "emission": "emissive"
    }
    
    # 속성 값을 먼저 검사합니다.
    excluded_props = []
    if cmds.getAttr(f"{shader}.transmission") == 0:
        excluded_props.extend(["transmission", "transmissionColor"])
    if cmds.getAttr(f"{shader}.coat") == 0:
        excluded_props.extend(["coat", "coatColor", "coatRoughness"])
    if cmds.getAttr(f"{shader}.sheen") == 0:
        excluded_props.extend(["sheen", "sheenColor", "sheenRoughness"])
    if cmds.getAttr(f"{shader}.emission") == 0:
        excluded_props.append("emission")

    for prop, attr_name in shader_props.items():
        if prop in excluded_props:
            continue  # 제외할 속성이면 건너뜁니다.
        
        prop_attr = f"{shader}.{prop}"
        if not is_connected(prop_attr):
            if prop in ["baseColor", "specularColor", "transmissionColor", "coatColor", "sheenColor"]:
                color_value = cmds.getAttr(prop_attr)[0]
                attributes[attr_name] = rgb_to_hex(color_value)
            else:
                value = cmds.getAttr(prop_attr)
                attributes[attr_name] = value
        else:
            print(f"{prop_attr} is connected to a texture or node, skipping.")

    if "specularColor" in attributes and "metalness" in attributes and "specularIntensity" in attributes:
        # 반사율 계산은 모든 필요한 속성이 있는 경우에만 수행합니다.
        metalness_weight = attributes["metalness"]
        specular_weight = attributes["specularIntensity"]
        specular_color = cmds.getAttr(f"{shader}.specularColor")[0] if not is_connected(f"{shader}.specularColor") else (1, 1, 1)
        reflectivity_value = calculate_reflectivity(metalness_weight, specular_weight, specular_color)
        attributes["reflectivity"] = reflectivity_value

    return attributes

def get_shader_properties_xlsx():
    """쉐이더 속성을 엑셀 파일로 내보냅니다. 특정 속성이 0이면 관련 속성들을 제외하고, 열 너비와 정렬을 설정합니다."""
    skip_shaders = ['lambert1', 'particleCloud1', 'shaderGlow1', 'standardSurface1']
    current_file = cmds.file(query=True, sceneName=True)
    file_path = os.path.dirname(current_file)
    output_file_path = os.path.join(file_path, "props.xlsx")

    if os.path.exists(output_file_path):
        os.remove(output_file_path)

    wb = Workbook()
    first_sheet = wb.active
    first_sheet.title = "StandardSurface Shaders"

    shaders = sorted(cmds.ls(materials=True))
    for shader in shaders:
        if shader not in skip_shaders and cmds.nodeType(shader) == "standardSurface":
            attr_data = get_shader_attributes(shader)
            if attr_data:
                ws = wb.create_sheet(title=shader)
                for attribute, value in attr_data.items():
                    ws.append([attribute, value])

                # A열 너비 설정, A열 왼쪽 정렬
                ws.column_dimensions['A'].width = 20
                for cell in ws['A']:
                    cell.alignment = Alignment(horizontal='left')

                # B열 너비 설정, B열 오른쪽 정렬
                ws.column_dimensions['B'].width = 10
                for cell in ws['B']:
                    cell.alignment = Alignment(horizontal='right')

    # 'Arnold Shaders' 시트가 비어있으면 삭제합니다.
    if 'StandardSurface Shaders' in wb.sheetnames and not list(first_sheet.rows):
        wb.remove(first_sheet)

    # 엑셀 파일 저장
    wb.save(output_file_path)
    MGlobal.displayInfo("props.xlsx 파일 생성이 완료되었습니다!!!")

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

 

이렇게 하면 Maya의 standardSurface 쉐이더 속성을 Excel 파일로 저장하는 스크립트를 만들었습니다. 이제 Three.js나 다른 플랫폼에서 이 속성을 쉽게 사용할 수 있습니다.

 
 
 
 

제 글이 마음에 드셨다면 광고 한 번씩 클릭 부탁드립니다.
제작에 큰 힘이 됩니다. 감사합니다.