关于翻录
一些论坛:
1、https://cs.rin.ru/forum/viewtopic.php?t=100672
这个帖子是用来查询和请求解压虚幻文件的AES KEY。
2、https://www.gildor.org/smf/index.php
这个是Umoder下兴者自己建立的论坛,主要是用来讨论 (寻找方法)解决一些有特殊加密的虚幻文件
3、https://forum.xentax.com/index.php
这是一个论坛,主要是解决各种非“通用”引擎的游戏文件
4、https://discord.gg/7yixAVhS
这是个Discord聊天群,主要用来解决一些有特殊加密的Unity3D的文件
[Main(Rain, _USERAIN, off)] _RainGroup("下雨", float) = 0 [Title(Rain, Puddle)] [Sub(Rain)] [HDR] _PuddleColor("Puddle Color", Color) = (1,1,1,1) [Sub(Rain)] _PuddleWeight("Puddle Weight", Range(0, 1)) = 0 [Title(Rain, Ripple)] [Sub(Rain)] [NoScaleOffset] _RippleMaskMap("Ripple Mask Map", 2D) = "black" {} [Sub(Rain)] _RippleTilling("Ripple Tilling", float) = 0.05 [Sub(Rain)] _RippleSpeed("Ripple Speed", float) = 1 [Sub(Rain)] _RippleNormalStrength("Ripple Normal Strength", Range(0, 1)) = 1 // 下雨 #if _USERAIN // pubble float finalPuddleWeight = _PuddleWeight * puddleWeight; albedo = lerp(albedo, _PuddleColor.rgb, saturate(finalPuddleWeight + _PuddleColor.a - 1)); roughnessRst = saturate(roughnessRst - 0.3); roughnessRst = lerp(roughnessRst, 0, finalPuddleWeight); finalNormalTS = lerp(finalNormalTS, float3(0,0,1), finalPuddleWeight); // ripple 1 float rippleMask1; float3 rippleNormal1 = CalcRippleNormal(positionWS, 0, rippleMask1); finalNormalTS = lerp(finalNormalTS, rippleNormal1, rippleMask1 * finalPuddleWeight); // ripple 2 float rippleMask2; float3 rippleNormal2 = CalcRippleNormal(positionWS, 0.5, rippleMask2); finalNormalTS = lerp(finalNormalTS, rippleNormal2, rippleMask2 * finalPuddleWeight); #endif float3 CalcRippleNormal(float3 positionWS, float offset, out float rippleMask) { half4 rippleMaskMap = SAMPLE_TEXTURE2D(_RippleMaskMap, sampler_RippleMaskMap, positionWS.xz * _RippleTilling + offset.xx); rippleMask = rippleMaskMap.r * saturate(1 - abs(rippleMaskMap.r + frac(_Time.y * _RippleSpeed + offset) - 1) * 10); float2 rippleNormalXY = rippleMaskMap.gb * 2 - 1; float rippleNormalZ = sqrt(1 - dot(rippleNormalXY, rippleNormalXY)); float3 rippleNormal = normalize(float3(rippleNormalXY * _RippleNormalStrength, rippleNormalZ)); return rippleNormal; }
#ifndef CUSTOM_WEATHER_INCLUDED #define CUSTOM_WEATHER_INCLUDED #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DBuffer.hlsl" uniform float _GlobalRoughnessOffset; uniform float _SnowWeight; uniform half4 _SnowColor; uniform TEXTURE2D(_SnowMaskMap); SAMPLER(sampler_SnowMaskMap); float CalcSnowMask(float3 positionWS, float3 NModel, out float3 snowNormalTS, out float snowAO) { // snow half NoUP = dot(NModel, float3(0,1,0)); half snowMask = saturate(NoUP + 0.2) * _SnowWeight; // half4 snowMaskMap = SAMPLE_TEXTURE2D(_SnowMaskMap, sampler_SnowMaskMap, positionWS.xz * 0.02); // normal float2 snowNormalXY = snowMaskMap.rg * 2 - 1; snowNormalXY *= 0.2; float snowNormalZ = sqrt(1 - dot(snowNormalXY, snowNormalXY)); snowNormalTS = normalize(float3(snowNormalXY, snowNormalZ)); // ao snowAO = snowMaskMap.b; return snowMask; } void BlendSnow(inout SurfaceData surfaceData, float3 positionWS, float3 NModel) { float3 snowNormalTS; float snowAO; half snowMask = CalcSnowMask(positionWS, NModel, snowNormalTS, snowAO); surfaceData.albedo = lerp(surfaceData.albedo, _SnowColor.rgb, snowMask); surfaceData.metallic = lerp(surfaceData.metallic, 0, snowMask); surfaceData.smoothness = lerp(surfaceData.smoothness, 0, snowMask); surfaceData.occlusion = lerp(surfaceData.occlusion, snowAO, snowMask); surfaceData.normalTS = lerp(surfaceData.normalTS, snowNormalTS, snowMask); } #endif
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Sirenix.OdinInspector; using TEngine; using Unity.Mathematics; using UnityEngine; using UnityEngine.Rendering; namespace GameLogic.TimeOfDay { [ExecuteInEditMode] public partial class TimeOfDay : MonoBehaviour { enum EDayPhaseStage { Transition, ToStay, Stay, } [Header("日阶段"), SerializeField] private float m_DayPhaseTransitionDuration = 10f; [SerializeField] private TimeOfDay_DayPhase[] m_DayPhases; [Header("天气"), SerializeField] private TimeOfDay_WeatherInfo m_WeatherInfo; private int m_ShaderID_Cubemap = Shader.PropertyToID("_Cubemap"); private int m_ShaderID_UseToggle = Shader.PropertyToID("_UseToggle"); private int m_ShaderID_Cubemap2 = Shader.PropertyToID("_Cubemap2"); private int m_ShaderID_Toggle = Shader.PropertyToID("_Toggle"); private string m_Key_USETOGGLE_ON = "_USETOGGLE_ON"; private Dictionary<EDayPhase, TimeOfDay_DayPhase> m_DicDayPhase; private EDayPhase m_CurrentDayPhase = EDayPhase.Day; private EWeatherType m_CurrentWeather = EWeatherType.SunnyDay; private EDayPhase m_PreviousDayPhase = EDayPhase.Day; private EWeatherType m_PreviousWeather = EWeatherType.SunnyDay; private Material m_MatSkybox; private Light m_MainLight; private TimeOfDay_Weather m_Weather; private EDayPhaseStage m_Stage; private float m_Timer; public static TimeOfDay Instance { get; private set; } private bool IsGameRunning => Application.isPlaying && ClientTimeHelper.HasSync; private TimeOfDay_DayPhase CurrentPoint => GetPoint(m_CurrentDayPhase); void Awake() { Instance = this; GameEvent.AddEventListener<int, int, int, int>(EventType.WorldStateSync, OnWorldStateSync); } void OnDestroy() { Instance = null; GameEvent.RemoveEventListener<int, int, int, int>(EventType.WorldStateSync, OnWorldStateSync); } private void OnEnable() { if (IsGameRunning) { // InitConfig(); // SyncTime(); m_CurrentDayPhase = (EDayPhase)DataCacheModule.Instance.WorldStateDataCache.DayPhaseId; m_CurrentWeather = (EWeatherType)DataCacheModule.Instance.WorldStateDataCache.WeatherId; } m_MatSkybox = RenderSettings.skybox; m_MainLight = RenderSettings.sun; if (m_DicDayPhase == null) { m_DicDayPhase = new(); foreach (var p in m_DayPhases) { m_DicDayPhase[p.m_DayPhase] = p; } } if (m_Weather == null) { m_Weather = new(this, m_WeatherInfo); } ClearWeather(); BeginWeather(m_CurrentWeather); m_Stage = EDayPhaseStage.ToStay; } void Update() { if (IsGameRunning) { UpdateDayPhase(); m_Weather.Update(); } #if UNITY_EDITOR else { UpdateDebugMode(); } #endif } TimeOfDay_DayPhase GetPoint(EDayPhase dayPhase) { m_DicDayPhase.TryGetValue(dayPhase, out var p); return p; } TimeOfDay_EnvData GetEnvData(EDayPhase dayPhase, EWeatherType weatherType) { TimeOfDay_DayPhase point = GetPoint(dayPhase); TimeOfDay_EnvData envData = null; if (weatherType == EWeatherType.LightRain || weatherType == EWeatherType.RainStorm) { var weatherEnvData = m_WeatherInfo.m_RainEnvData.FirstOrDefault(a => a.m_DayPhase == dayPhase); if (weatherEnvData != null) { envData = weatherEnvData.m_EnvData; } } else if (weatherType == EWeatherType.Snow) { var weatherEnvData = m_WeatherInfo.m_SnowEnvData.FirstOrDefault(a => a.m_DayPhase == dayPhase); if (weatherEnvData != null) { envData = weatherEnvData.m_EnvData; } } if (envData == null) envData = point.m_EnvData; return envData; } void UpdateStaying() { TimeOfDay_EnvData envData = GetEnvData(m_CurrentDayPhase, m_CurrentWeather); UpdateSkybox(envData.m_SkyCubeMap); m_MainLight.intensity = envData.m_LightIntensity; m_MainLight.color = envData.m_LightColor; ApplyGI(envData.m_GIInfo); } void UpdateToggleing(float weight) { TimeOfDay_EnvData fromEnv = GetEnvData(m_PreviousDayPhase, m_PreviousWeather); TimeOfDay_EnvData toEnv = GetEnvData(m_CurrentDayPhase, m_CurrentWeather); if (toEnv.m_SkyCubeMap != fromEnv.m_SkyCubeMap) { UpdateSkyboxToggleing(fromEnv.m_SkyCubeMap, toEnv.m_SkyCubeMap, weight); } else { UpdateSkybox(toEnv.m_SkyCubeMap); } m_MainLight.intensity = math.lerp(fromEnv.m_LightIntensity, toEnv.m_LightIntensity, weight); m_MainLight.color = Color.Lerp(fromEnv.m_LightColor, toEnv.m_LightColor, weight); } void UpdateDayPhase() { if (m_Stage == EDayPhaseStage.Transition) { m_Timer += Time.deltaTime; float weight = Mathf.Clamp01(m_Timer / m_DayPhaseTransitionDuration); UpdateToggleing(weight); if (weight >= 1) { m_Stage = EDayPhaseStage.ToStay; } } else if (m_Stage == EDayPhaseStage.ToStay) { UpdateStaying(); m_Stage = EDayPhaseStage.Stay; } else if (m_Stage == EDayPhaseStage.Stay) { } } #region sky void UpdateSkybox(Cubemap cubemap) { m_MatSkybox.SetTexture(m_ShaderID_Cubemap, cubemap); m_MatSkybox.SetFloat(m_ShaderID_UseToggle, 0); m_MatSkybox.DisableKeyword(m_Key_USETOGGLE_ON); } void UpdateSkyboxToggleing(Cubemap from, Cubemap to, float weight) { m_MatSkybox.SetTexture(m_ShaderID_Cubemap, from); m_MatSkybox.SetTexture(m_ShaderID_Cubemap2, to); m_MatSkybox.SetFloat(m_ShaderID_Toggle, weight); m_MatSkybox.SetFloat(m_ShaderID_UseToggle, 1); m_MatSkybox.EnableKeyword(m_Key_USETOGGLE_ON); } #endregion #region GI public static void ApplyGI(TimeOfDay_GIInfo giInfo) { if (giInfo == null) return; SetEnvironmentSettings(giInfo.m_EnvironmentSettings); SetReflectionProbes(giInfo); } private static void SetEnvironmentSettings(TimeOfDay_GIInfo.EnvironmentSettings es) { // 环境光设置 RenderSettings.ambientMode = es.m_AmbientMode; RenderSettings.ambientSkyColor = es.m_SkyColor; RenderSettings.ambientEquatorColor = es.m_EquatorColor; RenderSettings.ambientGroundColor = es.m_GroundColor; // 天空盒和环境光强度 RenderSettings.skybox = es.m_SkyboxMaterial; RenderSettings.ambientIntensity = es.m_EnvironmentIntensity; RenderSettings.subtractiveShadowColor = es.m_RealtimeColor; RenderSettings.ambientProbe = es.m_AmbientProbe; // reflection probe RenderSettings.defaultReflectionMode = DefaultReflectionMode.Custom; RenderSettings.reflectionBounces = es.m_ReflectionBounces; RenderSettings.reflectionIntensity = es.m_ReflectionIntensity; RenderSettings.defaultReflectionResolution = es.m_DefaultReflectionResolution; RenderSettings.customReflectionTexture = es.m_CustomReflectionTexture; // 雾效设置 RenderSettings.fog = es.m_Fog; RenderSettings.fogMode = es.m_FogMode; RenderSettings.fogColor = es.m_FogColor; RenderSettings.fogDensity = es.m_FogDensity; RenderSettings.fogStartDistance = es.m_FogStartDistance; RenderSettings.fogEndDistance = es.m_FogEndDistance; // 其他设置 RenderSettings.haloStrength = es.m_Halo; RenderSettings.flareFadeSpeed = es.m_FlareFadeSpeed; RenderSettings.flareStrength = es.m_FlareStrength; } static void SetReflectionProbes(TimeOfDay_GIInfo giInfo) { for (int i = 0; i < giInfo.m_ReflectionProbes.Length; i++) { var probeItem = giInfo.m_ReflectionProbes[i]; GameObject goProbe = GameObject.Find(probeItem.m_Path); if (!goProbe) continue; ReflectionProbe probe = goProbe.GetComponent<ReflectionProbe>(); if (!probe) continue; probe.customBakedTexture = probeItem.m_Cubemap; } } #endregion #region weather private void OnWorldStateSync(int oldDayPhaseId, int newDayPhaseId, int oldWeatherId, int newWeatherId) { m_PreviousDayPhase = (EDayPhase)oldDayPhaseId; m_CurrentDayPhase = (EDayPhase)newDayPhaseId; m_PreviousWeather = (EWeatherType)oldWeatherId; m_CurrentWeather = (EWeatherType)newWeatherId; m_Timer = 0; m_Stage = EDayPhaseStage.Transition; BeginWeather(m_CurrentWeather); } void BeginWeather(EWeatherType tarWeather) { m_Weather.BeginWeather(tarWeather); } [Button("清除天气")] private void ClearWeather() { m_Weather.Clear(); } #endregion } }
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace GameLogic.TimeOfDay { [Serializable] public class TimeOfDay_DayPhase { public EDayPhase m_DayPhase; public float m_StartMinutes; public TimeOfDay_EnvData m_EnvData; } [Serializable] public class TimeOfDay_EnvData { public Cubemap m_SkyCubeMap; public Color m_LightColor; public float m_LightIntensity; public TimeOfDay_GIInfo m_GIInfo; } public enum EDayPhase { Day = 1, Duck, Night, } }
//#define DEBUGMODE using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Sirenix.OdinInspector; using TEngine; using Unity.Mathematics; using UnityEngine; using UnityEngine.Rendering; namespace GameLogic.TimeOfDay { public partial class TimeOfDay { void UpdateDebugMode() { #if UNITY_EDITOR && DEBUGMODE UpdateDebugMode2(); #endif } #if UNITY_EDITOR && DEBUGMODE [Header("昼夜变化"), SerializeField, Range(0.0f, 1440.0f)] private float m_TimelineMinutes = 360.0f; [SerializeField, ReadOnly] private string m_CurrentTimeString; [SerializeField, Tooltip("速度:每秒多少分钟")] private float m_TimelineSpeedMinutes = 1f; private float m_OldTimeline = -1; void UpdateDebugMode2() { if (Application.isPlaying) { m_TimelineMinutes += m_TimelineSpeedMinutes * Time.deltaTime; // Change to the next day if (m_TimelineMinutes > 1440) m_TimelineMinutes = 0; EditorUpdate(); m_Weather.Update(); } else { if (m_OldTimeline != m_TimelineMinutes) { m_OldTimeline = m_TimelineMinutes; EditorUpdate(); } } } EDayPhase GetCurrentDayPhase() { int lastIndex = m_DayPhases.Length - 1; for (int i = lastIndex; i >= 0; --i) { var p = m_DayPhases[i]; if (m_TimelineMinutes > p.m_StartMinutes) { return p.m_DayPhase; } } return EDayPhase.Night; } void EditorUpdate() { EDayPhase curDayPhase = GetCurrentDayPhase(); if (m_CurrentDayPhase != curDayPhase) { m_PreviousDayPhase = m_CurrentDayPhase; m_CurrentDayPhase = curDayPhase; } m_CurrentDayPhase = curDayPhase; TimeOfDay_DayPhase curPoint = GetPoint(curDayPhase); TimeOfDay_DayPhase prePoint = m_CurrentDayPhase == EDayPhase.Day ? m_DayPhases.LastOrDefault() : GetPoint((EDayPhase)((int)m_CurrentDayPhase - 1)); float toggleEndMinutes = curPoint.m_StartMinutes + m_DayPhaseTransitionDuration; if (m_TimelineMinutes > curPoint.m_StartMinutes && m_TimelineMinutes < toggleEndMinutes) //在切换中 { float weight = Mathf.Clamp01((m_TimelineMinutes - curPoint.m_StartMinutes) / m_DayPhaseTransitionDuration); UpdateToggleing(weight); int curHours = GetCurrentHours(out int curMinutes); m_CurrentTimeString = $"{prePoint.m_DayPhase} -> {curPoint.m_DayPhase} {weight:p0} {curHours:d2}:{curMinutes:d2} {m_Weather.CurrentWeather}"; } else { UpdateStaying(); int curHours = GetCurrentHours(out int curMinutes); m_CurrentTimeString = $"{curPoint.m_DayPhase} {curHours:d2}:{curMinutes:d2} {m_Weather.CurrentWeather}"; } } int GetCurrentHours(out int curMinutes) { int currentHours = Mathf.FloorToInt(m_TimelineMinutes / 60); curMinutes = Mathf.RoundToInt(m_TimelineMinutes) % 60; return currentHours; } [Button("天晴")] private void BeginSunnyDay() { BeginWeather(EWeatherType.SunnyDay); } [Button("下小雨")] private void BeginLightRain() { BeginWeather(EWeatherType.LightRain); } [Button("下暴雨")] private void BeginRainStorm() { BeginWeather(EWeatherType.RainStorm); } [Button("下雪")] private void BeginSnow() { BeginWeather(EWeatherType.Snow); } [Button("彩虹")] private void BeginCaiHong() { BeginWeather(EWeatherType.CaiHong); } [Button("极光")] private void BeginJiGuang() { BeginWeather(EWeatherType.JiGuang); } [Button("霓虹")] private void BeginLiHong() { BeginWeather(EWeatherType.LiHong); } [Button("流星雨")] private void BeginLiuXinYu() { BeginWeather(EWeatherType.LiuXinYu); } /* void InitConfig() { if (!IsGameRunning) return; for (int i = 0; i < m_DayPhases.Length; i++) { var p = m_DayPhases[i]; ConfigSystem.Instance.Tables.TbDayPhase.DataMap.TryGetValue((int)p.m_DayPhase, out var config); if (config != null) { p.m_StartMinutes = config.StartTime; } else { Debug.LogError($"Cann't find config, day phase:{p.m_DayPhase}, table:DayPhase"); } } } [Button("同步至服务器时间")] void SyncTime() { if (!IsGameRunning) return; m_TimelineMinutes = HomeUI.GetDayMinutes(); } */ #endif } }
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; namespace GameLogic.TimeOfDay { public class TimeOfDay_GIInfo : ScriptableObject { [Serializable] public class EnvironmentSettings { // Environment 标签设置 [Header("Environment")] public Material m_SkyboxMaterial; public AmbientMode m_AmbientMode; public Color m_SkyColor = Color.gray; public Color m_EquatorColor = Color.gray; public Color m_GroundColor = Color.gray; public float m_EnvironmentIntensity = 1.0f; public Color m_RealtimeColor = Color.white; public SphericalHarmonicsL2 m_AmbientProbe; // reflection [Header("Reflection")] public int m_ReflectionBounces; public float m_ReflectionIntensity; public int m_DefaultReflectionResolution; public Texture m_CustomReflectionTexture; // Fog 设置 [Header("Fog Settings")] public bool m_Fog = false; public FogMode m_FogMode = FogMode.Exponential; public Color m_FogColor = Color.gray; public float m_FogDensity = 0.01f; public float m_FogStartDistance = 0.0f; public float m_FogEndDistance = 300.0f; // Halo 和 Flare 设置 [Header("Other Settings")] public float m_Halo = 0; public float m_FlareFadeSpeed = 3.0f; public float m_FlareStrength = 1.0f; } [Serializable] public class ReflectionProbeItem { public string m_Path; public Texture m_Cubemap; } public EnvironmentSettings m_EnvironmentSettings; public ReflectionProbeItem[] m_ReflectionProbes; } }
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; namespace GameLogic.TimeOfDay { public class TimeOfDay_Weather { enum EStage { UnStart, Begining, Staying, Fading, } private int m_ShaderID_GlobalRoughnessOffset = Shader.PropertyToID("_GlobalRoughnessOffset"); private int m_ShaderID_PuddleWeight = Shader.PropertyToID("_PuddleWeight"); private int m_ShaderID_RippleSpeed = Shader.PropertyToID("_RippleSpeed"); private int m_ShaderID_RippleNormalStrength = Shader.PropertyToID("_RippleNormalStrength"); private int m_ShaderID_SnowWeight = Shader.PropertyToID("_SnowWeight"); private int m_ShaderID_SnowMaskMap = Shader.PropertyToID("_SnowMaskMap"); private int m_ShaderID_SnowColor = Shader.PropertyToID("_SnowColor"); private GlobalKeyword m_Key_IsRaining, m_Key_UseSnowOn; private EStage m_Stage; private TimeOfDay m_TimeOfDay; private TimeOfDay_WeatherInfo m_Info; private float m_Timer; private float m_tarRainVFXCount, m_tarRippleNormalStrength, m_tarWetRoughnessOffset, m_tarPuddleWeight; public EWeatherType CurrentWeather { get; private set; } public EWeatherType PreviousWeather { get; private set; } public TimeOfDay_Weather(TimeOfDay timeOfDay, TimeOfDay_WeatherInfo info) { m_TimeOfDay = timeOfDay; m_Info = info; m_Key_IsRaining = GlobalKeyword.Create("_IS_RAINING"); m_Key_UseSnowOn = GlobalKeyword.Create("_USE_SNOW_ON"); } bool IsRain(EWeatherType weatherType) { return weatherType == EWeatherType.LightRain || weatherType == EWeatherType.RainStorm; } public void Update() { if (IsRain(CurrentWeather) || (m_Stage == EStage.Fading && IsRain(PreviousWeather))) { UpdateRain(); } if (CurrentWeather == EWeatherType.Snow || (m_Stage == EStage.Fading && PreviousWeather == EWeatherType.Snow)) { UpdateSnow(); } } #region 下雨 void UpdateRain() { if (m_Stage == EStage.UnStart) { } else if (m_Stage == EStage.Begining) { UpdateRainBegining(); } else if (m_Stage == EStage.Staying) { } else if (m_Stage == EStage.Fading) { UpdateRainFading(); } else { Debug.LogError("Unknown stage: " + m_Stage); } m_Info.m_RainVFX.transform.position = GetRainVFXPosition(out var rot); m_Info.m_RainVFX.transform.rotation = rot; } Vector3 GetRainVFXPosition(out Quaternion rot) { var mainCam = Camera.main; if (mainCam == null) { rot = Quaternion.identity; return Vector3.zero; } rot = Quaternion.Euler(0, mainCam.transform.rotation.eulerAngles.y, 0); Vector3 forward = mainCam.transform.transform.forward; forward.y = 0; forward.Normalize(); Vector3 p = mainCam.transform.position + Vector3.up * 1 + forward * 10; return p; } void UpdateRainBegining() { m_Timer += Time.deltaTime; m_Info.m_RainProgress = Mathf.Clamp01(m_Timer / 4.2f); // rain vfx if (m_Timer <= 2.1f) { float rainVFXCtrlWeight = Mathf.Clamp01(m_Timer / 2f); var emission = m_Info.m_RainVFX.emission; emission.rateOverTimeMultiplier = rainVFXCtrlWeight * m_tarRainVFXCount; m_Info.m_RainVFX.Play(); Shader.SetGlobalFloat(m_ShaderID_RippleNormalStrength, m_tarRippleNormalStrength * rainVFXCtrlWeight); Shader.SetGlobalFloat(m_ShaderID_GlobalRoughnessOffset, rainVFXCtrlWeight * m_tarWetRoughnessOffset); } // puddle if (m_Timer >= 1f && m_Timer < 4.1f) { float puddleWeight = Mathf.Clamp01((m_Timer - 1) / 3f); Shader.SetGlobalFloat(m_ShaderID_PuddleWeight, puddleWeight * m_tarPuddleWeight); } if (m_Timer > 4.2f) { m_Stage = EStage.Staying; Shader.SetGlobalFloat(m_ShaderID_RippleNormalStrength, m_tarRippleNormalStrength); Shader.SetGlobalFloat(m_ShaderID_GlobalRoughnessOffset, m_tarWetRoughnessOffset); Shader.SetGlobalFloat(m_ShaderID_PuddleWeight, m_tarPuddleWeight); } } void UpdateRainFading() { m_Timer += Time.deltaTime; m_Info.m_RainProgress = 1 - Mathf.Clamp01(m_Timer / 2f); // rain vfx if (m_Timer <= 1.1f) { float rainVFXCtrlWeight = 1 - Mathf.Clamp01(m_Timer / 1f); var emission = m_Info.m_RainVFX.emission; emission.rateOverTimeMultiplier = rainVFXCtrlWeight * m_tarRainVFXCount; m_Info.m_RainVFX.Play(); Shader.SetGlobalFloat(m_ShaderID_RippleNormalStrength, m_tarRippleNormalStrength * rainVFXCtrlWeight); } else if (m_Info.m_RainVFX.gameObject.activeSelf) { m_Info.m_RainVFX.Stop(); m_Info.m_RainVFX.gameObject.SetActive(false); } // global wet if (m_Timer >= 1 && m_Timer <= 3.1f) { float globalWetWeight = 1 - Mathf.Clamp01((m_Timer - 1) / 2f); Shader.SetGlobalFloat(m_ShaderID_GlobalRoughnessOffset, globalWetWeight * m_tarWetRoughnessOffset); } // puddle if (m_Timer >= 3f && m_Timer <= 8.1f) { float puddleWeight = 1 - Mathf.Clamp01((m_Timer - 3) / 5f); Shader.SetGlobalFloat(m_ShaderID_PuddleWeight, puddleWeight * m_tarPuddleWeight); } if (m_Timer > 8.2f) { m_Stage = EStage.UnStart; m_Info.m_RainProgress = 0; m_Info.m_RainVFX.Stop(); Shader.SetKeyword(m_Key_IsRaining, false); Shader.SetGlobalFloat(m_ShaderID_RippleNormalStrength, 0); Shader.SetGlobalFloat(m_ShaderID_GlobalRoughnessOffset, 0); Shader.SetGlobalFloat(m_ShaderID_PuddleWeight, 0); } } #endregion #region 下雪 void UpdateSnow() { if (m_Stage == EStage.UnStart) { } else if (m_Stage == EStage.Begining) { UpdateSnowBegining(); } else if (m_Stage == EStage.Staying) { } else if (m_Stage == EStage.Fading) { UpdateSnowFading(); } else { Debug.LogError("Unknown stage: " + m_Stage); } } void UpdateSnowBegining() { m_Timer += Time.deltaTime; m_Info.m_SnowProgress = Mathf.Clamp01(m_Timer / 15.2f); if (m_Timer >= 5 && m_Timer < 15.1f) { float snowMaskWeight = Mathf.Clamp01((m_Timer - 5) / 10f); Shader.SetGlobalFloat(m_ShaderID_SnowWeight, snowMaskWeight); } if (m_Timer > 15.2f) { m_Stage = EStage.Staying; Shader.SetGlobalFloat(m_ShaderID_SnowWeight, 1); } } void UpdateSnowFading() { m_Timer += Time.deltaTime; m_Info.m_SnowProgress = 1 - Mathf.Clamp01(m_Timer / 10.2f); // snow vfx m_Info.m_SnowVFX.Stop(); if (m_Timer >= 5 && m_Timer < 10.1f) { float snowMaskWeight = 1 - Mathf.Clamp01((m_Timer - 2) / 5f); Shader.SetGlobalFloat(m_ShaderID_SnowWeight, snowMaskWeight); } if (m_Timer > 10.2f) { m_Stage = EStage.UnStart; m_Info.m_SnowProgress = 0; m_Info.m_SnowVFX.Stop(); m_Info.m_SnowVFX.gameObject.SetActive(false); Shader.SetKeyword(m_Key_UseSnowOn, false); Shader.SetGlobalFloat(m_ShaderID_SnowWeight, 0); } } #endregion public void BeginWeather(EWeatherType tarWeather) { if (CurrentWeather == tarWeather) { return; } m_Timer = 0; PreviousWeather = CurrentWeather; CurrentWeather = tarWeather; if (tarWeather == EWeatherType.LightRain || tarWeather == EWeatherType.RainStorm) { Clear(); m_Stage = EStage.Begining; bool isRainStorm = tarWeather == EWeatherType.RainStorm; m_tarRainVFXCount = isRainStorm ? 2000 : 500f; float tarRippleSpeed = isRainStorm ? 2 : 1; m_tarRippleNormalStrength = 1; m_tarWetRoughnessOffset = isRainStorm ? m_Info.m_RainGlobalRoughnessOffset : m_Info.m_RainGlobalRoughnessOffset * 0.5f; m_tarPuddleWeight = isRainStorm ? 1 : 0.5f; m_Info.m_RainVFX.gameObject.SetActive(false); m_Info.m_RainVFX.gameObject.SetActive(true); Shader.SetKeyword(m_Key_IsRaining, true); Shader.SetGlobalFloat(m_ShaderID_RippleSpeed, tarRippleSpeed); } else if (tarWeather == EWeatherType.Snow) { Clear(); m_Stage = EStage.Begining; m_Info.m_SnowVFX.gameObject.SetActive(false); m_Info.m_SnowVFX.gameObject.SetActive(true); var emission = m_Info.m_SnowVFX.emission; emission.rateOverTimeMultiplier = 500; m_Info.m_SnowVFX.Play(); Shader.SetKeyword(m_Key_UseSnowOn, true); Shader.SetGlobalTexture(m_ShaderID_SnowMaskMap, m_Info.m_SnowMaskMap); Shader.SetGlobalColor(m_ShaderID_SnowColor, m_Info.m_SnowColor); } else if (tarWeather == EWeatherType.CaiHong) { if (m_Info.m_CaiHongVFX) m_Info.m_CaiHongVFX.SetActive(true); } else if (tarWeather == EWeatherType.JiGuang) { if (m_Info.m_JiGuangVFX) m_Info.m_JiGuangVFX.SetActive(true); } else if (tarWeather == EWeatherType.LiHong) { if (m_Info.m_LiHongVFX) m_Info.m_LiHongVFX.SetActive(true); } else if (tarWeather == EWeatherType.LiuXinYu) { if (m_Info.m_LiuXinYuVFX) m_Info.m_LiuXinYuVFX.SetActive(true); } else if (tarWeather == EWeatherType.SunnyDay) { if (PreviousWeather == EWeatherType.Snow || IsRain(PreviousWeather)) { m_Stage = EStage.Fading; } else { if (m_Info.m_CaiHongVFX) m_Info.m_CaiHongVFX.SetActive(false); if (m_Info.m_JiGuangVFX) m_Info.m_JiGuangVFX.SetActive(false); if (m_Info.m_LiHongVFX) m_Info.m_LiHongVFX.SetActive(false); if (m_Info.m_LiuXinYuVFX) m_Info.m_LiuXinYuVFX.SetActive(false); } } } public void Clear() { m_Info.m_RainProgress = 0; m_Info.m_RainVFX.Stop(); Shader.SetKeyword(m_Key_IsRaining, false); Shader.SetGlobalFloat(m_ShaderID_RippleNormalStrength, 0); Shader.SetGlobalFloat(m_ShaderID_GlobalRoughnessOffset, 0); Shader.SetGlobalFloat(m_ShaderID_PuddleWeight, 0); m_Info.m_SnowProgress = 0; m_Info.m_SnowVFX.Stop(); Shader.SetKeyword(m_Key_UseSnowOn, false); Shader.SetGlobalFloat(m_ShaderID_SnowWeight, 0); } } }
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; namespace GameLogic.TimeOfDay { [Serializable] public class TimeOfDay_WeatherInfo { [Header("下雨"), Range(0, 1)] public float m_RainProgress; public ParticleSystem m_RainVFX; public float m_RainGlobalRoughnessOffset = -0.3f; public WeatherEnvData[] m_RainEnvData; [Header("下雪"), Range(0, 1)] public float m_SnowProgress; public ParticleSystem m_SnowVFX; public WeatherEnvData[] m_SnowEnvData; public Texture m_SnowMaskMap; public Color m_SnowColor; [Header("彩虹")] public GameObject m_CaiHongVFX; [Header("极光")] public GameObject m_JiGuangVFX; [Header("霓虹")] public GameObject m_LiHongVFX; [Header("流星雨")] public GameObject m_LiuXinYuVFX; } [Serializable] public class WeatherEnvData { public EDayPhase m_DayPhase; public TimeOfDay_EnvData m_EnvData; } public enum EWeatherType { SunnyDay = 1, LightRain, RainStorm, Snow, CaiHong, JiGuang, LiHong, LiuXinYu, } }
using System.Collections; using System.Collections.Generic; using GameLogic; using GameLogic.TimeOfDay; using UnityEditor; using UnityEngine; using UnityEngine.SceneManagement; namespace ArtTools { public class TimeOfDayGIGeneratorWnd : EditorWindow { private string m_GIName = "GI_New"; private TimeOfDay_GIInfo m_TimeOfDayGIInfo; [MenuItem("Omnee美术/昼夜变化/保存GI 配置")] private static void CreateWnd() { EditorWindow.CreateWindow<TimeOfDayGIGeneratorWnd>("保存昼夜变化GI配置"); } private void OnGUI() { m_TimeOfDayGIInfo = EditorGUILayout.ObjectField(m_TimeOfDayGIInfo, typeof(TimeOfDay_GIInfo), false) as TimeOfDay_GIInfo; if (m_TimeOfDayGIInfo == null) { m_GIName = EditorGUILayout.TextField("新GI配置名称", m_GIName); } GUILayout.Space(20); if (m_TimeOfDayGIInfo) { if (GUILayout.Button("替换GI数据")) { TimeOfDayGIGenerator.SaveGIConfig(m_TimeOfDayGIInfo); EditorUtility.DisplayDialog("", "完成操作", "OK"); } GUILayout.Space(20); if (GUILayout.Button("编辑器应用此GI")) { TimeOfDayGIGenerator.ApplyGIConfig(m_TimeOfDayGIInfo); EditorUtility.DisplayDialog("", "完成操作", "OK"); } } else { if (GUILayout.Button("生成新GI配置")) { m_TimeOfDayGIInfo = TimeOfDayGIGenerator.CreateNewGIConfig(m_GIName); EditorUtility.DisplayDialog("", "完成操作", "OK"); } } } } }
using System.Collections; using System.Collections.Generic; using GameLogic; using GameLogic.TimeOfDay; using UnityEditor; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; namespace ArtTools { public static class TimeOfDayGIGenerator { public static void ApplyGIConfig(TimeOfDay_GIInfo config) { TimeOfDay.ApplyGI(config); RenderSettings.skybox.SetFloat("_UseToggle", 0); RenderSettings.skybox.DisableKeyword("_USETOGGLE_ON"); RenderSettings.skybox.SetTexture("_Cubemap", config.m_EnvironmentSettings.m_CustomReflectionTexture); RenderSettings.defaultReflectionMode = DefaultReflectionMode.Skybox; RenderSettings.customReflectionTexture = null; } public static TimeOfDay_GIInfo CreateNewGIConfig(string fileName) { Scene curScene = SceneManager.GetActiveScene(); string sceneFolder = curScene.path.Replace(".unity", ""); string newFolderName = "TimeOfDay"; string newFolder = $"{sceneFolder}/{newFolderName}"; if (!AssetDatabase.IsValidFolder(newFolder)) { AssetDatabase.CreateFolder(sceneFolder, newFolderName); } string configPath = $"{newFolder}/{fileName}.asset"; TimeOfDay_GIInfo config = AssetDatabase.LoadAssetAtPath<TimeOfDay_GIInfo>(configPath); if (config == null) { config = ScriptableObject.CreateInstance<TimeOfDay_GIInfo>(); AssetDatabase.CreateAsset(config, configPath); } return SaveGIConfig(config); } public static TimeOfDay_GIInfo SaveGIConfig(TimeOfDay_GIInfo config) { Lightmapping.Bake(); SaveEnvironmentSettings(config.m_EnvironmentSettings); SaveReflectionProbeSettings(config); EditorUtility.SetDirty(config); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.Log("SaveGIConfig done!!!"); return config; } private static void SaveEnvironmentSettings(TimeOfDay_GIInfo.EnvironmentSettings es) { // 环境光设置 es.m_AmbientMode = RenderSettings.ambientMode; es.m_SkyColor = RenderSettings.ambientSkyColor; es.m_EquatorColor = RenderSettings.ambientEquatorColor; es.m_GroundColor = RenderSettings.ambientGroundColor; // 天空盒和环境光强度 es.m_SkyboxMaterial = RenderSettings.skybox; es.m_EnvironmentIntensity = RenderSettings.ambientIntensity; es.m_RealtimeColor = RenderSettings.subtractiveShadowColor; es.m_AmbientProbe = RenderSettings.ambientProbe; // reflection probe es.m_ReflectionBounces = RenderSettings.reflectionBounces; es.m_ReflectionIntensity = RenderSettings.reflectionIntensity; es.m_DefaultReflectionResolution = RenderSettings.defaultReflectionResolution; es.m_CustomReflectionTexture = RenderSettings.skybox.GetTexture("_Cubemap"); // 雾效设置 es.m_Fog = RenderSettings.fog; es.m_FogMode = RenderSettings.fogMode; es.m_FogColor = RenderSettings.fogColor; es.m_FogDensity = RenderSettings.fogDensity; es.m_FogStartDistance = RenderSettings.fogStartDistance; es.m_FogEndDistance = RenderSettings.fogEndDistance; // 其他设置 es.m_Halo = RenderSettings.haloStrength; es.m_FlareFadeSpeed = RenderSettings.flareFadeSpeed; es.m_FlareStrength = RenderSettings.flareStrength; } static string GetGameObjectPath(GameObject go) { string path = go.name; Transform p = go.transform.parent; while (p) { path = $"{p.name}/{path}"; p = p.parent; } return path; } private static void SaveReflectionProbeSettings(TimeOfDay_GIInfo es) { List<TimeOfDay_GIInfo.ReflectionProbeItem> list = new(); ReflectionProbe[] probes = GameObject.FindObjectsByType<ReflectionProbe>(FindObjectsSortMode.None); foreach (ReflectionProbe probe in probes) { TimeOfDay_GIInfo.ReflectionProbeItem item = new() { m_Path = GetGameObjectPath(probe.gameObject), m_Cubemap = probe.customBakedTexture, }; list.Add(item); } es.m_ReflectionProbes = list.ToArray(); } } }
fur
using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using UnityEditor; using UnityEngine; using UnityEngine.Rendering; using UObj = UnityEngine.Object; namespace ArtTools { public abstract class FurMeshGeneratorBase { public const int MAX_VERTICES_FOR_16BITS_MESH = 50000; //NOT change this protected UObj m_SelectedObj; protected int m_LayerCount = 20; protected bool m_IsOptimize; private Mesh m_FurMesh; public abstract string GetFurMeshSavePath(); public abstract string GenerateFurMesh(string furMeshSavePath); public static FurMeshGeneratorBase Create(UObj selectedObj, int layerCount, bool isOptimize) { if (selectedObj is SkinnedMeshRenderer) { return new FurSMRGenerator(selectedObj, layerCount, isOptimize); } else if (selectedObj is MeshRenderer) { return new FurMRGenerator(selectedObj, layerCount, isOptimize); } else { Debug.LogError($"Unknown renderer:{selectedObj.name}"); return null; } } public FurMeshGeneratorBase(UObj selectedObj, int layerCount, bool isOptimize) { m_SelectedObj = selectedObj; m_LayerCount = layerCount; m_IsOptimize = isOptimize; } protected void SaveMesh(Mesh newMesh, string newPath) { string saveFolder = Path.GetDirectoryName(newPath); if (!AssetDatabase.IsValidFolder(saveFolder)) { string parentFolder = Path.GetDirectoryName(saveFolder); string folderName = Path.GetFileNameWithoutExtension(saveFolder); AssetDatabase.CreateFolder(parentFolder, folderName); } AssetDatabase.CreateAsset(newMesh, newPath); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); m_FurMesh = AssetDatabase.LoadAssetAtPath<Mesh>(newPath); } public bool UndoGenerateFurMesh() { if (m_FurMesh) { PrefabUtility.RevertObjectOverride(m_SelectedObj, InteractionMode.UserAction); string assetPath = AssetDatabase.GetAssetPath(m_FurMesh); AssetDatabase.DeleteAsset(assetPath); return true; } return false; } protected string GetFurMeshSavePath(Mesh originMesh) { string path = AssetDatabase.GetAssetPath(originMesh); string newPath; if (path.EndsWith(".fbx", StringComparison.OrdinalIgnoreCase)) { string folderPath = Path.GetDirectoryName(path).Replace("\\", "/"); newPath = $"{folderPath}/{originMesh.name}_Fur.asset"; } else if (path.EndsWith(".asset")) { newPath = path.Replace(".asset", "_Fur.asset"); } else { throw new InvalidOperationException("Unknown mesh path:" + path); } return newPath; } } }
using System; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; using UnityEngine.Rendering; using UObj = UnityEngine.Object; namespace ArtTools { public class FurMeshGeneratorWnd : EditorWindow { private UObj m_SelectedObj; private int m_LayerCount = 20; private bool m_IsOptimize; private string m_NewMeshSavePath; private FurMeshGeneratorBase m_Generator; [MenuItem("Omnee美术/毛皮网格生成工具")] private static void CreateWnd() { EditorWindow.CreateWindow<FurMeshGeneratorWnd>("毛皮网格生成工具"); } private void OnGUI() { UObj oldSelectedObj = m_SelectedObj; m_SelectedObj = EditorGUILayout.ObjectField("模型:", m_SelectedObj, typeof(Renderer), true); bool selectedObjChange = m_SelectedObj != oldSelectedObj; if (m_SelectedObj != null && (selectedObjChange || m_Generator == null)) { m_Generator = FurMeshGeneratorBase.Create(m_SelectedObj, m_LayerCount, m_IsOptimize); m_NewMeshSavePath = m_Generator?.GetFurMeshSavePath(); } m_LayerCount = EditorGUILayout.IntField("层数:", m_LayerCount); m_IsOptimize = EditorGUILayout.Toggle("是否优化mesh", m_IsOptimize); GUILayout.BeginHorizontal(); m_NewMeshSavePath = EditorGUILayout.TextField("毛皮网格保存路径:", m_NewMeshSavePath); if (GUILayout.Button("...", GUILayout.Width(40))) { string fileName = Path.GetFileNameWithoutExtension(m_NewMeshSavePath); m_NewMeshSavePath = EditorUtility.SaveFilePanelInProject("保存", fileName, "asset", "设置毛皮网格保存路径"); } GUILayout.EndHorizontal(); if (GUILayout.Button("生成毛皮")) { string error = m_Generator.GenerateFurMesh(m_NewMeshSavePath); if (!string.IsNullOrEmpty(error)) { EditorUtility.DisplayDialog("错误", $"生成毛皮网格出错,信息:{error}", "确定"); } else { EditorUtility.DisplayDialog("成功", $"生成毛皮网格成功!", "确定"); } } if (GUILayout.Button("撤销生成毛皮")) { if (m_Generator.UndoGenerateFurMesh()) EditorUtility.DisplayDialog("成功", $"撤销生成毛皮网格成功!", "确定"); m_Generator = null; } } } }
using System; using System.Collections.Generic; using System.IO; using Unity.VisualScripting; using UnityEditor; using UnityEngine; using UnityEngine.Rendering; using UObj = UnityEngine.Object; namespace ArtTools { public class FurMRGenerator : FurMeshGeneratorBase { private MeshFilter m_MeshFilter; public FurMRGenerator(UObj selectedObj, int layerCount, bool isOptimize) : base(selectedObj, layerCount, isOptimize) { m_MeshFilter = m_SelectedObj.GetComponent<MeshFilter>(); } public override string GetFurMeshSavePath() { return GetFurMeshSavePath(m_MeshFilter.sharedMesh); } public override string GenerateFurMesh(string furMeshSavePath) { MeshFilter mf = m_MeshFilter; if (mf == null) { return "请选择MeshFilter"; } if (mf.sharedMesh.subMeshCount > 1) { return "目标不能有多个子网格"; } Mesh newMesh = GenerateFurMesh(mf); SaveMesh(newMesh, furMeshSavePath); mf.sharedMesh = newMesh; EditorUtility.SetDirty(mf); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.Log($"生成毛皮网格:{mf.name}", mf); return null; } private Mesh GenerateFurMesh(MeshFilter mf) { Vector3 oldPos = mf.transform.position; Quaternion oldRot = mf.transform.rotation; mf.transform.position = Vector3.zero; mf.transform.rotation = Quaternion.identity; CombineInstance[] combinesToMerge = new CombineInstance[m_LayerCount]; //Matrix4x4[] bindPosesToMerge = new Matrix4x4[smr.bones.Length * m_layerCount]; //bonesToMerge = new Transform[smr.bones.Length * m_layerCount]; Vector2[] newUVs = new Vector2[mf.sharedMesh.uv.Length * m_LayerCount]; Color[] newColors = new Color[mf.sharedMesh.vertexCount * m_LayerCount]; //BoneWeight[] boneWeights = new BoneWeight[smr.sharedMesh.boneWeights.Length * m_layerCount]; int indexUV = 0, indexColor = 0; for (int i = 0; i < m_LayerCount; i++) { combinesToMerge[i] = new() { mesh = mf.sharedMesh, subMeshIndex = 0, transform = mf.transform.localToWorldMatrix, }; for (int j = 0; j < mf.sharedMesh.uv.Length; j++) { newUVs[indexUV] = mf.sharedMesh.uv[j]; ++indexUV; } Color tarColor = new Color((float)i / m_LayerCount, 0, 0); for (int j = 0; j < mf.sharedMesh.vertexCount; j++) { newColors[indexColor] = tarColor; ++indexColor; } } // Create mesh int verticesCount = mf.sharedMesh.vertexCount * m_LayerCount; Mesh finalMesh = new(); finalMesh.indexFormat = verticesCount > MAX_VERTICES_FOR_16BITS_MESH ? IndexFormat.UInt32 : IndexFormat.UInt16; finalMesh.name = $"{mf.sharedMesh.name}_Fur"; finalMesh.CombineMeshes(combinesToMerge, true, true); finalMesh.RecalculateBounds(); finalMesh.uv = newUVs; finalMesh.colors = newColors; if (m_IsOptimize) finalMesh.Optimize(); mf.transform.position = oldPos; mf.transform.rotation = oldRot; return finalMesh; } } }
using System; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; using UnityEngine.Rendering; using UObj = UnityEngine.Object; namespace ArtTools { public class FurSMRGenerator : FurMeshGeneratorBase { private SkinnedMeshRenderer m_SkinnedMeshRenderer; public FurSMRGenerator(UObj selectedObj, int layerCount, bool isOptimize) : base(selectedObj, layerCount, isOptimize) { m_SkinnedMeshRenderer = (SkinnedMeshRenderer)m_SelectedObj; } public override string GenerateFurMesh(string furMeshSavePath) { SkinnedMeshRenderer smr = m_SkinnedMeshRenderer; if (smr == null) { return "请选择SkinnedMeshRenderer"; } if (smr.sharedMesh.subMeshCount > 1) { return "目标不能有多个子网格"; } Mesh newMesh = GenerateFurMesh(out var bonesToMerge); SaveMesh(newMesh, furMeshSavePath); smr.sharedMesh = newMesh; smr.bones = bonesToMerge.ToArray(); EditorUtility.SetDirty(smr); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.Log($"生成毛皮网格:{smr.name}", smr); return null; } private Mesh GenerateFurMesh(out List<Transform> bonesToMerge) { SkinnedMeshRenderer smr = m_SkinnedMeshRenderer; Vector3 oldPos = smr.transform.position; Quaternion oldRot = smr.transform.rotation; smr.transform.position = Vector3.zero; smr.transform.rotation = Quaternion.identity; CombineInstance[] combinesToMerge = new CombineInstance[m_LayerCount]; //Matrix4x4[] bindPosesToMerge = new Matrix4x4[smr.bones.Length * m_layerCount]; //bonesToMerge = new Transform[smr.bones.Length * m_layerCount]; Vector2[] newUVs = new Vector2[smr.sharedMesh.uv.Length * m_LayerCount]; Color[] newColors = new Color[smr.sharedMesh.vertexCount * m_LayerCount]; //BoneWeight[] boneWeights = new BoneWeight[smr.sharedMesh.boneWeights.Length * m_layerCount]; int indexUV = 0, indexColor = 0; bonesToMerge = new(); List<Matrix4x4> bindPosesToMerge = new(); List<BoneWeight> boneWeights = new(); for (int i = 0; i < m_LayerCount; i++) { combinesToMerge[i] = new() { mesh = smr.sharedMesh, subMeshIndex = 0, transform = smr.transform.localToWorldMatrix, }; for (int j = 0; j < smr.bones.Length; j++) { var curBone = smr.bones[j]; if (!bonesToMerge.Contains(curBone)) { bonesToMerge.Add(curBone); bindPosesToMerge.Add(smr.sharedMesh.bindposes[j] * smr.transform.worldToLocalMatrix); } } for (int j = 0; j < smr.sharedMesh.uv.Length; j++) { newUVs[indexUV] = smr.sharedMesh.uv[j]; ++indexUV; } Color tarColor = new Color((float)i / m_LayerCount, 0, 0); for (int j = 0; j < smr.sharedMesh.vertexCount; j++) { newColors[indexColor] = tarColor; ++indexColor; } for (int j = 0; j < smr.sharedMesh.boneWeights.Length; j++) { BoneWeight bw = smr.sharedMesh.boneWeights[j]; bw.boneIndex0 = bonesToMerge.FindIndex(a => a == smr.bones[bw.boneIndex0]); bw.boneIndex1 = bonesToMerge.FindIndex(a => a == smr.bones[bw.boneIndex1]); bw.boneIndex2 = bonesToMerge.FindIndex(a => a == smr.bones[bw.boneIndex2]); bw.boneIndex3 = bonesToMerge.FindIndex(a => a == smr.bones[bw.boneIndex3]); boneWeights.Add(bw); } } // Create mesh int verticesCount = smr.sharedMesh.vertexCount * m_LayerCount; Mesh finalMesh = new(); finalMesh.indexFormat = verticesCount > MAX_VERTICES_FOR_16BITS_MESH ? IndexFormat.UInt32 : IndexFormat.UInt16; finalMesh.name = $"{smr.sharedMesh.name}_Fur"; finalMesh.CombineMeshes(combinesToMerge, true, true); finalMesh.RecalculateBounds(); finalMesh.bindposes = bindPosesToMerge.ToArray(); finalMesh.boneWeights = boneWeights.ToArray(); finalMesh.uv = newUVs; finalMesh.colors = newColors; if (m_IsOptimize) finalMesh.Optimize(); smr.transform.position = oldPos; smr.transform.rotation = oldRot; return finalMesh; } public override string GetFurMeshSavePath() { return GetFurMeshSavePath(m_SkinnedMeshRenderer.sharedMesh); } } }

浙公网安备 33010602011771号