jit.gl.model / jit.gl.shader を使って初音ミク(はちゅねみく)さんをメッシュに分解するエフェクト #jitter #maxmsp #glsl

メッシュ分解

スクリーンショット 2014-08-08 00.18.16ジオメトリシェーダを使ったエフェクトのテストです。メッシュが法線ベクトルの方向に飛んでいきます。Tell Your Worldで、もっとカッコよく使われていましたね。

Before はちゅねみくさん

スクリーンショット 2014-08-07 23.35.04

After はちゅねみくさん

スクリーンショット 2014-08-07 23.35.23モデルは、note.xさんで配布されているものを利用させて頂いております。実は、この処理は無理にシェーダでやらなくても、jit.genとjit.gl.mesh等々を使えば、CPU上でできたりします。

Before ちび初音ミク

スクリーンショット 2014-08-07 23.34.23

After ちび初音ミク

スクリーンショット 2014-08-07 23.34.26あれっデブミクさんになってしまった・・・メッシュが分解されて飛ぶ処理だと思っていたのですが、分解されてない?!処理を見なおさなければ。モデルは、モデモデさんで配布されているのを使わせて頂きました!

ジオメトリシェーダ

スクリーンショット 2014-08-07 23.41.14この画像では、元のはちゅねみくさんのモデルデータを保持しつつ、分解したトライアングルも描画しています。ジオメトリシェーダを使ってGPU上でメッシュの数を2倍にして表示するとできます。この処理はジオメトリシェーダらしいエフェクトと言えるかもしれません。

インタラクティブに音に反応


せっかくMax上で動作しているので、音に合わせてインタラクティブに変化させてみました。

JSX / GLSLコード

	<param name="size" type="float" default="0." />
	<language name="glsl" version="1.2">
		<bind param="tex0" program="fp" />
		<bind param="size" program="gp" />

Maxのためのパラメーター、バインドの指定です。size変数でメッシュの位置を制御しています。tex0は、jit.gl.textureから渡されるテクスチャです。jit.gl.modelにjit.gl.shaderを適応した場合に、テクスチャをjit.gl.textureで別途設定してあげる必要があります。

Vertex Shader

 #version 120

 varying vec2 VTexCoord0;
 varying vec3 VEyeNormal;
 varying vec3 VEyeVertex;

 void main()
 {
   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
   VTexCoord0 = vec2(gl_TextureMatrix[0]*gl_MultiTexCoord0);
   VEyeNormal = vec3(gl_ModelViewMatrix * vec4(gl_Normal, 0.0));
   VEyeVertex = vec3(gl_ModelViewMatrix * gl_Vertex);
 }

ほとんどジオメトリシェーダのための下準備しかしてないですね。

Geometry Shader

 #version 120
 #extension GL_EXT_geometry_shader4 : enable

uniform float size;

 //VARYING
 varying in vec2 VTexCoord0[3];   //[3] because this makes a triangle
 varying in vec3 VEyeNormal[3];
 varying in vec3 VEyeVertex[3];
 
 //GLOBALFUNCTION
 void main()
 {
   int i;
   vec3 newVertex;
   
   /*
   //Pass through the original vertex   
   for(i=0; i<gl_VerticesIn; i++)
   {
     gl_Position = gl_PositionIn[i];
     gl_TexCoord[0].st = VTexCoord0[i];
     EmitVertex();
   }
   EndPrimitive();
   */
   
   //Push the vertex out a little using the normal
   for(i=0; i<gl_VerticesIn; i++)
   {
     newVertex = VEyeNormal[i] * size + VEyeVertex[i];
     gl_Position = gl_ProjectionMatrix * vec4(newVertex, 1.0);
     gl_TexCoord[0].st = VTexCoord0[i];
     EmitVertex();
   }
   EndPrimitive();
}

三角形プリミティブを受け取って、各頂点を法線方向にずらしてプリミティブを描画しています。なのでメッシュに分割するエフェクトじゃなかった(笑)ネギミクさんは法線ベクトルの値がプリミティブに垂直だから分解されているようです。コメントアウトされている所では、元のプリミティブを描画できます。

