본문 바로가기

카테고리 없음

Substance Painter 파라미터를 Unreal Engine에서 사용하기 (with Json)

2023.02.26 - [분류 전체보기] - Susbtance Painter에서 커스텀 셰이더 사용하기

 

Susbtance Painter에서 커스텀 셰이더 사용하기

개요 Non-PBR 프로젝트에서도 캐릭터와 배경 텍스처링 과정에서 Substance Painter를 사용해야 할 일이 있다. 이때 커스텀 제작된 GLSL 셰이더로 작업 환경을 셋팅하고, 만들어진 텍스처들을 각 프로젝

prooveyourself.tistory.com

 

섭스턴스 페인터에서 커스텀 셰이더를 사용하는 경우가 아주 흔한 경우는 아닐 것 같지만, 나중에 다시 필요할 수도 있으니 작성..


JSON 사용이 필요한 경우

섭스턴스 페인터의 Export 창에는 요런 체크박스가 있다.

 

체크하면, 텍스처 파일 이외에 추가적으로 셰이더 파라미터들의 정보가 담긴 JSON파일을 익스포트 해 준다.

(일부러 만들어 넣어 둔 옵션 같은데, 구글링 해 봐도 이렇다 할 사용처가 나오지는 않는 것 같다 ...)

 

셰이더 파라미터는 일반적인 경우에는 사용할 일이 거의 없지만, 위 글에서처럼 커스텀 셰이더를 작성해서 사용한 경우, 섭스턴스 페인터에서 아티스트가 셰이더 파라미터도 조절하게 된다.

 

파라미터 갯수 x 텍스처셋 갯수가 늘어나면, 언리얼 엔진에서 이 파라미터들을 페인터에서 셋팅한 그대로 다시 셋팅해주는 작업이 필요할 텐데, 이것은 매우 번거로운 작업이다. 이때 이 JSON 파일이 사용된다.

 

여기에서는
셰이더 = 언리얼 엔진에서의 머터리얼,

셰이더 인스턴스 = 언리얼 엔진에서의 머터리얼 인스턴스,
셰이더 파라미터 = 언리얼 엔진에서의 머터리얼 인스턴스 파라미터

라고 가정하였다.

 

JSON 파일의 구성

위에서 본 체크박스를 체크하면 나오는 JSON 파일의 구성은 아래와 같다.

JSON 파일의 구성

format에는 버전 정보가, Scene에는 스케일 정보가 들어 있다. 

Shaders 안에는 각 셰이더 인스턴스별로 셰이더 파라미터가 들어 있다. 각 셰이더 이름 아래의 구성은 다음과 같다.

shaders 아래의 각 셰이더 인스턴스별 데이터

shader와 shaderInstance 란에서는 이 셰이더 인스턴스가 어느 셰이더를 사용하고 있는지 알 수 있다.

셰이더 파라미터 정보가 들어 있는 곳은 "parameters" : 아래인데, 파라미터만 있는 게 아니라 아래 정보도 들어 있다.

  • Common Parameters : ao Intensity, Emissive Intensity, HorizonFade, nbSamples (?)
  • Parallax Occlusion Mapping : maxPOMSamples, minPOMSamples, pomStrength, usePOM

위 부분은 필요 없는 정보들이기 때문에 읽어올 때 모두 0으로 치환해 주었다.

 

Unreal에서 JSON 읽어오기

JSON 플러그인을 활성화 해 주어야, 블루프린트에서 JSON 관련 노드를 사용할 수 있다.

위 플러그인으로 사용할 수 있게 되는 노드는 아래와 같다.

