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

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

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

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

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

HLSL

HLSLは、米マイクロソフト社によって開発された Direct3D APIで使われるプロプライエタリなシェーディング言語です。

Q&A

解決済

1回答

1042閲覧

UnityのShaderで1枚のテクスチャから部分的に切り出し出力テクスチャに任意の位置と大きさで複数のテクスチャを貼り付けたい

TwinTail

総合スコア1

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

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

HLSL

HLSLは、米マイクロソフト社によって開発された Direct3D APIで使われるプロプライエタリなシェーディング言語です。

0グッド

0クリップ

投稿2022/08/25 09:08

UnityのShaderで1枚のテクスチャからUVを指定して素材を切り出し動的にレイアウトを変更できるテクスチャを出力できるものを作ろうとしています。(最終的にはSkyBoxに使いたい)
イメージ説明

貼り付ける枚数が多いとコードが複雑になるかと思いますので2枚でも問題ありません。
1枚はこちらを参考にオフセット指定とクリッピングで実現できたのですが複数枚貼る方法がわかりませんでした。
https://teratail.com/questions/hds9mxsd75kydy
https://teratail.com/questions/flw1yal2i9rrz1

環境は以下になります。
Unity: 2021.3
OS: Windows 10

イメージ説明

(入力サンプル画像)

よろしくお願いします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

ちょっとやってみようかと思いまして、まず下記のようなシェーダーを用意しました。
方針としては、各パッチ(テクスチャから切り出して配置する個々の部分を、パッチワークになぞらえて「パッチ」と呼ぶことにしました)の配置先座標を配列として与えて、それに従ってマッピングする形で行くことにしました。

ShaderLab