Fragment Shader

#version 120

uniform sampler2DRect tex0;

void main()
{
  vec4 texel = texture2DRect(tex0, gl_TexCoord[0].st);
  gl_FragColor = texel;
}

テクスチャを引っ張ってきて色を付けているだけです。

おまけ ジオメトリシェーダ エフェクト その2

スクリーンショット 2014-08-07 17.20.20
ジオメトリシェーダにtriangleで入ってきたプリミティブをline_stripに変換するとこんな表現も簡単にできます。


jit.gl.model / jit.gl.shader を使って初音ミク(はちゅねみく)さんのテクスチャをいじる #jitter #maxmsp #glsl

Cycling74 Maxでもミクさん出してみたい!(今更感も凄いですが)

スクリーンショット 2014-08-07 03.05.19

jit.gl.model TIPS

  • はちゅねみくのモデルは、こちらで配布されているCollada(.dae)をダウンロード
  • jit.gl.modelに対してreadでobjを読んだら、問題なくモデルロードされた
  • テクスチャ画像(miku.png)をMaxのパスへ入れておかないと読み込まれないので注意
  • jit.gl.modelへ@textureを使って、jit.gl.textureに読み込ませたテクスチャを複数指定する事も可能
  • @shader アトリビュートで jit.gl.shaderがバインドできるので、シェーダ使える

シェーダによってマテリアルを変更

スクリーンショット 2014-08-07 02.55.10
スクリーンショット 2014-08-07 02.55.22元のテクスチャを全く使わないシェーダを割り当てるとこうなっちゃいますね。@smooth_shadingってGLSLではどう書くんだろう。

シェーダによる複数テクスチャ合成

スクリーンショット 2014-08-07 02.51.22雪ミクさん・・・?

スクリーンショット 2014-08-07 02.51.14炎のミクさん。静止画なので分かりづらいですが、動画テクスチャです。Maxは動画テクスチャの扱いやすさはピカイチらしいです。Vertex ShaderやGeometry Shaderを使って頂点やプリミティブをいじいじすると、音に合わせた表現とかはしやすそうですね。あとMMDのモデルを出してダンスなんかも試してみたいです。

GLSL / テッセレーションについて(余談)

  • いまGLSLのライティングやプロシージャルの処理を小分けのファイルに書いちゃってます。ゲーム制作や動画制作では重ねがけすると思うんだけど、どうしてるんだろう?Maxだと1オブジェクト描画に対しては基本的に1つのjit.gl.shaderしかバインドできないみたいだし。そうするとGLSLを関数化してライブラリにする必要がでてくると思うのだけど、方法がわからない。OpenGL SuperBible: Comprehensive Tutorial and Reference (4th Edition)には、書いてなかった。
  • ジオメトリシェーダを使ってテッセレーションを試してみたい。最近のGPUだとテッセレーション用のシェーダを使うらしいのですが、原理的にはGLSL1.2でもジオメトリシェーダを使ってできそう。Pixarはローポリゴンデータをレンダリング時にGPU上でテッセレーションしまくってハイポリにしているそうです。OpenSubDivというオープンソースライブラリも公開していてすげえ。

jit.gl.shader / GLSL アルファテスト&デプステストを使った透過テクスチャの描画 #maxmsp #jitter #glsl

OpenGLの透過&深度

スクリーンショット 2014-08-06 22.15.08
JitterのOpenGLにおいて、透過テクスチャを使う場合は、@blendenable 1 @depthenable 0 と設定します。ただし、その方法では奥行き(Z軸)も使ったポイントスプライト等の描画(@depathenable 1)をしたい場合に、上記画像のようにテクスチャの枠が出てしまう問題があります。描画するメッシュのZ座標が「画家のアルゴリズム」で想定される順番と逆方向に並んでいるために、手前の画像を描く方が先だと、後ろの画像がdepth testで描画されなくなっちゃうんですね。Zソートが出来れば良いですが、Jitterには実装されていないようです。

