Pilot für eine generierte Figur: der Kohlenbeißer g01 kommt als Meshy-Ergebnis in den Port und ersetzt dort das Phase-4-Modell. Eine Figur genügt, um Pipeline, Bild und Leistung zu bewerten; Anatomie, Gesichter und Haare bleiben Künstler-Sculpts oder lizenzierte Packs (Vorgabe vom 1. Oktober). - Meshy-Pipeline unter tools/meshy/: API-Client (meshy.py, Ledger, Schlüssel nie geloggt) und Blender-Generator (build_figure.py) aus den unveränderten Downloads in assets-source/meshy/g01/. Export über den Manifest-Eintrag kohlenbeisser mit "replaces": "g01"; LuneUnitLibraryBuilder hängt die Figur zusätzlich unter der Godot-ID ein. - UV-Material AI_Kohlenbeisser (Familie authored): die Karten der Figur selbst, Lava-Glut aus der Albedo-Alpha (Meshy 7.1 ohne Emission-Map) mit _Glow 1,25. - Schatten ohne Normalen-Versatz (LuneShadowBias, _ShadowNormalScale 0): die UV-Naht-Kopien tragen geknickte Normalen; URP ApplyShadowBias zog helle Nähte durch den Schatten. Die Figur wirft nur mit Tiefen-Bias. - GPU-Batched-Skinning (PlayerSettings.meshDeformation, LuneProjectSetup): 35 Meshy-Kohlenbeißer kosteten mit CPU-Skinning rund 1 ms Haupt- und 1,7 ms Render-Thread je Bild. - LuneBenchmark: -luneBenchmarkRound und -luneBenchmarkArmy (Figuren-Stress). LuneAssetPreview: LUNE_ONLY für Vorher/Nachher, Trefferframe aus der Kontaktzeit des Manifests. FigureViewsTests (Explicit) für die vier Spielmomente der Figur. - Tests: EditMode 29/29, PlayMode 14/14 (6 Explicit), Verify 718/0, Player-Selbsttest 30/30, Kerntests 457/457, Fingerabdrücke unverändert (DCE95C96556F3A1A, 7B8E43AEFC0D60C5). - Leistung (A/B im selben Fenster, 35x g01): Meshy -21 % Mid (109,7 zu 138,8) und -13 % Karte (91,7 zu 105,6) gegenüber Phase-4. GPU-Skinning hebt Mid um 7-27 % und entlastet Haupt- und Render-Thread. Standard-Partie ab Runde 21 ohne g01 im Bild: drei Varianten bis 6 %. - Doku: U5-VISUAL (U5.5), PORT-QA, PORT-STATUS, KNOWN-BEHAVIOR-DIFFERENCES; Bilder screenshots/u5.5/ und u5.5-gpu/, Rohdaten logs/u5.5/. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
134 lines
6.7 KiB
C#
134 lines
6.7 KiB
C#
using System.IO;
|
||
using System.Linq;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
using UnityEngine.Rendering;
|
||
using UnityEngine.Rendering.Universal;
|
||
|
||
namespace Luneraria.EditorTools
|
||
{
|
||
/// <summary>
|
||
/// Rendering setup of the project (URP asset and renderer, linear colour space, SSAO since
|
||
/// U5.4, skinning on the GPU since U5.5), idempotent.
|
||
/// Unity -batchmode -projectPath . -executeMethod Luneraria.EditorTools.LuneProjectSetup.ConfigureRendering
|
||
/// </summary>
|
||
public static class LuneProjectSetup
|
||
{
|
||
public const string SettingsFolder = "Assets/Luneraria/Settings/Rendering";
|
||
public const string PipelinePath = SettingsFolder + "/Luneraria_URP.asset";
|
||
public const string RendererPath = SettingsFolder + "/Luneraria_URP_Renderer.asset";
|
||
|
||
public static void ConfigureRendering()
|
||
{
|
||
int code = 0;
|
||
try
|
||
{
|
||
Configure();
|
||
}
|
||
catch (System.Exception error)
|
||
{
|
||
Debug.LogError("LUNE_SETUP failed: " + error);
|
||
code = 1;
|
||
}
|
||
EditorApplication.Exit(code);
|
||
}
|
||
|
||
public static UniversalRenderPipelineAsset Configure()
|
||
{
|
||
Directory.CreateDirectory(SettingsFolder);
|
||
AssetDatabase.Refresh();
|
||
var asset = AssetDatabase.LoadAssetAtPath<UniversalRenderPipelineAsset>(PipelinePath);
|
||
if (asset == null)
|
||
{
|
||
var renderer = ScriptableObject.CreateInstance<UniversalRendererData>();
|
||
AssetDatabase.CreateAsset(renderer, RendererPath);
|
||
ResourceReloader.ReloadAllNullIn(renderer, UniversalRenderPipelineAsset.packagePath);
|
||
asset = UniversalRenderPipelineAsset.Create(renderer);
|
||
AssetDatabase.CreateAsset(asset, PipelinePath);
|
||
}
|
||
// The Godot sun reaches 190 m with four splits (scripts/luneraria/view/lune_world.gd).
|
||
asset.shadowDistance = 190f;
|
||
asset.shadowCascadeCount = 4;
|
||
asset.supportsHDR = true;
|
||
asset.msaaSampleCount = 4;
|
||
asset.useSRPBatcher = true;
|
||
EditorUtility.SetDirty(asset);
|
||
Ssao(AssetDatabase.LoadAssetAtPath<UniversalRendererData>(RendererPath));
|
||
GraphicsSettings.defaultRenderPipeline = asset;
|
||
int current = QualitySettings.GetQualityLevel();
|
||
for (int level = 0; level < QualitySettings.names.Length; level++)
|
||
{
|
||
QualitySettings.SetQualityLevel(level, false);
|
||
QualitySettings.renderPipeline = asset;
|
||
}
|
||
QualitySettings.SetQualityLevel(current, false);
|
||
PlayerSettings.colorSpace = ColorSpace.Linear;
|
||
// U5.5: skinned figures deform on the GPU in batches (compute shaders). With CPU
|
||
// skinning 35 Meshy Kohlenbeißer (23,000 vertices each) cost about 1 ms of main and
|
||
// 1.7 ms of render thread per frame.
|
||
PlayerSettings.meshDeformation = MeshDeformation.GPUBatched;
|
||
MoveInto("Assets/UniversalRenderPipelineGlobalSettings.asset", SettingsFolder + "/UniversalRenderPipelineGlobalSettings.asset");
|
||
MoveInto("Assets/DefaultVolumeProfile.asset", SettingsFolder + "/DefaultVolumeProfile.asset");
|
||
AssetDatabase.SaveAssets();
|
||
Debug.Log("LUNE_SETUP rendering: " + PipelinePath + ", colour space " + PlayerSettings.colorSpace + ", skinning " + PlayerSettings.meshDeformation);
|
||
return asset;
|
||
}
|
||
|
||
/// <summary>SSAO intensity, radius (m) and fade distance (m) for the top-down camera (U5.4).</summary>
|
||
public const float SsaoIntensity = 1.2f;
|
||
public const float SsaoRadius = .35f;
|
||
public const float SsaoFalloff = 260f;
|
||
|
||
/// <summary>
|
||
/// Screen-space ambient occlusion (U5.4): contact shadows of figures and buildings on the
|
||
/// ground and occlusion in crevices, the depth cue of current renderers. Radius in metres
|
||
/// for figures of 1–2 m seen from 20–100 m; it fades only beyond the overview distance.
|
||
/// The feature is a sub-asset of the renderer and listed in its feature map; updated in place.
|
||
/// </summary>
|
||
static void Ssao(UniversalRendererData renderer)
|
||
{
|
||
if (renderer == null)
|
||
return;
|
||
var feature = renderer.rendererFeatures.OfType<ScreenSpaceAmbientOcclusion>().FirstOrDefault();
|
||
if (feature == null)
|
||
{
|
||
feature = ScriptableObject.CreateInstance<ScreenSpaceAmbientOcclusion>();
|
||
feature.name = "ScreenSpaceAmbientOcclusion";
|
||
AssetDatabase.AddObjectToAsset(feature, renderer);
|
||
AssetDatabase.TryGetGUIDAndLocalFileIdentifier(feature, out string _, out long localId);
|
||
var data = new SerializedObject(renderer);
|
||
SerializedProperty list = data.FindProperty("m_RendererFeatures");
|
||
list.arraySize++;
|
||
list.GetArrayElementAtIndex(list.arraySize - 1).objectReferenceValue = feature;
|
||
SerializedProperty map = data.FindProperty("m_RendererFeatureMap");
|
||
map.arraySize++;
|
||
map.GetArrayElementAtIndex(map.arraySize - 1).longValue = localId;
|
||
data.ApplyModifiedPropertiesWithoutUndo();
|
||
}
|
||
var settings = new SerializedObject(feature);
|
||
settings.FindProperty("m_Settings.Intensity").floatValue = SsaoIntensity;
|
||
settings.FindProperty("m_Settings.Radius").floatValue = SsaoRadius;
|
||
settings.FindProperty("m_Settings.Falloff").floatValue = SsaoFalloff;
|
||
settings.FindProperty("m_Settings.DirectLightingStrength").floatValue = .25f;
|
||
// Depth source after the opaques: URP copies the depth it already has, so SSAO needs no
|
||
// second geometry pass (the depth-normals prepass cost 19–28 % frame rate on the M5).
|
||
settings.FindProperty("m_Settings.Source").enumValueIndex = 0;
|
||
settings.FindProperty("m_Settings.Downsample").boolValue = true;
|
||
settings.FindProperty("m_Settings.AfterOpaque").boolValue = true;
|
||
settings.ApplyModifiedPropertiesWithoutUndo();
|
||
feature.SetActive(System.Environment.GetEnvironmentVariable("LUNE_SSAO") != "0");
|
||
EditorUtility.SetDirty(feature);
|
||
EditorUtility.SetDirty(renderer);
|
||
}
|
||
|
||
static void MoveInto(string from, string to)
|
||
{
|
||
if (File.Exists(from) && !File.Exists(to))
|
||
{
|
||
string error = AssetDatabase.MoveAsset(from, to);
|
||
if (!string.IsNullOrEmpty(error))
|
||
Debug.LogWarning("LUNE_SETUP move " + from + ": " + error);
|
||
}
|
||
}
|
||
}
|
||
}
|