1Shader "Unlit/Patches" 2{ 3 Properties 4 { 5 _MainTex ("Background Texture", 2D) = "white" {} 6 _Color ("Background Color", Color) = (0.15686274509, 0.50196078431, 1.0, 1.0) 7 [NoScaleOffset] _PatchTex ("Patch Texture", 2D) = "white" {} 8 } 9 SubShader 10 { 11 Tags { "Queue" = "Geometry" "RenderType" = "Opaque" } 12 13 Pass 14 { 15 CGPROGRAM 16 #pragma vertex vert 17 #pragma fragment frag 18 #include "UnityCG.cginc" 19 20 #define MAX_PATCH_COUNT_X 4 21 #define MAX_PATCH_COUNT_Y 4 22 23 struct appdata 24 { 25 float4 vertex : POSITION; 26 float2 uv : TEXCOORD0; 27 }; 28 29 struct v2f 30 { 31 float2 uv : TEXCOORD0; 32 float4 vertex : SV_POSITION; 33 }; 34 35 sampler2D _MainTex; 36 float4 _MainTex_ST; 37 half4 _Color; 38 sampler2D _PatchTex; 39 float4 _Bounds[MAX_PATCH_COUNT_X * MAX_PATCH_COUNT_Y]; 40 int2 _PatchCount; 41 42 v2f vert(appdata v) 43 { 44 v2f o; 45 o.vertex = UnityObjectToClipPos(v.vertex); 46 o.uv = TRANSFORM_TEX(v.uv, _MainTex); 47 return o; 48 } 49 50 half4 frag(v2f i) : SV_Target 51 { 52 // まず背景の色を決めて... 53 half4 color = tex2D(_MainTex, i.uv) * _Color; 54 55 // その上に各パッチを重ね描きしていく 56 [unroll] 57 for (int y = 0; y < _PatchCount.y; y++) 58 { 59 [unroll] 60 for (int x = 0; x < _PatchCount.x; x++) 61 { 62 // boundsのxyがパッチの左下のUV座標、zwが右上のUV座標を表すことにした 63 float4 bounds = _Bounds[y * MAX_PATCH_COUNT_X + x]; 64 65 // 面積を持たないパッチはスキップする 66 float2 size = bounds.zw - bounds.xy; 67 if (size.x * size.y <= 0.0) 68 { 69 continue; 70 } 71 72 // i.uvからパッチの左下座標を引いて原点をパッチ基準にしてやり、さらにパッチのサイズで割ることで 73 // パッチの左下が(0.0, 0.0)、右上が(1.0, 1.0)となるような座標を作る 74 float2 coord = (i.uv - bounds.xy) / size; 75 76 // _PatchTexから色を取得して... 77 half4 patchColor = tex2D(_PatchTex, (coord + float2(x, _PatchCount.y - y - 1)) / _PatchCount); 78 79 // パッチ範囲外の領域は透明に書き換える 80 patchColor.a *= 1.0 - any(abs(coord) - saturate(coord) > 0.0); 81 82 // 出来上がった色を下地の色に対してアルファブレンディングする 83 color.rgb = patchColor.rgb * patchColor.a + color.rgb * (1.0 - patchColor.a); 84 } 85 } 86 87 return color; 88 } 89 ENDCG 90 } 91 } 92}

上記のシェーダーを使用したマテリアルを作って適当なオブジェクトにセットし、さらにマテリアルにデータを送り込むための下記スクリプトをそのオブジェクトにアタッチしました。

C#

1using System; 2using UnityEngine; 3 4[RequireComponent(typeof(Renderer))] 5[ExecuteAlways] 6public class Patches : MonoBehaviour 7{ 8 public static readonly Vector2Int MaxPatchCount = new Vector2Int(4, 4); 9 private static readonly int BoundsProperty = Shader.PropertyToID("_Bounds"); 10 private static readonly int PatchCountProperty = Shader.PropertyToID("_PatchCount"); 11 [SerializeField] private Vector2Int patchCount = new Vector2Int(2, 2); 12 [SerializeField] private PatchBoundsRow[] bounds; 13 private Vector4[] flattenBounds; 14 private Vector2Int previousPatchCount; 15 private Material material; 16 17 private void Update() 18 { 19 // パッチの数は少なくとも1行1列とし、最大数もあまり大きくしないで 20 // せいぜい4行4列までとした 21 this.patchCount.Clamp(Vector2Int.one, MaxPatchCount); 22 23 if (this.patchCount != this.previousPatchCount) 24 { 25 // パッチの数が変わったら、それに合わせてパッチ範囲定義用配列のサイズも変更する 26 this.previousPatchCount = this.patchCount; 27 this.bounds ??= new PatchBoundsRow[this.patchCount.y]; 28 Array.Resize(ref this.bounds, this.patchCount.y); 29 for (var j = 0; j < this.patchCount.y; j++) 30 { 31 this.bounds![j].Bounds ??= new PatchBounds[this.patchCount.x]; 32 Array.Resize(ref this.bounds[j].Bounds, this.patchCount.x); 33 } 34 35 // パッチ範囲定義用配列はインスペクター上での見た目を優先して 36 // 入れ子の配列にしたが、マテリアルにデータを送る上では 37 // 一次元の固定長配列の方が好都合なので、別途用意した配列に 38 // データを書き写す 39 this.flattenBounds ??= new Vector4[MaxPatchCount.x * MaxPatchCount.y]; 40 for (var j = 0; j < this.patchCount.y; j++) 41 { 42 for (var i = 0; i < this.patchCount.x; i++) 43 { 44 var bl = this.bounds![j].Bounds![i].BottomLeft; 45 var tr = this.bounds![j].Bounds![i].TopRight; 46 this.flattenBounds[(j * MaxPatchCount.x) + i] = new Vector4(bl.x, bl.y, tr.x, tr.y); 47 } 48 } 49 } 50 51 if (this.material == null) 52 { 53 this.material = this.GetComponent<Renderer>()!.sharedMaterial; 54 } 55 56 // マテリアルにパッチ範囲データ、およびパッチ数を送る 57 this.material!.SetVectorArray(BoundsProperty, this.flattenBounds); 58 this.material!.SetVector(PatchCountProperty, new Vector4(this.patchCount.x, this.patchCount.y)); 59 } 60 61 private void Reset() 62 { 63 this.bounds = new PatchBoundsRow[this.patchCount.y]; 64 for (var j = 0; j < this.patchCount.y; j++) 65 { 66 this.bounds![j].Bounds = new PatchBounds[this.patchCount.x]; 67 } 68 69 this.bounds[0].Bounds![0].BottomLeft = new Vector2(0.1875f, 0.625f); 70 this.bounds[0].Bounds![0].TopRight = new Vector2(0.4375f, 0.875f); 71 this.bounds[0].Bounds![1].BottomLeft = new Vector2(0.5625f, 0.4375f); 72 this.bounds[0].Bounds![1].TopRight = new Vector2(0.875f, 0.75f); 73 this.bounds[1].Bounds![0].BottomLeft = new Vector2(0.3125f, 0.4375f); 74 this.bounds[1].Bounds![0].TopRight = new Vector2(0.375f, 0.5f); 75 this.bounds[1].Bounds![1].BottomLeft = new Vector2(0.0f, 0.0625f); 76 this.bounds[1].Bounds![1].TopRight = new Vector2(1.0f, 0.3125f); 77 } 78 79 [Serializable] 80 public struct PatchBounds 81 { 82 public Vector2 BottomLeft; 83 public Vector2 TopRight; 84 } 85 86 [Serializable] 87 public struct PatchBoundsRow 88 { 89 public PatchBounds[] Bounds; 90 } 91}

描画結果は下図のようになりました。

図

※念のため確認したいのですが、もしかして「テクスチャを出力」とおっしゃるのは出来上がった結果をテクスチャデータとして...つまりたとえば画像ファイルのような形で出力することを意図していらっしゃるのでしょうか?
ご提示いただいた別の方のご質問ではテクスチャデータの出力については話題に上がっていなかったため、「おそらくご質問者さんもレイアウト結果の画像を保存して後で利用できるようにしたいわけではなく、その都度レンダリングすることを想定していらっしゃるのだろう」と勝手に解釈して保存機能は省略してしまいましたが、もしそのような機能が必要でしたらコメントいただければ検討してみようと思います。

投稿2022/08/26 14:50

Bongo

総合スコア10807

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

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

TwinTail

2022/08/26 16:26

実装の確認が取れました。 ご回答いただきありがとうございました。 この半月、悩んでいた問題でしたので本当に感謝しております。 最終的な出力はSkyBoxで表示したいのですが素材がVideo映像の為、VideoPlayerのレンダーテクスチャに一旦描画したものを パッチテクスチャとして扱いバックグラウンドテクスチャをSkyBox用のレンダーテクスチャに展開してみました。 一瞬パッチ描画された動画がSkyboxとして表示されたのですが、Panoramicのシェーダーの設定を変更するとSkybox側が表示されなくなってしまいました。 もう少し探ってみたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問