GLSLを使ったAlpha Test(アルファテスト)

スクリーンショット 2014-08-06 22.14.55
上記問題を解決するために、Alpha Testという方法があります。OpenGLのWikiに載っていたのですが、フラグメントシェーダで透過値が0なら描画をキャンセルすればOKという簡単な手法です。ただし、この方法は透過がbinaryつまり0か1のどちらかしか無い場合にしか使えないようです。

上記画像はポイントスプライトのGLSLに追加で実装してみた結果です。綺麗に描画されましたね。

GLSL / Fragment Shader

#version 120
uniform sampler2DRect tex0;

void main (void)
{
	vec4 texel = texture2DRect(tex0, gl_TexCoord[0].st);
	
        //Alpha Test
	if(texel.a < 1.0)
		discard;
	
	gl_FragColor = texel;
}

半透明ポリゴンは・・・?

Wikiに書いてあるように、Depth Sort(Z軸でメッシュの並べ替え)が必要です。Sortの方法にはBSP Tree Sorting(バイナリ空間分割)やDepth peelingという手法があるそうです。そのうち必要になったら実装してみたいと思います。

おぱんちゅ

スクリーンショット 2014-08-06 22.36.30
ぱんつつくった by 貞子♂様 ありがとうございます!


jit.gl.shader / GLSL でマルチテクスチャ #maxmsp #jitter #glsl

スクリーンショット 2014-08-06 19.09.09

マルチテクスチャ

jit.gl.shaderの中で複数のテクスチャを利用する方法を調べました。Tutorial 43: A Slab of Your Very Own こちらで紹介されている方法です。jit.gl.slabで使うことを想定して紹介されていますが、jit.gl.shaderでも利用できるように少しコードを変更しています。

JXS / GLSLコード

<jittershader name="mrr.mixer.4ch.jxs">
	<description>
	</description>
		
	<param name="a" type="vec4" default="0.25 0.25 0.25 0.25"/>	
	<param name="b" type="vec4" default="0.25 0.25 0.25 0.25"/>	
	<param name="c" type="vec4" default="0.25 0.25 0.25 0.25"/>	
	<param name="d" type="vec4" default="0.25 0.25 0.25 0.25"/>	
<!-- 4つのテクスチャを使う場合にはparam要素の指定と、default値の指定が必須 -->
	<param name="tex0" type="int" default="0" />
	<param name="tex1" type="int" default="1" />
	<param name="tex2" type="int" default="2" />
	<param name="tex3" type="int" default="3" />
	<language name="glsl" version="1.2">
	<bind param="a" program="fp" />
	<bind param="b" program="fp" />
	<bind param="c" program="fp" />
	<bind param="d" program="fp" />
	<bind param="tex0" program="fp" />
	<bind param="tex1" program="fp" />
	<bind param="tex2" program="fp" />
	<bind param="tex3" program="fp" />

		<program name="vp" type="vertex">
<!-- GLSL内部で日本語コメントすると即落ちする -->
<!-- varying 次のシェーダーへ受け渡す / 前のシェーダから受け取る変数 -->
<!-- uniform Maxから直接受け取れる変数。param指定 -->
<![CDATA[

#version 120 

varying vec2 texcoord0;
varying vec2 texcoord1;
varying vec2 texcoord2;
varying vec2 texcoord3;

void main(void)
{
	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;	
	
	texcoord0 = vec2(gl_TextureMatrix[0] * gl_MultiTexCoord0);
	texcoord1 = vec2(gl_TextureMatrix[1] * gl_MultiTexCoord1);
	texcoord2 = vec2(gl_TextureMatrix[2] * gl_MultiTexCoord2);
	texcoord3 = vec2(gl_TextureMatrix[3] * gl_MultiTexCoord3);
}
]]>
		</program>
		<program name="fp" type="fragment">