Json 블루프린트 도구

  • Load JSon from FIle : Json 파일의 경로로부터 .JSON 파일을 Json Object 형태로 얻는다.
    Editor Utility Widget 에서 File 변수를 바로 노출시켜 직접 파일을 지정할 수 있게 하였다.

  • Has Field : 해당 field가 있는지 여부를 알아낸다.
  • Get Field : field 이름을 사용해 내용을 얻어올 수 있다.
    예를 들어, "format"이라는 이름을 넣으면, 위에서 본 json 파일에서는 { "version": "1.0" } 이라는 내용이 담긴 Json Object를 얻는다. "shaders" 라는 값을 넣으면, { "Eye Shader":{ ... }, "Face Shader": { ... }, "Hair Shader": { ... } }로 구성된 Json Object를 얻는다.
  • Set Field : field 이름을 사용해 해당 필드의 내용을 바꿔준다. 
  • Get Json String : Json Object로부터 string 형태로 정보를 얻는다.

Get Json String을 제외하면, 함수들이 해당 Field값을 Json Object로 돌려준다. 데이터를 사용하려면 최종적으로 string 형태가 되어야겠지만, 한번 String 형태가 된 문자들의 나열을 다시 의미있는 정보로 분리하는 게 아주 복잡하다. 필요한 부분에 도달했을 때 Json object를 Get Json String으로 변환하였다.

 

여기에서 몰랐던 점은, field가 가진 내용이 JSON Object형태가 아닌 경우는 Get FIeld로 얻어올 수 없다는 것이었다.

 

예를 들어, "parameters" 아래의 내용을 Json Object로 얻어왔을 때, 이 오브젝트에서 Ambient_ratio를 Get Field 함수로는 얻어올 수 없다. Ambient_ratio가 json object가 아니기 때문이다.

반면 Common Parameters 는 json object이기 때문에 Get Field 함수로 얻어올 수 있다.

위에서 본 것처럼 "parameters" 아래의 Json Object가 두 개나 있는데, 이 두 오브젝트는 지금은 필요 없는 부분이기 때문에 Set Field 함수로 0으로 치환해 주었다.

json object를 0으로 치환

이제 Get Json String 으로 스트링을 얻어오면, 콤마(,) 로 구분된 셰이더의 "파라미터" : "값" 목록들을 얻을 수 있다.

 

String 전처리

이렇게 얻은 String은 바로 사용할 수 없고, 필요없는 부분을 제거해야 했다.

먼저, 맨 뒤와 맨 앞에 있는 {, }를 제거했다.

그런 다음, Parse Into Array 함수로 콤마로 구분된 모든 String을 배열의 각 자리에 순서대로 저장한다.

이렇게 얻은 string 배열은 다음과 같은 형태가 될 것이다.

 

index[0] = ""파라미터1 이름" : 값"

index[1] = ""파라미터2 이름" : 값"

index[2] = ""파라미터3 이름" : 값"

...

 

벡터 파라미터의 경우

그런데 Json 파일을 다시 살펴보만, 여러 개의 숫자로 이루어진 벡터 파라미터의 경우 역시 콤마로 각 요소를 구분하고 있다.

"lightPosition_": [10, 10, 10]

이렇게 되면, 파라미터 유형이 벡터인 경우 배열에 아래와 같은 형태로 저장된다 ...

 

index[n] : ""lightPosition_": [10"

index[n+1] : "10"

index[n+2] : "10]"

다른 파라미터처럼 인덱스 하나에 한 개의 파라미터만 저장되게 하고 싶기 때문에, Parse into Array로 만든 배열을 다시 한 번 훒으면서 위와 같이 쪼개진 벡터가 있는지 살펴보고, 있다면 다시 인덱스 한 개에 모아주었다.

