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

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

詳細はこちら
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

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

Q&A

解決済

1回答

3691閲覧

Unity orthographicSize ちらつき

退会済みユーザー

退会済みユーザー

総合スコア0

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

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

0グッド

1クリップ

投稿2019/12/06 18:44

編集2019/12/06 22:30

前提・実現したいこと

orthographicSizeを変動させたときに生じるちらつきを直したい

該当のソースコード

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4using System.Diagnostics; 5public class CameraScript : MonoBehaviour 6{ 7 private Camera m_cam; 8 private float zoomSpeed = 10f * 3; 9 private float scrollSpeed = 20f * 5; 10 private float dragSpeed = 0.5f; 11 public Vector2 zoom = new Vector2(3f, 100f); 12 [HideInInspector] 13 public float targetZoom; 14 private bool wait = true; 15 public Camera cam 16 { 17 get 18 { 19 if(this.m_cam == null) 20 { 21 this.m_cam = base.GetComponent<Camera>(); 22 } 23 return this.m_cam; 24 } 25 } 26 public float currentZoom 27 { 28 get 29 { 30 return this.cam.orthographicSize; 31 } 32 set 33 { 34 if(this.cam.orthographicSize == value) 35 { 36 return; 37 } 38 this.cam.orthographicSize = value; 39 } 40 } 41 private void Start() 42 { 43 Application.targetFrameRate = 60; 44 this.currentZoom = 60f; 45 this.targetZoom = 60f; 46 base.transform.position = new Vector3(base.transform.position.x, base.transform.position.y, 47 -this.currentZoom); 48 base.StartCoroutine("Starter"); 49 } 50 [DebuggerHidden] 51 private IEnumerator Starter() 52 { 53 this.zoomSpeed /= 2f; 54 yield return new WaitForSeconds(1f); 55 this.targetZoom = 60f; 56 /*while(this.currentZoom > 33f) 57 { 58 yield return new WaitForSeconds(0.333f); 59 }*/ 60 this.zoomSpeed *= 2f; 61 this.wait = false; 62 } 63 private void Update() 64 { 65 if (!this.wait) 66 { 67 this.MovementKeyboardGame(); 68 this.MovementMouseGame(); 69 this.ZoomMouse(); 70 } 71 } 72 private void FixedUpdate() 73 { 74 this.GoToTargetZoom(); 75 } 76 private void MovementMouseGame() 77 { 78 if (Input.GetMouseButton(2)) 79 { 80 base.transform.Translate(-new Vector3(Input.GetAxis("Mouse X") * this.dragSpeed * Time.deltaTime * 100f, Input.GetAxis("Mouse Y") * this.dragSpeed * Time.deltaTime * 100f, 0f)); 81 } 82 } 83 84 private void MovementKeyboardGame() 85 { 86 base.transform.Translate(new Vector3(Input.GetAxis("Horizontal") * this.scrollSpeed * Time.deltaTime, Input.GetAxis("Vertical") * this.scrollSpeed * Time.deltaTime, 0f)); 87 } 88 89 private void ZoomMouse() 90 { 91 if (Input.GetAxis("Mouse ScrollWheel") != 0f) 92 { 93 this.targetZoom -= Input.GetAxis("Mouse ScrollWheel") * this.zoomSpeed; 94 this.targetZoom = Mathf.Clamp(this.targetZoom, this.zoom.x, this.zoom.y); 95 } 96 } 97 98 private void GoToTargetZoom() 99 { 100 if (this.currentZoom != this.targetZoom) 101 { 102 float num = Mathf.Clamp(Mathf.Abs(this.currentZoom - this.targetZoom), 0.3f, 0.5f); 103 float t = Time.deltaTime * this.zoomSpeed * num; 104 float num2 = Mathf.Lerp(this.currentZoom, this.targetZoom, t); 105 if (Mathf.Abs(num2 - this.targetZoom) < 0.01f) 106 { 107 this.currentZoom = this.targetZoom; 108 } 109 else 110 { 111 this.currentZoom = num2; 112 } 113 if (this.currentZoom >= 5f && this.currentZoom != -base.transform.position.z) 114 { 115 base.transform.Translate(new Vector3(0f, 0f, -(this.currentZoom + base.transform.position.z))); 116 } 117 } 118 } 119} 120

試したこと

