mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-03 23:32:06 +01:00
docs(tools): the material norm names the shaders it is written for, fix/cab-lighting from 2ecf589d with the sun highlight weighted by its cosine, headlights with their own highlight and param_glossiness as a Phong exponent, and how master differs; the validator reads both versions from the shader sources, its options, output levels and requirements are documented with the order in which to fix a vehicle
This commit is contained in:
@@ -1,15 +1,19 @@
|
|||||||
# Norma materiałów
|
# Norma materiałów
|
||||||
|
|
||||||
Wartości, przy których tekstury wyglądają poprawnie w obecnych shaderach (`shaders/light_common.glsl`, GGX z roughness = 1 − G).
|
Wartości, przy których tekstury wyglądają poprawnie w shaderach opengl33 (`shaders/light_common.glsl`, GGX z roughness = 1 − G).
|
||||||
`validate_materials.py` sprawdza .mat i tekstury według tej normy.
|
`validate_materials.py` sprawdza .mat i tekstury według tej normy.
|
||||||
|
|
||||||
|
Norma i opis niżej dotyczą shaderów z gałęzi `fix/cab-lighting` (commit 2ecf589d i nowsze). Na `master` shadery liczą jeszcze po staremu, różnice są opisane przy każdym punkcie. Wartości tekstur z tabel są dobre dla obu wersji, bo stare shadery różnią się tylko połyskiem przy niskim słońcu, reflektorami i `param_glossiness`.
|
||||||
|
|
||||||
## Jak shader liczy piksel
|
## Jak shader liczy piksel
|
||||||
|
|
||||||
- albedo = diffuse (sRGB) × kolor diffuse submodelu
|
- albedo = diffuse (sRGB) × kolor diffuse submodelu
|
||||||
- słońce rozproszone = 1.5 · (N·L)^1.25 · albedo · (1 − B)
|
- słońce rozproszone = 1.5 · (N·L)^1.25 · albedo · (1 − B)
|
||||||
- połysk słońca = GGX(roughness) · R · `param_specular`, dla metalu dodatkowo × albedo; bez Fresnela i bez N·L
|
- połysk słońca = GGX(roughness) · N·L · R · `param_specular`, dla metalu dodatkowo × albedo; bez Fresnela
|
||||||
- otoczenie = cubemapa(roughness) · EnvBRDF(F0, roughness) · alfa normalmapy · `param_reflection`, F0 = 0.04 dla niemetalu, albedo dla metalu
|
- otoczenie = cubemapa(roughness) · EnvBRDF(F0, roughness) · alfa normalmapy · `param_reflection`, F0 = 0.04 dla niemetalu, albedo dla metalu
|
||||||
- reflektory = (N·L + 0.1) · (1 + `param_specular`) · albedo
|
- reflektory: rozproszone 1.35 · (N·L + 0.1) · albedo, połysk jak od słońca (GGX · N·L · R · `param_specular`)
|
||||||
|
|
||||||
|
Do poprawek z gałęzi fix/cab-lighting połysk nie miał N·L (przy niskim słońcu nie gasł), a reflektory mnożyły jasność przez (1 + `param_specular`).
|
||||||
|
|
||||||
## Kanały
|
## Kanały
|
||||||
|
|
||||||
@@ -43,7 +47,7 @@ Wartości, przy których tekstury wyglądają poprawnie w obecnych shaderach (`s
|
|||||||
|
|
||||||
Zasady:
|
Zasady:
|
||||||
|
|
||||||
- `param_specular` = 1. Większy rozjaśnia też obiekt w reflektorach (×(1 + `param_specular`)).
|
- `param_specular` = 1; siłę połysku niesie R.
|
||||||
- Niemetal: R·`param_specular` 0.02–0.12. R = 255 daje połysk ~17× za mocny; obniżanie G zamiast R tylko rozmywa odblask, a nie go ścisza.
|
- Niemetal: R·`param_specular` 0.02–0.12. R = 255 daje połysk ~17× za mocny; obniżanie G zamiast R tylko rozmywa odblask, a nie go ścisza.
|
||||||
- Metal: B = 255, jasny diffuse (liniowo ≥ 0.2, sRGB ≥ ~125) i alfa normalmapy wysoko. Ciemny diffuse albo niska alfa = czarny metal.
|
- Metal: B = 255, jasny diffuse (liniowo ≥ 0.2, sRGB ≥ ~125) i alfa normalmapy wysoko. Ciemny diffuse albo niska alfa = czarny metal.
|
||||||
- Bez szumu w R, G, B i alfie normalmapy: w słońcu iskrzy.
|
- Bez szumu w R, G, B i alfie normalmapy: w słońcu iskrzy.
|
||||||
@@ -53,13 +57,40 @@ Zasady:
|
|||||||
| shader | roughness | połysk słońca |
|
| shader | roughness | połysk słońca |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| brak `shader:` / `default`, `reflmap` | 0.04 (lustro, tylko otoczenie × `param_reflection`) | brak |
|
| brak `shader:` / `default`, `reflmap` | 0.04 (lustro, tylko otoczenie × `param_reflection`) | brak |
|
||||||
| `normalmap`, `parallax`, `detail_*` | 1 − 1/`param_glossiness` (większa wartość = bardziej matowo) | `param_specular` · (r+g+b)/2 diffuse |
|
| `normalmap`, `parallax`, `detail_*`, `shadowlessnormalmap`, `sunlessnormalmap`, `default_detail` | (2 / (`param_glossiness` + 2))^¼ jak wykładnik Phonga: 10 (domyślnie) → 0.64, 75 → 0.40, 1000 → 0.21 | `param_specular` · (r+g+b)/2 diffuse |
|
||||||
| `shadowlessnormalmap`, `sunlessnormalmap`, `default_detail`, `rain_windscreen` | zawsze 0.04 | `param_specular` · (r+g+b)/2 diffuse |
|
| `rain_windscreen` | zawsze 0.04 | `param_specular` · (r+g+b)/2 diffuse |
|
||||||
|
|
||||||
|
Przed fix/cab-lighting `normalmap`, `parallax` i `detail_*` miały roughness 1 − 1/`param_glossiness` (większa wartość = bardziej matowo), a `shadowlessnormalmap`, `sunlessnormalmap` i `default_detail` zawsze 0.04. Walidator rozpoznaje obie wersje z plików shaderów.
|
||||||
|
|
||||||
## Walidator
|
## Walidator
|
||||||
|
|
||||||
|
Wymaga Pythona 3 z `numpy` i `Pillow` (`pip install numpy pillow`).
|
||||||
|
|
||||||
```
|
```
|
||||||
python3 tools/materials/validate_materials.py ~/Projects/pctga dynamic --fallback <katalog gry> --csv wyniki.csv
|
python3 tools/materials/validate_materials.py <katalog z assetami> [podkatalogi...] [opcje]
|
||||||
|
python3 tools/materials/validate_materials.py ~/pctga dynamic --fallback <katalog gry> --shaders <repo>/shaders --csv wyniki.csv
|
||||||
```
|
```
|
||||||
|
|
||||||
`BLAD` – trzeba poprawić, `UWAGA` – prawie na pewno źle wygląda, `--info` pokazuje też informacje.
|
| opcja | znaczenie |
|
||||||
|
|---|---|
|
||||||
|
| podkatalogi | co sprawdzić względem katalogu z assetami, domyślnie `dynamic`; może być węższe, np. `dynamic/pkp/sa103` |
|
||||||
|
| `--fallback DIR` | drugi katalog gry, w którym szukać tekstur, których nie ma w pierwszym (można podać kilka razy) |
|
||||||
|
| `--shaders DIR` | shadery, według których liczyć; domyślnie `shaders/` z tego repo. Podaj shadery gry albo `fix/cab-lighting`, żeby ocena zgadzała się z tym, co widać w symulatorze |
|
||||||
|
| `--csv FILE` | wyniki do pliku CSV: material, shader, poziom, opis |
|
||||||
|
| `--info` | pokazuje też informacje, nie tylko błędy i uwagi |
|
||||||
|
| `-j N` | liczba procesów, domyślnie wszystkie rdzenie; cały pctga trwa ok. 80 s |
|
||||||
|
|
||||||
|
Wynik: dla każdego materiału z problemem ścieżka `.mat`, shader i lista komunikatów, na końcu podsumowanie.
|
||||||
|
|
||||||
|
- `BLAD` – trzeba poprawić (np. połysk niemetalu kilkanaście razy za mocny, brak tekstury, nieistniejący shader).
|
||||||
|
- `UWAGA` – prawie na pewno źle wygląda (lustro na niemetalu, ciemny metal, szum w specgloss).
|
||||||
|
- `info` – tylko z `--info`, rzeczy do wiadomości.
|
||||||
|
|
||||||
|
Walidator czyta pliki shaderów i z nich rozpoznaje, co każdy shader robi ze specgloss, `param_glossiness`, `param_specular` i `param_reflection`, więc działa i dla starych, i dla nowych shaderów.
|
||||||
|
|
||||||
|
### Jak poprawiać
|
||||||
|
|
||||||
|
1. Uruchom walidator na swoim katalogu pojazdu, np. `dynamic/pkp/sa103`.
|
||||||
|
2. Najpierw `BLAD`: zwykle R w specgloss (niemetal ok. 15/255) albo `param_specular` (zostaw 1, siłę połysku daje R).
|
||||||
|
3. Potem `UWAGA`: G (gładkość) według tabeli wartości docelowych, B tylko 0 albo 255, metal z jasnym diffuse.
|
||||||
|
4. Poprawiaj źródłowe TGA, nie DDS, i uruchom walidator jeszcze raz.
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ The norm (see tools/materials/README.md):
|
|||||||
specgloss G 1 - perceptual roughness (param_glossiness cancels out when >= 1)
|
specgloss G 1 - perceptual roughness (param_glossiness cancels out when >= 1)
|
||||||
specgloss B metalness, 0 or 1; a metal takes its colour from the diffuse texture
|
specgloss B metalness, 0 or 1; a metal takes its colour from the diffuse texture
|
||||||
normalmap A reflectivity, 1 where the surface reflects, lower only in cavities
|
normalmap A reflectivity, 1 where the surface reflects, lower only in cavities
|
||||||
param_specular 1, it also scales the headlights (1 + param_specular)
|
param_specular 1, the strength of the highlight lives in R
|
||||||
|
|
||||||
usage: validate_materials.py GAMEDIR [SUBDIR ...] [--fallback DIR ...] [--shaders DIR] [--csv FILE]
|
usage: validate_materials.py GAMEDIR [SUBDIR ...] [--fallback DIR ...] [--shaders DIR] [--csv FILE] [--info] [-j N]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -82,7 +82,7 @@ class Shaders:
|
|||||||
info = {
|
info = {
|
||||||
'textures': textures,
|
'textures': textures,
|
||||||
'reflection_default': params.get('reflection', 'one'),
|
'reflection_default': params.get('reflection', 'one'),
|
||||||
'gloss': 'specgloss' if 'specgloss' in gloss else ('param' if 'param' in gloss else 'unset'),
|
'gloss': 'specgloss' if 'specgloss' in gloss else ('phong' if 'phong_glossiness' in gloss else ('param' if 'param' in gloss else 'unset')),
|
||||||
'spec': 'specgloss' if 'specgloss' in spec else ('diffuse' if 'tex_color' in spec else ('none' if spec.strip() in ('', '0.0') else 'const')),
|
'spec': 'specgloss' if 'specgloss' in spec else ('diffuse' if 'tex_color' in spec else ('none' if spec.strip() in ('', '0.0') else 'const')),
|
||||||
'metal': bool(re.search(r'metalic\s*=\s*[^;]*specgloss', text)),
|
'metal': bool(re.search(r'metalic\s*=\s*[^;]*specgloss', text)),
|
||||||
'reflectivity': 'normalmap' if re.search(r'reflectivity\s*=[^;]*normal', text) else ('reflmap' if re.search(r'reflectivity\s*=[^;]*reflmap', text) else 'param'),
|
'reflectivity': 'normalmap' if re.search(r'reflectivity\s*=[^;]*normal', text) else ('reflmap' if re.search(r'reflectivity\s*=[^;]*reflmap', text) else 'param'),
|
||||||
@@ -254,7 +254,7 @@ def check(mat, values, nested, shaders, resolver, images, rel):
|
|||||||
add(INFO, 'param_specular nie ustawiony, bierze jasnosc specular submodelu z e3d/t3d')
|
add(INFO, 'param_specular nie ustawiony, bierze jasnosc specular submodelu z e3d/t3d')
|
||||||
if ps is not None and ps > PARAM_SPECULAR_MAX:
|
if ps is not None and ps > PARAM_SPECULAR_MAX:
|
||||||
add(ERROR if ps > 2 * PARAM_SPECULAR_MAX else WARN,
|
add(ERROR if ps > 2 * PARAM_SPECULAR_MAX else WARN,
|
||||||
'param_specular %g: reflektory swieca na nim %gx mocniej niz przy 1' % (ps, (1 + ps) / 2))
|
'param_specular %g mnozy polysk %gx (norma 1, sile niesie R; w starszych shaderach rozjasnia tez w reflektorach)' % (ps, ps))
|
||||||
psv = ps if ps is not None else 1.0
|
psv = ps if ps is not None else 1.0
|
||||||
|
|
||||||
def image(name):
|
def image(name):
|
||||||
@@ -328,12 +328,16 @@ def check(mat, values, nested, shaders, resolver, images, rel):
|
|||||||
roughness = 0.04
|
roughness = 0.04
|
||||||
elif shader['gloss'] == 'unset':
|
elif shader['gloss'] == 'unset':
|
||||||
roughness = min(max(1 - 1 / max(pg_effective, 1), 0.04), 1)
|
roughness = min(max(1 - 1 / max(pg_effective, 1), 0.04), 1)
|
||||||
|
elif shader['gloss'] == 'phong':
|
||||||
|
roughness = min(max((2 / (pg_effective + 2)) ** 0.25, 0.04), 1)
|
||||||
if shader['spec'] == 'diffuse' and diffuse is not None:
|
if shader['spec'] == 'diffuse' and diffuse is not None:
|
||||||
lin = srgb_to_linear(diffuse[..., :3])
|
lin = srgb_to_linear(diffuse[..., :3])
|
||||||
e = pct((lin.sum(axis=2) * 0.5)[opaque], 50) * psv
|
e = pct((lin.sum(axis=2) * 0.5)[opaque], 50) * psv
|
||||||
if e > DIELECTRIC_SPEC[1] and roughness is not None and roughness < 0.7:
|
if e > DIELECTRIC_SPEC[1] and roughness is not None and roughness < 0.7:
|
||||||
suggestion = DIELECTRIC_SPEC[1] / 2 / max(e / psv, 1e-3)
|
suggestion = DIELECTRIC_SPEC[1] / 2 / max(e / psv, 1e-3)
|
||||||
kind = 'lustrzany punkt (shader %s zawsze ma roughness 0.04)' % shadername if shader['gloss'] == 'param' else 'roughness %.2f z param_glossiness %g' % (roughness, pg_effective)
|
kind = 'lustrzany punkt (shader %s zawsze ma roughness 0.04)' % shadername if shader['gloss'] == 'param' else 'roughness %.2f z param_glossiness %g' % (roughness, pg_effective)
|
||||||
|
if shader['gloss'] == 'phong' and pg is None:
|
||||||
|
kind += ' (domyslne glossiness 10)'
|
||||||
add(WARN, 'polysk z jasnosci diffuse %.2f, %s; param_specular ok. %.2f' % (e, kind, suggestion))
|
add(WARN, 'polysk z jasnosci diffuse %.2f, %s; param_specular ok. %.2f' % (e, kind, suggestion))
|
||||||
if shader['gloss'] == 'unset' and pg is not None and pg_effective <= 2:
|
if shader['gloss'] == 'unset' and pg is not None and pg_effective <= 2:
|
||||||
add(INFO, 'param_glossiness %g w shaderze %s: tu wieksza wartosc = bardziej matowo (roughness %.2f)' % (pg, shadername, roughness))
|
add(INFO, 'param_glossiness %g w shaderze %s: tu wieksza wartosc = bardziej matowo (roughness %.2f)' % (pg, shadername, roughness))
|
||||||
|
|||||||
Reference in New Issue
Block a user