🐍

【TouchDesigner】GLSLTOPで動的に変化する画像UIを整列させる

2023/07/19に公開

はじめに

弊社では以下のようなTouchDesignerでサイネージを使った体験型コンテンツを作る仕事が度々あります。
https://youtu.be/_9a6Yl1z2vA
元々グラフィック広告を強みとする会社のためデザイナーがたくさん在籍しており、UIにも勿論こだわって作られています。
デザインにおいてこだわるポイントは様々ありますが、その中でもフォントは一つの重要な要素です。
TouchDesignerでもフォントデータを読み込んで使用することが可能です。(※組み込みのフォントもたくさんあります。)
ただし外部フォントを使用する際ほとんどのフォントはライセンスでゲームやアプリなどシステムフォントとして組み込むことは許可されていませんので画像として書き出して使用するしかありません。

課題

TouchDesignerでテキストが画像になると何が困るかというと整列ができません。
※TOPではできませんがCOMPではできます。
LayoutTOPというノードがありますが、画像の縦横比が同じでないと以下の画像のようになります。
Fit Horizontalで並べると高さにばらつきが出てしまい文字の画像であれば尚更良くありません。
ちなみにLayoutTOPのResolutionは一つ目の入力に合わせられますので3枚並べた時一枚一枚の解像度が約3分の1に縮められてしまいます。

かといってFit Verticalで並べると縦は合いますが重なってしまいます。

もしノードで組むならCropTOPを使って並べたい画像の縦横比を計算して、左右に間隔を空けてOverTOPなどで重ねて並べる手法になると思いますが、画像が多くなるとノードだらけになり仕様変更などあった際メンテナンス性があまり良くありません。
今回はGLSLTOPを使いコードで仕様変更などに対応しやすいよう作りました。
完成は以下の動画の感じです。
https://twitter.com/usbhatyu/status/1681575988474568704

エディター

TouchDesignerのエディターは以下のようになっています。
左のImageA,ImageB,ImageCは合成したい画像達です。
Aは魚の画像固定です。
Bは調理方法の画像、Cは料理の画像をSwitchTOPで切り替えられるようになっています。
各画像widthはバラバラですがheightは200px固定です。(ここ大事!)
GLSLMultiTOPも上述のLayoutTOPと同じくResolutionは一つ目の入力に合わせられますので、最終解像度を指定してる紫のresolutionという名前のConstantTOPが最初に繋がっています。

次に他のノードを見る前にGLSLMultiTOPのUniform変数を見てみましょう。
これを見れば繋がりは大体わかると思います。

  • uImageWidths
    A,B,Cの画像のwidthが入ってきます。
  • uImageLength
    合成したい画像の数です。
    settingというConstantCHOPの値が入ってきます。
  • uResolution
    最終解像度を指定してる紫色のConstantTOPのResolutionが入ってきます。
  • uAlignTypeNum
    右揃え、中央揃え、左揃えの分岐用の値です。
    settingというConstantCHOPの値が入ってきます。

コード

GLSLMultiTOPの中身は以下のようになっています。
流れとしてはコメント通りなのでそこまで説明するものはないのですが、Uniform変数に入ってきた3枚の画像のwidthを元に比率の計算をしてUVをずらしてるイメージです。

precision highp float;
uniform vec3 uImageWidths;
uniform float uImageLength;
uniform vec2 uResolution;
uniform float uAlignTypeNum;

out vec4 fragColor;

void main()
{
	vec2 uv = vUV.st;
	
	// 画像全体の横解像度と三つの画像の横解像度の和で割って比率を計算
	float allRatio = (uImageWidths.x+uImageWidths.y+uImageWidths.z)/uResolution.x;

	if(uAlignTypeNum == 0.0){
		// 左揃え(そのまま)
		uv.s = uv.s;
	}else if(uAlignTypeNum == 1.0){
		// 中央揃え
		uv.s -= (1.0-allRatio)*0.5;
	}else if(uAlignTypeNum == 2.0){
		// 右揃え
		uv.s -= (1.0-allRatio);
	}
	
	// 全体の横幅を入力画像の数で割って一つの画像の横幅を計算
	float imageWidth = uResolution.x/uImageLength;
	// 全体の横幅を入力画像の数で割った解像度と元の横解像度で割って比率を計算
	float imageARatio = imageWidth/uImageWidths.x;
	float imageBRatio = imageWidth/uImageWidths.y;
	float imageCRatio = imageWidth/uImageWidths.z;
	// UV.sを入力画像数分倍にし、上記で計算した各画像の比率を掛けてもとの比率に戻す
	float imageAS = (uv.s*uImageLength*imageARatio);
	float imageBS = (uv.s*uImageLength*imageBRatio);
	float imageCS = (uv.s*uImageLength*imageCRatio);
	// 画像の比率分それぞれUVをずらして画像を配置
	vec4 imageAColor = texture(sTD2DInputs[1], vec2(imageAS,vUV.t));
	vec4 imageBColor = texture(sTD2DInputs[2], vec2(imageBS-(imageBRatio/imageARatio),vUV.t));
	vec4 imageCColor = texture(sTD2DInputs[3], vec2(imageCS-(imageCRatio/imageARatio)-(imageCRatio/imageBRatio),vUV.t));
	// 各画像を合成
	vec4 color = imageAColor + imageBColor + imageCColor;
	// 出力
	fragColor = TDOutputSwizzle(color);
}

まとめ

以上TouchDesignerならノードで簡単にできそうなのにできなかった事をGLSLで実装してみました。
ちなみに縦組みも同じ仕組みで基準を変えるだけでできます。
今後は記事はZennに書いていこうと思いますのでよろしくお願いします🙌

Discussion