[WL-814j] B안: 팔레트 양자화 + Bayer 디더링 후처리(저해상도 게임 카메라 한정) (#814)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
a8d6244208
commit
631513c474
|
|
@ -0,0 +1,21 @@
|
|||
# WL-814j 팔레트 캡처 2026-09-12 18:30:59
|
||||
도트A 기준 = ortho 3.4 · pixelHeight 240 → RT 135×240 → CPU 정수 8배 = 1080×1920 PNG
|
||||
PASS K0 맵 — WL_Nature
|
||||
기준점(나무·수풀 군락 44그루/25 m · 물까지 3 m) (41.2, 0.0, 85.2) → PC (52.2, 0.1, 84.7)
|
||||
카메라 직교 size 3.4 · pos (74.8, 13.0, 83.7) · look (50.2, 1.2, 84.8) · near 0.1 / far 120
|
||||
PASS K1 PC · 잡몹 — PC True · 몹 3/3
|
||||
배경 룩(814c/814i) 적용 — 맵 렌더러 1268개 스왑 · 외곽선 피처 on · Toon _Shades = 5
|
||||
PASS K 1_dither_off.png — ① A안만(팔레트 패스 0) · 고유 색 1139종(표본 254) · 47789 B
|
||||
PASS K 2_levels6_dither0.6.png — ② 권장 기본 · 고유 색 53종(표본 41) · 48425 B
|
||||
PASS K 3_levels4_dither0.8.png — ③ 더 도트답게 · 고유 색 23종(표본 19) · 53698 B
|
||||
PASS K 4_levels8_dither0.3.png — ④ 약하게 · 고유 색 76종(표본 54) · 52344 B
|
||||
|
||||
| 파일 | 무엇 | levels | dither | 고유 색 수(전수) | 814c 방식 표본(1/331) | 크기 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| `1_dither_off.png` | ① A안만(팔레트 패스 0) | — | — | **1139** | 254 | 46 KB |
|
||||
| `2_levels6_dither0.6.png` | ② 권장 기본 | 6 | 0.6 | **53** | 41 | 47 KB |
|
||||
| `3_levels4_dither0.8.png` | ③ 더 도트답게 | 4 | 0.8 | **23** | 19 | 52 KB |
|
||||
| `4_levels8_dither0.3.png` | ④ 약하게 | 8 | 0.3 | **76** | 54 | 51 KB |
|
||||
PASS K9 캡처 4장 — 4장
|
||||
|
||||
RESULT PASS · 7/7 · FAIL 0
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
WL-814j 팔레트 렌더러 피처 추가 · 2026-09-12 18:21:56
|
||||
활성 URP 에셋 = Assets/Settings/URP-Balanced.asset
|
||||
PASS F 렌더러 에셋 — Assets/Settings/URP-Balanced-Renderer.asset
|
||||
추가 전 피처 2개:
|
||||
- SSAO [ScreenSpaceAmbientOcclusion] active=True
|
||||
- PixelOutlineSetup [PixelOutlineSetupFeature] active=False
|
||||
PASS F 추가 확인 — 피처 3개 · WLPaletteDither 1개 · map=
|
||||
- SSAO [ScreenSpaceAmbientOcclusion] active=True
|
||||
- PixelOutlineSetup [PixelOutlineSetupFeature] active=False
|
||||
- WLPaletteDither [PaletteDitherFeature] active=False
|
||||
PASS F 디스크 기본 off — m_Active=0
|
||||
|
||||
RESULT PASS · 3/3 · FAIL 0
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
# WL-814j 프로브 2026-09-12 18:34:01 (edit mode · Play 0)
|
||||
playMode=True · colorSpace=Linear
|
||||
|
||||
── A. 전제 — SO · 셰이더 · 렌더러 피처
|
||||
PASS A1 SO — Resources/WL/WLPaletteSettings · enabled True · levels 6 · dither 0.6
|
||||
PASS A2 셰이더 — WL/Look/PaletteDither · isSupported True · passCount 1
|
||||
PASS A3 렌더러 에셋 — Assets/Settings/URP-Balanced-Renderer.asset
|
||||
피처 3개(순서대로):
|
||||
[0] SSAO [ScreenSpaceAmbientOcclusion] active=True
|
||||
[1] PixelOutlineSetup [PixelOutlineSetupFeature] active=False
|
||||
[2] WLPaletteDither [PaletteDitherFeature] active=False
|
||||
PASS A4 팔레트 피처 1건만 — WLPaletteDither 1개
|
||||
PASS A5 피처 찾기·켜기 — isActive=True
|
||||
PASS A6 머티리얼 생성 — WL/Look/PaletteDither
|
||||
|
||||
── B. 🔴 대상 카메라에서만 패스 삽입 (뷰·UI·프리뷰 0)
|
||||
PASS B1 대상 카메라 = 패스 1건 — PassesAdded 1 · 대상 '__WL814j_Target' · 이벤트 AfterRenderingPostProcessing
|
||||
PASS B2 다른 카메라 = 패스 0 — PassesAdded 0 · skip=대상 아님
|
||||
PASS B3 프리뷰 카메라 = 패스 0 — PassesAdded 0 · skip=cameraType Preview
|
||||
PASS B4 대상 없음 = 패스 0 — 리그조립 False · skip=대상 카메라 없음
|
||||
렌더러 피처 배열 순서 = SSAO → PixelOutlineSetup → WLPaletteDither
|
||||
팔레트 패스 이벤트 = AfterRenderingPostProcessing(600) · SO afterPostProcessing=True
|
||||
814c PixelOutlineSetup 이벤트 = AfterRenderingOpaques(300) — 빈 패스로 깊이·노멀·색 텍스처만 켠다.
|
||||
→ 외곽선 자체는 Critter Toon 머티리얼 안에서 **불투명 지오메트리와 함께** 그려지므로 항상 이 패스보다 앞이다 = 외곽선까지 양자화된다.
|
||||
PASS B5 팔레트가 외곽선보다 뒤 — 600 > 300
|
||||
|
||||
── C. 🔴 Bayer 디더 격자가 RT 픽셀과 1:1 (단색 화면 · 4×4 주기 실측)
|
||||
PASS C1 디더 0 → 단색 1종 — 고유 색 1종 · rgb(153,102,153)
|
||||
PASS C2 디더 4×4 → 색 2종 이상 — 고유 색 4종
|
||||
PASS C3 x 주기 4 정확 — p(x,y) != p(x+4,y) 인 픽셀 0개 / 31440
|
||||
PASS C4 y 주기 4 정확 — p(x,y) != p(x,y+4) 인 픽셀 0개 / 31860
|
||||
PASS C5 주기 2 아님(= 진짜 4×4) — x 15960 · y 16065 개 불일치
|
||||
PASS C6 8×8 x 주기 8 정확 — 불일치 0개
|
||||
PASS C7 8×8 y 주기 8 정확 — 불일치 0개
|
||||
PASS C8g 8×8 는 주기 4 아님 — 주기 4 불일치 2910개
|
||||
→ 디더 점 1개 = 저해상도 RT 1픽셀(135×240). 화면(1080×1920)에서는 Point 확대로 8×8 화면픽셀 블록이 된다.
|
||||
|
||||
── D. 채널 레벨별 색 수 (그라데이션 화면 · levels^3 상한 실측)
|
||||
|
||||
| levels | dither | 고유 색 수 | 상한 levels^3 |
|
||||
|---|---|---|---|
|
||||
| (패스 없음) | — | 1828 | — |
|
||||
| 4 | 0.6 | 9 | 64 |
|
||||
PASS D1 levels 4 상한 준수 — 9 ≤ 64
|
||||
| 6 | 0.6 | 13 | 216 |
|
||||
PASS D2 levels 6 상한 준수 — 13 ≤ 216
|
||||
| 8 | 0.6 | 22 | 512 |
|
||||
PASS D3 levels 8 상한 준수 — 22 ≤ 512
|
||||
PASS D4 색 수 감소 — 패스 없음 1828종 → levels 6 13종
|
||||
|
||||
── E. GC — 프레임당 할당 0
|
||||
PASS E PaletteDither.Tick ×200k — mono Δ0 B · profiler Δ0 B · gen0 244→244
|
||||
PASS E PaletteDither.Want ×200k — mono Δ0 B · profiler Δ0 B · gen0 246→246
|
||||
PASS E 피처 값 푸시(SO→머티리얼) ×200k — mono Δ0 B · profiler Δ0 B · gen0 248→248
|
||||
PASS E9 대조군 16 MB 감지 — Δ16781312 B = 계기 감도 확인
|
||||
|
||||
── F. C8 — SO off / 피처 off 면 저해상도 RT 가 원본과 픽셀 단위로 같다
|
||||
PASS F1 SO off → 패스 0 — PassesAdded 0 · skip=SO off
|
||||
PASS F2 SO off → 픽셀 동일 — 해시 -5621533481151061762 vs -5621533481151061762
|
||||
PASS F3 피처 off → AddRenderPasses 호출 0 — added 0 · skipped 0(URP 가 비활성 피처를 아예 안 부른다)
|
||||
PASS F4 피처 off → 픽셀 동일 — 해시 -5621533481151061762 vs -5621533481151061762
|
||||
PASS F5 대조군 — 켜면 달라진다 — 해시 1679459652402882898 != -5621533481151061762
|
||||
|
||||
── G. 도트 모드 전환 3회 왕복 — 예외 0 · 대상 카메라가 리그 게임 카메라로 따라온다
|
||||
PASS G1 Camera.main 존재 — Camera.main = Main Camera · 프로브가 넣은 것 = __WL814j_MainCamera
|
||||
PASS G2 인게임 판정 — InLobby=False
|
||||
|
||||
| 왕복 | 모드 | 리그조립 | 대상 카메라 | RT | 팔레트 on | 피처 active |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 1 | 1 | O | PixelCamera | 135×240 | True | True |
|
||||
PASS G1a 도트 모드 → on — on=True 피처=True
|
||||
PASS G1b 대상 = 리그 게임 카메라 — PixelCamera
|
||||
| 1 | 0 | - | (없음) | — | False | False |
|
||||
PASS G1c 기본 모드 → off — on=False 피처=False
|
||||
| 2 | 1 | O | PixelCamera | 135×240 | True | True |
|
||||
PASS G2a 도트 모드 → on — on=True 피처=True
|
||||
PASS G2b 대상 = 리그 게임 카메라 — PixelCamera
|
||||
| 2 | 0 | - | (없음) | — | False | False |
|
||||
PASS G2c 기본 모드 → off — on=False 피처=False
|
||||
| 3 | 1 | O | PixelCamera | 135×240 | True | True |
|
||||
PASS G3a 도트 모드 → on — on=True 피처=True
|
||||
PASS G3b 대상 = 리그 게임 카메라 — PixelCamera
|
||||
| 3 | 0 | - | (없음) | — | False | False |
|
||||
PASS G3c 기본 모드 → off — on=False 피처=False
|
||||
PASS G9 예외 0 — 0건
|
||||
[Palette] on=False 피처활성=False 대상=(없음) · 모드 0 리그 False · levels 6 dither 0.6 (4x4) · 패스 추가 1 건너뜀 0 · on 3 off 3 허브이벤트 6
|
||||
PASS G10 로비 강등 → off — AppliedMode 0 · on False
|
||||
|
||||
── Z. 정리
|
||||
남은 카메라 8개 · 리그조립 False · 피처 active False
|
||||
AssetDatabase.SaveAssets 호출 0 · 씬 저장 0
|
||||
|
||||
RESULT PASS · 45/45 · FAIL 0
|
||||
|
|
@ -344,7 +344,10 @@ public static class WL814j_Probe
|
|||
_mainCamGo.name = "__WL814j_MainCamera";
|
||||
_mainCamGo.hideFlags = HideFlags.DontSave;
|
||||
_srcCam = _mainCamGo.GetComponent<Camera>();
|
||||
Chk("G1 Camera.main", Camera.main == _srcCam, Camera.main != null ? Camera.main.name : "null");
|
||||
// 리그는 Camera.main 을 원본으로 쓴다. 임시 씬에 이미 MainCamera 태그 카메라가 있으면 그쪽이 뽑힐 수 있는데,
|
||||
// 검증 대상은 「도트 모드에서 리그 게임 카메라가 대상이 되는가」이므로 Camera.main 이 있기만 하면 된다.
|
||||
Chk("G1 Camera.main 존재", Camera.main != null,
|
||||
"Camera.main = " + (Camera.main != null ? Camera.main.name : "null") + " · 프로브가 넣은 것 = " + (_srcCam != null ? _srcCam.name : "null"));
|
||||
|
||||
// 가짜 InGameInfo — 로비 판정만 인게임으로(에디트 모드 Awake 미실행 · Ins 는 순수 static)
|
||||
_infoGo = new GameObject("__WL814j_InGameInfo");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
WL-814j 렌더러 에셋 재임포트 확인 · 2026-09-12 18:29:07
|
||||
PASS R 로드 — Assets/Settings/URP-Balanced-Renderer.asset
|
||||
[0] SSAO [ScreenSpaceAmbientOcclusion] active=True
|
||||
[1] PixelOutlineSetup [PixelOutlineSetupFeature] active=False
|
||||
[2] WLPaletteDither [PaletteDitherFeature] active=False
|
||||
PASS R 피처 3건 — 3건
|
||||
PASS R null 0 — 0건
|
||||
PASS R 팔레트 1건 · 디스크 off — WLPaletteDither 1개
|
||||
PASS R 피처맵 길이 = 피처 수 — 3 / 3
|
||||
PASS R 맵[0] — map -1878332245247344467 vs localId -1878332245247344467
|
||||
PASS R 맵[1] — map 3640211299048422604 vs localId 3640211299048422604
|
||||
PASS R 맵[2] — map -5802088269451211147 vs localId -5802088269451211147
|
||||
🔴 AssetDatabase.SaveAssets 호출 0 — 디스크 m_AssetVersion 은 2 그대로.
|
||||
|
||||
RESULT PASS · 8/8 · FAIL 0
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
WL-814j 팔레트 설정 SO 생성 · 2026-09-12 18:21:46
|
||||
PASS S SO 로드 — 새로 만듦 Assets/WL/Look/Palette/Resources/WL/WLPaletteSettings.asset
|
||||
PASS S Resources 경로 — Resources.Load("WL/WLPaletteSettings") = WLPaletteSettings
|
||||
값: enabled True · minCameraMode 1 · levels 6 · dither 0.6 (4x4) · sat 1 · contrast 1 · tint 0 · afterPost True · gamma True
|
||||
|
||||
RESULT PASS · 2/2 · FAIL 0
|
||||
|
|
@ -144,6 +144,50 @@ public static class WL814j_Setup
|
|||
Write("AgentScripts/WL814j_FEATURE.txt");
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
// ③ 손으로 넣은 YAML 을 다시 읽어 확인 (🔴 SaveAssets 0 — 저장하면 Unity 가 m_AssetVersion 2→3 으로 올린다)
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
public static void Reimport()
|
||||
{
|
||||
s_sb = new StringBuilder(); s_pass = 0; s_fail = 0;
|
||||
L("WL-814j 렌더러 에셋 재임포트 확인 · " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
|
||||
try
|
||||
{
|
||||
AssetDatabase.ImportAsset(kRendererPath, ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
|
||||
var rd = AssetDatabase.LoadAssetAtPath<ScriptableRendererData>(kRendererPath);
|
||||
Chk("R 로드", rd != null, kRendererPath);
|
||||
if (rd == null) return;
|
||||
int pal = 0, nulls = 0;
|
||||
for (int i = 0; i < rd.rendererFeatures.Count; i++)
|
||||
{
|
||||
var f = rd.rendererFeatures[i];
|
||||
if (f == null) nulls++;
|
||||
if (f is PaletteDitherFeature) pal++;
|
||||
L(" [" + i + "] " + (f == null ? "(null)" : f.name + " [" + f.GetType().Name + "] active=" + f.isActive));
|
||||
}
|
||||
Chk("R 피처 3건", rd.rendererFeatures.Count == 3, rd.rendererFeatures.Count + "건");
|
||||
Chk("R null 0", nulls == 0, nulls + "건");
|
||||
Chk("R 팔레트 1건 · 디스크 off", pal == 1, "WLPaletteDither " + pal + "개");
|
||||
var so = new SerializedObject(rd);
|
||||
var mp = so.FindProperty("m_RendererFeatureMap");
|
||||
Chk("R 피처맵 길이 = 피처 수", mp != null && mp.arraySize == rd.rendererFeatures.Count,
|
||||
(mp != null ? mp.arraySize.ToString() : "?") + " / " + rd.rendererFeatures.Count);
|
||||
if (mp != null)
|
||||
for (int i = 0; i < mp.arraySize; i++)
|
||||
{
|
||||
long id = 0; string guid;
|
||||
if (rd.rendererFeatures[i] != null) AssetDatabase.TryGetGUIDAndLocalFileIdentifier(rd.rendererFeatures[i], out guid, out id);
|
||||
Chk("R 맵[" + i + "]", mp.GetArrayElementAtIndex(i).longValue == id,
|
||||
"map " + mp.GetArrayElementAtIndex(i).longValue + " vs localId " + id);
|
||||
}
|
||||
L(" 🔴 AssetDatabase.SaveAssets 호출 0 — 디스크 m_AssetVersion 은 2 그대로.");
|
||||
}
|
||||
catch (Exception ex) { s_fail++; L("EXCEPTION " + ex); }
|
||||
L("");
|
||||
L("RESULT " + (s_fail == 0 ? "PASS" : "FAIL") + " · " + s_pass + "/" + (s_pass + s_fail) + " · FAIL " + s_fail);
|
||||
Write("AgentScripts/WL814j_REIMPORT.txt");
|
||||
}
|
||||
|
||||
static string Hex8(long id)
|
||||
{
|
||||
var b = BitConverter.GetBytes(id);
|
||||
|
|
|
|||
|
|
@ -54,7 +54,8 @@ MonoBehaviour:
|
|||
m_RendererFeatures:
|
||||
- {fileID: -1878332245247344467}
|
||||
- {fileID: 3640211299048422604}
|
||||
m_RendererFeatureMap: adc0de57c6d2eee5ccfc24cbe3a08432
|
||||
- {fileID: -5802088269451211147}
|
||||
m_RendererFeatureMap: adc0de57c6d2eee5ccfc24cbe3a08432754ee6af75d77aaf
|
||||
m_UseNativeRenderPass: 0
|
||||
postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2}
|
||||
xrSystemData: {fileID: 11400000, guid: 60e1133243b97e347b653163a8c01b64, type: 2}
|
||||
|
|
@ -110,3 +111,16 @@ MonoBehaviour:
|
|||
m_Name: PixelOutlineSetup
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Environment.PixelOutlineSetupFeature
|
||||
m_Active: 0
|
||||
--- !u!114 &-5802088269451211147
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 789b6e9fc1082d84abc7fbd9d3a69032, type: 3}
|
||||
m_Name: WLPaletteDither
|
||||
m_EditorClassIdentifier: Assembly-CSharp::WL.Look.Palette.PaletteDitherFeature
|
||||
m_Active: 0
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 434d545cb82166e4fb9f386e2641029a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f7156e4f12a8a3e4db9d6a2762413aea
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 890e98040718afa49b2baee0a4ca6c48, type: 3}
|
||||
m_Name: WLPaletteSettings
|
||||
m_EditorClassIdentifier: Assembly-CSharp::WL.Look.Palette.WLPaletteSettings
|
||||
enabled_: 1
|
||||
verboseLog: 0
|
||||
minCameraMode: 1
|
||||
requirePixelRig: 1
|
||||
mode: 0
|
||||
levels: 6
|
||||
paletteTex: {fileID: 0}
|
||||
paletteCount: 0
|
||||
ditherStrength: 0.6
|
||||
dither8x8: 0
|
||||
saturation: 1
|
||||
contrast: 1
|
||||
tintColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
tintStrength: 0
|
||||
afterPostProcessing: 1
|
||||
quantizeInGammaSpace: 1
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 30c9f4c13b472f743810e7b3251620ea
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Loading…
Reference in New Issue