ModelTranslator: base 贴图写 PNG 前做 linear->sRGB 编码
Blender Image.pixels 对 sRGB 贴图返回线性值,直接写 Non-Color PNG 会偏暗; 现按 Unity sRGB 导入预期编码 RGB,alpha(AO/透明)保持原值。附单元测试。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
ea93b3b217
commit
60c83a3765
@@ -33,6 +33,20 @@ def decode_sphere_map(enc01):
|
|||||||
return np.concatenate([xy * 2.0, (l * 2.0 - 1.0)[..., None]], axis=-1)
|
return np.concatenate([xy * 2.0, (l * 2.0 - 1.0)[..., None]], axis=-1)
|
||||||
|
|
||||||
|
|
||||||
|
def linear_to_srgb(c):
|
||||||
|
c = np.clip(c, 0.0, 1.0)
|
||||||
|
return np.where(c <= 0.0031308, c * 12.92,
|
||||||
|
1.055 * np.power(c, 1.0 / 2.4) - 0.055)
|
||||||
|
|
||||||
|
|
||||||
|
def encode_base_texture_for_png(rgba):
|
||||||
|
"""Base RGB is stored for Unity sRGB import; alpha keeps AO/opacity data."""
|
||||||
|
out = np.array(rgba, dtype=np.float32, copy=True)
|
||||||
|
out[..., :3] = linear_to_srgb(out[..., :3])
|
||||||
|
out[..., 3] = np.clip(out[..., 3], 0.0, 1.0)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
# ---------------- 以下仅在 Blender 内执行 ----------------
|
# ---------------- 以下仅在 Blender 内执行 ----------------
|
||||||
|
|
||||||
|
|
||||||
@@ -316,7 +330,7 @@ def _convert_material(mat, model_name, outdir, max_size, used_snames, src_dir):
|
|||||||
stem = model_name if sname == model_name else "%s_%s" % (model_name, sname)
|
stem = model_name if sname == model_name else "%s_%s" % (model_name, sname)
|
||||||
base_png = os.path.abspath(os.path.join(outdir, "%s_base.png" % stem))
|
base_png = os.path.abspath(os.path.join(outdir, "%s_base.png" % stem))
|
||||||
mix_png = os.path.abspath(os.path.join(outdir, "%s_mix.png" % stem))
|
mix_png = os.path.abspath(os.path.join(outdir, "%s_mix.png" % stem))
|
||||||
_save_png(base_png, base)
|
_save_png(base_png, encode_base_texture_for_png(base))
|
||||||
_save_png(mix_png, mix)
|
_save_png(mix_png, mix)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
import bl_convert as bc
|
||||||
|
|
||||||
|
|
||||||
|
class TestBaseTextureEncoding(unittest.TestCase):
|
||||||
|
def test_base_texture_rgb_is_encoded_as_srgb_and_alpha_is_preserved(self):
|
||||||
|
rgba = np.array([[[0.0, 0.18, 0.5, 0.37]]], dtype=np.float32)
|
||||||
|
|
||||||
|
encoded = bc.encode_base_texture_for_png(rgba)
|
||||||
|
|
||||||
|
expected_rgb = np.array([0.0, 0.461356, 0.735357], dtype=np.float32)
|
||||||
|
np.testing.assert_allclose(encoded[0, 0, :3], expected_rgb, rtol=0, atol=1e-5)
|
||||||
|
self.assertAlmostEqual(float(encoded[0, 0, 3]), 0.37, places=6)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user