<![CDATA[

#version 120
		 
uniform vec4 a;
uniform vec4 b;
uniform vec4 c;
uniform vec4 d;

varying vec2 texcoord0;
varying vec2 texcoord1;
varying vec2 texcoord2;
varying vec2 texcoord3;
		 
uniform sampler2DRect tex0;
uniform sampler2DRect tex1;
uniform sampler2DRect tex2;
uniform sampler2DRect tex3;

void main()
{
	vec4 input0 = texture2DRect(tex0,texcoord0);
	vec4 input1 = texture2DRect(tex1,texcoord1);
	vec4 input2 = texture2DRect(tex2,texcoord2);
	vec4 input3 = texture2DRect(tex3,texcoord3);
	
	gl_FragColor = a * input0 + b*input1 + c*input2 + d*input3;
	
}
]]>
		</program>
	</language>
</jittershader>

jit.gl.pixと比較

上記のサンプルであれば、jit.gl.pixで出力した結果をjit.gl.*系のオブジェクトへバインドすれば同様の事ができるのでjit.gl.pixで書いた方が良いと思われます。Vertex ShaderやGeometry ShaderでのVertexの操作やテクスチャの細かいバインディングする場合に必要になる処理ですね。


jit.gl.shader / GLSL でポイントスプライト #maxmsp #jitter #glsl

ジオメトリシェーダ

ジオメトリシェーダは、レンダリングパイプラインにおいて、バーテックスシェーダの後に配置されており、GPU上でプリミティブの変換や増減が可能なシェーダだそうです。

スクリーンショット 2014-08-05 19.29.40

ポイントスプライト

ポイントスプライトという手法は、CPUからpointsで与えた点群をジオメトリシェーダを用いてtriangleなどに変換しテクスチャを貼ることで、GPUの計算能力を活かしてテクスチャ付きのパーティクルをつくる方法です。

スクリーンショット 2014-08-05 18.46.52

実装してみた

今回はGLSL1.2で使えるジオメトリシェーダ(Geometry Shader)を用いて、ポイントスプライトを実装してみました。OpenGL 4.0 シェーディング言語 -実例で覚えるGLSLプログラミング-に掲載されているコードを参考にしています。ジオメトリシェーダは、Maxのjit.gl.shaderでもちゃんと使えるのですが、ググっても解説がないためつまずくポイントも多かったため、少し詳しく紹介します。

スクリーンショット 2014-08-05 18.46.57

パラメーターの宣言

<param name="size" type="vec2" default="1. 1." />

ジオメトリシェーダに限らない事ですが、Maxから指定するパラメーターは、param要素として指定します。初期値も与えられますがコンマで区切っては駄目みたいなので注意です。

パラメーターのバインド

<language name="glsl" version="1.2">
        <bind param="size" program="gp" />
        <bind param="tex0" program="fp" />
        <program> 
        </program>
</language>

宣言したパラメーターが、どのプログラマブルシェーダで利用されるかをprogram属性にbindします。例:sizeをgeometry shaderで利用する。例外ですがテクスチャをシェーダで扱いたい場合には、Maxの場合tex0というパラメーターが予約されているようで、宣言していなくても使えました。マルチテクスチャをやる場合については未調査です。

サンプルコードで使われているプログラム名称

  • “gp” : geometry shader
  • “fp” : fragment shader
  • “vp” : vertex shader

ジオメトリシェーダの宣言

<program name="gp" vertices_out="4" input_type="points" output_type="triangle_strip" type="geometry">
<![CDATA[
 /*glslのプログラムを記述*/
]]>
</program>

program要素を宣言します。vertices_out / input_type / output_typeが合ってないと実行できません。今回は、”points”を”triangle_strip”に変換し”4”つのvertexが出力されるという設定になっています。

ジオメトリシェーダのコード

#version 120
#extension GL_EXT_geometry_shader4 : enable

uniform vec2 size;
varying in vec2 texdim0[1];

