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

[c]
#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;
}
[/c]

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

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コード

[c]
<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>
[/c]

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

パラメーターの宣言

[html]
<param name="size" type="vec2" default="1. 1." />
[/html]
ジオメトリシェーダに限らない事ですが、Maxから指定するパラメーターは、param要素として指定します。初期値も与えられますがコンマで区切っては駄目みたいなので注意です。

パラメーターのバインド

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

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

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

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

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

[html]
<program name="gp" vertices_out="4" input_type="points" output_type="triangle_strip" type="geometry">
<![CDATA[
/*glslのプログラムを記述*/
]]>
</program>
[/html]
program要素を宣言します。vertices_out / input_type / output_typeが合ってないと実行できません。今回は、”points”を”triangle_strip”に変換し”4”つのvertexが出力されるという設定になっています。

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

[c]
#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();
}
[/c]
#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になっていない可能性があります。

続きを読む jit.gl.shader / GLSL でポイントスプライト #maxmsp #jitter #glsl

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

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

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

JXS / GLSL

[html]
<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>
[/html]

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コード

[html]
<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>
[/html]

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