🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

2回答

4520閲覧

UnityのUIにモーションブラーをかけたい

bh_evan

総合スコア18

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2019/12/17 18:32

編集2020/01/06 13:11

前提・実現したいこと

UnityのImageやTextに対してモーションブラーをかけたいです。
SpriteやTextMeshProなどでも構いませんが、できれば上記の二点に適用できると嬉しいです。

できるだけ追加料金のかからない方法がいいです。

試したこと

「post processing stack v2」のMotion Blurは試しましたが、Bloomなど他の機能は適用されるのに対してMotion Blurだけは適用されません。

  • Post processingのMotion Blurが3Dオブジェクトに適用されているのは確認しました。
  • CanvasのRender CameraはScreen Space - Cameraで行いました。

補足情報

Unity 2019.2.15f1

2019/1/6 補足
イメージ説明
左の”L”がSprite  右の”L”がImage

イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

(回答の続き)

UIオブジェクト用のシェーダーはUI-Default.shaderとInternal-MotionVectors.shaderを折衷し...

ShaderLab

1Shader "Hidden/GraphicMotionVectors" 2{ 3 Properties 4 { 5 _MainTex ("Sprite Texture", 2D) = "white" {} 6 _Color ("Tint", Color) = (1,1,1,1) 7 _StencilComp ("Stencil Comparison", Float) = 8 8 _Stencil ("Stencil ID", Float) = 0 9 _StencilOp ("Stencil Operation", Float) = 0 10 _StencilWriteMask ("Stencil Write Mask", Float) = 255 11 _StencilReadMask ("Stencil Read Mask", Float) = 255 12 _ColorMask ("Color Mask", Float) = 15 13 } 14 15 SubShader 16 { 17 Tags 18 { 19 "Queue"="Transparent" 20 "IgnoreProjector"="True" 21 "RenderType"="Transparent" 22 "PreviewType"="Plane" 23 "CanUseSpriteAtlas"="True" 24 } 25 26 Stencil 27 { 28 Ref [_Stencil] 29 Comp [_StencilComp] 30 Pass [_StencilOp] 31 ReadMask [_StencilReadMask] 32 WriteMask [_StencilWriteMask] 33 } 34 35 Cull Off 36 Lighting Off 37 ZWrite Off 38 ZTest LEqual 39 Blend SrcAlpha OneMinusSrcAlpha 40 ColorMask [_ColorMask] 41 42 Pass 43 { 44 CGPROGRAM 45 #pragma vertex vert 46 #pragma fragment frag 47 #pragma target 2.0 48 49 #include "UnityCG.cginc" 50 #include "UnityUI.cginc" 51 52 #pragma multi_compile_local _ UNITY_UI_CLIP_RECT 53 54 struct appdata_t 55 { 56 float4 vertex : POSITION; 57 float4 color : COLOR; 58 float2 texcoord : TEXCOORD0; 59 UNITY_VERTEX_INPUT_INSTANCE_ID 60 }; 61 62 struct v2fMotion 63 { 64 float4 vertex : SV_POSITION; 65 fixed4 color : COLOR; 66 float2 texcoord : TEXCOORD0; 67 float4 worldPosition : TEXCOORD1; 68 float4 transferPos : TEXCOORD2; 69 float4 transferPosOld : TEXCOORD3; 70 UNITY_VERTEX_OUTPUT_STEREO 71 }; 72 73 sampler2D _MainTex; 74 fixed4 _Color; 75 fixed4 _TextureSampleAdd; 76 float4 _ClipRect; 77 float4 _MainTex_ST; 78 float4x4 _NonJitteredVP; 79 float4x4 _PreviousMVP; 80 81 v2fMotion vert(appdata_t v) 82 { 83 v2fMotion OUT; 84 UNITY_SETUP_INSTANCE_ID(v); 85 UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); 86 87 // まず通常のGraphicの方法にならって頂点の処理を行い... 88 OUT.worldPosition = v.vertex; 89 OUT.vertex = UnityObjectToClipPos(OUT.worldPosition); 90 OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex); 91 OUT.color = v.color * _Color; 92 93 // モーションベクター用の追加処理として、前回と今回のクリップ座標を求める 94 OUT.transferPos = mul(_NonJitteredVP, mul(unity_ObjectToWorld, OUT.worldPosition)); 95 OUT.transferPosOld = mul(_PreviousMVP, OUT.worldPosition); 96 97 return OUT; 98 } 99 100 half4 frag(v2fMotion IN) : SV_Target 101 { 102 half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color; 103 #ifdef UNITY_UI_CLIP_RECT 104 color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect); 105 #endif 106 107 // 透明部分は破棄する 108 clip(color.a - 0.001); 109 110 // 正規化ビューポート座標におけるピクセルの前回の位置、今回の位置を求める 111 float3 hPos = (IN.transferPos.xyz / IN.transferPos.w); 112 float3 hPosOld = (IN.transferPosOld.xyz / IN.transferPosOld.w); 113 float2 vPos = (hPos.xy + 1.0f) / 2.0f; 114 float2 vPosOld = (hPosOld.xy + 1.0f) / 2.0f; 115 116 // 座標の変化量を出力する 117 half2 uvDiff = vPos - vPosOld; 118 return half4(uvDiff, 0, 1); 119 } 120 ENDCG 121 } 122 } 123}

UIオブジェクトには下記スクリプトをアタッチし...

C#

1using System.Collections; 2using System.Collections.Generic; 3using System.Reflection; 4using UnityEngine; 5using UnityEngine.Rendering; 6using UnityEngine.UI; 7 8[RequireComponent(typeof(RectTransform), typeof(CanvasRenderer), typeof(Graphic))] 9public class GraphicMotionVectorsEnabler : MonoBehaviour 10{ 11 private static readonly int StencilCompProperty = Shader.PropertyToID("_StencilComp"); 12 private static readonly int StencilProperty = Shader.PropertyToID("_Stencil"); 13 private static readonly int StencilOpProperty = Shader.PropertyToID("_StencilOp"); 14 private static readonly int StencilWriteMaskProperty = Shader.PropertyToID("_StencilWriteMask"); 15 private static readonly int StencilReadMaskProperty = Shader.PropertyToID("_StencilReadMask"); 16 private static readonly int ColorMaskProperty = Shader.PropertyToID("_ColorMask"); 17 private static readonly int NonJitteredVpProperty = Shader.PropertyToID("_NonJitteredVP"); 18 private static readonly int PreviousMvpProperty = Shader.PropertyToID("_PreviousMVP"); 19 private static readonly YieldInstruction WaitForEndOfFrame = new WaitForEndOfFrame(); 20 21 private static readonly PropertyInfo WorkerMeshInfo = typeof(Graphic).GetProperty( 22 "workerMesh", 23 BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.NonPublic); 24 25 private static readonly MethodInfo DoMeshGenerationInfo = typeof(Graphic).GetMethod( 26 "DoMeshGeneration", 27 BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic); 28 private Graphic graphic; 29 private CommandBuffer commands; 30 private Material motionVectorsMaterial; 31 private Material graphicMaterial; 32 private Matrix4x4 previousModelMatrix; 33 private readonly Dictionary<Camera, Matrix4x4> previousVpMatrices = new Dictionary<Camera, Matrix4x4>(); 34 35 private void Start() 36 { 37 this.graphic = this.GetComponent<Graphic>(); 38 this.motionVectorsMaterial = new Material(Shader.Find("Hidden/GraphicMotionVectors")); 39 this.graphicMaterial = this.graphic.materialForRendering; 40 this.previousModelMatrix = this.transform.localToWorldMatrix; 41 this.commands = new CommandBuffer {name = "Render Graphic Motion Vectors"}; 42 this.StartCoroutine(this.UpdatePreviousParameters()); 43 } 44 45 private void OnRenderObject() 46 { 47 var canvas = this.graphic.canvas; 48 49 // Screen Space - Overlayはサポートしない 50 if (canvas.renderMode == RenderMode.ScreenSpaceOverlay) 51 { 52 return; 53 } 54 var currentCamera = canvas.worldCamera; 55 if ((currentCamera == null) || (currentCamera.cameraType != CameraType.Game)) 56 { 57 return; 58 } 59 60 // Graphicにメッシュ生成させ、その直後のworkerMeshを使って描画する 61 DoMeshGenerationInfo.Invoke(this.graphic, null); 62 var mesh = WorkerMeshInfo.GetValue(null) as Mesh; 63 var currentModelMatrix = transform.localToWorldMatrix; 64 var currentVpMatrix = currentCamera.nonJitteredProjectionMatrix * currentCamera.worldToCameraMatrix; 65 if (!this.previousVpMatrices.TryGetValue(currentCamera, out var previousVpMatrix)) 66 { 67 previousVpMatrix = currentVpMatrix; 68 } 69 70 this.previousVpMatrices[currentCamera] = currentVpMatrix; 71 this.motionVectorsMaterial.mainTexture = this.graphic.mainTexture; 72 this.motionVectorsMaterial.color = this.graphicMaterial.color; 73 this.motionVectorsMaterial.SetFloat(StencilCompProperty, this.graphicMaterial.GetFloat(StencilCompProperty)); 74 this.motionVectorsMaterial.SetFloat(StencilProperty, this.graphicMaterial.GetFloat(StencilProperty)); 75 this.motionVectorsMaterial.SetFloat(StencilOpProperty, this.graphicMaterial.GetFloat(StencilOpProperty)); 76 this.motionVectorsMaterial.SetFloat(StencilWriteMaskProperty, this.graphicMaterial.GetFloat(StencilWriteMaskProperty)); 77 this.motionVectorsMaterial.SetFloat(StencilReadMaskProperty, this.graphicMaterial.GetFloat(StencilReadMaskProperty)); 78 this.motionVectorsMaterial.SetFloat(ColorMaskProperty, this.graphicMaterial.GetFloat(ColorMaskProperty)); 79 this.motionVectorsMaterial.SetMatrix(NonJitteredVpProperty, currentVpMatrix); 80 this.motionVectorsMaterial.SetMatrix(PreviousMvpProperty, previousVpMatrix * this.previousModelMatrix); 81 this.commands.Clear(); 82 this.commands.SetRenderTarget(BuiltinRenderTextureType.MotionVectors); 83 this.commands.DrawMesh(mesh, currentModelMatrix, this.motionVectorsMaterial); 84 Graphics.ExecuteCommandBuffer(this.commands); 85 } 86 87 private IEnumerator UpdatePreviousParameters() 88 { 89 while (true) 90 { 91 yield return WaitForEndOfFrame; 92 93 this.previousModelMatrix = this.transform.localToWorldMatrix; 94 } 95 } 96}

実行すると下図のようなモーションベクターが描画され...

図1

それに基づいてモーションブラーがかかり、下図のような見た目になりました。
右上は特別な処置は行っていない3Dオブジェクト、左上はSpriteRendererにより描画されるスプライト(SpriteMotionVectorsEnablerをアタッチ)です。
下半分はUIオブジェクト(それぞれGraphicMotionVectorsEnablerをアタッチ)で、右下はImage、左下はTextです。

図2

ただし、透明オブジェクト同士の前後関係調整は不十分です。上図ではUIがスプライトより手前に表示されるよう配置しているのですが、モーションベクター描画ではスプライトの方が手前になってしまっています。OnRenderObjectの実行順はZソートとは無関係なようですので、もし正しい順序にしたい場合は自前でモーションベクター描画のタイミングをコントロールする必要がありそうです。とはいえ、モーションブラーがかかる部分はどうせ高速で動いているでしょうから、多少ぼかしが狂っていても目立たないんじゃないかとは思います。
すみませんが即興で書いたもので十分なテストを行っていないため、他にもうまく描画されないケースがあるかもしれません。

投稿2019/12/29 04:30

編集2019/12/29 05:02
Bongo

総合スコア10811

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

bh_evan

2020/01/06 13:10

全方位へのスライドをメインに考えていたので、この方法で実装できるのは非常に助かりました。 Spriteの描画順に関しても、SpriteではなくImageでの制作に変更したので問題なさそうです。 しかし、私自身シェーダーを扱うのが初めてなので、正しい実装方法を心得ていなかったからでしょうか、補足に付け加えました画像のようになってしまいました。InspectorはSpriteに実装したものです。 Shaderの作り方からなのか、置く場所を間違えたのか、基礎のようなことかもしれませんが正しい実装方法を教えていただけたら幸いです。
Bongo

2020/01/06 20:28

いえ、説明不足ですみませんが回答中に例示しましたHidden/SpriteMotionVectorsとHidden/GraphicMotionVectorsはご自身でマテリアルを作ってスプライトやUIオブジェクトにセットすることを想定したものではなく、SpriteMotionVectorsEnablerやGraphicMotionVectorsEnablerが内部で勝手に使うシェーダーです(ですのでプレイモードで動作確認する分にはそのままでもいいですが、ビルド後でも動作させるにはメニューの「Edit」→「Project Settings...」の「Graphics」→「Always Included Shaders」にこれらシェーダーを追加しておかないと、ビルド時にこれらシェーダーは使われていないと判断されて削除されてしまうでしょう)。 デフォルトだとスプライトのマテリアルは「Sprites-Default」、UIオブジェクトのマテリアルは「None (Material)」になっているかと思いますが、この状態に戻してみてください。 ※それとPost Processing StackのMotion Blurはオンになっているでしょうかね?ご提示いただいた失敗映像を拝見すると、正常にモーションブラーがかかるはずのシリンダーもボケていないように見えるのですが...回転速度が遅くてボケ量が小さいだけならいいのですが、もしMotion Blurをオフにしてしまっている場合はオンにする必要があります。
bh_evan

2020/01/07 12:49

丁寧にありがとうございます。おかげで理想通りのブラーをかけられました。 画像はPostProcesssingはオンでしたが、スピードが足りずに見えにくかったかもしれません。
guest

0

ベストアンサー

もし回転するホイールのようなものをぼかして美しく見せたいのでしたら、Radial Blur Shader - Texture - Unity Forumで話題に取り上げられているような円形ぼかしがいいんじゃないかと思います。なんでしたら特殊なシェーダーも使わずに、画像処理ソフトで事前にぼかしたテクスチャを何種類か用意しておいて回転速度に応じて差し替えるのでもいいかもしれません。
同じくオブジェクトの動きが平行移動だけ、あるいは拡大縮小だけなのであれば同様に移動ぼかし、放射状ぼかしを行うことで対応できそうです。ですが複雑な動きをする場合にはPost Processing Stackのモーションブラーを使いたいことがあるかもしれません。

Post Processing Stackは【Unity】5.4&5.5新機能を深掘り(Motion Vectors編) - 株式会社ロジカルビートなどで紹介されているモーションベクターテクスチャを見てぼかしの向きや大きさを決定しているのだろうと思います。
何とかしてそこに動きを書き込んでやればモーションブラーをかけてくれるのではないかと考え、まずSpriteRenderer用としてビルトインシェーダーのSprites-Default.shaderとInternal-MotionVectors.shaderを折衷した下記シェーダーを作り...

ShaderLab

1Shader "Hidden/SpriteMotionVectors" 2{ 3 Properties 4 { 5 [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} 6 _Color ("Tint", Color) = (1,1,1,1) 7 [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0 8 [HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1) 9 [HideInInspector] _Flip ("Flip", Vector) = (1,1,1,1) 10 [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {} 11 [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0 12 } 13 14 SubShader 15 { 16 Tags 17 { 18 "Queue"="Transparent" 19 "IgnoreProjector"="True" 20 "RenderType"="Transparent" 21 "PreviewType"="Plane" 22 "CanUseSpriteAtlas"="True" 23 } 24 25 Cull Off 26 Lighting Off 27 ZWrite Off 28 Blend One OneMinusSrcAlpha 29 30 Pass 31 { 32 CGPROGRAM 33 #pragma vertex vert 34 #pragma fragment frag 35 #pragma target 2.0 36 #pragma multi_compile_instancing 37 #pragma multi_compile_local _ PIXELSNAP_ON 38 #pragma multi_compile _ ETC1_EXTERNAL_ALPHA 39 #include "UnitySprites.cginc" 40 41 float4x4 _NonJitteredVP; 42 float4x4 _PreviousMVP; 43 44 struct v2fMotion 45 { 46 float4 vertex : SV_POSITION; 47 fixed4 color : COLOR; 48 float2 texcoord : TEXCOORD0; 49 float4 transferPos : TEXCOORD1; 50 float4 transferPosOld : TEXCOORD2; 51 UNITY_VERTEX_OUTPUT_STEREO 52 }; 53 54 v2fMotion vert(appdata_t IN) 55 { 56 v2fMotion OUT; 57 58 UNITY_SETUP_INSTANCE_ID (IN); 59 UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); 60 61 // まず通常のスプライトの方法にならって頂点の処理を行い... 62 float4 inputPos = UnityFlipSprite(IN.vertex, _Flip); 63 OUT.vertex = UnityObjectToClipPos(inputPos); 64 OUT.texcoord = IN.texcoord; 65 OUT.color = IN.color * _Color * _RendererColor; 66 #ifdef PIXELSNAP_ON 67 OUT.vertex = UnityPixelSnap(OUT.vertex); 68 #endif 69 70 // モーションベクター用の追加処理として、前回と今回のクリップ座標を求める 71 OUT.transferPos = mul(_NonJitteredVP, mul(unity_ObjectToWorld, inputPos)); 72 OUT.transferPosOld = mul(_PreviousMVP, inputPos); 73 74 return OUT; 75 } 76 77 half4 frag(v2fMotion IN) : SV_Target 78 { 79 // 透明部分は破棄する 80 clip((SampleSpriteTexture(IN.texcoord).a * IN.color.a) - 0.001); 81 82 // 正規化ビューポート座標におけるピクセルの前回の位置、今回の位置を求める 83 float3 hPos = (IN.transferPos.xyz / IN.transferPos.w); 84 float3 hPosOld = (IN.transferPosOld.xyz / IN.transferPosOld.w); 85 float2 vPos = (hPos.xy + 1.0f) / 2.0f; 86 float2 vPosOld = (hPosOld.xy + 1.0f) / 2.0f; 87 88 // 座標の変化量を出力する 89 half2 uvDiff = vPos - vPosOld; 90 return half4(uvDiff, 0, 1); 91 } 92 ENDCG 93 } 94 } 95}

スプライトにアタッチするスクリプトは下記のようにし...

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4using UnityEngine.Rendering; 5 6[RequireComponent(typeof(SpriteRenderer))] 7public class SpriteMotionVectorsEnabler : MonoBehaviour 8{ 9 private static readonly int PixelSnapProperty = Shader.PropertyToID("PixelSnap"); 10 private static readonly int RendererColorProperty = Shader.PropertyToID("_RendererColor"); 11 private static readonly int FlipProperty = Shader.PropertyToID("_Flip"); 12 private static readonly int NonJitteredVpProperty = Shader.PropertyToID("_NonJitteredVP"); 13 private static readonly int PreviousMvpProperty = Shader.PropertyToID("_PreviousMVP"); 14 private static readonly YieldInstruction WaitForEndOfFrame = new WaitForEndOfFrame(); 15 private new SpriteRenderer renderer; 16 private CommandBuffer commands; 17 private Material motionVectorsMaterial; 18 private Material spriteMaterial; 19 private Matrix4x4 previousModelMatrix; 20 private readonly Dictionary<Camera, Matrix4x4> previousVpMatrices = new Dictionary<Camera, Matrix4x4>(); 21 22 private void Start() 23 { 24 this.renderer = this.GetComponent<SpriteRenderer>(); 25 this.motionVectorsMaterial = new Material(Shader.Find("Hidden/SpriteMotionVectors")); 26 this.spriteMaterial = this.renderer.sharedMaterial; 27 this.previousModelMatrix = this.transform.localToWorldMatrix; 28 this.commands = new CommandBuffer {name = "Render Sprite Motion Vectors"}; 29 this.commands.SetRenderTarget(BuiltinRenderTextureType.MotionVectors); 30 this.commands.DrawRenderer(this.renderer, this.motionVectorsMaterial); 31 this.StartCoroutine(this.UpdatePreviousParameters()); 32 } 33 34 private void OnRenderObject() 35 { 36 var currentCamera = Camera.current; 37 if ((currentCamera == null) || (currentCamera.cameraType != CameraType.Game)) 38 { 39 return; 40 } 41 42 var currentVpMatrix = currentCamera.nonJitteredProjectionMatrix * currentCamera.worldToCameraMatrix; 43 if (!this.previousVpMatrices.TryGetValue(currentCamera, out var previousVpMatrix)) 44 { 45 previousVpMatrix = currentVpMatrix; 46 } 47 48 this.previousVpMatrices[currentCamera] = currentVpMatrix; 49 this.motionVectorsMaterial.color = this.spriteMaterial.color; 50 this.motionVectorsMaterial.SetFloat(PixelSnapProperty, this.spriteMaterial.GetFloat(PixelSnapProperty)); 51 this.motionVectorsMaterial.SetColor( 52 RendererColorProperty, 53 this.spriteMaterial.GetColor(RendererColorProperty)); 54 this.motionVectorsMaterial.SetVector(FlipProperty, this.spriteMaterial.GetVector(FlipProperty)); 55 this.motionVectorsMaterial.SetMatrix(NonJitteredVpProperty, currentVpMatrix); 56 this.motionVectorsMaterial.SetMatrix(PreviousMvpProperty, previousVpMatrix * this.previousModelMatrix); 57 Graphics.ExecuteCommandBuffer(this.commands); 58 } 59 60 private IEnumerator UpdatePreviousParameters() 61 { 62 while (true) 63 { 64 yield return WaitForEndOfFrame; 65 66 this.previousModelMatrix = this.transform.localToWorldMatrix; 67 } 68 } 69}

(すみませんが文字数がかさんでしまったので、続きは別回答へ)

投稿2019/12/29 04:29

Bongo

総合スコア10811

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問