jit.gl.model から読み込んだテクスチャのを取得し jit.gl.mesh / jit.gl.pbr で描画、そして決別・・・ #jitter #maxmsp

昨日の記事の続きです。まだ未読の方は是非ご覧ください。

jit.gl.modelにロードしたテクスチャマッピング済みのモデルからGPU上のUVマップ後のテクスチャを取得したいが方法が見つけられなくjit.gl.shaderから取得はできそうという内容でした。

その後、一晩寝てみたら落ち着いて、OpenGL対応の環境でテクスチャが取得できないなんてころあるんかと思い直し(諦めが悪い)

もう一度 jit.gl.modelのReferenceを見直すと gettexnames というメッセージを発見しました。

取得してみると

と .jpgの名前が取得できます。これは単にUVマップ前のテクスチャなのかなと思っ他のですが、先頭に “u514*****_” とついている事に気がつきました。おっこれはjit.gl.texture上に読み込まれた画像などにつく名前じゃないか?と

そこで jit_gl_texture + 先ほど取得したファイル名を組み合わせてjit.gl.pwindowに送ったところ見事にjit.gl.model内のテクスチャが取得できました。

しかしですね、複数グループにまたがるようなモデルをロードすると・・・描画されない

jit.gl.meshにはマテリアルが1つしかアタッチできないので描画が破綻してしまします。(昨日のパターン3の問題は解決できない)

ん・・・やはり厳しそうです。このリポジトリにあるglTFを色々と読み込んでみたのですが、3Dモデルって規格化されていないからテクスチャの順番だったり数だったりが全然揃ってないんですね。実は業務でUnityを使ったことが何度かあるのですが、やはり世界中で使われて鍛え上げられてるだけあって3D読み込みでこんな問題が起こったことは無く、もうこれはMaxでやろうとするのは厳しいと感じました・・・。

jit.gl(OpenGL)個人的総括

薄々は感じていましたが、UnityやUEのようにモデル読み込みを利用して3DCGのステージやオブジェクト・キャラクターなどをロードしてMV・VJ・アプリ等いじるには Jitterというかjit.glはあまりにも貧弱です。これでは3Dで何か制作する前に調整や調査で人生が終わってしまうような気がしました。

一ヶ月捧げてきたのも、とある敬愛する音楽アーティストさんのためにご協力したい思いから進めてきたのですが、時間がかかりすぎる・・・・もちろんプリミティブな3D描画・GPUの仕組みは以前より理解が進んだので集中講義だったと思えば無駄では無かったと思います。

ディスプレイに表示するまでとにかく行列演算

それでもMax自体はUDP送信や簡易MIDI/UIコントローラー、音楽関係、rnbo~やJitterで唯一イケてる jit.gl.pix(GLSLを出力できる)Node.js連携と良い部分も多いので使い続けると思います。今回はあくまで3Dでゴリゴリやるのは厳しいなと感じました。

jit.gl.mesh触り始めた時は何も分かってませんでした・・・
https://twitter.com/mirrorboy/status/1663814002814877702?s=20
jit.gl を調査して3週間の成果・・・ぶっちゃけUnityならジオメトリシェーダーの書き方を調べてDay1で出来そうではある

後学のために今回つくったコードは残しておきます。Jitterで3Dをやりたい物好きの方がいたら応援したいと思います。

code

