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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C#

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

Unity

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

Q&A

解決済

1回答

2482閲覧

Unity 変換行列からPositionを取り出す

退会済みユーザー

退会済みユーザー

総合スコア0

C#

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

Unity

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

0グッド

0クリップ

投稿2020/08/15 05:12

編集2020/08/16 02:53

前提・実現したいこと

変換行列からPositionを取り出したい

該当のソースコード

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4using System.Linq; 5 6public class Example : MonoBehaviour 7{ 8 Matrix4x4[][] matrix4X4s; 9 Matrix4x4[] flatMatrix; 10 11 public Material material; 12 13 HashSet<Matrix4x4> matriHash = new HashSet<Matrix4x4>(); 14 15 private void Start() 16 { 17 int seed = Random.Range(-999999, 999999); 18 19 float[,] noiseMap = Noise.GenerateNoiseMap(200, 200, seed, 50, 4, 0.5f, 2, Vector2.zero); 20 21 int rows = 200; 22 int columns = 200; 23 24 flatMatrix = new Matrix4x4[rows * columns]; 25 26 for (int x = 0; x < rows; x++) 27 { 28 for (int y = 0; y < columns; y++) 29 { 30 float height = noiseMap[y, x]; 31 32 int index = (x * columns) + y; 33 34 Vector2 pos = new Vector2(x, y); 35 Quaternion rotation = Quaternion.identity; 36 Vector3 scale = Vector3.one; 37 38 if (height < 0.35f) 39 { 40 flatMatrix[index] = Matrix4x4.TRS(pos, rotation, scale); 41 matriHash.Add(flatMatrix[index]); 42 } 43 } 44 } 45 46 foreach (Matrix4x4 hash in matriHash) 47 { 48 var position = new Vector3(hash[0, 3], hash[1, 3], hash[2, 3]); 49 Vector2 pos = new Vector2(position.x, position.y); 50 posHash.Add(pos); 51 } 52 } 53 54 Vector2 getScreenDownLeft() 55 { 56 Vector2 pos = new Vector2(0, 0); 57 Vector2 downLeft = Camera.main.ScreenToWorldPoint(pos); 58 return downLeft; 59 } 60 61 Vector2 getScreenUpRight() 62 { 63 Vector2 pos = new Vector2(Screen.width, Screen.height); 64 Vector2 upRight = Camera.main.ScreenToWorldPoint(pos); 65 return upRight; 66 } 67 68 HashSet<Vector2> getScreenPos() 69 { 70 HashSet<Vector2> hash = new HashSet<Vector2>(); 71 72 for (int x = (int)getScreenDownLeft().x - 5; x < (int)getScreenUpRight().x + 5; x++) 73 { 74 for (int y = (int)getScreenDownLeft().y - 5; y < (int)getScreenUpRight().y + 5; y++) 75 { 76 Vector2 pos = new Vector2(x, y); 77 hash.Add(pos); 78 } 79 } 80 81 return hash; 82 } 83 84 Matrix4x4[] array = new Matrix4x4[1023]; 85 86 HashSet<Matrix4x4> hashMat = new HashSet<Matrix4x4>(); 87 88 HashSet<Vector2> posHash = new HashSet<Vector2>(); 89 90 private void Update() 91 { 92 foreach (Vector2 item in getScreenPos()) 93 { 94 GetMatrix(item); 95 } 96 97 foreach (var m in matrix4X4s) 98 { 99 Graphics.DrawMeshInstanced(MeshGenerator.basicMesh, 0, material, m); 100 } 101 } 102 103 Matrix4x4[][] GetMatrix(Vector2 pos) 104 { 105 if (posHash.Contains(pos)) 106 { 107 foreach(Matrix4x4 item in matriHash) 108 { 109 var position = new Vector2(item[0, 3], item[1, 3]); 110 Vector2 matriPos = new Vector2(position.x, position.y); 111 if(pos == matriPos) 112 { 113 hashMat.Add(item); 114 } 115 } 116 } 117 118 array = hashMat.ToArray(); 119 120 matrix4X4s = array.Select((m, i) => (m, i / 1023)) 121 .GroupBy(t => t.Item2) 122 .Select(g => g.Select(t => t.Item1).ToArray()).ToArray(); 123 124 return matrix4X4s; 125 } 126} 127