배열 안의 스트링 중 [ 을 포함하는 인덱스가 n번째라면, n, n+1, n+2번째의 스트링을 모아서 [x, y, z]형태로 만들어, n번째 인덱스의 내용으로 저장해주는 것이다.
(이후 Linear Color 형태로 사용할 때는 (R=x,G=y,B=z) 형태로 변환해 주어야 한다)

이 작업이 끝나고 나면, n+1과 n+2에 남은 숫자들은 필요 없으니까 지워준다.

 

index[n+1] : "10"

index[n+2] : "10]"

 

배열의 내용이 위와 같은 형태인 경우, 즉 스트링의 총 길이가 3글자보다 작거나 같은 경우 공백으로 대치하였다.

 

이제 완전한 "파라미터 이름" : 값 형태의 배열을 얻었다.

 

Map 형태로 변환하기

아직 파라미터 이름과 파라미터 값이 같은 배열에 스트링으로 묶여 저장되어 있다.

이것을 의미 있는 정보로 사용하려고, 파라미터 : 값 이 각각 key-value 로 맵핑되어 있는 Map 형태의 자료를 만들었다.

Split 노드를 사용하면 스트링을 특정 문자를 기준으로 두 개의 스트링으로 나눌 수 있다.

이때, " " (큰따옴표) 와 : (세미콜론) 은 제거하였다.

 

 

파라미터 값으로 머터리얼 파라미터 생성하기

이제 위에서 만들어진 Map에서 파라미터 이름을 key로 검색해서, 원하는 파라미터 값을 value로 얻을 수 있게 되었다.

머터리얼 인스턴스(constant) 생성과 파라미터 변경은 에디터 내에서만 할 수 있는 작업이다.

 

미리 생성한 Material Instance의 파라미터 info를 살펴보고, 파라미터 이름을 Map에서 검색했을 때 일치하는 이름이 있다면, 해당 값을 머터리얼 파라미터에 적용한다.

Scalar Parameter의 경우에는 이렇게 간단하게 되지만, 벡터 파라미터의 경우에는 스트링을 Linear Color 형태로 변환해 주는 작업이 한번 더 필요하다.

Vector Paramter는 Linear Color를 입력값으로 받는다.
그냥은 안되는 것 같아 조금 무식한 방법으로 해 보았다 : [0. 0. 0] 형태를 다시 콤마(,)를 기준으로 배열로 변환한 다음, 1, 2, 3번째 인덱스마다 각각의 분기로 처리를 해서 원본 배열의 값을 바꿔주었다.

Editor Utility Widget으로 만들어 본 UI이다.
Json 파일을 불러오면, 아래에 Json 파일의 각 텍스처셋에 해당하는 슬롯들이 생성된다.

섭스턴스 페인터에서는 텍스처 셋 패널에 해당하는 목록이다.

 

각 텍스처셋의 이름을 MI(머터리얼 인스턴스)의 이름으로 사용하도록 하였다. 예를 들어, 지금 생성하는 텍스처셋의 이름은 Head_Accesory 이기 때문에, 생성되는 머터리얼 인스턴스의 이름은 MI_Head_Accesory 이다.

적용된 셰이더 이름이 Shader 란에 자동으로 입력되는 것을 볼 수 있다.

 

버튼을 눌러 생성된 머터리얼 인스턴스를 보면, Json에서 읽어온 파라미터들이 잘 적용되어 있는 것을 볼 수 있다.

 

문제점

1. Set MIC Parameter Editor Only 노드로는, 머터리얼 인스턴스에서 체크박스가 켜지지 않은 파라미터는 수정할 수 없는 것 같다. (이 체크박스는 아직 블루프린트에서 접근 안되는 것 같음)

이렇게 되어 있는 파라미터들은 Set 할 수 없음

나는 Create MIC 노드를 이용하지 않고, 미리 만들어 놓은 머터리얼 인스턴스를 Duplicate Asset 하는 방법을 사용하였다. 이렇게 하면, 원본이 되는 MI를 미리 셋팅해 둘 수 있다. 이 원본 MI의 체크박스를 모두 켜 두면, 임시방편으로 해결이 될 것 같긴 하다 ..

 

2. 텍스처 파라미터는 경로를 읽어오기 때문에, 엔진 상의 텍스처 에셋 경로로 재설정이 필요하다. 정상적으로 작동하려면 프로젝트 내의 해당 경로에 같은 이름의 텍스처 에셋을 미리 준비해 두어야 한다.