void main() {
	gl_Position = gl_ModelViewProjectionMatrix * gl_PositionIn[0];
 	gl_Position.x -= size.x/2;
 	gl_Position.y += size.y/2;
 	gl_TexCoord[0].st = vec2(0,0) * texdim0[0];
 	EmitVertex();
 	gl_Position.y -= size.y;
 	gl_TexCoord[0].st = vec2(0,1) * texdim0[0];
 	EmitVertex();
 	gl_Position.x += size.x;
 	gl_Position.y += size.y;
 	gl_TexCoord[0].st = vec2(1,0) * texdim0[0];
 	EmitVertex();
 	gl_Position.y -= size.y;
 	gl_TexCoord[0].st = vec2(1,1) * texdim0[0];
 	EmitVertex();
}

#extension GL_EXT_geometry_shader4 : enable でジオメトリシェーダを有効化しています。main関数では、Maxから受け取ったsizeを使ってポイント(頂点)を、四つ頂点へと増加させています。特殊な書き方ですがEmitVertex()を呼ぶことでgl_texCoord[0]に設定されている頂点を出力しています。gl_TexCoordには、テクスチャの参照位置を入れていますね。Maxから受け取るテクスチャの座標が正規化いないので、texdim0[0]を掛けています。

JXS TIPS

  • 日本語でコメントが書かれているとMaxが即落ちる
  • ポイントスプライトで使うテクスチャのバインド先は、jit.gl.shaerではなく、jit.gl のオブジェクト
  • jit.gl.mesh: unbinding vertex array: GL Error: Invalid operation

    というエラーが出た場合は、jit.gl.meshのdraw_modeがpointsになっていない可能性があります。

Continue reading


jit.gl.shader / GLSL でフォグ #maxmsp #jitter #glsl

スクリーンショット 2014-08-05 03.50.41

OpenGL Superbibleのサンプルコードです。

JXS / GLSL

<jittershader name="mrr-3colored-lights">
	<description>
	</description>

	<language name="glsl" version="1.2">
		<program name="vp" type="vertex">
<![CDATA[

#version 120 

void main(void)
{
	gl_Position = gl_ModelViewProjectionMatrix *gl_Vertex;
	
	vec3 lightPos = gl_LightSource[0].position.xyz;
	
	vec3 N = normalize(gl_NormalMatrix * gl_Normal);
	vec4 V = gl_ModelViewMatrix *gl_Vertex;
	vec3 L = normalize(lightPos - V.xyz);
	vec3 H = normalize(L + vec3(0,0,1));
	
	const float specularExp = 128.0;
	
	
	float NdotL =max(0,dot(N,L));
	vec4 diffuse = gl_Color * vec4(NdotL);
	
	float NdotH = max(0,dot(N,H));
	vec4 specular = vec4(0);
	
	if(NdotL >0){
		specular = vec4(pow ( NdotH,specularExp));
	}
	
	gl_FogFragCoord =length(V);
	gl_FrontColor = (diffuse + specular);
}
]]>
		</program>
		<program name="fp" type="fragment">
<![CDATA[

#version 120

void main()
{
	 const float e = 2.71828;
	 float fogFactor = pow(0.5 *gl_FogFragCoord,2);
	 fogFactor = clamp(pow(e,-fogFactor),0,1);
	 
	 const vec4 fogColor =vec4(0.5,0.5,0.5,0.4);
	 gl_FragColor = mix(fogColor,gl_Color,fogFactor);
}
]]>
		</program>
	</language>
</jittershader>

Vertex Shaderの方でカメラからの距離を算出しておいて、Fragment Shadreで色を計算する時にFogの方程式を使ってFog量を決めています。


jit.gl.shader / GLSL でプロシージャルテクスチャ(星)生成 その2 #maxmsp #jitter #glsl

スクリーンショット 2014-08-05 01.25.04

配色がRedbull感

OpenGL Superbibleのサンプルコードです。レッドブルが飲みたくなる配色。

JXS / GLSLコード

<jittershader name="mrr-3colored-lights">
	<description>
	</description>

	<language name="glsl" version="1.2">
		<program name="vp" type="vertex">