C#

1using UnityEngine; 2using System.Collections; 3 4public static class Noise 5{ 6 7 public static float[,] GenerateNoiseMap(int mapWidth, int mapHeight, int seed, float scale, int octaves, float persistance, float lacunarity, Vector2 offset) 8 { 9 float[,] noiseMap = new float[mapWidth, mapHeight]; 10 11 System.Random prng = new System.Random(seed); 12 Vector2[] octaveOffsets = new Vector2[octaves]; 13 for (int i = 0; i < octaves; i++) 14 { 15 float offsetX = prng.Next(-1000000, 1000000) + offset.x; 16 float offsetY = prng.Next(-1000000, 1000000) + offset.y; 17 octaveOffsets[i] = new Vector2(offsetX, offsetY); 18 } 19 20 if (scale <= 0) 21 { 22 scale = 0.0001f; 23 } 24 25 float maxNoiseHeight = float.MinValue; 26 float minNoiseHeight = float.MaxValue; 27 28 float halfWidth = mapWidth / 2f; 29 float halfHeight = mapHeight / 2f; 30 31 32 for (int y = 0; y < mapHeight; y++) 33 { 34 for (int x = 0; x < mapWidth; x++) 35 { 36 37 float amplitude = 1; 38 float frequency = 1; 39 float noiseHeight = 0; 40 41 for (int i = 0; i < octaves; i++) 42 { 43 float sampleX = (x - halfWidth) / scale * frequency + octaveOffsets[i].x; 44 float sampleY = (y - halfHeight) / scale * frequency + octaveOffsets[i].y; 45 46 float perlinValue = Mathf.PerlinNoise(sampleX, sampleY) * 2 - 1; 47 noiseHeight += perlinValue * amplitude; 48 49 amplitude *= persistance; 50 frequency *= lacunarity; 51 } 52 53 if (noiseHeight > maxNoiseHeight) 54 { 55 maxNoiseHeight = noiseHeight; 56 } 57 else if (noiseHeight < minNoiseHeight) 58 { 59 minNoiseHeight = noiseHeight; 60 } 61 noiseMap[x, y] = noiseHeight; 62 } 63 } 64 65 for (int y = 0; y < mapHeight; y++) 66 { 67 for (int x = 0; x < mapWidth; x++) 68 { 69 noiseMap[x, y] = Mathf.InverseLerp(minNoiseHeight, maxNoiseHeight, noiseMap[x, y]); 70 } 71 } 72 73 return noiseMap; 74 } 75 76}

試したこと

カメラに映っているメッシュだけをGraphics.DrawMeshInstancedを使って描画したかったのですが、カメラに映っているワールド座標と、変換行列に代入されている座標を一致しているか確認する方法が、分からなかったため、質問しました。

【追記】

回答者様の回答をもとに、スクリプトを変更しました。
ですが、カメラに映っているメッシュだけを描画はしているのですが、0.1~0.3FPSしか出ません。
Graphics.DrawMeshのほうが軽いくらいです……。どのように書き換えたらよいでしょうか?

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

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

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

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

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

guest

回答1

0

ベストアンサー

UnityEngine.Matrix4x4 - Unity スクリプトリファレンスによると...

Matrices in Unity are column major; i.e. the position of a transformation matrix is in the last column, and the first three columns contain x, y, and z-axes.

Unityの行列は列優先です。すなわち変換行列の「位置」は最終列にあり、最初の3列が「X軸」「Y軸」「Z軸」です。

とのことです。そしてその下のサンプルコードでは...

C#

