| 유니티 6.0 버전에서 URP 커스텀 셰이더를 작성하고 있습니다. PBR 셰이더를 만든다고 했을때, "Metallic Workflow", "Specular Workflow"에 따라서 Metallic Map과 Specular Map을 별도로 표시 해야 됩니다. 이때는, 셰이더 자체만으로는 힘들고 CS 파일도 추가 해야 합니다. ShaderGUI를 상속하여 OnGUI에서 Inspector창의 셰이더 속성 UI를 추가 하면 됩니다. public class CustomPBR_CS_GUI : ShaderGUI
{ public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties) { ..... } } 1. 셰이더 속성 찾기셰이더의 Properties {}에 선언된 값들을 MaterialProperty로 연결MaterialProperty workflow = FindProperty("_WorkflowMode",
properties);
MaterialProperty baseMap = FindProperty("_BaseMap", properties); MaterialProperty baseColor = FindProperty("_BaseColor", properties); MaterialProperty metallicMap = FindProperty("_MetallicMap", properties); MaterialProperty metallic = FindProperty("_Metallic", properties); MaterialProperty specMap = FindProperty("_SpecGlossMap", properties); MaterialProperty specColor = FindProperty("_SpecColor", properties); MaterialProperty smoothness = FindProperty("_Smoothness", properties); MaterialProperty bumpMap = FindProperty("_BumpMap", properties); MaterialProperty bumpScale = FindProperty("_BumpScale", properties); 2. 워크플로우 선택 UIa) UI 값 변경 여부를 감지EditorGUI.BeginChangeCheck();
변경된 경우에만 material 값 적용 → Undo / 성능 최적화 b) 라벨을 출력EditorGUILayout.Space(10);
EditorGUILayout.LabelField("1. WORKFLOW SELECTION", EditorStyles.boldLabel); 간격을 두기위해 세로 여백을 추가(10px) 텍스트 라벨을 인스펙트에 출력 c) 드롭다운 메뉴 출력string[] options = { "Metallic Workflow", "Specular
Workflow" };
int mode = (int)workflow.floatValue; mode = EditorGUILayout.Popup("Selected Mode", mode, options); if (EditorGUI.EndChangeCheck()) { workflow.floatValue = (float)mode; } 옵션 배열 선언 string[] options = { "Metallic Workflow", "Specular Workflow" };
머티리얼 값 → int로 변환 int mode = (int)workflow.floatValue; 왜 floatValue 인가? MaterialProperty는 모든 수치 값을 float로 저장하기 때문에 왜 int로 캐스팅? EditorGUILayout.Popup()은 int 인덱스를 사용하기 때문에 mode = EditorGUILayout.Popup("Selected Mode", mode, options); 인스펙터에 드롭다운 UI를 생성한다. 첫번째 인자 "Selected Mode" : 라벨 이름 두번째 인자 mode : 현재 선택 세번째 인자 options : 목록 반환값 : 사용자가 바꾸면 새 인덱스를 반환 if (EditorGUI.EndChangeCheck()) { workflow.floatValue = (float)mode; } 다시 workflow.floatValue에 값에 대입하여 셰이더 로직에서 사용 할수 있습니다. d) 베이스 맵 정보EditorGUILayout.Space(15);
// 3. 알베도/컬러 (한 줄로 표시) EditorGUILayout.LabelField("2. BASE MAPS", EditorStyles.boldLabel); materialEditor.TexturePropertySingleLine(new GUIContent("Albedo (RGB)"), baseMap, baseColor); EditorGUILayout.Space(15); 중복해서 나오는 함수 EditorGUILayout의 Space( ), LabelField( ) 설명은 생략 하겠습니다. materialEditor.TexturePropertySingleLine(new GUIContent("Albedo (RGB)"), baseMap, baseColor); ![]() TexturePropertySingleLine로 한 줄에 텍스처 선택 슬롯을 생성합니다. 첫번째 인자 : GUIContent입니다. 화면에 그릴 GUI 요소(버튼, 레이블, 박스 등)의 실제 '내용물(Content)'을 정의하는 클래스입니다. 간단히 말해, "무엇을 그릴 것인가(텍스트, 이미지, 툴팁)"를 담는 데이터 컨테이너입니다 두번째 인자: 텍스쳐 슬롯입니다. 셰이더 코드 : _BaseMap ("Albedo", 2D) = "white" {} 세번째 인자: 텍스처와 곱해지는 컬러 값 셰이더 코드 : _BaseColor ("Base Color", Color) = (1, 1, 1, 1) 세번째 인자를 생략하면 GUI에는 칼라픽커가 출력이 되지 않습니다. e) 선택한 workflow 값에 따라, Metallic 정보와 스펙큘러 정보중 하나만 출력 합니다.이것을 보여주기 위해 CS 파일로 GUI를 만든 이유입니다.선택한 workflow에 따라 Metallic 정보와 스펙큘러 정보중 하나만 보여줘, 디자이너가 어떤 텍스쳐를 사용해야 할지 확실하게 알수 있습니다. // 4. 모드별 UI (0:
Metallic, 1: Specular)
if (workflow.floatValue < 0.5f) { // Metallic UI } else { // Specular UI } Metallic 정보 Metallic 정보를 그룹화 해서 보여 줍니다. EditorGUILayout.BeginVertical("helpBox");
EditorGUILayout.LabelField("METALLIC MODE ACTIVE", EditorStyles.miniBoldLabel); materialEditor.TexturePropertySingleLine(new GUIContent("Metallic(R) Smoothness(A)"), metallicMap); materialEditor.ShaderProperty(metallic, "Metallic Intensity"); materialEditor.ShaderProperty(smoothness, "Smoothness Intensity"); EditorGUILayout.EndVertical(); EditorGUILayout.LabelField("METALLIC MODE ACTIVE", EditorStyles.miniBoldLabel); 박스 상단에 상태 안내 텍스트를 표시 합니다. miniBoldLabel:
EditorGUILayout.BeginVertical 시각적 그룹을 만들고 안의 요소들을 세로로 정렬하여 보여 줍니다. EditorGUILayout.BeginVertical("helpBox"); ... EditorGUILayout.EndVertical(); "helpBox"이 외에도 여러가지가 있습니다. 아래는 각 옵션을 넣고 출력 했을때 결과 화면입니다. "box" ![]() "window" ![]() "groupBox" ![]() "helpBox" ![]() "notificationText" - 빈 공백이 많이 생기네요. ![]() "TextArea" ![]() "TextField" ![]() materialEditor.TexturePropertySingleLine(new GUIContent("Metallic(R) Smoothness(A)"), metallicMap); TexturePropertySingleLine로 한 줄에 텍스처 선택 슬롯을 생성합니다. 첫번째 인자에는 new GUIContent("Metallic(R) Smoothness(A)") 텍스트를 출력합니다. 두번째 인자 metallicMap은 셰이더 코드에서 _MetallicMap ("Metallic Map", 2D) = "white" {} 프로퍼티의 텍스쳐 슬롯입니다. materialEditor.ShaderProperty(metallic, "Metallic Intensity"); materialEditor.ShaderProperty(smoothness, "Smoothness Intensity"); Shader Property : 셰이더 내에 선언된 _Metallic이나 _Glossiness 같은 수치값(Float/Range)을 조절할 수 있는 슬라이더나 입력 칸을 만듭니다. 첫 번째 인자인 metallic은 셰이더 프로퍼티 객체이고, 두 번째 인자인 "Metallic Intensity"는 인스펙터 창에 나타날 레이블 이름입니다. 셰이더 프로퍼티에 있는 정보는 다음과 같습니다. _Metallic ("Metallic Scale", Range(0, 1)) = 1.0 _Smoothness ("Smoothness Scale", Range(0, 1)) = 0.5 스펙큘러 정보 스펙큘러 정보를 그룹화 해서 보여 줍니다. EditorGUILayout.BeginVertical("helpBox");
EditorGUILayout.LabelField("SPECULAR MODE ACTIVE", EditorStyles.miniBoldLabel); materialEditor.TexturePropertySingleLine(new GUIContent("Specular(RGB) Smoothness(A)"), specMap, specColor); materialEditor.ShaderProperty(smoothness, " Smoothness Intensity"); EditorGUILayout.EndVertical(); 위에서 설명했으니 스펙큘러 정보 그룹의 설명은 생략 합니다. HDR //CS 파일
materialEditor.TexturePropertySingleLine(new GUIContent("Emission"), FindProperty("_EmissionMap", properties), FindProperty("_EmissionColor", properties)); //셰이더 파일 [HDR] _EmissionColor ("Emission Color", Color) = (0, 0, 0, 1) _EmissionMap ("Emission Map", 2D) = "white" {} 아래는 skull의 전체 창입니다. Emission 맵은 HDR을 사용하기 때문에 칼라 픽커에 HDR 문구가 표시 됩니다. 일반 텍스쳐는 Albedo 칼라 픽커 처럼 표시 됩니다. ![]() CustomPBR_CS_GUI.cs 파일 using UnityEngine;
using UnityEditor; public class CustomPBR_CS_GUI : ShaderGUI { public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties) { // 1. 속성 찾기 MaterialProperty workflow = FindProperty("_WorkflowMode", properties); MaterialProperty baseMap = FindProperty("_BaseMap", properties); MaterialProperty baseColor = FindProperty("_BaseColor", properties); MaterialProperty metallicMap = FindProperty("_MetallicMap", properties); MaterialProperty metallic = FindProperty("_Metallic", properties); MaterialProperty specMap = FindProperty("_SpecGlossMap", properties); MaterialProperty specColor = FindProperty("_SpecColor", properties); MaterialProperty smoothness = FindProperty("_Smoothness", properties); MaterialProperty bumpMap = FindProperty("_BumpMap", properties); MaterialProperty bumpScale = FindProperty("_BumpScale", properties); // 2. 값 변경 체크 시작 EditorGUI.BeginChangeCheck(); EditorGUILayout.Space(10); EditorGUILayout.LabelField("1. WORKFLOW SELECTION", EditorStyles.boldLabel); // --- 방법 A: 드롭다운 팝업 (가장 안정적) --- string[] options = { "Metallic Workflow", "Specular Workflow" }; int mode = (int)workflow.floatValue; mode = EditorGUILayout.Popup("Selected Mode", mode, options); // --- 방법 B: 선택 버튼 형태를 원하시면 위 Popup을 주석처리하고 아래 SelectionGrid를 사용하세요 --- // mode = GUILayout.SelectionGrid(mode, options, 2, EditorStyles.miniButtonMid); if (EditorGUI.EndChangeCheck()) { workflow.floatValue = (float)mode; } EditorGUILayout.Space(15); // 3. 알베도/컬러 (한 줄로 표시) EditorGUILayout.LabelField("2. BASE MAPS", EditorStyles.boldLabel); materialEditor.TexturePropertySingleLine(new GUIContent("Albedo (RGB)"), baseMap, baseColor); EditorGUILayout.Space(15); // 4. 모드별 UI (0: Metallic, 1: Specular) if (workflow.floatValue < 0.5f) { // Metallic UI EditorGUILayout.BeginVertical("TextField"); EditorGUILayout.LabelField("METALLIC MODE ACTIVE", EditorStyles.miniBoldLabel); materialEditor.TexturePropertySingleLine(new GUIContent("Metallic(R) Smoothness(A)"), metallicMap); materialEditor.ShaderProperty(metallic, "Metallic Intensity"); materialEditor.ShaderProperty(smoothness, "Smoothness Intensity"); EditorGUILayout.EndVertical(); } else { // Specular UI EditorGUILayout.BeginVertical(EditorStyles.helpBox); EditorGUILayout.LabelField("SPECULAR MODE ACTIVE", EditorStyles.miniBoldLabel); materialEditor.TexturePropertySingleLine(new GUIContent("Specular(RGB) Smoothness(A)"), specMap, specColor); materialEditor.ShaderProperty(smoothness, " Smoothness Intensity"); EditorGUILayout.EndVertical(); } EditorGUILayout.Space(15); // 5. 공통 속성 EditorGUILayout.LabelField("3. SURFACE DETAILS", EditorStyles.boldLabel); materialEditor.TexturePropertySingleLine(new GUIContent("Normal Map"), bumpMap, bumpScale); materialEditor.TexturePropertySingleLine(new GUIContent("Occlusion Map"), FindProperty("_OcclusionMap", properties), FindProperty("_OcclusionStrength", properties)); materialEditor.TexturePropertySingleLine(new GUIContent("Height Map"), FindProperty("_ParallaxMap", properties), FindProperty("_Parallax", properties)); EditorGUILayout.Space(15); // 6. 이펙트 EditorGUILayout.LabelField("4. EXTRA EFFECTS", EditorStyles.boldLabel); materialEditor.TexturePropertySingleLine(new GUIContent("Emission"), FindProperty("_EmissionMap", properties), FindProperty("_EmissionColor", properties)); materialEditor.ShaderProperty(FindProperty("_UseFresnel", properties), "Enable Fresnel"); materialEditor.ShaderProperty(FindProperty("_MatCapStrength", properties), "MatCap Strength"); materialEditor.TexturePropertySingleLine(new GUIContent("MatCap Texture"), FindProperty("_MatCapTex", properties)); EditorGUILayout.Space(10); materialEditor.TextureScaleOffsetProperty(baseMap); } } workflow GUI는 어떤걸로 할까요? 방법 A와 같은 드롭다운 방식과 방법 B와 같은 버튼 형태를 취향에 따라 선택하면 됩니다. // --- 방법 A: 드롭다운 팝업 (가장 안정적) --- mode = EditorGUILayout.Popup("Selected Mode", mode, options); // --- 방법 B: 선택 버튼 형태를 원하시면 위 Popup을 주석처리하고 아래 SelectionGrid를 사용하세요 --- mode = GUILayout.SelectionGrid(mode, options, 2, EditorStyles.miniButtonMid); ![]() |