<![CDATA[

#version 120 

varying vec3 N,L,V;

void main(void)
{
	vec3 lightPos = gl_LightSource[0].position.xyz;

	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;	
	
	//map object-space position onto unit sphere
	V =gl_Vertex.xyz;
	
	//eye-space normal
	N = gl_NormalMatrix * gl_Normal;
	
	//eye-space light vector
	vec4 Veye = gl_ModelViewMatrix * gl_Vertex;
	L =lightPos - Veye.xyz;	
}

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

#version 120

varying vec3 V;
varying vec3 N;
varying vec3 L;

const vec3 myRed 	= vec3(0.6,0,0);
const vec3 myYellow = vec3(0.6,0.5,0);
const vec3 myBlue 	= vec3(0,0.3,0.6);

//Star Vertex
const vec3 myHalfSpace0 = vec3(0.31,0.95,0);
const vec3 myHalfSpace1 = vec3(-0.81,0.59,0);
const vec3 myHalfSpace2 = vec3(-0.81,-0.59,0);
const vec3 myHalfSpace3 = vec3(0.31,-0.95,0);
const vec3 myHalfSpace4 = vec3(1.,0.,0.);

const float stripeThickness = 0.4;
const float starSize = 0.2;
const float smoothEdgeTol = 0.005;
const float ambientLighting = 0.5;
const float specularExp = 128.0;
const float specularIntensity = 1.;

void main()
{
	vec4 distVector;
	float distScalar;
	
	vec3 NN = normalize(N);
	vec3 NL = normalize(L); //for ambient;
	vec3 NH = normalize(NL +vec3(0,0,1)); //for specular
	vec3 NV = normalize(V);
	
	float myInOut = -3;
	
	distVector.x = dot(NV, myHalfSpace0);
	distVector.y = dot(NV, myHalfSpace1);
	distVector.z = dot(NV, myHalfSpace2);
	distVector.w = dot(NV, myHalfSpace3);
	distScalar = dot(NV,myHalfSpace4);
	
	distVector += starSize;
	distScalar += starSize;
	
	//map 0 ~ 1
	distVector = smoothstep(0.,smoothEdgeTol, distVector);
	distScalar = smoothstep(0.,smoothEdgeTol, distScalar);
	
	myInOut += dot(distVector, vec4(1.0));
	myInOut += distScalar;
	myInOut = clamp(myInOut, 0,1);
	
	vec3 surfColor = mix(myYellow, myRed, myInOut);
	
	myInOut = smoothstep(0,smoothEdgeTol,abs(NV.z) - stripeThickness);
	surfColor = mix(myBlue,surfColor,myInOut);
	
	surfColor *= (ambientLighting + vec3(max(0,dot(NN,NL))));	
	surfColor += (specularIntensity * vec3(pow(max(0,dot(NN,NH)),specularExp)));
	
	gl_FragColor = vec4(surfColor,1);
}
]]>
		</program>
	</language>
</jittershader>

星の各点との内積をとって、星の頂点方向の長さ(0以上1以下)を合計し星のサイズをオフセットとして加算。その値が閾値を超えたら赤色にして、超えなかったら黄色に塗って星を描画している。星の頂点位置とVertexの単位ベクトルの関係性を使って星の形を描画しているのだが、ちょっとまだ理解しきれていない・・・。


jit.gl.shader / GLSL でプロシージャルテクスチャ(ビーチボール)生成

スクリーンショット 2014-08-04 23.30.07

夏だしビーチボールで夏感を演出

嘘です。プログラムだけでテクスチャ生成する手法をプロシージャルテクスチャと呼ぶそうですが、OpenGL Superbibleにビーチボールのサンプルがあったので実装してみました。法線ベクトルやVertexの位置を利用して色を決めてますね。まだアルゴリズムを飲み込めてないので、ポケモンのハイパーボールを練習で作ってみようか。

JXS / GLSLコード

<jittershader name="mrr-3colored-lights">
	<description>
	</description>

	<language name="glsl" version="1.2">
		<program name="vp" type="vertex">