----------begin_max5_patcher----------
2084.3ocyassajZCF95jmBKTuqoSwGA5Uock5KPq5MUUi7v3LC6x.HNjjsq1
285CLGxFXvvXXajxvBiI+966+n+s2ub+cdaxeUT4A9EveCt6tub+c2oej5A2
0d+cdG3uFmxqzCyKS7R9lO58f4qpEuVqebQYRV8wml0bHIKUTqeCX6CK30w6
Sx1stTDWajHKJXk+C.D0WcAiz2H+D7Om+Ck2Te7uje6SS1pEobZ7SPjm5Ye8
96Ue7fkXHN+vAw4o6IP7wj5U6RWcHeqHE7yfi2Jp1e9thMkWElOkmUmwOHz+
A+0MxuNOC7G7rJvukmt0qex.yzjPn5SlulKT7x.bgRbOwiEWNEtfeB8tXbUI
+qdbJIMAVSQ.EujWdFEcR.UeNKd.8NgpAIVegPL.GSVQ6ArnifnPeKVYtXdh
HiKo3KEm40p+bgvHK0zd8AdcYxqdO.77NIhKXI1TLh5wQnFrArYrNBDHTwAP
J0JGATeXcCOamBk5qcgTJYJPkWWW1jbUPoFhdrRpVTlvSWq7h7FxF.RYqnpK
9C.4dUucBSbzYIWJcFkSo0mMU7cm1NMmu0P5iSgSIDEtCH3UDLzmPQHe4yvH
oSe3Tohd053PGZf2jUvi+DvGTMZq7HsUNloU8A9S0JWmooOMOw09xfzwhSrO
1Xaq8iICgSrUdy8BXrCAbobNH.aaNTzIlQCqaMwzYQSU21ONmTldYt6J9Nw6
.5TfXqZEYbPwQtMhEzgpQUl5V2TB3wOJwJ.CPxek2k+zSUhZo6KFPAgi1E1j
fxWeAYT1WgCn1kT1965j6fP3bmUaG+vA9537REUjjm4csP6TUncFsMAmayrE
wbclswPC6EI61WutJlmJrfBfj4H4dD00Tfox1jrs4ubqk1xLg9HPcQsgsE5t
JfN1.gVX2ilTcqaZpqOa.O1BVB03BQN9oypQgD9+YsZnDpDpe6OnPenT4Fpy
uG5aVw13y0YgJlD4xrdbYrqmk0mO1LeAP5afOMfzSsqXm5oSc4ByJJEEhrsf
RAe6ns9CvWX825C3JTxftrGFhrmSJyyt76rtcLvPMJMo0M10H+wzNFFcJPYa
YdwSIohoNeQZkiY9RHNrlSFatqqnMB2UvX.88QdNZG5fRPU1MqeIYa8d0WwH
q5PqFvVnUS21hsKLgAOZDjrd0GUlHfh8404qqpa1ljuNM+o5064ooqQeZk30
SMlKMISDm2jUeoZej95gnnUnPBIH.GFEfBBPLYoLQl5eoSKX+tz0Jr1TJ50j
KHvy47Y0m9r70AOlj8TRVhbAdvIF+KhRVgoTVfOzG6GgBvx7.PBz4K1Kh7c2
w6HjQ5vJQgt1UKveUPzE+PocrvFe+umU6KkGOMSVKwUK0GpnIZ.aNVsCgtPQ
d9wd7ItV6.XAJjSPl8vHRcyT.tpaVcVcuq6jUxn6ikdgKp8jfXEDGrsz8AUH
ykU491HsfFJjHiWEFFr924aJShWugWI9PdZd4phyct8M4Lvis6WlEDQ7CunM
9jHG4E.cokvNY8+8z2GKL1YZi8Hz.VBgWMhee+1I18ce9vhMkfGU6QGP9ujK
.qSxHbPxfRz0eAwAtuYmyoCwOL5PcsM0nMNPjaWjGdRqYnQVcXyH1HV0VRcs
MesckqQsNvAV1ydY0TGZ29buKBz7m4e9C7Rcfl3yAZ.dOn938iKKWZFlNvfx
iiSapRxyVWl2rauJu7ZcF5zj3AdUwgjppjmE8MrtiK163t9zscPmlt5wchFu
b+p8GZCd5sH4qWVTGa4MDsx28sRfWntXuU3eIJ2xy3VXGZ1iAY8e56fNxUK.
10wA.56RtQ5QTNGLBD0t03TWRHn4mPpAof3TAuz4QqfgmNkHSO+iYp0I4f8l
cOIyFNJuUg8p96MGdRzT69vZJNCBil5Fwd0fQX+4mlt7jH8HuoNWV0RRL.Bd
baI+E8Qs.TKYircoht2V9ng1Su1UvgXyP0LtuDt87rsoBCWHyGVymRWMfHMj
olSdELz88wf3xx3504.M3orAYZakaKbC4d0p4j28nYSQjyhhl5wqVMX9XJT5
jsmsYmmWfSS0IlX3CSUj+bzDFm2Cl9Lv2ImKsYBFscd6AnKTuKkQD2tgyysV
9TA8Vzns1Ec5XcLJ36YiFUIw1IIghAMwQnvYwDG98F9CcbIgLygISFNKXFNS
EPrsDf9s0cK6aN135Is54ukUpxaJiOJ61N5HSEcZRrUTUmjw0GplyCByjChA
9mNY9QIIjMRhbZP4kaEk8qzsVzR9D3OjnMi4lDjZovx5.GPPTGHIL1B1DhdC
tcDapNzSCylLG.xH5hIIKDDzEFHAVHnHR2pM37KZRzbXwXkn05wf2KZzsIZa
LVON+tMIYSPl.jqjzPQYzghbuQjMhFylEiHjM7qSbRg1HIeWkuXvbhtvlgYi
e.yErGCtTdbzEy2lRrIrsKDDbgRDQWH4nNHoKiNhrXEKPVnpIwKUUIXaTRG0
j2ljXKE2Yi0.ZVRTYkngj2jH0QomwjEa8G13D.YK0Jc9lhubEcZ05FchGnME
2bjyuMIsXE2fsYwvThqjzfrmKBqfBVpjLHqbvbgdBsPYYzQCFxd.QbkjrJB7
MKIq0RvaURP3Bk5zHInERxMXBYgjPNQRXKjD9layo+HZwys2Q0EIXDz21k86
7RZrWzTm21Xq6XMx4n1FP6hf+VsdS36C+a13AdQwyhxp1gqEh2A9GyKOclM8
NjjYtUeh58JEOmbb7D8S3kw6SpEw5+iXn1AjWYDOyqlK4xrljV5TBOoH0api
dWIKZO3Z58949ud++AjHl8AM
-----------end_max5_patcher-----------

jit.gl.model のマテリアル・テクスチャマッピングについての調査報告 #jitter #maxmsp

3Dモデルを読み込むためのオブジェクト jit.gl.model のテクスチャマッピングについて調査したので書き残しておきます。上記は様々な苦労の果てに描画された至極単純なシェーディングの一枚・・・涙

jit.gl.modelの裏側では assimp

jit.gl.modelでglTFのpbrマテリアルモデルを読み込むとテクスチャ・マテリアルが反映された状態のモデルが読み込まれます。

ここでJitter大好きおじさん達からしたら、「えっ、jit.gl.materialもjit.gl.pbrも、ましてやjit.gl.textureでテクスチャ指定してないのに何なん?」ってなりますよね。私はなりました。

これに対しては Max8 の最近のアップデートに

と書いてありました。理由もよくわかりませんが、適応されるんだなと暗記します。モデル付属のテクスチャも自動で読んでくれています。

ちなみにjit.gl.modelはさまざまなモデルファイルに対応していますが、裏側ではassimpというC / C++ ライブラリで出来ているようなのです。glTFなど最近の3Dモデルも問題なく読み込めるようになったりするのはこのライブラリが進化しているからだと予想できます。adobeがsupportしてgithubもそこそこ活発なようなので今後も開発は続けられると予想されますね。

