forked from microsoft/TRELLIS.2
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdebug_shade_components.py
More file actions
174 lines (137 loc) · 6.65 KB
/
debug_shade_components.py
File metadata and controls
174 lines (137 loc) · 6.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#!/usr/bin/env python3
"""
Debug script to compare shade() intermediate values between fork and official.
This script captures:
1. Diffuse irradiance lookup
2. Specular prefilter values
3. FG LUT values
4. NdotV values
5. Reflection vectors
"""
import os
os.environ['OPENCV_IO_ENABLE_OPENEXR'] = '1'
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"
import sys
import torch
import numpy as np
from PIL import Image
# Check for saved debug data
FORK_DIR = "test_output_pbr"
OFFICIAL_DIR = "test_output_pbr_official"
def load_debug_tensor(path):
"""Load a debug tensor if it exists."""
if os.path.exists(path):
return np.load(path)
return None
def compare_tensors(name, fork_data, official_data, mask=None):
"""Compare two tensors and print statistics."""
if fork_data is None or official_data is None:
print(f" {name}: MISSING DATA")
return
if mask is not None:
fork_data = fork_data[mask]
official_data = official_data[mask]
fork_flat = fork_data.flatten()
official_flat = official_data.flatten()
fork_mean = fork_flat.mean()
official_mean = official_flat.mean()
if official_mean > 1e-6:
ratio = fork_mean / official_mean
else:
ratio = float('inf') if fork_mean > 1e-6 else 1.0
print(f" {name}:")
print(f" Fork mean: {fork_mean:.6f} std: {fork_flat.std():.6f}")
print(f" Official mean: {official_mean:.6f} std: {official_flat.std():.6f}")
print(f" Ratio: {ratio:.4f}")
def main():
print("=" * 70)
print("SHADE() COMPONENT COMPARISON")
print("=" * 70)
# Load mask for geometry filtering
fork_mask = np.array(Image.open(f"{FORK_DIR}/mask_pbr_00.png")).astype(np.float32) / 255.0
official_mask = np.array(Image.open(f"{OFFICIAL_DIR}/mask_pbr_00.png")).astype(np.float32) / 255.0
if fork_mask.ndim == 3:
fork_mask = fork_mask.max(axis=-1) > 0
else:
fork_mask = fork_mask > 0
if official_mask.ndim == 3:
official_mask = official_mask.max(axis=-1) > 0
else:
official_mask = official_mask > 0
# Load existing data
fork_shaded = np.array(Image.open(f"{FORK_DIR}/shaded_00.png")).astype(np.float32) / 255.0
official_shaded = np.array(Image.open(f"{OFFICIAL_DIR}/shaded_00.png")).astype(np.float32) / 255.0
fork_clay = np.array(Image.open(f"{FORK_DIR}/clay_00.png")).astype(np.float32) / 255.0
official_clay = np.array(Image.open(f"{OFFICIAL_DIR}/clay_00.png")).astype(np.float32) / 255.0
fork_base = np.array(Image.open(f"{FORK_DIR}/base_color_00.png")).astype(np.float32) / 255.0
official_base = np.array(Image.open(f"{OFFICIAL_DIR}/base_color_00.png")).astype(np.float32) / 255.0
fork_metallic = np.array(Image.open(f"{FORK_DIR}/metallic_00.png")).astype(np.float32) / 255.0
official_metallic = np.array(Image.open(f"{OFFICIAL_DIR}/metallic_00.png")).astype(np.float32) / 255.0
fork_roughness = np.array(Image.open(f"{FORK_DIR}/roughness_00.png")).astype(np.float32) / 255.0
official_roughness = np.array(Image.open(f"{OFFICIAL_DIR}/roughness_00.png")).astype(np.float32) / 255.0
fork_normal = np.array(Image.open(f"{FORK_DIR}/normal_pbr_00.png")).astype(np.float32) / 255.0
official_normal = np.array(Image.open(f"{OFFICIAL_DIR}/normal_pbr_00.png")).astype(np.float32) / 255.0
# Convert normal from [0,1] to [-1,1]
fork_normal_3d = fork_normal * 2 - 1
official_normal_3d = official_normal * 2 - 1
# Compute luminance
def luminance(img):
if img.ndim == 3:
return 0.299 * img[..., 0] + 0.587 * img[..., 1] + 0.114 * img[..., 2]
return img
fork_lum = luminance(fork_shaded)
official_lum = luminance(official_shaded)
fork_clay_2d = fork_clay[..., 0] if fork_clay.ndim == 3 else fork_clay
official_clay_2d = official_clay[..., 0] if official_clay.ndim == 3 else official_clay
# Pre-SSAO computation
fork_valid = fork_mask & (fork_clay_2d > 0.01)
official_valid = official_mask & (official_clay_2d > 0.01)
fork_pre = fork_lum[fork_valid] / fork_clay_2d[fork_valid]
official_pre = official_lum[official_valid] / official_clay_2d[official_valid]
print("\n1. FINAL OUTPUT COMPARISON:")
compare_tensors("Shaded luminance", fork_lum, official_lum, fork_mask)
print("\n2. PRE-SSAO COMPARISON:")
compare_tensors("Pre-SSAO", fork_pre, official_pre, None)
print("\n3. MATERIAL PROPERTIES:")
compare_tensors("Base color", fork_base, official_base, fork_mask)
compare_tensors("Metallic", fork_metallic, official_metallic, fork_mask)
compare_tensors("Roughness", fork_roughness, official_roughness, fork_mask)
print("\n4. NORMAL COMPARISON:")
# Compare normal statistics rather than direct difference due to different mask sizes
fork_n = fork_normal_3d[fork_mask]
official_n = official_normal_3d[official_mask]
print(f" Fork normal mean: {fork_n.mean(axis=0)}")
print(f" Official normal mean: {official_n.mean(axis=0)}")
# For pixels that overlap in both masks
overlap_mask = fork_mask & official_mask
if overlap_mask.sum() > 0:
fork_n_overlap = fork_normal_3d[overlap_mask]
official_n_overlap = official_normal_3d[overlap_mask]
dot_product = np.sum(fork_n_overlap * official_n_overlap, axis=-1)
dot_product = np.clip(dot_product, -1, 1)
angle_diff = np.arccos(dot_product) * 180 / np.pi
print(f" Overlapping pixels: {overlap_mask.sum()}")
print(f" Normal angle difference:")
print(f" Mean: {angle_diff.mean():.4f}°")
print(f" Std: {angle_diff.std():.4f}°")
print(f" Median: {np.median(angle_diff):.4f}°")
print(f" Within 10°: {100 * (angle_diff < 10).mean():.1f}%")
print("\n5. SSAO ANALYSIS:")
fork_f_occ = 1 - fork_clay_2d
official_f_occ = 1 - official_clay_2d
compare_tensors("f_occ", fork_f_occ, official_f_occ, fork_mask)
print("\n" + "=" * 70)
print("KEY FINDING: Compare pre-SSAO brightness ratio")
print("=" * 70)
pre_ratio = fork_pre.mean() / official_pre.mean()
print(f"Pre-SSAO brightness ratio: {pre_ratio:.4f}")
print(f"This ~{100*(1-pre_ratio):.1f}% difference is in the PBR shading.")
print()
print("POTENTIAL CAUSES:")
print(" 1. Missing spec = lerp(spec, diffuse, roughness) in shade()")
print(" 2. Different cubemap sampling (diffuse irradiance)")
print(" 3. Different specular prefilter values")
print(" 4. Different FG LUT sampling")
print(" 5. Normal-based lighting direction differences")
if __name__ == "__main__":
main()