<![CDATA[

#version 120 

varying vec3 N,L,V;

void main(void)
{
	vec3 lightPos = gl_LightSource[0].position.xyz;

	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;	
	
	//map object-space position onto unit sphere
	V =gl_Vertex.xyz;
	
	//eye-space normal
	N = gl_NormalMatrix * gl_Normal;
	
	//eye-space light vector
	vec4 Veye = gl_ModelViewMatrix * gl_Vertex;
	L =lightPos - Veye.xyz;	
}

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

#version 120

varying vec3 V;
varying vec3 N;
varying vec3 L;

const vec3 myRed 	= vec3(1,0,0);
const vec3 myYellow = vec3(1,1,0);
const vec3 myGreen 	= vec3(0,1,0);
const vec3 myBlue 	= vec3(0,0,1);
const vec3 myWhite 	= vec3(1,1,1);
const vec3 myBlack 	= vec3(0,0,0);

const vec3 northHalfSpace = vec3(0,0,1);
const vec3 northeastHalfSpace = vec3(0.707,0,0.707);
const vec3 northwestHalfSpace = vec3(-0.707,0,0.707);
const float capSize = 0.03;
const float smoothEdgeTol = 0.0005;
const float ambientLighting = 0.1;
const float specularExp = 10.0;
const float specularIntensity = 0.5;

void main()
{
	vec3 NN = normalize(N);
	vec3 NL = normalize(L); //for ambient;
	vec3 NH = normalize(NL +vec3(0,0,1)); //for specular
	vec3 NV = normalize(V);
	
	float mirror = (NV.x >= 0) ? 1. : -1.;
	NV.xz *= mirror; //
	
	vec4 distance;
	distance.x = dot(NV, northHalfSpace);
	distance.y = dot(NV, northeastHalfSpace);
	distance.z = dot(NV, northwestHalfSpace);
	
	distance.w = abs(NV.y) - 1.0 + capSize;	
	distance = smoothstep(vec4(0.), vec4(smoothEdgeTol), distance);
	
	
	vec3 surfColor = mix(myBlack,myRed,distance.x);
	surfColor += mix(myBlack,myGreen,distance.y * (1.0 - distance.z));
	surfColor += mix(surfColor, myBlue, 1.0 - distance.y);
	
	//top bottom
	surfColor = mix(surfColor, myWhite, distance.w);
	
	//ambient
	surfColor *= (ambientLighting + vec3(max(0,dot(NN,NL))));
	//specluar
	surfColor += (specularIntensity *vec3(pow(max(0,dot(NN,NH)),specularExp)));
	
	gl_FragColor = vec4(surfColor,1);
}
]]>
		</program>
	</language>
</jittershader>

jit.gl.shader / GLSL で specular lighting を実装してみた #maxmsp #jitter #glsl

スクリーンショット 2014-08-04 22.09.47

Specular Lighting

OpenGL Superbibleのサンプルコードを練習がてら実装してみました。こちらの本は5th Editionも出ているのですが、Maxの対応がOpenGL2.0 / GLSL1.2 までなので、4th Editionを使っています。

JXS / GLSLサンプルコード

<jittershader name="mrr.specular">
	<description>
	specular-light
	</description>

	<language name="glsl" version="1.2">
		<program name="vp" type="vertex">
<![CDATA[

#version 120 

void main(void)
{
	//normal MVP transform
	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;	

	//Lighting
	vec3 lightPos = gl_LightSource[0].position.xyz;		

	vec3 N = normalize(gl_NormalMatrix * gl_Normal); 
	vec4 V = gl_ModelViewMatrix * gl_Vertex;
	vec3 L = normalize(lightPos - V.xyz);
	vec3 H = normalize(L + vec3(0,0,1));
	
	const float specularExp = 128.;
	
	//calculate diffuse lighting
	float NdotL = max(0,dot(N,L));
	gl_FrontColor = gl_Color *vec4(NdotL);
	
	//calculate specular lighting
	float NdotH = 0;//max(0.0, dot(N,H));
	
	if(NdotL > 0.0){
		NdotH = max(0.0,dot(N,H) * 8.0 - 7.0);
	}
	
	gl_TexCoord[0] = vec4(NdotH,0,0,1);	
}
]]>
		</program>
		<program name="fp" type="fragment">