jit.gl.modelのマテリアル

jit.gl.modelを描画する際に jit.gl.meshと組み合わせたり、マテリアルやシェーディングを色々といじりたくなりますがこれがハイパー難解でした。色々あったのですが結論だけ用途に合わせていくつかのパターンで紹介します。

パターン1 : デフォルト pbr マテリアルを利用

@material_mode というアトリビュートがあるので、こちらを3:JMTL(Use Jitter Material)としておけば、自動設定されたCycling74オススメのpbr materialになります。単に3Dを表示させるだけならこれでいいでしょう。

ただし、現状(8.5.4)だと roughness / metalness などの jit.gl.pbr でいじれるようなアトリビュートは変更できません。モデルのテクスチャによって設定されています。

つまり色合いを調整したかったら3Dソフトに戻って変更してねという事です。私は3Dソフトについては素人なので具体的な方法は分かりません。

デフォルトpbrの問題は jit.gl.enviroment などを利用して環境マップを設定しないとめちゃ暗いなどの問題もあります。またMaxで3Dをやる薄い理由の一つでもある @outputmatrix 1 をした瞬間に利用できなくなり、デフォルトpbrを利用したオーディオリアクティブなメッシュなどを作り出すことはできなさそうです。(やり方見つけた方いたら教えてください)

パターン2 : jit.gl.shader (GLSL)マテリアルを利用

では自分好みのシェーディングをしたい場合はどうでしょうか

@material_mode 0~3 に設定しておきます。そうすると一旦テクスチャが反映されていない状態になります。

続いて jit.gl.shader に@name xxx と名前をつけ、jit.gl.model @shader xxx と指定します。この時 @materialで指定しないように注意して下さい。

具体的にどのような色を描画するかGLSLのシェーダーを書きます。サンプルプログラムを下の方に載せてあります。ポイントは ” <param name=”modelTexture” type=”int” default=”0″/> ” の部分で、modelTextureという変数のインデックスを変更する事で albedo / normal / roughness / metallic / ambient occlusion / height / emission /emvironment などのUVマップ完了後テクスチャにアクセスできます。modelTextureというのは私がつけた名前なので好きな変数名に変更できますが、変数がsampler2DRectと結びつく事でこの挙動を引き出しているのでしょう・・・また番号と内容の対応は要調査が必要です。

例えば modelTexture0 ~ 5 などを default = “0” ~ “5” のように作成すると全てのテクスチャにアクセスしながらシェーディングが行えます。

jit.gl.shader の例

<jittershader name="simplematerial">
	<description>
	simple material for 3d model
	</description>
    <param name="modelViewProjectionMatrix" type="mat4" state="MODELVIEW_PROJECTION_MATRIX" />
    <param name="textureMatrix0" type="mat4" state="TEXTURE0_MATRIX" />
    <param name="position" type="vec3" state="POSITION" />
    <param name="texcoord" type="vec2" state="TEXCOORD" />
	<param name="modelTexture" type="int" default="0"/>
	<language name="glsl" version="4.1">
		<bind param="width" program="fp" />
		<bind param="height" program="fp" />
		<bind param="threshold" program="fp" />
        <bind param="modelViewProjectionMatrix" program="vp" />
        <bind param="textureMatrix0" program="vp" />
        <bind param="position" program="vp" />
        <bind param="texcoord" program="vp" />
        <bind param="modelTexture" program="fp" />
		<program name="vp" type="vertex">
<![CDATA[
  
#version 410
 
in vec3 position; 
in vec2 texcoord; 

uniform mat4 modelViewProjectionMatrix;
uniform mat4 textureMatrix0;
 
out vec2 texcoord0;

void main( void ) 
{
	gl_Position = modelViewProjectionMatrix*vec4(position, 1);
	texcoord0 = vec2(textureMatrix0*vec4(texcoord, 0., 1.));
}

]]>
</program>
		<program name="fp" type="fragment">
<![CDATA[

#version 410

in vec2 texcoord0;

// samplers
uniform sampler2D modelTexture;

out vec4 outColor;

// entry point
void 
main()
{
    outColor = texture(modelTexture,texcoord0).xyzw;
}  
]]>
		</program>
	</language>
</jittershader>

パターン2 : 他のモデルのテクスチャをみてみよう

jit.gl.shader で様々なテクスチャを引いてこれる仕様を見つけるのが非常に困難で疲れた・・・のでもう少しお付き合いください。ひとつ車のモデルの例で見てみましょう。この画像自体はパターン1のデフォルトpbrシェーダーで描画されてます。やはり暗めですね。

この車のモデルには複数の元テクスチャがあるのですが、jit.gl.shaderで参照できるUVマップ後のテクスチャと比較してみましょう。

before(合成される前のテクスチャファイル)

after (jit.gl.model と jit.gl.shaderの組み合わせで取得できるテクスチャ)

なんと5種類しかないんですね。これは画像として用意されてる元のテクスチャをjit.gl.modelで読み込んだ瞬間に3D空間で使いやすい形式に再合成されているということですね。枚数が減ったからといって無駄になっているテクスチャはなさそうです。これを見つけるのに2日は費やしました。

パターン3(微妙):jit.gl.material や jit.gl.pbrを利用する

例えば、jitterのパターンとして jit.gl.model @outputomatrix 1 として内部のモデル行列をjit.gl.meshに送って描画する事があると思います。その時のテクスチャマッピングはjit.gl.meshに対してjit.gl.materialやjit.gl.pbr を接続し、jit.gl.textureなどを通して別途テクスチャを読み込み、マテリアルを反映させます。

