質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.50%
Unity

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

Q&A

解決済

1回答

957閲覧

shaderで作った円の大きさが変わってしまう

tuna_onigiri

総合スコア11

Unity

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

0グッド

0クリップ

投稿2022/04/06 09:33

・作っているもの
マウスの動きに合わせて視野範囲が変化するというものを作っています.
planeに,shaderで同じ大きさの固定円(A)とマウスの動きに合わせて動く円(B)を連続で配置することで,視野範囲が大きくなっているように見せようと考えています.

イメージとしては,
カーソルを右に移動させたら右側に徐々に視野が大きくなる,斜め下に移動したら右側が徐々に円に戻りながら斜め下が徐々に広がるという感じです.
イメージ説明

・できていないこと
固定円(A)と動く円(B)をshaderで作成したところ,動く円(B)がとても小さく表示されました.固定円(A)と同じように記入しましたが,どこを間違えているのかわかりません.
調べてみましたが解決できませんでした…

以下,現在の状態と参考URLです.

▼shader

Shader "Custom/patternTest" { Properties { _HolePos1("Hole vector1", Vector) = (0.5,0.5,1.0,1.0) _HolePos2("Hole vector2", Vector) = (0.5,0.5,1.0,1.0) _Color1("Colo1r", Color) = (0.0,0.0,0.0,1.0) _Color2("Color2", Color) = (0.0,0.0,0.0,1.0) _HoleSize1("Hole Size1", float) = 0.1 _HoleSize2("Hole Size2", float) = 0.1 _BlurThick1("Blur Thick1", float) = 0.01 _BlurThick2("Blur Thick2", float) = 0.01 } SubShader { Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"} ZWrite Off Blend SrcAlpha OneMinusSrcAlpha Pass{ CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" float _HoleSize1; float _HoleSize2; float _BlurThick1; float _BlurThick2; fixed4 _HolePos1; fixed4 _HolePos2; fixed4 _Color1; fixed4 _Color2; struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; }; struct v2f { float4 pos : POSITION; float2 uv : TEXCOORD0; }; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = float2(v.texcoord.x, v.texcoord.y); return o; } half4 frag (v2f i) : COLOR { half4 col1 = _Color1; half4 col2 = _Color2; float2 pos1 = i.uv; float2 pos2 = i.uv; float col1_a = 0.5; float col2_a = 0.5; float dist1 = distance(pos1, float2(_HolePos1.x, _HolePos1.y)); float dist2 = distance(pos2, float2(_HolePos2.x, _HolePos2.y)); _HoleSize1 = 0.01; _HoleSize2 = 0.01; _BlurThick1 = 0.1; _BlurThick2 = 0.1; col1.a = col1_a; col2.a = col2_a; //--------------------円(A)-------------------- if(dist1 < _HoleSize1) { clip(-1.0); } else if(dist1 < _HoleSize1 + _BlurThick1) { col1.a = (dist1 - _HoleSize1) * 10.0; col1.a = pow(col1.a, 2.0); if(col1.a >= col1_a) col1.a = col1_a; } //--------------------円(B)-------------------- if(dist2 < _HoleSize2) { clip(-1.0); } else if(dist2 < _HoleSize2 + _BlurThick2) { col2.a = (dist2 - _HoleSize2) * 10.0; col2.a = pow(col2.a, 2.0); if(col2.a >= col2_a) col2.a = col2_a; } return col1; return col2; } ENDCG } } }

▼円(B)を動かすためのscript

using System.Collections; using System.Collections.Generic; using UnityEngine; public class PatternHole : MonoBehaviour { private float mouseX; private float mouseY; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { mouseX = Input.mousePosition.x; mouseY = Input.mousePosition.y; var pinPos = new Vector3(mouseX / Screen.width, mouseY / Screen.height, 0f); GetComponent<Renderer>().material.SetVector("_HolePos2", pinPos); } }

▼実行時の画面(画像の文字は説明用の後付けです)
イメージ説明

▼shader参考URL
リンク内容

初めて投稿するため,失礼な点やわかりにくい点がありましたら申し訳ございません.
解決方法のヒントなどありましたら,ご教授いただけると幸いです.
よろしくお願いいたします.

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

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

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

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

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

guest

回答1

0

ベストアンサー

コードの各部分を円A用と円B用に二重化してやるという発想は妥当かと思うのですが、frag末尾の...

ShaderLab

1 return col1; 2 return col2;

というのはまずいでしょうね。このように記述してもfragから出力される色はcol1だけで、col2は無視されてしまうかと思います。
結局、円Bについては上の方の...

ShaderLab

1 if(dist2 < _HoleSize2) 2 { 3 clip(-1.0); 4 }

という部分だけが結果に影響することになるでしょう。円B中心の半径_HoleSize2未満の小さい部分だけがclipによってくり抜かれ、周りのグラデーションがない小さな円になってしまったものと思われます。

円Bにも周りにグラデーション領域ができるようにするとなると、一案としては「中心点が一番近い円の色を返す」というのはどうでしょう。つまりdist1dist2を比べ、dist1の方が近かったらcol1を、dist2の方が近かったらcol2を返すというわけです。

ShaderLab

1 return lerp(col1, col2, step(dist2, dist1));

穴をマウスポインタに向かって伸ばす件について

下記のような穴のスクリプトと...

C#

1using System; 2using UnityEngine; 3using UnityEngine.Rendering; 4 5[RequireComponent(typeof(Renderer))] 6public class Hole : MonoBehaviour 7{ 8 private const int SpokeCount = 32; 9 private const float SmoothingThreshold = 0.001f; 10 private static readonly int CenterProperty = Shader.PropertyToID("_Center"); 11 private static readonly int InverseMaxSpokeLengthProperty = Shader.PropertyToID("_InverseMaxSpokeLength"); 12 private static readonly int SpokeLengthsProperty = Shader.PropertyToID("_SpokeLengths"); 13 private static readonly int SmoothingFactorProperty = Shader.PropertyToID("_SmoothingFactor"); 14 15 [SerializeField][Min(0.0f)] private float maxSpokeLength = 0.4f; // ローカル空間におけるスポークの最大長さ 16 [SerializeField][Min(0.0f)] private float mouseAngleHalfWidth = 15.0f; // スポークの長さが半減するマウスポインタとスポークの角度差 17 18 public Vector2 center; // 穴の中心のローカルXY座標 19 [Min(0.0f)] public float smoothTime = 1.0f; // スポークが目標長さに伸縮するおよその時間 20 [Range(0.0f, 0.05f)] public float outlineSmoothness = 0.025f; // 穴の輪郭のなめらかさ 21 public bool showDebugGizmos; // スポークとマウスポインタ位置をシーンビュー上に表示するか 22 23 private readonly Vector2[] spokeDirections = new Vector2[SpokeCount]; 24 private readonly float[] spokeLengths = new float[SpokeCount]; 25 private readonly float[] spokeTargetLengths = new float[SpokeCount]; 26 private readonly float[] spokeVelocities = new float[SpokeCount]; 27 private LocalKeyword enableSmoothing; 28 private Camera mainCamera; 29 private Material material; 30 private float spokeLengthFactor; 31 32 private void Start() 33 { 34 this.material = this.GetComponent<Renderer>().material; 35 this.enableSmoothing = new LocalKeyword(this.material.shader, "ENABLE_SMOOTHING"); 36 this.mainCamera = Camera.main; 37 for (var i = 0; i < SpokeCount; i++) 38 { 39 var spokeAngle = (2.0f * Mathf.PI * i) / SpokeCount; 40 this.spokeDirections[i] = new Vector2(Mathf.Cos(spokeAngle), Mathf.Sin(spokeAngle)); 41 } 42 this.spokeLengthFactor = (Mathf.Log(0.5f) * Mathf.Rad2Deg * Mathf.Rad2Deg) / (this.mouseAngleHalfWidth * this.mouseAngleHalfWidth); 43 } 44 45 private void Update() 46 { 47 // まず、マウスポインタの位置をもとに各スポークの目標長さを決める 48 // スポークの向きがマウスポインタの方角からずれるほど、スポークの 49 // 長さを釣り鐘形に割り引くことでなめらかな外形を作る 50 var mouseRay = this.mainCamera.ScreenPointToRay(Input.mousePosition); 51 if (!new Plane(this.transform.forward, this.transform.position).Raycast(mouseRay, out var enter)) 52 { 53 return; 54 } 55 var mouseWorldPosition = mouseRay.GetPoint(enter); 56 var mousePosition = (Vector2)this.transform.InverseTransformPoint(mouseWorldPosition); 57 var relativeMousePosition = mousePosition - this.center; 58 var mouseLength = Mathf.Min(relativeMousePosition.magnitude, this.maxSpokeLength); 59 if (mouseLength > 0.0f) 60 { 61 var mouseDirection = relativeMousePosition.normalized; 62 for (var i = 0; i < SpokeCount; i++) 63 { 64 var angle = Mathf.Acos(Mathf.Clamp(Vector2.Dot(mouseDirection, this.spokeDirections[i]), -1.0f, 1.0f)); 65 this.spokeTargetLengths[i] = Mathf.Exp(this.spokeLengthFactor * angle * angle) * mouseLength; 66 } 67 } 68 else 69 { 70 Array.Fill(this.spokeTargetLengths, 0.0f); 71 } 72 73 // 各スポークの長さを目標値に向かってなめらかに伸縮させる 74 for (var i = 0; i < SpokeCount; i++) 75 { 76 this.spokeLengths[i] = Mathf.SmoothDamp( 77 this.spokeLengths[i], 78 this.spokeTargetLengths[i], 79 ref this.spokeVelocities[i], 80 this.smoothTime); 81 if (this.showDebugGizmos) 82 { 83 Debug.DrawLine( 84 this.transform.TransformPoint(this.center), 85 this.transform.TransformPoint((this.spokeDirections[i] * this.spokeLengths[i]) + this.center), 86 Color.green); 87 } 88 } 89 90 // マウスポインタの位置に十字を表示する 91 if (this.showDebugGizmos) 92 { 93 const float crossRadius = 8.0f; 94 var mouseScreenPosition = this.mainCamera.WorldToScreenPoint(mouseWorldPosition); 95 Debug.DrawLine( 96 this.mainCamera.ScreenToWorldPoint(mouseScreenPosition + (Vector3.left * crossRadius)), 97 this.mainCamera.ScreenToWorldPoint(mouseScreenPosition + (Vector3.right * crossRadius)), 98 Color.yellow); 99 Debug.DrawLine( 100 this.mainCamera.ScreenToWorldPoint(mouseScreenPosition + (Vector3.down * crossRadius)), 101 this.mainCamera.ScreenToWorldPoint(mouseScreenPosition + (Vector3.up * crossRadius)), 102 Color.yellow); 103 } 104 105 // 穴の中心、スポークの長さ、その他マテリアルに必要なデータをセットする 106 this.material.SetVector(CenterProperty, this.center); 107 this.material.SetFloat(InverseMaxSpokeLengthProperty, 1.0f / this.maxSpokeLength); 108 this.material.SetFloatArray(SpokeLengthsProperty, this.spokeLengths); 109 this.material.SetKeyword(this.enableSmoothing, this.outlineSmoothness > SmoothingThreshold); 110 this.material.SetFloat(SmoothingFactorProperty, 1.0f / this.outlineSmoothness); 111 } 112 113 private void OnDestroy() 114 { 115 Destroy(this.material); 116 } 117}

板のマテリアル用に下記のようなシェーダーを用意しました。

ShaderLab

1Shader "Unlit/Hole" 2{ 3 Properties 4 { 5 _Color ("Base Color", Color) = (0.0, 0.0, 0.0, 0.5) // 背景の色 6 _InnerRadius ("Inner Radius", Range(0.0, 1.0)) = 0.1 // 縁のグラデーションが始まる半径 7 _OuterRadius ("Outer Radius", Range(0.0, 1.0)) = 0.2 // 縁のグラデーションが終わる半径 8 } 9 SubShader 10 { 11 Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" } 12 13 ZWrite Off 14 Blend SrcAlpha OneMinusSrcAlpha 15 16 Pass 17 { 18 CGPROGRAM 19 #pragma multi_compile _ ENABLE_SMOOTHING 20 #pragma vertex vert 21 #pragma fragment frag 22 #include "UnityCG.cginc" 23 #define SPOKE_COUNT 32 24 25 struct v2f 26 { 27 float2 position : TEXCOORD0; 28 float4 vertex : SV_POSITION; 29 }; 30 31 v2f vert(float4 vertex : POSITION) 32 { 33 v2f o; 34 o.vertex = UnityObjectToClipPos(vertex); 35 o.position = vertex.xy; 36 return o; 37 } 38 39 float2 _Center; 40 float _InverseMaxSpokeLength; 41 float _SpokeLengths[SPOKE_COUNT]; 42 float _SmoothingFactor; 43 fixed4 _Color; 44 float _InnerRadius; 45 float _OuterRadius; 46 47 // 線分abと点pの距離を得る 48 float distance(float2 a, float2 b, float2 p) 49 { 50 float2 s = b - a; 51 float l2 = dot(s, s); 52 if (l2 > 0.0) 53 { 54 float2 v = p - a; 55 float f = dot(v, s) / l2; 56 return f <= 0.0 ? distance(a, p) : f >= 1.0 ? distance(b, p) : abs((v.x * s.y) - (v.y * s.x)) / sqrt(l2); 57 } 58 return distance(a, p); 59 } 60 61 fixed4 frag(v2f i) : SV_Target 62 { 63 float minimumDistance = 1.#INF; 64 65 #ifdef ENABLE_SMOOTHING 66 float nearness = exp2(-distance(_Center, i.position) * _SmoothingFactor); 67 float weightSum = 1.0; 68 #endif 69 70 for (int j = 0; j < SPOKE_COUNT; j++) 71 { 72 // ピクセルと各スポークの距離を調べて... 73 float2 direction; 74 sincos(2.0 * UNITY_PI * j / SPOKE_COUNT, direction.y, direction.x); 75 float d = distance(_Center, _Center + direction * _SpokeLengths[j], i.position); 76 77 #ifdef ENABLE_SMOOTHING 78 // 外形をスムージングする場合、まずピクセルと各スポークの近さを積算する 79 // このとき、スポークの長さに応じて積算量に重みをつける 80 float weight = _SpokeLengths[j] * _InverseMaxSpokeLength; 81 nearness += exp2(-d * _SmoothingFactor) * weight; 82 weightSum += weight; 83 #else 84 // 外形をスムージングしない場合、単純にピクセルと各スポークの距離を調べて最短のものを選ぶ 85 minimumDistance = min(minimumDistance, d); 86 #endif 87 } 88 89 // 外形をスムージングする場合、minimumDistanceを積算した近さから逆算する 90 #ifdef ENABLE_SMOOTHING 91 minimumDistance = -log2(nearness / weightSum) / _SmoothingFactor; 92 #endif 93 94 // 完全透明部分はクリッピングする 95 float2 radii = float2(min(_InnerRadius, _OuterRadius), max(_InnerRadius, _OuterRadius)); 96 clip(minimumDistance - radii.x); 97 98 // 縁の部分のグラデーションを決める 99 fixed4 color = _Color; 100 color.a *= smoothstep(radii.x, radii.y, minimumDistance); 101 return color; 102 } 103 ENDCG 104 } 105 } 106}

穴を中心に仮想的なスポークを放射状に配置し、それらがマウスポインタの位置に応じて伸縮するようにしてみました。シェーダー上ではこれらスポークとの距離をもとに穴を開けるという作戦です。

図

投稿2022/04/07 21:41

編集2022/04/08 23:35
Bongo

総合スコア10807

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

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

tuna_onigiri

2022/04/08 04:05

ご回答してくださりありがとうございます. 円の大きさというよりも,円(B)のグラデーションの部分が反映されていない結果だったのですね… 教えていただいた方法を試してみたいと思います!
Bongo

2022/04/08 23:36

お目通しいただきありがとうございます。あのあと、個人的な興味からご質問の背景として説明いただいた「マウスの動きに合わせて視野範囲が変化する」という件について検討してみましたので追記しました。ですが、ご質問者さんの場合は円を連続的に配置する方針とのことですから、それとは少々異なったアプローチで進めたため余計な混乱を招く蛇足になってしまうかもしれません。単に私が「この動きは何かに使えそうだ」と思って勝手にやったことですので、ちょっとした参考程度としてとらえていただけるとありがたいです。
tuna_onigiri

2022/04/11 02:43

追記ありがとうございます。とても滑らかに動いていて驚きました! 最初に回答いただいた方法だとうまく反映できませんでした…。 もう一度見直し、追記の方法も参考にしながら修正していこうと思います! わかりやすく丁寧なBongoさんのご回答に感謝いたします。
tuna_onigiri

2022/04/13 06:04

先の方法ですが、Unlit Shaderを使用していなかったのが原因でした。 原因に気が付くことができて良かったです…。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問