JXSから外部.vert / .frag などのシェーダーを読み込む #jitter #n4m #maxmsp

Jitter & GLSLで遊ぶのを効率化したい

昨日に引き続きMax8のシェーダーで遊んでいるんですが、JXSはXMLの中にGLSLを書くような形でVisual Studio Code のコード補完やらフォーマッターなどで扱いにくかったので、JXSと.vert / .fragを分離して扱う方法を模索しました。

※この記事はMax中級者以上向きにざっくり書いてるので分かりやすくはないです。

JXSから外部シェーダー(.vert / .frag)は読める

Cycling74のforumなどを探してみるとJXSから外部シェーダー読めるよと書いてあります。具体的な書き方は見つけられなかったんですが多分こんな感じという書き方をしたらいけました。
  <program name="vp" type="vertex" source="default.vert" />
  <program name="fp" type="fragment" source="default.frag" />

これで.vert / .frag をMax Projectのotherフォルダなどパスが通ってる所に置いておけば読めます。

シェーダーを切り替えたい ⇨ JXSの動的生成(ご参考)

ではシェーダーを色々書いて遊ぼうとして .frag を量産したとしても、上記のJXS上のファイル名をいちいち変更するのはめんどくさいです。XMLに詳しくないのですが動的に変数を与えて書き換えるという事は出来なさそうな・・・?

という訳でJXS自体を動的に生成すれば良いと思いつき、こういう時に役立つ node.scriptオブジェクトことNode.js先生にお願いしました。MaxにNode.jsくっついて本当に良かった・・・

node.js / node for max のJXS生成サンプルコード

const fs = require('fs');
const MaxAPI = require('max-api');

const generateJXS = (vertexShaderFilename, fragmentShaderFilename, outputJxsFilename) => {
  const jxsTemplate = `
  <jittershader name="default">
    <description>Default Slab </description>
    <param name="scale" type="float" default="1.0" />
    <param name="tex0" type="int" default="0" />
    <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="u_resolution" type="vec2" state="TEXDIM0"/>
    <param name="color" type="vec4" state="COLOR" />
    <param name="u_time" type="float"/>
    <language name="glsl" version="1.5">
      <bind param="scale" program="fp" />
      <bind param="tex0" program="fp" />
      <bind param="modelViewProjectionMatrix" program="vp" />
      <bind param="textureMatrix0" program="vp" />
      <bind param="position" program="vp" />
      <bind param="texcoord" program="vp" />
      <bind param="color" program="vp" />
      <bind param="u_resolution" program="fp"/>
      <bind param="u_time" program="fp"/>
      <program name="vp" type="vertex" source="${vertexShaderFilename}" />
      <program name="fp" type="fragment" source="${fragmentShaderFilename}" />
    </language>
  </jittershader>
  `;

  fs.writeFileSync(outputJxsFilename, jxsTemplate.trim());
  MaxAPI.post(`JXS file '${outputJxsFilename}' generated with '${vertexShaderFilename}' and '${fragmentShaderFilename}'.`);
  MaxAPI.outlet('read',outputJxsFilename);
};

MaxAPI.addHandler("generate_jxs", (vertexShaderFilename, fragmentShaderFilename, outputJxsFilename) => {
  generateJXS(vertexShaderFilename, fragmentShaderFilename, outputJxsFilename);
});

.vert / .frag などを引数で渡して generate_jxs 関数をMaxからコールすれば、アウトレットから今つくった.jxsファイルを出力します。後段に jit.gl.slab を接続しておけば自動的にシェーダーが切り替わるという訳です。

folderオブジェクトでVSCodeで書いた.frag名を取得し umenuにセット、umenuからの出力を利用したりすればシェーダー切り替えを簡略化する事は難しくないでしょう。ただしparam など独自変数をjit.gl.slabへ伝えたい場合はparam / bind / uniform などの書き換えが必要ですので、こんな方法もあるんかなという程度あくまで JXS / node.scriptのサンプルとしてご利用ください。上手くつくればShaderToyのコードなんかも動くと思います。

default.vert(ご参考)

#version 410

in vec3 position;
in vec2 texcoord;
in vec4 color;

out jit_PerVertex {
    vec2 texcoord;
    vec4 color;
} jit_out;
uniform mat4 modelViewProjectionMatrix;
uniform mat4 textureMatrix0;


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

リアルタイムグラフィックスの数学 ― GLSLではじめるシェーダプログラミング のサンプルコードで試してみた

ちょいちょい勉強しているこちらの書籍 ⇨ リアルタイムグラフィックスの数学 ― GLSLではじめるシェーダプログラミング の.frag のサンプルコードを利用して切り替えテストしてみました。

無事に.fragをさくさく切り替えるという事ができました。

(余談ですが)#version 330 core のサンプルを #version 410に置換して利用したのですが、他は何も変更しないで動きました。GLSL3以上は後方互換性が担保されているのでしょうか?

Happy Patching!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です