ここで問題があります。パターン2の画像のように jit.gl.modelが1つのUVマップ後テクスチャを生成するのに複数の元テクスチャを利用していると、jit.gl.meshにおいて綺麗にテクスチャマッピんグをする事はできません。jit.gl.material / jit.gl.pbrに対して複数の元テクスチャを読み込みむ事ができないためですね。

この手法で上手く行く可能性があるのは元テクスチャとUVマップ後のテクスチャが1:1対応している場合なのかなと予想します。上記の車のテクスチャの例を見ると、8枚の画像から5枚のテクスチャを生成しており、albed以外でも複数の元テクスチャを参照してテクスチャマッピングをしているので様々3Dモデルを動的に読み込んで表現するのであればパターン3はオススメできません。

パターン2であれば、Vertex Shaderも使える!!!ので頂点・メッシュに対して変形処理を書くことができます。

まとめ

jit.gl.modelの難解なテクスチャマッピングの仕様を調査してみました。冒頭の画像はjit.gl.shaderでようやくシェーディングを描き始める事ができた画像という感じです。

昔JitterのVJを見る機会がありましたが jit.gl.gridshapeかjit.gl.platoなどのプリミティブな図形が利用されがちなのが理解できてきました。モデル読み込み&マテリアル適応の概念が難しすぎる。継承・関係性の仕様が隠蔽されていて厳しい!

今回の調査で自分でシェーダーを描きさえすれば好きなモデルを使えそうでという事で一歩前進したでしょうか。ぶっちゃけJitterは仕様を調べるのが大変すぎて何もつくれていません。MaxへのLoveで突き進んできましたが、これ以上理解が難しい問題があったら大人しくUnityかUnreal Engine使おうと思います。

厳しい

リンク

3Dモデルはこちらからお借りしました ⇨ glTF Sample Model – https://github.com/KhronosGroup/glTF-Sample-Models

js object を使って jit.gl を構築する検討 #jitter #maxmsp

jit.gl はgl-core導入などもあり、pbrマテリアルやGPUインスタンシングなどの新規オブジェクトの採用もあり以前よりは今時な絵作りなどもしやすい環境になりつつあるようです。

しかし、そもそも論として3D向けのオブジェクトならアトリビュート・パラメーターの設定を大量にしなければならない割にデータ保存がめんどくさかったり、目に見えないCONTEXTを用いてオブジェクトを繋ぐ(パッチをほとんど繋がない)というMaxの常識から大幅はかけ離れたパッチング体験になっています。

それならjsオブジェクトGithub CopilotやCodeGPTなどを利用しつつ、jit.gl部分は全部自動生成すれば良いんじゃないかと試してみました。

結果上記のようなパッチが生成され、以下のような画像をつくることができました。

コードには

  • オブジェクトの生成・削除
  • オブジェクトのアトリビュート設定
  • オブジェクトの関数コール
  • オブジェクト同士の接続

が含まれており、Maxの基本的な操作は叶っているかなと思います。あまり整理してないのでご参考程度です。

JS code for js object

// Desc: Create a jit.gl.gridshape object and connect it to a jit.gl.handle object

“use_strict”

autowatch = 1;

inlets = 1;

outlets = 1;

// Create a new object

function erase() {

// Get a reference to this patcher

if(this==null) return;

var p = this.patcher;

if(p==null) return;

// Get the first object in the patcher

var obj = p.firstobject;

if(obj == null) return;

// Loop through all objects

while (obj) {

// Get a reference to the next object before we delete this one

var next_obj = obj.nextobject;

// Check if this object is the js object

if (obj.maxclass !== “js”) {

// Remove this object

p.remove(obj);

}

// Move to the next object

obj = next_obj;

}

init();

var create_msg = this.patcher.newdefault(getNewPosition(), “message”);

create_msg.set(“create”);

this.patcher.connect(create_msg, 0, this.box, 0);

var erase_msg = this.patcher.newdefault(getNewPosition(), “message”);

erase_msg.set(“erase”);

this.patcher.connect(erase_msg, 0, this.box, 0);

}

var index = 0;

function init(){

index = 0;

}

function getNewPosition(){

const numReturn = 30;

const xs = 150;

const ys = 30;

var x = Math.floor(index / numReturn) * xs;

var y = (index % numReturn) * ys;

index++;

return [x,y];

}

