Project_WL/Assets/WL/Look/Character/WLCharacterBacklightSetting...

167 lines
12 KiB
C#
Raw Normal View History

// ─────────────────────────────────────────────────────────────────────────────
// WLCharacterBacklightSettings.cs — 캐릭터 역광(림·라이트 랩·피부 투과) + 번짐(Bloom) 값 (C45 · 코드 상수 금지)
//
// 2026-09-17 PD 「캐릭터가 조금 더 돋보이게」 + 팁(차가운 역광 · SSS · Bloom).
// enabled_ = 0 → 아무 것도 덧붙이지 않는다(캐릭터 재질 배열 원래대로) · bloomEnabled = 0 → 카메라/볼륨 0 변경.
// 🔴 Debug.LogError / Debug.LogException 을 쓰지 않는다(813z 규칙).
// ─────────────────────────────────────────────────────────────────────────────
using UnityEngine;
namespace WL.Look.Character
{
public class WLCharacterBacklightSettings : ScriptableObject
{
[Header("스위치")]
[Tooltip("0 = 역광 패스 전체 off(재질 배열 원래대로).")]
public int enabled_ = 1;
[Tooltip("1 이면 적용 내역을 로그로 남긴다.")]
public int verboseLog = 0;
[Header("대상")]
[Tooltip("이 셰이더 이름의 재질을 가진 렌더러에만 덧붙인다(얼굴 Unlit/Transparent · 파티클 제외). 비우면 파티클 외 전부.")]
public string shaderNameFilter = "Shader Graphs/Toon";
[Tooltip("재질 이름에 이 글자가 들어 있으면 피부로 보고 피부 투과(SSS)를 켠 재질을 물린다.")]
public string[] skinMaterialHints = { "Skin" };
[Header("⓪ 본체 밝히기 — 2026-09-17 PD 「캐릭터가 어둡게 보인다 · 원한 건 더 밝고 화사하게」")]
[Tooltip("1 = 캐릭터 본체(Toon) 재질 값을 렌더러별 프로퍼티 블록으로 덮는다(재질 에셋 0줄 · 0 = 덮지 않음).")]
public int baseBrighten = 1;
[Tooltip("그늘의 최소 밝기(원본 0.2). 클수록 그늘이 밝다.")]
[Range(0f, 1f)] public float baseMinimumDarkness = 0.38f;
[Tooltip("그늘 색(원본 (0.216,0.209,0.371) = 어두운 남색). 밝고 따뜻하게.")]
public Color baseShadowColor = new Color(0.46f, 0.38f, 0.44f, 0f);
[Tooltip("구름 그늘 세기(원본은 지형과 같은 값). 0 = 캐릭터는 구름 그늘을 안 받는다(항상 화사). 음수 = 재질 값 그대로.")]
public float baseCloudStrength = 0f;
[Header("① 역광 림 — 2026-09-17 PD 「너무 푸른색 느낌」 → 태양색 계열의 따뜻한 흰빛(차가운 하늘색 (0.28,0.62,1) 에서 변경) · 「라이팅이 과하다」 → 약하게")]
[ColorUsage(false, true)] public Color rimColor = new Color(1f, 0.96f, 0.88f, 1f);
[Tooltip("림 밝기 배수(1 초과 = HDR · Bloom 이 집어낸다).")]
public float rimStrength = 1.0f;
[Header("①-b 화면 공간 깊이 림(기본) — 실루엣 바깥쪽에만 · 화면 픽셀 굵기 일정")]
[Tooltip("1 = 깊이 림 사용(URP 에셋의 Depth Texture 가 꺼져 있으면 자동으로 프레넬만 쓴다). 실측: 프레넬만 쓰면 머리카락 안쪽 가닥마다 줄이 생긴다.")]
public int rimUseDepth = 1;
[Tooltip("띠 굵기 = 화면 짧은 변 × 이 값(0.0045 → 1080 기준 ≈ 5 px).")]
[Range(0f, 0.02f)] public float rimWidth = 0.003f;
[Tooltip("역광 쪽 이웃 픽셀이 이만큼(m) 더 멀면 실루엣으로 본다(작으면 머리카락 가닥에도 생긴다).")]
public float rimDepthThreshold = 0.12f;
[Tooltip("깊이 림에 섞는 프레넬 림의 양 — 가까울 때(두께감).")]
[Range(0f, 1f)] public float rimFresnelNear = 0.25f;
[Tooltip("깊이 림에 섞는 프레넬 림의 양 — 멀 때(0 = 깊이 림만 · 가닥 줄 없음).")]
[Range(0f, 1f)] public float rimFresnelFar = 0f;
[Header("①-a 프레넬 림(보조 · 깊이 텍스처가 없을 때는 이것만)")]
[Tooltip("가까울 때(rimNearDistance 이하)의 띠 굵기 — 클수록 가는 띠(0.4 = 굵게 · 0.7 = 가늘게).")]
[Range(0f, 1f)] public float rimThreshold = 0.52f;
[Tooltip("멀 때(rimFarDistance 이상)의 띠 굵기 — 작게 줘서 굵게. 게임 카메라(섬 12 m · 던전 11 m)에서 캐릭터가 ~70 px 라 가는 띠는 1 px 로 사라진다(실측).")]
[Range(0f, 1f)] public float rimThresholdFar = 0.36f;
[Tooltip("이 거리(m) 이하 = 가까움.")]
public float rimNearDistance = 3f;
[Tooltip("이 거리(m) 이상 = 멂.")]
public float rimFarDistance = 12f;
[Tooltip("띠 가장자리 부드러움(작을수록 셀 느낌).")]
[Range(0.001f, 0.5f)] public float rimSoftness = 0.05f;
[Tooltip("0 = 둘레 전부 · 1 = 가상 역광 쪽 윤곽만.")]
[Range(0f, 1f)] public float rimDirectional = 0.85f;
[Tooltip("림이 바탕을 덮는 정도(1 = 림 색 그대로 · 0 = 순수 가산). 실측: 순수 가산은 노란 머리·살색 위에서 흰 테가 돼 차가운 색이 안 보였다.")]
[Range(0f, 1f)] public float rimOpacity = 0.5f;
[Header("가상 역광 위치(카메라 기준) — 캐릭터 뒤 + 위 + 옆")]
[Tooltip("위쪽 성분(클수록 머리·어깨 윗선에 맺힌다).")]
public float backlightUp = 0.7f;
[Tooltip("옆 성분(+ = 화면 오른쪽 · − = 왼쪽). 메인 조명이 왼쪽 앞에서 오므로 오른쪽 뒤가 자연스럽다.")]
public float backlightSide = 0.8f;
[Header("② 라이트 랩 — 빛이 안쪽으로 스며드는 옅은 번짐")]
[ColorUsage(false, true)] public Color wrapColor = new Color(1f, 0.95f, 0.88f, 1f);
public float wrapStrength = 0.06f;
public float wrapPower = 1.6f;
[Header("③ 피부 투과(SSS 흉내) — 피부 재질에만")]
[ColorUsage(false, true)] public Color sssColor = new Color(1f, 0.32f, 0.22f, 1f);
public float sssStrength = 1.0f;
public float sssPower = 1.7f;
[Header("④ 얼굴 포인트 라이트 — 얼굴 주변만 살짝 밝히는 빛(2026-09-17 PD 「얼굴 쪽을 살짝 포인트로 하이라이트」)")]
[Tooltip("0 = 끔.")]
public int faceEnabled = 1;
[ColorUsage(false, true)] public Color faceColor = new Color(1f, 0.93f, 0.85f, 1f);
[Tooltip("밝기(가산). 0.2 = 은은 · 0.5 = 뚜렷.")]
public float faceStrength = 0.16f;
[Tooltip("빛이 닿는 반지름 = 얼굴(머리) 렌더러 경계 크기 × 이 값. 3 = 얼굴에서 가장 밝고 발끝으로 갈수록 옅어지는 몸 전체 채움빛(「밝고 화사」).")]
public float faceRadiusScale = 3.0f;
[Tooltip("0 = 빛 방향을 향한 면만 · 1 = 둥글게 감싸는 빛.")]
[Range(0f, 1f)] public float faceWrap = 0.8f;
[Tooltip("빛 위치(카메라 기준): 위쪽 성분.")]
public float faceLightUp = 0.6f;
[Tooltip("빛 위치(카메라 기준): 옆 성분(− = 화면 왼쪽 · 역광의 반대쪽).")]
public float faceLightSide = -0.4f;
[Tooltip("얼굴 중심으로 쓸 렌더러 이름 힌트(앞에서부터 찾는다 · 못 찾으면 얼굴 빛 없음).")]
public string[] faceRendererHints = { "Face", "Head" };
[Header("등급 — 2026-09-17 PD 「톤은 전체 공유 · 플레이어 1등급 · 몬스터와 NPC 2등급 · 배경(프랍) 3등급」")]
[Tooltip("1 = 2·3등급도 적용한다. 0 = 플레이어(1등급)만.")]
public int tiersEnabled = 1;
[Tooltip("2등급(몬스터·NPC·동료) — 림 세기/불투명도(1등급보다 약하게).")]
public float tier2RimStrength = 0.7f;
[Range(0f, 1f)] public float tier2RimOpacity = 0.35f;
[Tooltip("2등급 — 몸 전체 채움빛 세기(얼굴 중심 없음).")]
public float tier2FillStrength = 0.07f;
[Tooltip("2등급 — 본체가 Toon 그래프일 때: 그늘 최소 밝기에 더하는 양 · 그늘 색을 공유 그늘 색 쪽으로 섞는 비율(재질마다 원래 값이 달라 상대값으로).")]
[Range(0f, 0.5f)] public float tier2MinimumDarknessAdd = 0.10f;
[Range(0f, 1f)] public float tier2ShadowBlend = 0.6f;
public Color tier2ShadowColor = new Color(0.36f, 0.31f, 0.40f, 0f);
[Tooltip("2등급 — 본체가 MK Toon(힘민지 몬스터)일 때 그늘 색 `_GoochDarkColor`(원본 검정). 전체가 공유하는 따뜻한 보랏빛 그늘 계열.")]
public Color tier2MkShadowColor = new Color(0.30f, 0.25f, 0.34f, 1f);
[Tooltip("2등급 대상 역할(원본 eRole 이름). PC 는 1등급이라 제외.")]
public string[] tier2Roles = { "Mob", "Pet", "Worker", "Fairy", "EnemyPC", "Farmer", "Summon", "BattleNPC", "EnemyPet", "EnemySummon" };
[Tooltip("2등급 재검색 주기(s) — 새로 나온 몬스터에 붙인다.")]
public float tier2ScanSeconds = 1.5f;
[Tooltip("3등급(배경 프랍) — 덧그리기 없음(비용 0) · 공유 재질의 런타임 복사본에 그늘 값만 살짝: 그늘 최소 밝기에 더하는 양 · 공유 그늘 색 쪽으로 섞는 비율.")]
[Range(0f, 0.5f)] public float tier3MinimumDarknessAdd = 0.06f;
[Range(0f, 1f)] public float tier3ShadowBlend = 0.35f;
public Color tier3ShadowColor = new Color(0.29f, 0.27f, 0.39f, 0f);
[Tooltip("3등급 대상 셰이더 이름(이 셰이더를 쓰는 MeshRenderer · 캐릭터/몬스터/지형/풀 제외).")]
public string[] tier3ShaderNames = { "Shader Graphs/Toon" };
[Tooltip("3등급 재검색 주기(s) — 섬 확장·설치물처럼 나중에 생기는 프랍.")]
public float tier3ScanSeconds = 5f;
[Header("⑤ 번짐(Bloom) — 역광이 화사하게 번지게(전체 화면 패스 · 모바일 비용 있음)")]
[Tooltip("0 = 카메라·볼륨을 건드리지 않는다.")]
public int bloomEnabled = 1;
[Tooltip("이 씬이 떠 있을 때만 켠다(전투 맵의 기존 후처리는 그대로).")]
public string[] bloomScenes = { "Level01" };
[Tooltip("이 밝기 이상만 번진다(HDR 1 초과 = 림·반짝임만).")]
public float bloomThreshold = 1.2f;
public float bloomIntensity = 0.4f;
[Range(0f, 1f)] public float bloomScatter = 0.55f;
[Tooltip("1 = 카메라 HDR 을 켠다(림만 골라 번지게 · 끄면 흰 거품·밝은 모래도 같이 번진다).")]
public int bloomHdr = 1;
[Tooltip("1 = 1/4 해상도에서 시작(모바일 권장).")]
public int bloomQuarter = 1;
[Tooltip("블러 단계 수(작을수록 쌈).")]
public int bloomMaxIterations = 4;
// ───────────────────────────────────────── 싱글턴(Resources)
public const string ResourcePath = "WL/WLCharacterBacklightSettings";
static WLCharacterBacklightSettings s_inst;
static bool s_tried;
public static WLCharacterBacklightSettings Instance
{
get
{
if (s_inst == null && !s_tried)
{
s_tried = true;
s_inst = Resources.Load<WLCharacterBacklightSettings>(ResourcePath);
}
return s_inst;
}
}
public static void Invalidate() { s_inst = null; s_tried = false; }
}
}