본문 바로가기

Unity/작업방식

Unity: Workflow. 셰이더 그래프에서 단축키 사용하기 (2)

이전 글에서 이어집니다.

 

셰이더 그래프 패키지를 수정해볼까 싶었는데, 상당히 폐쇄적인 접근제한자로 보호되고 있어 액세스조차 불가했습니다. 이전 글에서 소개드린 포럼의 글의 날짜가 3월~4월 부근이기에 얼마 되지 않은 따끈따끈한 스레드인줄 알았습니다. 그래서 굳이 작업하지 않고 패키지를 수정해서 사용하다보면 금방 업데이트 되겠구나 싶었죠. 그러나 2020년 3~4월에 활발이 대화가 오고가다 2021년 4월이 되어 아직도 단축키 기능이 없다는 불평이 달렸다는걸 뒤늦게 알아챘습니다. 1년동안 전혀 변화가 없다니... 어쩌면 영원히 개발되지 않을지도 모른다는 생각이 들었습니다.

 

셰이더 그래프 패키지 코드를 수정하지 않고 기능을 집어넣을 방법이 없을까 고민을 해봤습니다. 그 결과 리플렉션을 이용해서 모든 필드에 접근하고 함수를 호출할 수는 있게 되었지만, 그래프에디터의 생성자가 호출될 때 마다 끝부분에 원하는 코드를 실행시키기는 불가능했습니다. 단순한 리플렉션으로는 구현할 수 없고, 리플렉션과 후킹 테크닉을 이용해 실제로 에디터에 로드 된 DLL을 메모리상에서 변조하여 사용자 코드를 실행시켜야했습니다.

 

빌드 후에는 함수를 후킹하고 다른 코드가 실행되도록 하는 것이 큰 문제는 아니지만, DLL이 수시로 로드 언로드되는 에디터 환경에서 안정적으로 매번 후킹하는 프레임워크를 만들어두기는 무리가 있다 판단했습니다. 이 방식은 시간을 갖고 천천히 작업할 예정입니다. 

 

유니티 패키지는 에디터가 켜질 때마다 로컬에 저장해둔 캐시와 비교해 달라진 부분이 있으면 원본으로 복원시킵니다. 이에 착안하여 사용할 수 있는 방식이 2가지 있습니다.

 

방법 1. 로컬 캐시 파일을 미리 수정

Appdata 폴더에 위치한 패키지 캐시를 직접 수정해둡니다.

 

원본을 수정해두면 같은 시스템에서 로드되는 모든 프로젝트에서 수정된 패키지를 복사해 가져옵니다. 한 번만 작업하고도 모든 프로젝트에서 사용가능한 편리한 방법입니다. 그러나 이런식으로 수정된 패키지가 늘어나다보면 어떤 게 수정된 것인지 구분하기 힘들어지며 협업에 있어 방해됩니다. 그치만 셰이더 그래프로 노드 작성을 더 빠르게 하기 위한 기능만을 추가하는 것이므로, 버전 관리나 모든 협업작업자들이 동일한 패키지를 사용할 필요는 없습니다.

 

Appdata/Local/Unity/cache/packages/packages.unity.com 폴더에 가면 이미 다운로드 받아진 패키지 캐시들이 저장되어있습니다. 유니티 에디터가 켜질 때, 프로젝트의 Library/PackageCache와 비교해 다른 점이 있다면, 여기에서 복사해옵니다. 여기 폴더를 이전 글에서 했던 것처럼 수정해두면 매번 미리 수정된 패키지를 가져와 사용할 수 있게 됩니다.

 

방법 2. 새 파일을 가져올 때마다 매번 수정

스크립트를 작성해 원본이 로드될 때 마다 수정본으로 바꿔치는 방식입니다.

목적은 셰이더 그래프 패키지를 커스텀 패키지로 옮겨 수정하지 않고, 그대로 둔 채로 원하는 코드를 실행하기 위함입니다.

 

아래 깃허브는 두 번째 방식을 사용해 구현했습니다. 편의를 위해 이런 스타일로 개발되었지만 정상적인 작업방식이 아니여서 다소 불안정할 수 있습니다.

 

github.com/seonghwan-dev/ShaderGraphEnhancer

 

seonghwan-dev/ShaderGraphEnhancer

Simple shortcut system into unity shader graph without modifying package by hacking style. - seonghwan-dev/ShaderGraphEnhancer

github.com

 

조금 편리하게 사용하기 위해 UPM 방식으로 구성했습니다. 패키지가 설치되고 컴파일되면서, Library/PackageCache의 파일을 수정해버립니다. 수정된 코드 역시 다시 컴파일되고, 수정된 부분을 참조해 사용자 코드가 실행됩니다.

 

원리

1. InternalsVisibleTo

[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Seonghwan.ShaderGraph.Enhancer.Editor")]

 

이 키워드는 다른 어셈블리에서도 이 어셈블리의 internal 클래스에 접근하도록 허락해줍니다. 제가 직접 생성한 어셈블리에서도 보호되고있는 Internal 키워드의 타입들을 사용할 수 있게 해줍니다.

 

2. 스태틱 델리게이트

public static Action<MaterialGraphView, GraphData> installedCallback;

 

GraphEditorView 클래스내에 정적 콜백을 하나 정의했습니다.

 

installedCallback?.Invoke(graphView, graph);

 

생성자 마지막 라인에 콜백을 호출해주는 코드를 작성했습니다.

 

3. InitializeOnLoad

컴파일 후 도메인이 로드될 때 호출되는 함수에서 초기화 작업을 진행해줍니다.

 

UnityEditor.ShaderGraph.Drawing.GraphEditorView.installedCallback += (v, g) =>
{
	KeyboardShortcutHelper keyboardShortcutHelper = new KeyboardShortcutHelper(v, g);
};

 

콜백을 등록해주면, GraphEditorView가 생성될 때 마다 키보드 바인딩이 이루어집니다. 

 

주의

에디터가 켜지는 시간이 미약하게 길어질 수 있습니다.