function create() {

var mytoggle = this.patcher.newdefault(getNewPosition(), “toggle”);

var myprint = this.patcher.newdefault(getNewPosition(), “print”);

var myworld = this.patcher.newdefault(getNewPosition(), “jit.world”, “TEST”);

myworld.setattr(“enable”, 1);

var myfps = this.patcher.newdefault(getNewPosition(),”jit.fpsgui”);

this.patcher.connect(myworld, 1, myfps, 0);

var myhandle = this.patcher.newdefault(getNewPosition(), “jit.gl.handle”, “TEST”);

myhandle.setattr(“auto_rotate”,1); // Same as the existing object

var mycamera = this.patcher.newdefault(getNewPosition(), “jit.gl.camera”, “TEST”);

var myNoise = this.patcher.newdefault(getNewPosition(),”jit.noise”,4,”float32″,512,512);

var myPwindow = this.patcher.newdefault(getNewPosition(),”jit.pwindow”, “TEST”);

this.patcher.connect(myNoise, 0, myPwindow, 0);

var myMaterial = this.patcher.newdefault(getNewPosition(),”jit.gl.material”, “TEST”);

myMaterial.setattr(“name”,”myMaterial”); // Same as the existing object

this.patcher.connect(myNoise, 0, myMaterial, 0);

myNoise.bang();

var myNode = this.patcher.newdefault(getNewPosition(),”jit.gl.node”, “TEST”);

myNode.setattr(“name”,”subnode”); // Same as the existing object

this.patcher.connect(myhandle, 0, myNode, 0);

for(var i =0 ; i< 1000 ; i++){

var myGridshape = this.patcher.newdefault(getNewPosition(),”jit.gl.plato”, “subnode”);

//myGridshape.setattr(“name”,”myGridshape”); // Same as the existing object

myGridshape.setattr(“color”,[Math.random(), Math.random(), Math.random()]);

myGridshape.setattr(“smooth_shading”,1);

myGridshape.setattr(“position”,[Math.random()-0.5, Math.random()-0.5, Math.random()-0.5]);

myGridshape.setattr(“rotatexyz”,[Math.random() * 360, Math.random()* 360, Math.random()* 360]);

myGridshape.setattr(“scale”,Math.random()*0.01);

myGridshape.setattr(“material”, “myMaterial”);

}

mytoggle.bang();

this.patcher.connect(mytoggle, 0, myprint, 0);

//this.patcher.disconnect(obj1, 0, obj2, 0);

}

post(“initialized”);

トーンマッピング(初級)のオートルミナンス #jitter #maxmsp

ポストプロセスについての本を読むと、トーンマッピングといった自動的に明るさや色味を調整する技術がある事がわかりました。例えばゲームなどでステージが切り替わった際にある程度自動的に明るさを調整してくれるようなものです。

今回はjit.gl.pix上のみで動作するオートルミナンスの試作をつくったので公開しておきます。まだ試作という感じなので参考程度にどうぞ

Code (jit.gl.pix)

今回は複数のnearestや関数を利用するためcodeboxで書きました。

  • paramは直接codebox内部で宣言する方法もありますが、一旦codeboxの外部で受け取るようにすれば、codeboxで同様の変数名を利用できます。この書き方だと min / max の指定もしやすいのでおすすめです。
  • 入力されたテクスチャから代表的な5ピクセルを取得し、gray関数から明るさ(graymean)を求めています。この処理は本当は画面全体に行いたいのですが効率化の観点から擬似的に行なっています。ピクセルシェーダー毎に同じ処理をしてしまっているので、外部で計算できると良さそうですが、jit.gl.pixで行ううまい方法を思いつかなかったのでこのような書き方になっています。
  • paramで設定した目標の明るさとの差分を取り、それを入力されたピクセルに足し合わせています。大抵こういう処理をする場合に生の値を足し合わせるとガチャガチャした変化になりやすいので responseの値を使ってゆっくり変化するようにしています。

ちょっとまだ改善の余地ありそうなのですが・・・ご参考にどうぞ

Patch