Graphics.DrawMeshで正方形のメッシュを100×100描画しているのですが、上記スクリプトを使用してカメラのorthographicSizeを動的に変更させると、描画しているテクスチャにちらつきが生じてしまいます。
対策方法を調べたのですが、現状出て来なかったので、どうしても分からず質問しました。ちらつきをなくす方法はあるでしょうか?
Unity2D 描画がちらつくの方法では解決しませんでした。

・使用しているシェーダーのコード

ShaderLab

1Shader "Unlit/World" 2{ 3 Properties 4 { 5 [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {} 6 7 // 見た目調整用プロパティを変更、前回と異なり大きくするほど鋭くなる 8 [PowerSlider(10)] _EdgeSharpness ("Edge Sharpness", Range(0, 100.0)) = 10.0 9 10 _EdgeDist ("Edge Disturbance", Range(0.0, 1.0)) = 0.05 11 _EdgeDistFreq ("Edge Disturbance Frequency", Range(0.0, 16.0)) = 4.0 12 } 13 14 SubShader 15 { 16 Tags { "RenderType"="Opaque" } 17 18 Pass 19 { 20 CGPROGRAM 21 #pragma vertex vert 22 #pragma fragment frag 23 #include "UnityCG.cginc" 24 25 // 頂点アトリビュートからはUV関連を削除 26 struct appdata 27 { 28 float4 vertex : POSITION; 29 float4 color : COLOR; 30 }; 31 32 // tileUvには(0, 0)、(1, 0)、(0, 1)、(1, 1)のいずれかが入る 33 struct v2f 34 { 35 float4 vertex : SV_POSITION; 36 float2 tileUv : TEXCOORD0; 37 float weight : TEXCOORD1; 38 float2 modelPos : TEXCOORD2; 39 }; 40 41 inline float random(float2 st) { 42 return frac(sin(dot(st, float2(12.9898, 78.233))) * 43758.5453123); 43 } 44 45 float noise(float2 st) 46 { 47 float2 i = floor(st); 48 float2 f = frac(st); 49 float a = random(i); 50 float b = random(i + float2(1.0, 0.0)); 51 float c = random(i + float2(0.0, 1.0)); 52 float d = random(i + float2(1.0, 1.0)); 53 float2 u = f * f * (3.0 - 2.0 * f); 54 55 return dot(float2(lerp(a, b, u.x), lerp(c - a, d - b, u.x)), float2(1.0, u.y)); 56 } 57 58 float fractal(float2 st) 59 { 60 float4 amp = float4(1.0, 0.5, 0.25, 0.125); 61 float4 v; 62 63 v.x = noise(st); 64 st = st * 2.0 + float2(14.1421356237, 17.3205080757); 65 v.y = noise(st); 66 st = st * 2.0 + float2(22.360679775, 26.4575131106); 67 v.z = noise(st); 68 st = st * 2.0 + float2(31.4159265359, 27.1828182846); 69 v.w = noise(st); 70 71 return dot(v, amp) / dot(1.0, amp); 72 } 73 74 sampler2D _MainTex; 75 float4 _MainTex_ST; 76 float4 _MainTex_TexelSize; 77 float _EdgeSharpness; 78 float _EdgeDist; 79 float _EdgeDistFreq; 80 81 // ユニフォーム変数を追加 82 #define TYPE_COUNT_MAX 8 83 int _TypeCount; 84 float4 _TypeUvs[TYPE_COUNT_MAX / 2]; 85 float2 _TileSize; 86 float _TypeLevels[TYPE_COUNT_MAX]; 87 88 float4 _Color; 89 90 v2f vert(appdata v) 91 { 92 v2f o; 93 o.tileUv = v.color.xy; 94 o.weight = v.color.a; 95 o.modelPos = v.vertex.xy; 96 o.vertex = UnityObjectToClipPos(v.vertex); 97 return o; 98 } 99 100 fixed4 frag(v2f i) : SV_Target 101 { 102 // まずは前回同様weightを計算する 103 float f = dot(float2(_EdgeDist, 1.0 - _EdgeDist), float2(fractal(i.modelPos * _EdgeDistFreq), 0.5)); 104 float upper = 1.0 - 2.0 * (1.0 - i.weight) * (1.0 - f); 105 float lower = 2.0 * i.weight * f; 106 float weight = lerp(lower, upper, step(0.5, i.weight)); 107 108 // weightを比較してどの範囲にいるか判定し、それを越えない最大のインデックスを調べる 109 uint index = 0; 110 [unroll(TYPE_COUNT_MAX)] 111 for (int k = 0; k < _TypeCount; k++) 112 { 113 if (_TypeLevels[k] < weight) 114 { 115 index = k + 1; 116 continue; 117 } 118 break; 119 } 120 index = min(index, _TypeCount - 1); 121 122 // さらに、その一つ下・一つ上のインデックスを求めておく 123 uint prevIndex = index > 0 ? index - 1 : 0; 124 uint nextIndex = min(index + 1, _TypeCount - 1); 125 126 // 範囲の下限・上限の高さを取得し、lowerLevelを0、upperLevelを1としたときのweightの位置を調べる 127 float lowerLevel = (index == prevIndex) ? 0.0 : _TypeLevels[prevIndex]; 128 float upperLevel = (index == nextIndex) ? 1.0 : _TypeLevels[index]; 129 float weightInRange = (lowerLevel == upperLevel) ? 0.0 : (weight - lowerLevel) / (upperLevel - lowerLevel); 130 131 // weightInRangeが0.5以上かどうかでnextIndex、prevIndexのいずれかを選択する 132 uint edgeIndex = weightInRange >= 0.5 ? nextIndex : prevIndex; 133 134 // weightInRangeが0.5の時0、そこより範囲境界に近いほど1になるような値を作る 135 // このとき_EdgeSharpness乗して、カーブを適宜鋭くする 136 float edgeFactor = saturate(pow(abs(weightInRange * 2.0 - 1.0), _EdgeSharpness)); 137 138 // 範囲中心および範囲境界に対応するUVを取得し、そこからそれぞれ色を取得する 139 float4 edgeVector = _TypeUvs[edgeIndex / 2]; 140 float4 centerVector = _TypeUvs[index / 2]; 141 float2 edgeUv = lerp(edgeVector.xy, edgeVector.zw, edgeIndex % 2); 142 float2 centerUv = lerp(centerVector.xy, centerVector.zw, index % 2); 143 float2 uvOffset = i.tileUv * _TileSize; 144 fixed4 edgeColor = tex2D(_MainTex, edgeUv + uvOffset); 145 fixed4 centerColor = tex2D(_MainTex, centerUv + uvOffset); 146 147 // 範囲中心でcenterColorが100%、境界部分でcenterColorとedgeColorが 148 // 50%混合になるように2つの色を混ぜ合わせる 149 return lerp(centerColor, edgeColor, edgeFactor * 0.5); 150 } 151 ENDCG 152 } 153 } 154}

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

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

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

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

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

guest

回答1

0

ベストアンサー

ちらつきの出かたはどのような感じでしょうか?
たとえば、以前のご質問(Unity テクスチャ 反復周期)のようにテクスチャサンプリング座標を反復させている場合、下図のように反復の境界でちらつきが発生する可能性があります。

図1

これは、テクスチャのサンプリング座標が境界部分で不連続になることによるミップマップレベル選択の失敗によるものかと思います。
このようなケースでは、以前の別のご質問(Unity2D テクスチャが潰れる)に例示しましたコード中でちょこっと言及したtex2Dgradを使ってみてはいかがでしょうか?

ShaderLab

1Shader "Unlit/Terrain" 2{ 3 Properties 4 { 5 _MainTex ("Texture", 2D) = "white" {} 6 [IntRange] _Magnification ("Magnification", Range(1, 32)) = 8 7 } 8 SubShader 9 { 10 Tags { "Queue"="Transparent" "RenderType"="Transparent" } 11 LOD 100 12 13 Pass 14 { 15 ZWrite Off 16 ZTest Always 17 Blend SrcAlpha OneMinusSrcAlpha 18 19 CGPROGRAM 20 #pragma vertex vert 21 #pragma fragment frag 22 #pragma multi_compile_instancing 23 // make fog work 24 #pragma multi_compile_fog 25 26 #include "UnityCG.cginc" 27 28 struct appdata 29 { 30 float4 vertex : POSITION; 31 float2 uv : TEXCOORD0; 32 UNITY_VERTEX_INPUT_INSTANCE_ID 33 }; 34 35 struct v2f 36 { 37 float2 uv : TEXCOORD0; 38 UNITY_FOG_COORDS(1) 39 float4 vertex : SV_POSITION; 40 }; 41 42 sampler2D _MainTex; 43 float4 _MainTex_ST; 44 int _Magnification; 45 46 v2f vert (appdata v) 47 { 48 v2f o; 49 UNITY_SETUP_INSTANCE_ID(v) 50 o.vertex = UnityObjectToClipPos(v.vertex); 51 float3 worldPosition = mul(unity_ObjectToWorld, v.vertex); 52 float2 blockCoord = worldPosition.xy / _Magnification; 53 o.uv = TRANSFORM_TEX(blockCoord, _MainTex); 54 UNITY_TRANSFER_FOG(o,o.vertex); 55 return o; 56 } 57 58 fixed4 frag (v2f i) : SV_Target 59 { 60 float2 uv = frac(i.uv); 61 62 // 画面上で1ピクセルずれると、サンプリング位置がどれだけずれるかを求める 63 // uvはブロック境界で不連続になるのに対し、i.uvは連続しているので 64 // i.uvの変化率をテクスチャサンプリング位置変化率として採用してやれば 65 // ミップマップレベルが正しく選択される 66 float2 duvdx = ddx(i.uv); 67 float2 duvdy = ddy(i.uv); 68 69 // sample the texture 70 // tex2Dgradを使ってサンプリングする 71 fixed4 col = tex2Dgrad(_MainTex, uv, duvdx, duvdy); 72 73 // apply fog 74 UNITY_APPLY_FOG(i.fogCoord, col); 75 return col; 76 } 77 ENDCG 78 } 79 } 80}

図2

追記
Unlit/Worldの場合は何を手がかりにサンプリング座標の変化率を算出するべきかですが、シェーダーコード以外のスクリプトが「Unity2D テクスチャブレンド」のものとほとんど変わらないと仮定しますと、好都合なことにシェーダーコード内のmodelPosが利用できそうです。
これはメッシュが持っている頂点座標ですが、メッシュは縦横1.0の正方形が並んだ形に作られていて、この正方形内ぴったりにタイルの画像を貼り付ける形になっています。ですのでmodelPosの変化率をもとにサンプリング位置の変化率を算出できるはずです。

ですがUnlit/Worldのケースだともっと影響の大きい要因として、元のテクスチャがそれぞれの地形を並べて一枚にしている都合上隣のタイルの色が漏れて格子模様(元のテクスチャは地形を横に並べているため色漏れは横方向にだけ発生するので、「縦縞模様」と言った方が適切かもしれません)が生じてしまう現象があるかと思います。
ですのでUnity2D テクスチャが潰れるのシェーダーコードと同様に、タイルの外周部分をサンプリング座標の飛び幅分だけそぎ落とす処置も組み合わせてみました。変更部分はfragの後半だけです。

ShaderLab

1 fixed4 frag(v2f i) : SV_Target 2 { 3 // まずは前回同様weightを計算する 4 float f = dot(float2(_EdgeDist, 1.0 - _EdgeDist), float2(fractal(i.modelPos * _EdgeDistFreq), 0.5)); 5 float upper = 1.0 - 2.0 * (1.0 - i.weight) * (1.0 - f); 6 float lower = 2.0 * i.weight * f; 7 float weight = lerp(lower, upper, step(0.5, i.weight)); 8 9 // weightを比較してどの範囲にいるか判定し、それを越えない最大のインデックスを調べる 10 uint index = 0; 11 [unroll(TYPE_COUNT_MAX)] 12 for (int k = 0; k < _TypeCount; k++) 13 { 14 if (_TypeLevels[k] < weight) 15 { 16 index = k + 1; 17 continue; 18 } 19 break; 20 } 21 index = min(index, _TypeCount - 1); 22 23 // さらに、その一つ下・一つ上のインデックスを求めておく 24 uint prevIndex = index > 0 ? index - 1 : 0; 25 uint nextIndex = min(index + 1, _TypeCount - 1); 26 27 // 範囲の下限・上限の高さを取得し、lowerLevelを0、upperLevelを1としたときのweightの位置を調べる 28 float lowerLevel = (index == prevIndex) ? 0.0 : _TypeLevels[prevIndex]; 29 float upperLevel = (index == nextIndex) ? 1.0 : _TypeLevels[index]; 30 float weightInRange = (lowerLevel == upperLevel) ? 0.0 : (weight - lowerLevel) / (upperLevel - lowerLevel); 31 32 // weightInRangeが0.5以上かどうかでnextIndex、prevIndexのいずれかを選択する 33 uint edgeIndex = weightInRange >= 0.5 ? nextIndex : prevIndex; 34 35 // weightInRangeが0.5の時0、そこより範囲境界に近いほど1になるような値を作る 36 // このとき_EdgeSharpness乗して、カーブを適宜鋭くする 37 float edgeFactor = saturate(pow(abs(weightInRange * 2.0 - 1.0), _EdgeSharpness)); 38 39 // 範囲中心および範囲境界に対応するUVを取得し、そこからそれぞれ色を取得する 40 float4 edgeVector = _TypeUvs[edgeIndex / 2]; 41 float4 centerVector = _TypeUvs[index / 2]; 42 float2 edgeUv = lerp(edgeVector.xy, edgeVector.zw, edgeIndex % 2); 43 float2 centerUv = lerp(centerVector.xy, centerVector.zw, index % 2); 44 45 // 色取得の前に、modelPos変化率とUV変化率を算出する 46 float2 dPosdx = ddx(i.modelPos); 47 float2 dPosdy = ddy(i.modelPos); 48 float2 dUvdx = dPosdx * _TileSize; 49 float2 dUvdy = dPosdy * _TileSize; 50 51 // 色漏れ防止のため、modelPos変化率の分だけtileUvの外周を 52 // そぎ落としてから_TileSizeをかけてuvOffsetとする 53 float2 cornerOffset = abs(dPosdx) + abs(dPosdy); 54 float2 uvOffset = lerp(cornerOffset, 1.0 - cornerOffset, i.tileUv) * _TileSize; 55 56 // tex2Dの代わりにtex2Dgradを使って色を取得する 57 // このとき、第3・第4引数としてUV変化率を使用する 58 fixed4 edgeColor = tex2Dgrad(_MainTex, edgeUv + uvOffset, dUvdx, dUvdy); 59 fixed4 centerColor = tex2Dgrad(_MainTex, centerUv + uvOffset, dUvdx, dUvdy); 60 61 // 範囲中心でcenterColorが100%、境界部分でcenterColorとedgeColorが 62 // 50%混合になるように2つの色を混ぜ合わせる 63 return lerp(centerColor, edgeColor, edgeFactor * 0.5); 64 }

テクスチャの設定はGenerate Mip MapsをオンにしてFilter ModeをTrilinearとした状態で、対策なしだと下図のように縦スジが目立ってしまいますが...

図3

対策ありだと下図のようにマシになりました。

図4

投稿2019/12/06 21:42

編集2019/12/07 04:18
Bongo

総合スコア10811

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

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

退会済みユーザー

退会済みユーザー

2019/12/06 22:07

回答ありがとうございます。 質問なのですが、fixed4 frag (v2f i) : SV_Targetが既に定義されている場合はどのように書けばよいのでしょうか? 調べたのですが、複数Passを使うなどしか出て来ませんでした。複数のfragを設定することは出来ないのでしょうか?
Bongo

2019/12/06 22:15

各パスで最終的な色を出力する関数は1つだけしか選べないと思います。 複数のfragを作りたい...というのはどういう状況なのかちょっと想像しにくいのですが、作りかけの状態でかまいませんのでシェーダーコードをご提示いただければ何か代替案のアドバイスをお出しできるかもしれません(今回のご質問と直接関係しない内容でしたら、新規のご質問として投稿いただいたほうがいいかもしれませんね)。
退会済みユーザー

退会済みユーザー

2019/12/06 22:30

シェーダーのコードを追記しました。前の質問で回答していただいたテクスチャブレンドのコードを使用しています。
Bongo

2019/12/06 22:54

複数のfragを使いたいとおっしゃるのはどういう理由なのか(何らかの条件でfragを切り替えたい...とか)ご説明いただけますでしょうか? シェーダーコード内にコメントとして「ここの条件が○○のときはこちらのfrag、そうでなければこちらのfrag」のような具合で追記いただいてもかまいません。 それとも、このUnlit/Worldシェーダーに今回の回答で例示しましたちらつき対策を組み込みたい...ということでしょうか?
退会済みユーザー

退会済みユーザー

2019/12/06 22:57

説明不足で申し訳ないです。 >このUnlit/Worldシェーダーに今回の回答で例示しましたちらつき対策を組み込みたい...ということでしょうか? そうです。Worldシェーダーに例示していただいたコードを組み込みたいと思ったのですが、どのように書いたらいいか分からなかったので…。
Bongo

2019/12/07 04:19

Unlit/Worldの場合だと、境界のちらつきを防ぐには隣のタイルからの色漏れを防止する処置も必要かと思います。改造案を追記しましたが、いかがでしょうか?
退会済みユーザー

退会済みユーザー

2019/12/07 06:36

回答ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問