<![CDATA[

#version 120

void main()
{
	//add diffuse & specular
	gl_FragColor = gl_Color + gl_TexCoord[0];
}
]]>
		</program>
	</language>
</jittershader>

おまけ

スクリーンショット 2014-08-04 21.38.19
法線ベクトルで色を塗ってみました。


Cycling74 Max で GLSL1.2 を使ったシェーディングをはじめよう #maxmsp #jitter #glsl

スクリーンショット 2014-08-04 16.08.30

シェーダがあるでは無いか!

Maxを使ったインタラクティブアプリケーションの開発は、音や音楽とのインタラクションや、Ableton Liveとの連携、コンパイルしないで変更を加え続けられるリアルタイム性など、他には無い独自なメリットが色々とあると思います。

しかし3Dの描画に関しては、OpenGLの対応バージョンが2.0止まりな事もあり、openFrameworksやUnity等の開発環境と比べると少し見劣りします。とはいえ、プログラマブルシェーダを利用する事ができるので、可能性としては例えばSIGGRAPHの論文を実装して最新のレンダリングもできるのではないかな・・・!?

そこで今回はMaxの3D描画を強化するべく、jitterに関するGLSL周辺の調査とサンプルプログラム試してみました。

Maxにおけるシェーダについて

  • JXSファイル:XML形式でMaxとやりとりを行うパラメーター等を記述するファイル。またGLSL等のシェーダをこのファイルに読込むか直接書く事でシェーダを利用する。.jxsファイルで保存したらjit.gl.shaderとjit.gl.slabでreadして使う。
  • シェーダの種類:Vertex Shader / Geometry Shader / Fragment Shader が利用可能
  • シェーダ言語:GLSL1.2 / CG / ARB
  • 豊富なサンプル:Mac版のフォルダパス→ /Applications/Max 6.1/Cycling ’74/jitter-shaders Geometry ShaderやGPGPU、Audio処理用のシェーダも。ジオメトリシェーダを使う際は、プリミティブの種類がシェーダと合ってないと利用できないので注意。
  • Cycling74 Forumでは、GLSL1.5(OpenGL3)そうとうのシェーダーが使えるという記述がありましたが、私の環境(Macbook Pro Retina / Mavericks)では利用できませんでした。
  • jit.gl.pix:フラグメントシェーダのみに特化したオブジェクト。jit.gl.slabと互換。

jit.gl.pixでは使えないバーテックスとジオメトリが鍵になりそうな気がしますね。今後の記事でサンプルファイルなどを紹介できればと思います。

JXS / GLSLサンプルコード

<jittershader name="mrr-test-simple">
	<description>
	test
	</description>

	<language name="glsl" version="1.2">
		<program name="vp" type="vertex">
<![CDATA[

#version 120 

void main(void)
{
	vec4 clipCoord = gl_ModelViewProjectionMatrix *gl_Vertex;
	gl_Position = clipCoord;
	gl_FrontColor = gl_Color;

	vec3 ndc =clipCoord.xyz / clipCoord.w;

	gl_FrontSecondaryColor = vec4((ndc *0.5) +0.5,1);

	//gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
]]>
		</program>
		<program name="fp" type="fragment">
<![CDATA[

#version 120

void main()
{
	//gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
	gl_FragColor =mix(gl_Color,vec4(vec3(gl_SecondaryColor),1.),1);
}
]]>
		</program>
	</language>
</jittershader>

上記画像は、jit.gl.shaderに上記JXSファイルを読み込ませて、jit.gl.gridshapeとjit.gl.multiple で作ったモデルに対してアタッチして生成した画像です。glsl部分は、外部ファイルを読込む事も可能です。

参考サイト