----------begin_max5_patcher----------
2561.3oc6bszbaibD9rzuBr7jkMEy7.OyljZ2T0dK4PV6aqSwBDXDI7BN.Ev
PIJ6x+2y7..Dfjfb.AAEpTVUQIg4Y2eSOc2Sid32t+tIKR1RxmX7WM9Ci6t6
a2e2cxhDEbWwy2MYs+1fX+bYylPIujr3KSlpphQ1xjE+kH1rEYAI49kUQ2rN
YCKlvj8CUTZTnr47g3QLnrop1wdMknnDwnMesOKKZ6joFSlX7eKZXpOKXUDc
47LR.S0VK3LvTCjoi3OVxeiPy.UcgSFQzRp.JJ662eu3WS0jg8YrrMQSpnfL
+0DFIaNg5uHVRvfixwvC4XjWqb7I4QG2YVSMLssmAAdPOKS.B445hcmZ.s.m
gkKJTvFRBIijmlPyISt.r3DK9KimkFs03S+1G+jwuP4XjQrfL7oAjiKQfODe
ftmThXY7bwTtIiHjJNrDMjSrfRIDnqsdnlbfHYEPRAlb2jmhhIOSxxiRn0ZM
GjSSqU7c05h.H+RhbfbmVUTDUUjUUQYjmiJ6uYUo9Yb1gw4EAmJPqs1lS1ML
IgjLJWFsTBWtjVPRxEOw5Qdpe.Y2xEgVN.S1ARParDlbrUajfNVR3BY6tCo3
BIKiSB9SRXMge9RVJgFQS4hWDJymUvCUUGRdxeSLa9SITVdzWkTBji+Gq9mJ
nziVofWjrwulE4GWwDKyhBSnBhnwJhn3xoiyeJ1wpNyHaA0O8HclKSxglVpL
myjaxW3mIVvJzDfJqjkjD2rpp9ESdhUTcZDktGJxRRauxrnkqNQeWjvqb8oF
aYM4y2PU0NmKavlm6+bSzl4GGWrSu4vu0mFwUKSXQpk.DnpRk1vU4AYIwwM3
WUMOejZB4x5AjWhBYqjSTcgAdyiRKEhlTsJGFsjjyZVFyeYdyRxYup.8ZEsY
QwdYtFi0owbtnYC36RhxY4qRdIungkBZ0Afc1Jqu2ttZyFkeJ0mMUgJsqXTp
aliEVF+BW8fAf+G+sFv5c5n1Y1WWpU8dzt4lVUWhwhsJXOod.f69pKOTkInr
boMkBkPWefhyJFn1QCvwQC7jSyt1PoYTSWSIWacd1EdaXWkbAe+.adk8zCjN
PcV5.0OoCjEplzgI91JcDvs2IZYqLMpVMGydx9fwYPio5reQJ+nbqvV4qA1x
dODoEaes48QUepL48292bCQRC4g+i5zr.QjMXYl+quKHINI6gu84rOS+LS86
LB20ApgrlYaMdOWDB44Y7ghRdk+ebgJWmpR9prMPn4OKG.9muW9OAb66jLi+
twyjf2w60T9mGjMK4omxILdMfYX2pNJ9vcMDvK+cThOWGG6cQT3T037fpq7F
.OdC9fXZTi7T0edXWePmnOOVzoGOnWXM50Acx77j2dyT4GwhxZhOUL.xEHAZ
7.GpKe.V+AT8Gv0ev7gG9KVxQNjDy7EJD3C4d5Fdznb5pnAtDs.aqS6b+NWK
F5cCz6qL97yE8AIG7iT+jaipOt9sta1C1SydpSSo79cbYzC1YidlSzgYMcA8
wl2NGoiins4djbxE0ebXHOYSVP4ZToFYilDhPLmKhW5R3eTwhh1o0pvEQDPM
HB7.RDPMAhRD6nqP2uqj2vfeX0qfeXhtFA+noxxKIDHhCOm9RDML4EsizEDd
MhzkIRxsPty3XGAj3glgq8CxhCIXauYHqpgI+UZPcfXLEML2KSd.Zq7QGIcA
E54zQQ.+7T9XMOSrCZxaKB3zKD.4AtLDHymtb3C+2yQKoDl16Qr6Pn+zHPeX
W2YHnK1C3BLs7brE6Xf13eD1uSF1O6eD1ueD1ueD1uaVX+9oGM93+42+zED.
m9FeuhWUlqLDNVdil.d8X2gBy9AEX0QevHEH3Ib27zPA51.Een6PQOiqGla.
vZLBEEgGW33hHrT8L13n9Fk.auZvDzCLZBTPINEF4SMZBSWndFv0QOikxyKn
0HCppeZ.C3rdFVcm9AVl1Emvq.rFOAfJecRBaUNij18WDE75nd1QhFNvyiI3
aCl79tuYxqePAzTs8AL1TO2cnvpm5Uf.0VkxiBOZfhmIAb8GfNCHl8cahqq5
PNxMHlNmeaxnU1.2ysIlP65pQGOxF4uD8UiW6Nd31S7P4mhkbqhIbz3puDN1
1c3vtu6TrGmvQfeL26VkiZbm25tNDn0UQGBRoCABAiFkHwD5R1ptKp.tNJUU
gXcLIqHdAtc2+C7UYmCRYhAMZ7NcUdLJa4htuew6Z71hwHklD2wkh0tamA5d
c7ICh++CCMPmqg3QIb3LZfiBWTuDCL1Wkzqvy73Iq+a1Q4jBHes6vg4U47bi
uMLbkoHtR0tCH3qg7gJZziH8oWV52XoU52fsAirTN8MLuqf8IuqFjLQxQy7u
QblUQxBMHIAjktYAjHpJ6kxRIYgpWjM7skzPdsSZfggz.ZRZBqZC1Zmot.DF
LfoQlT7TqDpCNfXgHPF5uaZvvB6tfECFUnszo0PliidcYOBZnnBKcUhXdPCG
X8a5SZn1IsgQ+1Q.iVrJLjxwHrt6ocu4.j1jl8MWrB0k0tgReLB1k8c2T+JP
cJwmuo9UfzUyMZH0bC0Uy8fRDtcwwzAiJb5hQrAiJz0sB3P5bCzpKJVFJC5P
ytbpfACKzU+Oz8Vq+WeRyocRC8FSZl2ZC55ZS.hG6WFFX+x7eK6KMy+EIF1k
j5+qI449KIGj6+jsoIYL48OU6KBD3BYdnJy9UQR205z7N5BXxfj0qIT1AL4u
tgkX7uN8WmIkK80Sj8isvClbtqtf55TahUei1.pwf0Shdr8LvoVyaly7b4SV
B03i9zbi+YRb3U91e7RRVbXwk+PsaPj7gOEmv2YRWJ9eNRktgUdINpBv34+N
gA2g6ExBe5xye+PTwX1QtEBCgW+uwfjWQrX+WiixYcWaQIFDDGkthHR8eUN2
Wsbejqw+YuWY7OgQAB0j9YutCdZtpTmlZU5Dp19AUpfDoiW0nEl4+BKod90e
bQy3jjz5OG5y72+p1H39VBgq+h7j3MLBmDWod6ManbQsWHYyVmV6MxHutNUa
ANUi9yHpBSWm7bDQTRsFT.2ab.l.WWm5Ya99bh5p3SYbcHyE2SjVrqU0KsLR
d.6tHJNNxOK7Tb6IZiVLqGDC.bNF+VyrK8yVvs6LiSrswqs2DsXULxD.rcQN
u0rZXT9JR9o3zVagVLpEFArgV0SRnAfQGINXYdY9XTl4SE53550KVBLusLN9
BcsDodeqEWQxty5Kj2RMJ2Mw5.fryMeebJ7XeW+KPkCb4uwIyZwc+Zmp+.bW
24w4FMO1ZLOkGGsWSjoFSD5JLOXnFST82s0kOSfazZDVm0nlr85nvzjHJqPJ
2Rlyw1EesYYZd3S7itotD+pzSV8XOo6luLmgT3p4aEYPmoa0hN5VIFC0QkF9
ZLQWDxozXu2MZWLE6cS126Vre3MXu8au992bc4sV+X2XcgQy6+98+OLTPZsN
-----------end_max5_patcher-----------

