diff --git a/Batches/version.txt b/Batches/version.txt index 684a941c9..c03b60b0a 100644 --- a/Batches/version.txt +++ b/Batches/version.txt @@ -1 +1 @@ -v3.5.1 \ No newline at end of file +v3.6.0 \ No newline at end of file diff --git a/README.md b/README.md index fb2398be5..d672c7dc0 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ Logo: by [@otama_jacksy](https://twitter.com/otama_jacksy) -v3.5.1 +v3.6.0 * 作成: 獏星(ばくすたー) -* 2024/01/30 +* 2024/02/25 WindowsでVRMを表示し、追加のデバイスなしで動かせるアプリケーションです。 diff --git a/README_en.md b/README_en.md index 5cfdcd9db..fd73a4e14 100644 --- a/README_en.md +++ b/README_en.md @@ -5,10 +5,10 @@ Logo: by [@otama_jacksy](https://twitter.com/otama_jacksy) -v3.5.1 +v3.6.0 * Author: Baxter -* 2024/Jan/30 +* 2024/Feb/25 The VRM avatar application without any special device. diff --git a/VMagicMirror/Assets/Baku/VMagicMirror/Scenes/MainViewer_Profiles/PostProcessing Profile.asset b/VMagicMirror/Assets/Baku/VMagicMirror/Scenes/MainViewer_Profiles/PostProcessing Profile.asset index 59ffa7b48..1c6b6b76f 100644 --- a/VMagicMirror/Assets/Baku/VMagicMirror/Scenes/MainViewer_Profiles/PostProcessing Profile.asset +++ b/VMagicMirror/Assets/Baku/VMagicMirror/Scenes/MainViewer_Profiles/PostProcessing Profile.asset @@ -25,6 +25,37 @@ MonoBehaviour: scanline: overrideState: 1 value: 0.117 +--- !u!114 &-4968429724501136736 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1c7e0b21f9fde1c429ce58be087a0258, type: 3} + m_Name: VmmAlphaEdge + m_EditorClassIdentifier: + active: 0 + enabled: + overrideState: 1 + value: 1 + thickness: + overrideState: 1 + value: 15 + threshold: + overrideState: 1 + value: 0.5 + edgeColor: + overrideState: 1 + value: {r: 1, g: 1, b: 1, a: 1} + outlineOverwriteAlpha: + overrideState: 1 + value: 0.8 + highQualityMode: + overrideState: 1 + value: 0 --- !u!114 &-4431195256121071682 MonoBehaviour: m_ObjectHideFlags: 3 @@ -112,6 +143,7 @@ MonoBehaviour: - {fileID: 114612591270388130} - {fileID: -679367385162691271} - {fileID: 1713800730463470002} + - {fileID: -4968429724501136736} --- !u!114 &114612591270388130 MonoBehaviour: m_ObjectHideFlags: 3 @@ -124,7 +156,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 48a79b01ea5641d4aa6daa2e23605641, type: 3} m_Name: Bloom m_EditorClassIdentifier: - active: 1 + active: 0 enabled: overrideState: 1 value: 1 diff --git a/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/AvatarControl/Motion/FK/BoneFrameReducer.cs b/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/AvatarControl/Motion/FK/BoneFrameReducer.cs index 6bae5dd14..4160b9704 100644 --- a/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/AvatarControl/Motion/FK/BoneFrameReducer.cs +++ b/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/AvatarControl/Motion/FK/BoneFrameReducer.cs @@ -1,6 +1,8 @@ -using System.Collections; using System.Collections.Generic; +using UniRx; using UnityEngine; +using UniVRM10; +using VRM; using Zenject; namespace Baku.VMagicMirror.FK @@ -20,11 +22,9 @@ public class BoneFrameReducer : MonoBehaviour [SerializeField] private float updateInterval = 0.05f; //NOTE: 揺れものはフルFPSでええねん、という事です - private readonly Dictionary _bones = new Dictionary(); - private readonly Dictionary _reducedRotations = - new Dictionary(); - private readonly Dictionary _tempRotations = - new Dictionary(); + private readonly Dictionary _bones = new(); + private readonly Dictionary _reducedRotations = new(); + private readonly Dictionary _tempRotations = new(); private Vector3 _reducedHipsPosition; private Vector3 _tempHipsPosition; @@ -34,15 +34,22 @@ public class BoneFrameReducer : MonoBehaviour private bool _frameReduceEnabled = false; + private VRM10InstanceUpdater _instanceUpdater; + private Vrm10Instance _vrm10Instance; + [Inject] - public void Initialize(IMessageReceiver receiver, IVRMLoadable vrmLoadable) + public void Initialize( + IMessageReceiver receiver, + IVRMLoadable vrmLoadable, + VRM10InstanceUpdater instanceUpdater + ) { vrmLoadable.VrmLoaded += OnModelLoaded; vrmLoadable.VrmDisposing += OnModelUnloaded; receiver.AssignCommandHandler( VmmCommands.UseFrameReductionEffect, - c => _frameReduceEnabled = c.ToBoolean() - ); + c => _frameReduceEnabled = c.ToBoolean()); + _instanceUpdater = instanceUpdater; } private void OnModelLoaded(VrmLoadedInfo info) @@ -50,6 +57,7 @@ private void OnModelLoaded(VrmLoadedInfo info) _bones.Clear(); _reducedRotations.Clear(); _tempRotations.Clear(); + _vrm10Instance = info.instance; for (var i = (int) HumanBodyBones.Hips; i < (int) HumanBodyBones.LastBone; i++) { @@ -69,15 +77,28 @@ private void OnModelLoaded(VrmLoadedInfo info) private void OnModelUnloaded() { _bones.Clear(); + _vrm10Instance = null; _hasModel = false; } private void Start() { - StartCoroutine(RestoreFullFpsPose()); + _instanceUpdater.PreRuntimeProcess + .Subscribe(_ => PreRuntimeProcess()) + .AddTo(this); + _instanceUpdater.PostRuntimeProcess + .Subscribe(_ => PostRuntimeProcess()) + .AddTo(this); + // StartCoroutine(RestoreFullFpsPose()); } - private void LateUpdate() + //やってること + // 1. 仮想骨が読み取られる前に「reduceした状態で適用するボーン値」を取って + // - キャッシュ値で上書きする + // - たまにキャッシュ値を更新する + // 2. 仮想骨が転写されたら、上書きしてしまった値は戻してく + // - ※揺れものの計算に対して無害にするためだが、VRM1.0とこの処理は相性がすごく悪いかも… + private void PreRuntimeProcess() { if (!_hasModel || !_frameReduceEnabled) { @@ -85,9 +106,6 @@ private void LateUpdate() return; } - //2つのことをやる - // 1. 一定間隔で「reduceした状態で適用するボーン値」を取る - // 2. ボーン情報をreduce値に全部書き換えて描画させてから描画後に元に戻す -> 揺れものの計算をいい感じにするため _updateCount += Time.deltaTime; if (_updateCount >= updateInterval) { @@ -114,23 +132,21 @@ private void LateUpdate() } } - private IEnumerator RestoreFullFpsPose() + private void PostRuntimeProcess() { - var eof = new WaitForEndOfFrame(); - while (true) + if (!_hasModel || !_frameReduceEnabled) { - yield return eof; - if (!_hasModel || !_frameReduceEnabled) - { - continue; - } + return; + } - _bones[HumanBodyBones.Hips].position = _tempHipsPosition; - foreach (var rotPair in _tempRotations) - { - _bones[rotPair.Key].localRotation = rotPair.Value; - } + _bones[HumanBodyBones.Hips].position = _tempHipsPosition; + foreach (var rotPair in _tempRotations) + { + _bones[rotPair.Key].localRotation = rotPair.Value; } + + //TODO: 揺れものの暴れ対策で必要なのはそうだが、メチャクチャ重たいはずなのでもっと軽い方法で代替したい… + _vrm10Instance.Runtime.ReconstructSpringBone(); } } } diff --git a/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Editor/BuildHelper.cs b/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Editor/BuildHelper.cs index e3bfc1b1e..7e0c7e719 100644 --- a/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Editor/BuildHelper.cs +++ b/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Editor/BuildHelper.cs @@ -27,8 +27,8 @@ public static void ResetScriptSymbols() [MenuItem("VMagicMirror/Symbols: Dev Standard", false, 11)] public static void PrepareDevStandardSymbols() => PrepareScriptDefineSymbol(false, false); - [MenuItem("VMagicMirror/Symbols: Prod Full", false, 12)] - public static void PrepareDeFullSymbols() + [MenuItem("VMagicMirror/Symbols: Dev Full", false, 12)] + public static void PrepareDevFullSymbols() => PrepareScriptDefineSymbol(true, false); [MenuItem("VMagicMirror/Symbols: Prod Standard", false, 21)] diff --git a/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Environment/CameraAndLights/LightingController.cs b/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Environment/CameraAndLights/LightingController.cs index a5210cdd7..186f5b8af 100644 --- a/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Environment/CameraAndLights/LightingController.cs +++ b/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Environment/CameraAndLights/LightingController.cs @@ -22,6 +22,7 @@ public class LightingController : MonoBehaviour private Color _color = Color.white; private Bloom _bloom; + private VmmAlphaEdge _vmmAlphaEdge; private VmmVhs _vmmVhs; private VmmMonochrome _vmmMonochrome; private bool _handTrackingEnabled = false; @@ -29,6 +30,9 @@ public class LightingController : MonoBehaviour //制限版でGUI側にtrue相当の値が表示されるが、これはGUI側が別途決め打ちしてくれてる private bool _showEffectDuringTracking = false; + private bool _windowFrameVisible = true; + private bool _enableOutlineEffect = false; + [Inject] public void Initialize(IMessageReceiver receiver) { @@ -89,7 +93,38 @@ public void Initialize(IMessageReceiver receiver) float[] bloomRgb = message.ToColorFloats(); SetBloomColor(bloomRgb[0], bloomRgb[1], bloomRgb[2]); }); - + + receiver.AssignCommandHandler( + VmmCommands.WindowFrameVisibility, + message => + { + _windowFrameVisible = message.ToBoolean(); + SetOutlineEffectActive(_windowFrameVisible, _enableOutlineEffect); + }); + receiver.AssignCommandHandler( + VmmCommands.OutlineEffectEnable, + message => + { + _enableOutlineEffect = message.ToBoolean(); + SetOutlineEffectActive(_windowFrameVisible, _enableOutlineEffect); + }); + receiver.AssignCommandHandler( + VmmCommands.OutlineEffectThickness, + message => SetOutlineEffectThickness(message.ToInt()) + ); + receiver.AssignCommandHandler( + VmmCommands.OutlineEffectColor, + message => + { + var rgb = message.ToColorFloats(); + var color = new Color(rgb[0], rgb[1], rgb[2]); + SetOutlineEffectEdgeColor(color); + }); + receiver.AssignCommandHandler( + VmmCommands.OutlineEffectHighQualityMode, + message => SetOutlineEffectHighQualityMode(message.ToBoolean()) + ); + receiver.AssignCommandHandler( VmmCommands.EnableImageBasedHandTracking, message => @@ -110,6 +145,7 @@ public void Initialize(IMessageReceiver receiver) private void Start() { _bloom = postProcess.profile.GetSetting(); + _vmmAlphaEdge = postProcess.profile.GetSetting(); _vmmMonochrome = postProcess.profile.GetSetting(); _vmmVhs = postProcess.profile.GetSetting(); } @@ -209,6 +245,19 @@ private void SetBloomIntensity(float intensity) private void SetBloomThreshold(float threshold) => _bloom.threshold.value = threshold; + private void SetOutlineEffectActive(bool windowFrameVisible, bool enableOutlineEffect) + => _vmmAlphaEdge.active = !windowFrameVisible && enableOutlineEffect; + + //NOTE: GUIからは整数指定するが設定上はfloat + private void SetOutlineEffectThickness(int thickness) + => _vmmAlphaEdge.thickness.Override(thickness); + + private void SetOutlineEffectEdgeColor(Color color) + => _vmmAlphaEdge.edgeColor.Override(color); + + private void SetOutlineEffectHighQualityMode(bool useHighQualityMode) + => _vmmAlphaEdge.highQualityMode.Override(useHighQualityMode); + private void UpdateRetroEffectStatus() { bool enableEffect =_handTrackingEnabled && diff --git a/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Environment/PostProcessing/VmmAlphaEdge.cs b/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Environment/PostProcessing/VmmAlphaEdge.cs new file mode 100644 index 000000000..051c5bbb5 --- /dev/null +++ b/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Environment/PostProcessing/VmmAlphaEdge.cs @@ -0,0 +1,39 @@ +using System; +using UnityEngine; +using UnityEngine.Rendering.PostProcessing; + +namespace Baku.VMagicMirror +{ + + [Serializable] + [PostProcess(typeof(VmmAlphaEdgeRenderer), PostProcessEvent.AfterStack, "Vmm/AlphaEdge", allowInSceneView: false)] + public sealed class VmmAlphaEdge : PostProcessEffectSettings + { + public FloatParameter thickness = new() { value = 20f }; + public FloatParameter threshold = new() { value = 1f }; + public ColorParameter edgeColor = new() { value = Color.white }; + public FloatParameter outlineOverwriteAlpha = new() { value = 0.02f }; + public BoolParameter highQualityMode = new() { value = false }; + } + + public sealed class VmmAlphaEdgeRenderer : PostProcessEffectRenderer + { + private static readonly int Thickness = Shader.PropertyToID("_Thickness"); + private static readonly int Threshold = Shader.PropertyToID("_Threshold"); + private static readonly int EdgeColor = Shader.PropertyToID("_EdgeColor"); + private static readonly int OutlineOverwriteAlpha = Shader.PropertyToID("_OutlineOverwriteAlpha"); + private static readonly int HighQualityMode = Shader.PropertyToID("_HighQualityMode"); + + public override void Render(PostProcessRenderContext context) + { + var sheet = context.propertySheets.Get(Shader.Find("Hidden/Vmm/AlphaEdge")); + sheet.properties.SetFloat(Thickness, settings.thickness); + sheet.properties.SetFloat(Threshold, settings.threshold); + sheet.properties.SetColor(EdgeColor, settings.edgeColor); + sheet.properties.SetFloat(OutlineOverwriteAlpha, settings.outlineOverwriteAlpha); + sheet.properties.SetFloat(HighQualityMode, settings.highQualityMode ? 1f : 0f); + + context.command.BlitFullscreenTriangle(context.source, context.destination, sheet, 0); + } + } +} diff --git a/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Environment/PostProcessing/VmmAlphaEdge.cs.meta b/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Environment/PostProcessing/VmmAlphaEdge.cs.meta new file mode 100644 index 000000000..de4d7a21a --- /dev/null +++ b/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Environment/PostProcessing/VmmAlphaEdge.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1c7e0b21f9fde1c429ce58be087a0258 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Interprocess/Model/VmmCommands.cs b/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Interprocess/Model/VmmCommands.cs index 21df37e0a..64dd2ce85 100644 --- a/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Interprocess/Model/VmmCommands.cs +++ b/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/Interprocess/Model/VmmCommands.cs @@ -173,6 +173,11 @@ public static class VmmCommands public const string BloomThreshold = nameof(BloomThreshold); public const string BloomColor = nameof(BloomColor); + public const string OutlineEffectEnable = nameof(OutlineEffectEnable); + public const string OutlineEffectThickness = nameof(OutlineEffectThickness); + public const string OutlineEffectColor = nameof(OutlineEffectColor); + public const string OutlineEffectHighQualityMode = nameof(OutlineEffectHighQualityMode); + public const string ShowEffectDuringHandTracking = nameof(ShowEffectDuringHandTracking); public const string WindEnable = nameof(WindEnable); diff --git a/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/ModelLoad/VRMLoad/VRM10InstanceUpdater.cs b/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/ModelLoad/VRMLoad/VRM10InstanceUpdater.cs index e8075a359..08fe0d6bd 100644 --- a/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/ModelLoad/VRMLoad/VRM10InstanceUpdater.cs +++ b/VMagicMirror/Assets/Baku/VMagicMirror/Scripts/ModelLoad/VRMLoad/VRM10InstanceUpdater.cs @@ -1,4 +1,5 @@ using System; +using UniRx; using UnityEngine; using UniVRM10; using Zenject; @@ -13,6 +14,11 @@ public class VRM10InstanceUpdater : MonoBehaviour { private bool _hasModel; private Vrm10Instance _instance; + + private readonly Subject _preRuntimeProcessed = new(); + private readonly Subject _postRuntimeProcessed = new(); + public IObservable PostRuntimeProcess => _postRuntimeProcessed; + public IObservable PreRuntimeProcess => _preRuntimeProcessed; [Inject] public void Initialize(IVRMLoadable vrmLoadable) @@ -34,7 +40,9 @@ private void LateUpdate() { if (_hasModel) { + _preRuntimeProcessed.OnNext(Unit.Default); _instance.Runtime.Process(); + _postRuntimeProcessed.OnNext(Unit.Default); } } } diff --git a/VMagicMirror/Assets/Baku/VMagicMirror/Shaders/PostProcessing/VmmAlphaEdge.shader b/VMagicMirror/Assets/Baku/VMagicMirror/Shaders/PostProcessing/VmmAlphaEdge.shader new file mode 100644 index 000000000..6d0a5f44c --- /dev/null +++ b/VMagicMirror/Assets/Baku/VMagicMirror/Shaders/PostProcessing/VmmAlphaEdge.shader @@ -0,0 +1,165 @@ +Shader "Hidden/Vmm/AlphaEdge" +{ + HLSLINCLUDE + + #include "Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl" + + TEXTURE2D_SAMPLER2D(_MainTex, sampler_MainTex); + TEXTURE2D_SAMPLER2D(_CameraDepthTexture, sampler_CameraDepthTexture); + float4 _MainTex_TexelSize; + + float4 _EdgeColor; + float _Thickness; + float _Threshold; + float _OutlineOverwriteAlpha; + float _HighQualityMode; + + float SampleAlpha(float2 uv) + { + return SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv).a; + } + + float GetAlphaBasedD(VaryingsDefault i) + { + float4 offsets = (_Thickness / 1.41421) * _MainTex_TexelSize.xyxy * float4(-0.5, -0.5, 0.5, 0.5); + const float lu = SampleAlpha(i.texcoord + offsets.xw); + const float rd = SampleAlpha(i.texcoord + offsets.zy); + const float ru = SampleAlpha(i.texcoord + offsets.zw); + const float ld = SampleAlpha(i.texcoord + offsets.xy); + + float d; + + // optionalに8方位に増やす: ちょっと見栄えがよくなる + float3 offsets2 = _Thickness * _MainTex_TexelSize.xyx * float3(0.5, 0.5, 0.0); + if (_HighQualityMode > 0.5) + { + const float up = SampleAlpha(i.texcoord + offsets2.zy); + const float down = SampleAlpha(i.texcoord - offsets2.zy); + const float left = SampleAlpha(i.texcoord + offsets2.xz); + const float right = SampleAlpha(i.texcoord - offsets2.xz); + d = max(max(max( + abs(lu - rd), + abs(ld - ru)), + abs(left - right)), + abs(up - down) + ); + // d = length(float4( + // lu - rd, + // ld - ru, + // left - right, + // up - down + // )); + } + else + { + d = max(abs(lu - rd), abs(ld - ru)); + // d = length(float2(lu - rd, ld - ru)); + } + + return d; + + // (ボツ) さらに頑張るパターン : 重たいし、この方向で高解像度にしてもVRM自体のoutlineがギザギザだと見栄えが改善しない + // float count = 8.0 - 0.001; + // float i2rad = 3.14159265 / count; + // float d = 0.0; + // for (float k = 0; k < count; k += 1.0) + // { + // float angle = k * i2rad; + // float2 diff = (_Thickness * 0.5) * _MainTex_TexelSize.xy * float2(cos(angle), sin(angle)); + // float a1 = SampleAlpha(i.texcoord + diff); + // float a2 = SampleAlpha(_MainTex, sampler_MainTex, i.texcoord - diff).a; + // d = max(d, abs(a1 - a2)); + // } + } + + // NOTE: Depthでエッジ検出する方法も試したが、とくにShadowBoardの実装と相性が悪いのを重く見て不採用にしている + float SampleDepth01(float2 uv) + { + return Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_CameraDepthTexture, uv)); + } + + float GetDepthBasedD(VaryingsDefault i) + { + //NOTE: 最終的にDepthTextureのtexelSizeにしたほうがいいかも + float4 offsets = (_Thickness / 1.41421) * _MainTex_TexelSize.xyxy * float4(-0.5, -0.5, 0.5, 0.5); + const float lu = SampleDepth01(i.texcoord + offsets.xw); + const float rd = SampleDepth01(i.texcoord + offsets.zy); + const float ru = SampleDepth01(i.texcoord + offsets.zw); + const float ld = SampleDepth01(i.texcoord + offsets.xy); + + float d; + + // optionalに8方位に増やす: ちょっと見栄えがよくなる + float3 offsets2 = _Thickness * _MainTex_TexelSize.xyx * float3(0.5, 0.5, 0.0); + if (_HighQualityMode > 0.5) + { + const float up = SampleDepth01(i.texcoord + offsets2.zy); + const float down = SampleDepth01(i.texcoord - offsets2.zy); + const float left = SampleDepth01(i.texcoord + offsets2.xz); + const float right = SampleDepth01(i.texcoord - offsets2.xz); + d = length(float4( + lu - rd, + ld - ru, + left - right, + up - down + )); + } + else + { + d = length(float2(lu - rd, ld - ru)); + } + return d; + } + + float4 Frag(VaryingsDefault i) : SV_Target + { + float4 original = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord); + float4 result = original; + + //test: write depth + // result.rgb = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_CameraDepthTexture, i.texcoord)); + // return result; + + float d = GetAlphaBasedD(i); + //float d = GetDepthBasedD(i); + + // NOTE: もともと半透明/不透明な場所はアウトライン上書きをしない…というのが第2項 + const float outline = step(_Threshold, d) * step(original.a, _OutlineOverwriteAlpha); + + result.a = lerp(original.a, 1.0, outline); + result.rgb = lerp(original.rgb, _EdgeColor.rgb, outline); + float2 xyOffsets = (_Thickness * 0.5) * _MainTex_TexelSize.xy; + + // NOTE: 画面端が不透明なとき、その領域にはOutlineの色が適用される + // これにより、バストアップ構図で背景透過していると下端にoutlineが効くようになる + const float edge_factor = + step(1.0 - xyOffsets.x, i.texcoord.x) + + step(i.texcoord.x, xyOffsets.x) + + step(1.0 - xyOffsets.y, i.texcoord.y) + + step(i.texcoord.y, xyOffsets.y); + + const float apply_edge_outline = + step(1.0, edge_factor) * step(_OutlineOverwriteAlpha, original.a); + result.a = lerp(result.a, _EdgeColor.a, apply_edge_outline); + result.rgb = lerp(result.rgb, _EdgeColor.rgb, apply_edge_outline); + + return result; + } + + ENDHLSL + + SubShader + { + Cull Off ZWrite Off ZTest Always + + Pass + { + HLSLPROGRAM + + #pragma vertex VertDefault + #pragma fragment Frag + + ENDHLSL + } + } +} \ No newline at end of file diff --git a/VMagicMirror/Assets/Baku/VMagicMirror/Shaders/PostProcessing/VmmAlphaEdge.shader.meta b/VMagicMirror/Assets/Baku/VMagicMirror/Shaders/PostProcessing/VmmAlphaEdge.shader.meta new file mode 100644 index 000000000..de820fce4 --- /dev/null +++ b/VMagicMirror/Assets/Baku/VMagicMirror/Shaders/PostProcessing/VmmAlphaEdge.shader.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 46ea3e410cd895144a0928a45dbe589d +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VMagicMirror/ProjectSettings/GraphicsSettings.asset b/VMagicMirror/ProjectSettings/GraphicsSettings.asset index 93be028ef..bae0bf57d 100644 --- a/VMagicMirror/ProjectSettings/GraphicsSettings.asset +++ b/VMagicMirror/ProjectSettings/GraphicsSettings.asset @@ -3,7 +3,7 @@ --- !u!30 &1 GraphicsSettings: m_ObjectHideFlags: 0 - serializedVersion: 13 + serializedVersion: 15 m_Deferred: m_Mode: 1 m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} @@ -13,9 +13,6 @@ GraphicsSettings: m_ScreenSpaceShadows: m_Mode: 1 m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} - m_LegacyDeferred: - m_Mode: 1 - m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} m_DepthNormals: m_Mode: 1 m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} @@ -44,7 +41,9 @@ GraphicsSettings: - {fileID: 16001, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 4800000, guid: b296327b68990cc40bd6a2067223ab41, type: 3} - {fileID: 4800000, guid: 20913afc4b215a04b9fbe903fbb2d0fa, type: 3} + - {fileID: 4800000, guid: 46ea3e410cd895144a0928a45dbe589d, type: 3} m_PreloadedShaders: [] + m_PreloadShadersBatchTimeLimit: -1 m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} m_CustomRenderPipeline: {fileID: 0} @@ -56,6 +55,7 @@ GraphicsSettings: m_LightmapStripping: 0 m_FogStripping: 0 m_InstancingStripping: 0 + m_BrgStripping: 0 m_LightmapKeepPlain: 1 m_LightmapKeepDirCombined: 1 m_LightmapKeepDynamicPlain: 1 @@ -70,3 +70,7 @@ GraphicsSettings: m_LightsUseColorTemperature: 0 m_DefaultRenderingLayerMask: 1 m_LogWhenShaderIsCompiled: 0 + m_SRPDefaultSettings: {} + m_LightProbeOutsideHullStrategy: 0 + m_CameraRelativeLightCulling: 0 + m_CameraRelativeShadowCulling: 0 diff --git a/WPF/VMagicMirrorConfig/Common/AppConsts.cs b/WPF/VMagicMirrorConfig/Common/AppConsts.cs index 9f7cc51f9..2573cad8a 100644 --- a/WPF/VMagicMirrorConfig/Common/AppConsts.cs +++ b/WPF/VMagicMirrorConfig/Common/AppConsts.cs @@ -11,6 +11,6 @@ public static class AppConsts public static string AppFullNameWithEnvSuffix => AppFullName + (TargetEnvironmentChecker.CheckDevEnvFlagEnabled() ? "(Dev)" : ""); - public static VmmAppVersion AppVersion => new VmmAppVersion(3, 5, 1); + public static VmmAppVersion AppVersion => new VmmAppVersion(3, 6, 0); } } diff --git a/WPF/VMagicMirrorConfig/Model/Entity/LightSetting.cs b/WPF/VMagicMirrorConfig/Model/Entity/LightSetting.cs index 9eb5a8bc4..678117b39 100644 --- a/WPF/VMagicMirrorConfig/Model/Entity/LightSetting.cs +++ b/WPF/VMagicMirrorConfig/Model/Entity/LightSetting.cs @@ -53,6 +53,17 @@ public class LightSetting : SettingEntityBase #endregion + #region Outline Effect + + public bool EnableOutlineEffect { get; set; } = false; + public int OutlineEffectThickness { get; set; } = 20; + public int OutlineEffectR { get; set; } = 255; + public int OutlineEffectG { get; set; } = 255; + public int OutlineEffectB { get; set; } = 255; + public bool OutlineEffectHighQualityMode { get; set; } = false; + + #endregion + #region Wind public bool EnableWind { get; set; } = true; diff --git a/WPF/VMagicMirrorConfig/Model/InterProcess/Core/MessageFactory.cs b/WPF/VMagicMirrorConfig/Model/InterProcess/Core/MessageFactory.cs index 9c97ca64d..06be9d16f 100644 --- a/WPF/VMagicMirrorConfig/Model/InterProcess/Core/MessageFactory.cs +++ b/WPF/VMagicMirrorConfig/Model/InterProcess/Core/MessageFactory.cs @@ -284,6 +284,11 @@ private static Message WithArg(int content, [CallerMemberName] string command = public Message BloomIntensity(int intensityPercent) => WithArg(intensityPercent); public Message BloomThreshold(int thresholdPercent) => WithArg(thresholdPercent); + public Message OutlineEffectEnable(bool active) => WithArg(active); + public Message OutlineEffectThickness(int thickness) => WithArg(thickness); + public Message OutlineEffectColor(int r, int g, int b) => WithArg($"{r},{g},{b}"); + public Message OutlineEffectHighQualityMode(bool enable) => WithArg(enable); + public Message WindEnable(bool enableWind) => WithArg(enableWind); public Message WindStrength(int strength) => WithArg(strength); public Message WindInterval(int percentage) => WithArg(percentage); diff --git a/WPF/VMagicMirrorConfig/Model/SettingModel/LightSettingModel.cs b/WPF/VMagicMirrorConfig/Model/SettingModel/LightSettingModel.cs index 4fa250e1f..b4ad67f42 100644 --- a/WPF/VMagicMirrorConfig/Model/SettingModel/LightSettingModel.cs +++ b/WPF/VMagicMirrorConfig/Model/SettingModel/LightSettingModel.cs @@ -1,5 +1,4 @@ using System; -using System.Threading.Tasks; namespace Baku.VMagicMirrorConfig { @@ -48,6 +47,18 @@ public LightSettingModel(IMessageSender sender) : base(sender) BloomG = new RProperty(s.BloomG, _ => sendBloomColor()); BloomB = new RProperty(s.BloomB, _ => sendBloomColor()); + EnableOutlineEffect = new RProperty(s.EnableOutlineEffect, v => SendMessage(factory.OutlineEffectEnable(v))); + OutlineEffectThickness = new RProperty(s.OutlineEffectThickness, v => SendMessage(factory.OutlineEffectThickness(v))); + Action sendOutlineEffectColor = () => + SendMessage(factory.OutlineEffectColor(OutlineEffectR?.Value ?? 255, OutlineEffectG?.Value ?? 255, OutlineEffectB?.Value ?? 255)); + OutlineEffectR = new RProperty(s.OutlineEffectR, _ => sendOutlineEffectColor()); + OutlineEffectG = new RProperty(s.OutlineEffectG, _ => sendOutlineEffectColor()); + OutlineEffectB = new RProperty(s.OutlineEffectB, _ => sendOutlineEffectColor()); + OutlineEffectHighQualityMode = new RProperty( + s.OutlineEffectHighQualityMode, + v => SendMessage(factory.OutlineEffectHighQualityMode(v)) + ); + EnableWind = new RProperty(s.EnableWind, b => SendMessage(factory.WindEnable(b))); WindStrength = new RProperty(s.WindStrength, i => SendMessage(factory.WindStrength(i))); WindInterval = new RProperty(s.WindInterval, i => SendMessage(factory.WindInterval(i))); @@ -97,6 +108,18 @@ public LightSettingModel(IMessageSender sender) : base(sender) #endregion + #region OutlineEffect + + public RProperty EnableOutlineEffect { get; } + public RProperty OutlineEffectThickness { get; } + public RProperty OutlineEffectHighQualityMode { get; } + + public RProperty OutlineEffectR { get; } + public RProperty OutlineEffectG { get; } + public RProperty OutlineEffectB { get; } + + #endregion + #region Wind public RProperty EnableWind { get; } @@ -149,6 +172,17 @@ public void ResetBloomSetting() BloomThreshold.Value = setting.BloomThreshold; } + public void ResetOutlineEffectSetting() + { + var setting = LightSetting.Default; + EnableOutlineEffect.Value = setting.EnableOutlineEffect; + OutlineEffectThickness.Value = setting.OutlineEffectThickness; + OutlineEffectR.Value = setting.OutlineEffectR; + OutlineEffectG.Value = setting.OutlineEffectG; + OutlineEffectB.Value = setting.OutlineEffectB; + OutlineEffectHighQualityMode.Value = setting.OutlineEffectHighQualityMode; + } + public void ResetWindSetting() { var setting = LightSetting.Default; @@ -163,6 +197,7 @@ public override void ResetToDefault() ResetLightSetting(); ResetShadowSetting(); ResetBloomSetting(); + ResetOutlineEffectSetting(); ResetWindSetting(); ResetImageQuality(); } diff --git a/WPF/VMagicMirrorConfig/Resources/English.xaml b/WPF/VMagicMirrorConfig/Resources/English.xaml index 347fb112b..fc1690a45 100644 --- a/WPF/VMagicMirrorConfig/Resources/English.xaml +++ b/WPF/VMagicMirrorConfig/Resources/English.xaml @@ -73,6 +73,7 @@ Screenshot Saved Folder... + Outline is not applied because "Transparent Window" is off. Game Input also uses gamepad! @@ -457,6 +458,12 @@ Translate (2 way): Intensity [%] Threshold [%] + Outline (require transparent background) + Enable Outline Effect + Outline if BG Transparent + Thickness + High Quality Mode (*Require higher GPU resources) + Wind Enable Wind Wind diff --git a/WPF/VMagicMirrorConfig/Resources/Japanese.xaml b/WPF/VMagicMirrorConfig/Resources/Japanese.xaml index 3fc75fff6..f4cf643fd 100644 --- a/WPF/VMagicMirrorConfig/Resources/Japanese.xaml +++ b/WPF/VMagicMirrorConfig/Resources/Japanese.xaml @@ -73,6 +73,7 @@ スクリーンショット 保存フォルダ... + 「背景を透過」がオフのため縁取りは無効になっています。 ゲーム入力でもゲームパッドが使われています! @@ -457,6 +458,12 @@ png画像やglb/gltfモデルが利用可能です。 強さ[%] しきい値[%] + 縁取り (背景透過でのみ有効) + 縁取り (背景透過時のみ) + 縁取りエフェクトをオン + 縁の分厚さ + 高品質モード (*GPU負荷が増えます) + 風を有効化 diff --git a/WPF/VMagicMirrorConfig/View/ControlPanel/StreamingPanel.xaml b/WPF/VMagicMirrorConfig/View/ControlPanel/StreamingPanel.xaml index 522f5fdac..ed4b93da4 100644 --- a/WPF/VMagicMirrorConfig/View/ControlPanel/StreamingPanel.xaml +++ b/WPF/VMagicMirrorConfig/View/ControlPanel/StreamingPanel.xaml @@ -527,12 +527,31 @@ IsChecked="{Binding EnableShadow.Value}" /> + + + + + + + + + + diff --git a/WPF/VMagicMirrorConfig/View/SettingWindowTabs/EffectSettingPanel.xaml b/WPF/VMagicMirrorConfig/View/SettingWindowTabs/EffectSettingPanel.xaml index c257615b7..eff95109b 100644 --- a/WPF/VMagicMirrorConfig/View/SettingWindowTabs/EffectSettingPanel.xaml +++ b/WPF/VMagicMirrorConfig/View/SettingWindowTabs/EffectSettingPanel.xaml @@ -7,7 +7,7 @@ xmlns:view="clr-namespace:Baku.VMagicMirrorConfig.View" xmlns:vm="clr-namespace:Baku.VMagicMirrorConfig.ViewModel" mc:Ignorable="d" - d:DesignHeight="1350" + d:DesignHeight="2050" d:DesignWidth="350" > @@ -495,6 +495,144 @@ + + + + + + + + + + + + + + + + +