2023.02.26 - [분류 전체보기] - Susbtance Painter에서 커스텀 셰이더 사용하기
Susbtance Painter에서 커스텀 셰이더 사용하기
개요 Non-PBR 프로젝트에서도 캐릭터와 배경 텍스처링 과정에서 Substance Painter를 사용해야 할 일이 있다. 이때 커스텀 제작된 GLSL 셰이더로 작업 환경을 셋팅하고, 만들어진 텍스처들을 각 프로젝
prooveyourself.tistory.com
섭스턴스 페인터에서 커스텀 셰이더를 사용하는 경우가 아주 흔한 경우는 아닐 것 같지만, 나중에 다시 필요할 수도 있으니 작성..
JSON 사용이 필요한 경우
섭스턴스 페인터의 Export 창에는 요런 체크박스가 있다.

체크하면, 텍스처 파일 이외에 추가적으로 셰이더 파라미터들의 정보가 담긴 JSON파일을 익스포트 해 준다.
(일부러 만들어 넣어 둔 옵션 같은데, 구글링 해 봐도 이렇다 할 사용처가 나오지는 않는 것 같다 ...)
셰이더 파라미터는 일반적인 경우에는 사용할 일이 거의 없지만, 위 글에서처럼 커스텀 셰이더를 작성해서 사용한 경우, 섭스턴스 페인터에서 아티스트가 셰이더 파라미터도 조절하게 된다.
파라미터 갯수 x 텍스처셋 갯수가 늘어나면, 언리얼 엔진에서 이 파라미터들을 페인터에서 셋팅한 그대로 다시 셋팅해주는 작업이 필요할 텐데, 이것은 매우 번거로운 작업이다. 이때 이 JSON 파일이 사용된다.
여기에서는
셰이더 = 언리얼 엔진에서의 머터리얼,
셰이더 인스턴스 = 언리얼 엔진에서의 머터리얼 인스턴스,
셰이더 파라미터 = 언리얼 엔진에서의 머터리얼 인스턴스 파라미터
라고 가정하였다.
JSON 파일의 구성
위에서 본 체크박스를 체크하면 나오는 JSON 파일의 구성은 아래와 같다.

format에는 버전 정보가, Scene에는 스케일 정보가 들어 있다.
Shaders 안에는 각 셰이더 인스턴스별로 셰이더 파라미터가 들어 있다. 각 셰이더 이름 아래의 구성은 다음과 같다.

shader와 shaderInstance 란에서는 이 셰이더 인스턴스가 어느 셰이더를 사용하고 있는지 알 수 있다.
셰이더 파라미터 정보가 들어 있는 곳은 "parameters" : 아래인데, 파라미터만 있는 게 아니라 아래 정보도 들어 있다.
- Common Parameters : ao Intensity, Emissive Intensity, HorizonFade, nbSamples (?)
- Parallax Occlusion Mapping : maxPOMSamples, minPOMSamples, pomStrength, usePOM
위 부분은 필요 없는 정보들이기 때문에 읽어올 때 모두 0으로 치환해 주었다.
Unreal에서 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으로 치환해 주었다.

이제 Get Json String 으로 스트링을 얻어오면, 콤마(,) 로 구분된 셰이더의 "파라미터" : "값" 목록들을 얻을 수 있다.
String 전처리
이렇게 얻은 String은 바로 사용할 수 없고, 필요없는 부분을 제거해야 했다.
먼저, 맨 뒤와 맨 앞에 있는 {, }를 제거했다.

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

이렇게 얻은 string 배열은 다음과 같은 형태가 될 것이다.
index[0] = ""파라미터1 이름" : 값"
index[1] = ""파라미터2 이름" : 값"
index[2] = ""파라미터3 이름" : 값"
...
벡터 파라미터의 경우
그런데 Json 파일을 다시 살펴보만, 여러 개의 숫자로 이루어진 벡터 파라미터의 경우 역시 콤마로 각 요소를 구분하고 있다.

이렇게 되면, 파라미터 유형이 벡터인 경우 배열에 아래와 같은 형태로 저장된다 ...
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 형태의 자료를 만들었다.

이때, " " (큰따옴표) 와 : (세미콜론) 은 제거하였다.
파라미터 값으로 머터리얼 파라미터 생성하기
이제 위에서 만들어진 Map에서 파라미터 이름을 key로 검색해서, 원하는 파라미터 값을 value로 얻을 수 있게 되었다.
머터리얼 인스턴스(constant) 생성과 파라미터 변경은 에디터 내에서만 할 수 있는 작업이다.

미리 생성한 Material Instance의 파라미터 info를 살펴보고, 파라미터 이름을 Map에서 검색했을 때 일치하는 이름이 있다면, 해당 값을 머터리얼 파라미터에 적용한다.
Scalar Parameter의 경우에는 이렇게 간단하게 되지만, 벡터 파라미터의 경우에는 스트링을 Linear Color 형태로 변환해 주는 작업이 한번 더 필요하다.



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

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

각 텍스처셋의 이름을 MI(머터리얼 인스턴스)의 이름으로 사용하도록 하였다. 예를 들어, 지금 생성하는 텍스처셋의 이름은 Head_Accesory 이기 때문에, 생성되는 머터리얼 인스턴스의 이름은 MI_Head_Accesory 이다.
적용된 셰이더 이름이 Shader 란에 자동으로 입력되는 것을 볼 수 있다.



버튼을 눌러 생성된 머터리얼 인스턴스를 보면, Json에서 읽어온 파라미터들이 잘 적용되어 있는 것을 볼 수 있다.
문제점
1. Set MIC Parameter Editor Only 노드로는, 머터리얼 인스턴스에서 체크박스가 켜지지 않은 파라미터는 수정할 수 없는 것 같다. (이 체크박스는 아직 블루프린트에서 접근 안되는 것 같음)

나는 Create MIC 노드를 이용하지 않고, 미리 만들어 놓은 머터리얼 인스턴스를 Duplicate Asset 하는 방법을 사용하였다. 이렇게 하면, 원본이 되는 MI를 미리 셋팅해 둘 수 있다. 이 원본 MI의 체크박스를 모두 켜 두면, 임시방편으로 해결이 될 것 같긴 하다 ..
2. 텍스처 파라미터는 경로를 읽어오기 때문에, 엔진 상의 텍스처 에셋 경로로 재설정이 필요하다. 정상적으로 작동하려면 프로젝트 내의 해당 경로에 같은 이름의 텍스처 에셋을 미리 준비해 두어야 한다.