色収差(Chromatic Aberration)エフェクト #jitter #maxmsp

山田監督作品もさる事ながら、そのほかの映像作品・ゲーム・VJなどでもよく見た、2010年代にかなり流行ったポストエフェクトと言えそうな色収差です。

Gen Code

  • 右上では、色のズレ方を計算しています。snormから取得した値からlengthで値を取ってしまうとアーティファクトが発生するのでabsにするのが肝でした。
  • 3つのsampleを用意し、色のズレを反映した位置からピクセルを取得します。
  • 下の方の処理は、ズラした色の色相を回転させるために120度づつズラしたsin波を合成(三相交流)して位相で色味を調整できるようにしています。RGBのズレだけだと画面の色が偏ってる場合に効果が発揮されない場合があるので色相変更できると良い感じです。

exportcodeすればGLSLでも利用できるので、Max8以外の環境をお使いの方もご参考ください。

Patch

----------begin_max5_patcher----------
3137.3oc2c0ziaiaF97L+JD7wry5RRQJJ0SYaQu0KsI2VrXfrsFakUVRPRd9
HK1CyjEsMnAHWZykfhhtaPaA1to.En.oa6+FmO18eQoHkskmwxCojoFMCBRb
LIk466Cede4K+Pjew960aPzodo8L9oFepwd68E6u2d7jxSXuhuuWuotmNLvM
kWrdOxOqe7I9gihNo2Ah7CmMMZVVfWFuDnhT8GwKezfG8wPzhhJJW1Ywdh5L
+m6votYI9m16.id8L9rhBF6lMbhe33CS7FlIJqIh1GbfADx+.Ssx+.Ya2GAs
Mc.1.LwgZQIK+MROKbX9iBWIm9gKDSXdZe496m+OGHot6lkkLyu2RQLwcpWl
WxgdgtCB3ZDXiPB7pPhSkHxVw.aPeBSocPbjf.3P.6KKel00whDykadMOYlW
uaV8lzH81xtd58vYIGWKMOz6DlTuPly7NMaoQv3f9w9mZ7vewCdnw8CYHhwv
IIQLtr+vCcG3kjv9eQgRaiffa0FYbvg409rDuq0Ng.yQGSDlCRXrbXE+GxKo
.KJ.i85cjef2wdIo4pxpRyf133RIuWoGIGAeTD+Gx9fkI4GJRhrLoDui8W77
3ko5lvTmLltjqo4XyoV3dq9YhF4kDxnhKLf4skEhDuUKugHM1cn2p1IuEMB4
04BPBhL4jHLfSoPPpfZgvfUHEicLNHZ3m6MpDGm0.E6E5GFm3k5ElIZkKm8H
uibmEjc3QQgYo9OlKIvbC1Mj+QER5FyLWW3pwmj36FrTIFm3OJJLWHVqEIO4
EUGS+HBKkxJCuDgtwa3gYLPFzTQloLkbV5.2j7FrBCdzhLyhhBVOqkOWf2QY
EYG6GFdITLKJt5LS7GOYKO6fHVlS21uMOmzCmEJx8PF2H6vT2iWGsybCBJLw
W+m+T2PelorWlunI.AVlovo2jzgIQAAqouhbNdC4Lhw0G5ch+nrI7JpLYfUb
+3EjndKakG4O1KMa8zxbGmtdJoYmI.8RIMaPgsLyiwz3.lVrdAXVI9oYoShN
IsnfKHZkAfUQET11tr+x0R+R9MGFMcJy3XokaYmm+RuvwYSle9ea9Ee07K98
yO+uO+hud9S9eyexuY9Eey7m7GmeweY9E+y4O42M+7m8gW95O7srx7px+TA9
gdCilElUlIxchUxl7vJK059hAkxor+X6x03FbyZ4H7XXYVpOIyR9YqHli8Vz
KTg2KMfvu8MO8Cu7Mu86e06+2m2SYEmdcJtvyIETVwQftfh+CO8eM+hue9S9
GyO+0r+OCDd2qd56e9KUGDHxABVTTmCD9ve3+9i+ou9su46d2qdMCGD.x6ew
+Y9EeK2J6EJiFPmqAMb38uY3f6bfw6+tu4G9qO+cO6EyOm4W4YyO+OKvie72
9b0ggqyk.wlHh4pSZXrfJntdecdDvXtdaRo2HJ9ZAoutdmNzMvy.X7fe0u9g
H1mvpUd3lU90bDT8fVpzOgkXXpPfXrqWYjKWEZPsCz7SLf8IJCGv0ICpCHPG
ReJarb1h9MMQWOf.aK.AUC3ng7CSKGNbPYeV.DcD33ipgwBDZsifCSgYi0sb
1gYSMVvB3.itSvNv6H3.3bmfcfZHb.fqYrbamc.2QvgXfIcH1wGUCv.raLUr
M4wjgb3yeZWHti6oNX.bZXuJH6hNYA2A.C6FxLHlcTvnFlIflFMJV3yvFZdG
fYzvfuVFJZmyJoN8tBZXj4DVupki8x71b2IfFF4E1ztOpTnFcGtQpeXMfiFF
WtI1VL6e1qlumasTiFFEJxBJnFEAk2cnFYmDE6WC.ogwgRnHtiih4FtH.rsB
Hf1ZTJl0.N.6F3fBgcrQoTGWGNNMMtbZeRWzyw8Lpm4hSCCGEaSEVKXt6Cry
sa5QSiGE.J66nCwO365GiIy7L.8MtOq9Ee5dZMFpuCbGwY.hEbfZ0Ybwdr2P
CP9eTFTra5fbAh9gsL4QpRjnaGyVxP5D+Gabl5.hcS63gONFBg6pEC6NNVxw
iSUGOnMMnURGFOdr53gUSmHDw3c6d.RCbhzzYXm1ochntQCsgizi.4wqcmwn
g1zQ64.6v3g5cxPa5hN.Q24bhPZnSDBObrtmKj6wButuxnQCcfTr0nfhfQLM
uUuFc16Dr.AwcrEnKd4Kyh7XQCcjZI1rTHwjsaB6L6KHw.83uwDWcndpa9P2
MlOE6oNGRGZxzHpiFMLj0hMXHDHriHcFCH2Aop6LY2r05J1.1H6awSBucSiJ
ylOq6lTxs9kz0F1zQ4JbndW.JZ3Lua5XwGLW2CK3wl1mj+W06PogySFlTfFb
2FTXGZC4p9pbSsa5v4DQgPocr0loVfAcm35.QsuK.FVM24Qw6Wc2BLDwmt5M
rk4DwrHJ05tdDDycRXYDQfHl3NSLppuIhHncSDpV3NVWNwQAYQCcSpFQPaFQ
vWiK1CjxyhnaFwKMFo6zmyGqLAA2XmrbsGJ1AM2IHHjc.Ag4skzEIHpG0JF2
7NdHcQOHLtQVDiknNAwbmQPDaKfND+HMLJYp5ycl0tIrUwVFA0Y5uslKJCpo
6NSKRGcQHp4p1gZZTHztLdn9pTgZ3jhfJ19tcP.wcZbfmw8GDMKbT9w6hA6w
lFqNBA1QlPHwKpqUGxE6NBhZ5qaFg2g7cY.ZWYk0Agn50Ms4tYF5EKdSGpeZ
+v57BJ1POLNqBYQlEqnkfBlhrMrnhiDBbOIe8CcDGFY0bkpVc1UkeX9TwIRD
u5yyey.QZzrjgKZkJZGMVWNF4kl4Gt7j75SW0kxkJXTxHwQjlbMNpJYanFqP
zvUKZnaVQKeCWUknAuYEs7MTZ61fZCTgqAM9LsHExR3gv1toCJssHrsI7RKZ
46ntNJpkuigaYBOREt1MNgG05rJYwGT2UznsuaBYEs7sDdKS3UhqoKBOQV.J
exYykh1CfjWznUKZ5gVQLkTzJXU5g.QjTHHZTHPxhD15TJfP4iTAoU4P5dwx
OxEzlUsCUZ3.pU3vTZ3vVm8pCwRiGlZDOxeEfkTLv5rYAHcyBzRqzChzzCGs
ROrjFOzqWLfzxAUqxgrtOzIKUVtgN8mijkXPsa6HawxhOTcZBiskUJzIgkJa
yzhYUQORgrVM15rOWprsH15zvg5nRKRqNrB4EMXaOrB4EMT0hldFiuirzJGZ
aO8CRKZ7yxp1cP9JHas9jA5H6XlVnD5YTrNJECn1DCocapyHQsopHEZCKrTQ
JP5RJvJM1QsAFJDFptnETomjEcFPNEohTnKZAEphTnsVDIEBcNaBxZkRzYfd
DrRS3k1FGhziXUm9JvxBFXcNMB4aZ3KEb3VvhVM.WroJSoOn0EMnrscs5HVj
G0Z83aktiotvjzfMa8skirNDH5rKSDREoPaMSPUjBc0wMRkNM01DNCjeg.z5
16AH+vSoZUNnJgG5agQrTBOzmbPTZJMzW6h7Q2YpU7PVyVGGsJFRuJy.8RSk
dch1.QRyc4ohvgTK9yB3TjhhWGyqeE2sZSL+yWbyKa7IWyMu7hKp5xW9ta3Z
pd40uR0WvxXw0Pt338lT5xtqW4K9W94k+Vt1kW+d9c.K6nPiG3FlZ7yhBFsi
uxpOIJIXTwMVs3xpk0vc+iBhX.V33kaC7KAYlWEdLk79befa3XYuupKN0E.N
Wy8UcMfD9cVef6YA9oYpeKluP+GF3GOwK+hIVbi.urgcCGJXW6EcO6ui7Gly
ScSNaE7vdr3YYKupuKISUBbPbwstn0hyyjk+ZiRbOIKp7s+6lIgAQQwk+9H2
L2KeQfmq8UrA7cGjFELKyiIhSDusIyBYjpS7R5OMtzaJ.+xDeIYeaE5y8CEX
5zni88xSoTAJf6YT.FXaSK+xSdYMgK3LKLlOiCyuEqqv82xmRJeoWQcG3GD3
6lLZaZ6VJiTJqCzD.XZr4MsxN1MYf6Xu9LgsJcs5hHkpZhv.fkc46g3aFUcj
e5DuzsooUVBoTThIBXAIkuBI0fh1j9acyxRl4qtKyMziJtROiRz4.Dt1MKa0
cNTjXtbyqYNvbyp3HT8zba.+jOPblDotlu5.xor9ye30eYpDvwkCPr.Ttlw+
K09p+RwpdkVhcZUat4ZF1nZ1QhJFUZ6fT6JhzVUDVlJZGTOqOoU5TipYEILH
biiO1KIsnz75f4O3QQbti8A7u5GJ9Jg+0Dui8WTdLOE2DlEbFy7sHvsdmZI7
40K+URNIj4NQPDYZ294wSw70j2QRZbwvP3tj1+K2++SD3n..
-----------end_max5_patcher-----------