Fortsetzung des angefangenen U5.3-Stands vom 2. Oktober.
- Fünf neue Sätze aus bake_pbr.py (metal, cloth, hide, bone, wood) für
Türme, Ancients, Grotte und die Schlundkrone. Ringborkes Rinde (RB_Bark)
auf ihren eigenen UVs mit dem 4k-Satz (Rauigkeit zu ORM gepackt),
Normalen im Tangentenraum des Meshes. Aufnahmen aller drei Lanes.
- Befund: Seit U5 war die Welt halb so hell und ohne Grün (Mid-Lane
Helligkeit 0,27, Grün 0 %). Ursachen und Korrekturen:
- Die PBR-Albedos wurden über die Godot-Farben multipliziert (Rasen
etwa x0,05). Jetzt teilt der Shader jede Karte durch ihren Mittel-
wert; die Godot-Farbe bleibt der Durchschnitt, die Karte liefert
Struktur. Ebenso die Rauigkeit, wo das Material eine eigene hat.
- Normal- und ORM-Karten waren als sRGB-Farbe importiert. Jetzt
Normalkarte bzw. linear.
- bake_pbr.py verzerrte alle Muster 10:1 (Regenstreifen). Stein, Erde,
Laub, Wasser und die neuen Sätze ohne Maserung neu gebacken.
- Der Treffer-Flash ersetzte die Grundfarbe aller Materialplätze; im
Nahkampf waren die Armeen weiß und der Boss gelb. Jetzt Tönung der
eigenen Farbe je Platz, wenig Emission, 16 Stufen.
- Nebel 0,0085 verschleierte die Kartenansicht zu 35-50 %; jetzt 0,0045.
Ergebnis Mid-Lane: Helligkeit 0,41-0,44, Grün 31-39 % (U3: 0,45 / 27 %).
- Tests: EditMode 29/29, PlayMode 13/13, Verify 718/0, Kerntests .NET
457/457, Fingerabdrücke unverändert (DCE95C96556F3A1A, 7B8E43AEFC0D60C5).
- Leistung: A/B mit einem U5.2-Player unter gleicher Last, kein messbarer
Unterschied (91,4 gegen 93,8 fps Karte, 124,9 gegen 125,3 fps Mid).
- Messwerkzeug tools/port/png_stats.py; Doku U5-VISUAL (U5.3),
PORT-STATUS, PORT-QA; Bilder screenshots/u5.3/ als JPEG.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
90 lines
3.3 KiB
Python
90 lines
3.3 KiB
Python
"""Brightness and colour of screenshots (U5.3), without external libraries.
|
||
|
||
Prints for each PNG (8-bit RGB/RGBA, every 4th pixel in both directions):
|
||
luma mean Rec. 709 luma of the sRGB values (0 black … 1 white)
|
||
sat mean HSV saturation
|
||
green share of pixels with a green hue (saturation > .15, hue 61°–162°)
|
||
blue share of pixels with a blue hue (saturation > .15, hue 180°–259°)
|
||
Usage: python3 tools/port/png_stats.py file.png [file.png …] [--crop x0 y0 x1 y1]
|
||
"""
|
||
import colorsys
|
||
import struct
|
||
import sys
|
||
import zlib
|
||
|
||
|
||
def read_png(path):
|
||
data = open(path, 'rb').read()
|
||
if data[:8] != b'\x89PNG\r\n\x1a\n':
|
||
raise ValueError(path + ': not a PNG')
|
||
pos, idat = 8, []
|
||
while pos < len(data):
|
||
length, kind = struct.unpack('>I4s', data[pos:pos + 8])
|
||
body = data[pos + 8:pos + 8 + length]
|
||
if kind == b'IHDR':
|
||
width, height, depth, color, _, _, interlace = struct.unpack('>IIBBBBB', body)
|
||
elif kind == b'IDAT':
|
||
idat.append(body)
|
||
pos += 12 + length
|
||
if depth != 8 or interlace != 0 or color not in (2, 6):
|
||
raise ValueError('%s: needs 8-bit RGB/RGBA without interlace' % path)
|
||
bpp = 3 if color == 2 else 4
|
||
raw = zlib.decompress(b''.join(idat))
|
||
stride = width * bpp
|
||
rows, prev, i = [], bytearray(stride), 0
|
||
for _ in range(height):
|
||
kind, line = raw[i], bytearray(raw[i + 1:i + 1 + stride])
|
||
i += 1 + stride
|
||
for x in range(stride):
|
||
a = line[x - bpp] if x >= bpp else 0
|
||
b = prev[x]
|
||
c = prev[x - bpp] if x >= bpp else 0
|
||
if kind == 1:
|
||
line[x] = (line[x] + a) & 255
|
||
elif kind == 2:
|
||
line[x] = (line[x] + b) & 255
|
||
elif kind == 3:
|
||
line[x] = (line[x] + ((a + b) >> 1)) & 255
|
||
elif kind == 4:
|
||
p = a + b - c
|
||
pa, pb, pc = abs(p - a), abs(p - b), abs(p - c)
|
||
line[x] = (line[x] + (a if pa <= pb and pa <= pc else b if pb <= pc else c)) & 255
|
||
rows.append(bytes(line))
|
||
prev = line
|
||
return width, height, bpp, rows
|
||
|
||
|
||
def stats(path, crop=None):
|
||
width, height, bpp, rows = read_png(path)
|
||
x0, y0, x1, y1 = crop if crop else (0, 0, width, height)
|
||
luma = sat = green = blue = n = 0
|
||
for y in range(y0, y1, 4):
|
||
row = rows[y]
|
||
for x in range(x0, x1, 4):
|
||
r, g, b = row[x * bpp] / 255, row[x * bpp + 1] / 255, row[x * bpp + 2] / 255
|
||
hue = colorsys.rgb_to_hls(r, g, b)[0]
|
||
top = max(r, g, b)
|
||
s = 0 if top == 0 else (top - min(r, g, b)) / top
|
||
luma += 0.2126 * r + 0.7152 * g + 0.0722 * b
|
||
sat += s
|
||
n += 1
|
||
if s > .15 and .17 < hue < .45:
|
||
green += 1
|
||
if s > .15 and .5 < hue < .72:
|
||
blue += 1
|
||
return luma / n, sat / n, green / n, blue / n
|
||
|
||
|
||
def main(argv):
|
||
crop = None
|
||
if '--crop' in argv:
|
||
at = argv.index('--crop')
|
||
crop = tuple(int(v) for v in argv[at + 1:at + 5])
|
||
argv = argv[:at] + argv[at + 5:]
|
||
for path in argv:
|
||
luma, sat, green, blue = stats(path, crop)
|
||
print('%-48s luma %.3f sat %.3f green %.2f blue %.2f' % (path.split('/')[-1], luma, sat, green, blue))
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main(sys.argv[1:])
|