1using UnityEngine; 2 3public class ExampleScript : MonoBehaviour 4{ 5 void Start() 6 { 7 // get matrix from the Transform 8 var matrix = transform.localToWorldMatrix; 9 // get position from the last column 10 var position = new Vector3(matrix[0,3], matrix[1,3], matrix[2,3]); 11 Debug.Log("Transform position from matrix is: " + position); 12 } 13}

といった風に、変換行列から平行移動成分を取り出している例が載っていました。

やたら遅い件について追記

カメラ範囲内のタイルを抜き出す処理がうまくいってないんじゃないかと思います。Updateの...

C#

1foreach (Vector2 item in this.getScreenPos()) 2{ 3 this.GetMatrix(item); 4}

の部分がいまいち意図不明ですね...
不必要そうな部分をばっさりカットして下記のようにしてみました。rowscolumnsStartUpdateの両方で使用する形になったのでローカル変数からフィールドに変更しました。flatMatrixを作るときのxyの使い方も「横方向がx、縦方向がy」に統一した方が自然だろうと思って変更しています。

C#

1using UnityEngine; 2 3public class Example : MonoBehaviour 4{ 5 public Material material; 6 public int rows = 200; 7 public int columns = 200; 8 9 Matrix4x4[] flatMatrix; 10 readonly Matrix4x4[] array = new Matrix4x4[1023]; 11 12 void Start() 13 { 14 int seed = Random.Range(-999999, 999999); 15 float[,] noiseMap = Noise.GenerateNoiseMap(columns, rows, seed, 50, 4, 0.5f, 2, Vector2.zero); 16 flatMatrix = new Matrix4x4[rows * columns]; 17 for (int y = 0; y < rows; y++) 18 { 19 for (int x = 0; x < columns; x++) 20 { 21 float height = noiseMap[x, y]; 22 int index = (y * columns) + x; 23 Vector2 pos = new Vector2(x, y); 24 Quaternion rotation = Quaternion.identity; 25 Vector3 scale = Vector3.one; 26 if (height < 0.35f) 27 { 28 flatMatrix[index] = Matrix4x4.TRS(pos, rotation, scale); 29 } 30 } 31 } 32 } 33 34 Vector2 GetScreenDownLeft() 35 { 36 Vector2 pos = new Vector2(0, 0); 37 Vector2 downLeft = Camera.main.ScreenToWorldPoint(pos); 38 return downLeft; 39 } 40 41 Vector2 GetScreenUpRight() 42 { 43 Vector2 pos = new Vector2(Screen.width, Screen.height); 44 Vector2 upRight = Camera.main.ScreenToWorldPoint(pos); 45 return upRight; 46 } 47 48 void Update() 49 { 50 // まずスクリーン左下・右上座標を求めて... 51 Vector2 downLeft = GetScreenDownLeft(); 52 Vector2 upRight = GetScreenUpRight(); 53 54 // 余白分だけ範囲を拡張し... 55 Vector2 margin = new Vector2(5, 5); 56 downLeft -= margin; 57 upRight += margin; 58 59 // 整数化し... 60 Vector2Int downLeftInt = new Vector2Int(Mathf.RoundToInt(downLeft.x), Mathf.RoundToInt(downLeft.y)); 61 Vector2Int upRightInt = new Vector2Int(Mathf.RoundToInt(upRight.x), Mathf.RoundToInt(upRight.y)); 62 63 // 200x200の範囲をはみ出さないようにクランプする 64 downLeftInt = Vector2Int.Min( 65 Vector2Int.Max(downLeftInt, Vector2Int.zero), 66 new Vector2Int(columns - 1, rows - 1)); 67 upRightInt = Vector2Int.Min( 68 Vector2Int.Max(upRightInt, Vector2Int.zero), 69 new Vector2Int(columns - 1, rows - 1)); 70 71 // 右上から左下を引き、描画領域のサイズとする 72 Vector2Int areaSize = upRightInt - downLeftInt; 73 74 // 描画するべきタイルの総数は、描画領域の幅×高さとなる 75 int tileCount = areaSize.x * areaSize.y; 76 77 // 描画済みタイル数を記録する変数を用意する 78 int drawnTileCount = 0; 79 80 // タイルを描画していく 81 while (drawnTileCount < tileCount) 82 { 83 // まず、今回のループで描画するべきタイル数を求める 84 // 未描画のタイル数は「タイル総数 - 描画済みタイル数」であり 85 // さらにそれを1023個を超えないようにクランプする 86 int tileCountToDraw = Mathf.Min(tileCount - drawnTileCount, 1023); 87 88 // 描画したい領域をflatMatrixからarrayへコピーしていく 89 // このとき、連続している部分はなるべく丸ごとコピーしてやった方が効率的だと思われる 90 // そこで、まずコピー済みタイル数を記録する変数を用意する 91 int transferredTileCount = 0; 92 93 // そして横方向オフセットを表す変数を用意する 94 // 初期値は描画済みタイル数を領域幅で割った余りとなる 95 int xOffset = drawnTileCount % areaSize.x; 96 while (transferredTileCount < tileCountToDraw) 97 { 98 // 今回のループでコピーするべきタイル数を求める 99 // まず、未コピーのタイル数を超えてはならず... 100 int tileCountToTransfer = tileCountToDraw - transferredTileCount; 101 102 // 現在の横オフセットから描画領域右端までのタイル数を超えてはならない 103 tileCountToTransfer = Mathf.Min(tileCountToTransfer, areaSize.x - xOffset); 104 105 // 描画領域座標系におけるコピー開始点の位置はこうなり... 106 int localTileIndex = drawnTileCount + transferredTileCount; 107 Vector2Int localTilePosition = new Vector2Int(localTileIndex % areaSize.x, localTileIndex / areaSize.x); 108 109 // それに描画領域の左下隅を足せばワールド座標系における位置となり... 110 Vector2Int worldTilePosition = localTilePosition + downLeftInt; 111 112 // flatMatrixのインデックスに直すとこうなる 113 int worldTileIndex = (worldTilePosition.y * columns) + worldTilePosition.x; 114 115 // flatMatrixのworldTileIndex番から数えてtileCountToTransfer個のデータを 116 // arrayのtransferredTileCount番以降にコピーする 117 System.Array.Copy(flatMatrix, worldTileIndex, array, transferredTileCount, tileCountToTransfer); 118 119 // 横オフセットを更新する 120 // とはいえ、2回目以降のループでは常に0となるはず 121 // 末尾では0でない可能性があるが、そもそもループが終了するので考慮不要 122 xOffset = 0; 123 124 // コピー済みタイル数を更新する 125 transferredTileCount += tileCountToTransfer; 126 } 127 128 // タイルをtileCountToDraw個描画する 129 Graphics.DrawMeshInstanced(MeshGenerator.basicMesh, 0, material, array, tileCountToDraw); 130 131 // 描画済みタイル数を更新する 132 drawnTileCount += tileCountToDraw; 133 } 134 } 135}

また、MeshGeneratorはどのような作りになっているでしょうか?
もしbasicMeshが「アクセスするたびに新規Meshを生成して返すプロパティ」みたいな作りだったとすると、ここもスピードダウンの原因になりそうです。
さしあたり私の場合は下記のように「アクセスされたときにMeshが未生成なら生成し、それ以降は生成済みのMeshオブジェクトを返すプロパティ」としてみました。もしご自身で作成されたMeshGeneratorが速度低下を起こしそうか気になるようでしたら、コードをご提示いただければ何かアドバイスできるかもしれません。

C#

1using UnityEngine; 2 3public static class MeshGenerator 4{ 5 private static Mesh quadMesh; 6 7 public static Mesh basicMesh 8 { 9 get 10 { 11 if (quadMesh == null) 12 { 13 quadMesh = new Mesh 14 { 15 name = "Quad", 16 vertices = new[] 17 { 18 new Vector3(-0.5f, -0.5f, 0), 19 new Vector3(-0.5f, 0.5f, 0), 20 new Vector3(0.5f, -0.5f, 0), 21 new Vector3(0.5f, 0.5f, 0), 22 }, 23 uv = new[] 24 { 25 new Vector2(0, 0), 26 new Vector2(0, 1), 27 new Vector2(1, 0), 28 new Vector2(1, 1), 29 }, 30 triangles = new[] 31 { 32 0, 1, 2, 33 3, 2, 1, 34 } 35 }; 36 37 quadMesh.RecalculateNormals(); 38 quadMesh.RecalculateBounds(); 39 quadMesh.UploadMeshData(true); 40 } 41 42 return quadMesh; 43 } 44 } 45}

図

投稿2020/08/15 16:10

編集2020/08/16 09:48
Bongo

総合スコア10807

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

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

退会済みユーザー

退会済みユーザー

2020/08/16 12:45

一行一行、説明まで書いていただき、ありがとうございます! とても参考になります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問