GLSLで書いたシェーダーをUnityのマテリアルにセット、WebGL出力してみます

Unityでシェーダーを書く機会は中々ありませんでしたが、今後のためにもGLSLでシェーダーを書いてみます。

GLSLの特徴はOpenGLに使えるという点です。ということは、スマートフォンゲームやWebGL(ブラウザ)に応用することもできます。また、言語的には、ほぼCに近い、ポインタのないC言語といった印象があります。(今回、細かな言語仕様の説明は割愛させていただきます。)

まずはGLSL on ブラウザで書く

まずは純粋にGLSLのフラグメントシェーダーだけを書いて試してみたいと思います。
これにはGLSL Sandboxというサイトを使います。このサイトでは、WebGL経由でシェーダーのGLSLコードをリアルタイムプレビュー、さらにjsdoitのようにギャラリーやフォークなどもできます。先にここで書く理由は、ちょっと寄り道してみたかったからです。w
WebでもUnityでもどっちでも動くと良いですし。

ちなみに、GLSL Sandboxの開発にはthree.jsの@mrdoob氏など、著名開発者が名を連ねています。

凝ると際限がないと思いますが、そこそこ見栄えしてきたら次のステップへいきます。

29g51155224222

デモはこちら : http://glslsandbox.com/e#29054.0

ソースは以下の通りです。
ポイントは(というほどではありませんが・・)時間経過を元にして三角関数を動かしているところです。

#ifdef GL_ES
precision mediump float;
#endif

uniform float time;
uniform vec2 resolution;

void main( void ) {

	vec2 position = ( gl_FragCoord.xy / resolution.xy ) - 0.5 ;
	vec3 col = vec3(0.0,0.0,0.0);
	
	//15個円を描く
	for(int i = 0; i < 15; i++){
		float red = 0.01  / abs(length(position +  float(i) * 0.1)  - 1.0 * abs(sin(time))) ;
		col.r += red;
	}
	
	//時間変化に合わせて色を変える
	col.g = abs(cos(time));
	col.b = abs(sin(time));

	gl_FragColor = vec4( col , 1.0 );

}

シェーダーのコードをUnityのマテリアルに持っていく

さて、フラグメントシェーダーのアルゴリズムができたので、Unityのマテリアルを作ってシェーダーをセットしていきます。

ここらへんは以下のページが非常に参考になりました。
「Unity で GLSL によるカスタムシェーダを利用する方法」
http://tips.hecomi.com/entry/20130324/1364136769

マテリアルを作って・・・

スクリーンショット 2015-11-25 18.21.19

シェーダーを作って・・・

スクリーンショット 2015-11-25 18.22.12

シェーダーを書いていきます。

GLSL SandboxのコードをUnity用にする。

先の参考ページをもとにシェーダーのコードを書いていきます。
というか、書いていて気づいたんですがUnityはシェーダーの変更があった場合、リアルタイムに反映してくれるんですね。こりゃ知らなかった。。良いですね。

出来上がったシェーダーはこちら

Shader "Custom/GLSL Shader" {
	SubShader {
		Pass {
			GLSLPROGRAM

#ifdef VERTEX
void main()
{
	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
#endif

#ifdef FRAGMENT
	uniform vec4 _Time;
	uniform vec4 _ScreenParams;

void main()
{
			
	vec2 position = ( gl_FragCoord.xy / _ScreenParams.xy ) - 0.5 ;
	vec3 col = vec3(0.0,0.0,0.0);
	float time = _Time.x * 15.0;

	for(int i = 0; i < 15; i++){
		float red = 0.01  / abs(length(position +  float(i) * 0.1)  - 1.0 * abs(sin(time))) ;
		col.r += red;
	}
	
	col.g = abs(cos(time));
	col.b = abs(sin(time));

	gl_FragColor = vec4( col , 1.0 );
}
#endif

			ENDGLSL
		}
	}
}

ここまでで、こんな感じです。(それっぽいcubeを足しました)

スクリーンショット 2015-11-25 18.32.26

いよいよ再生・・・

こうなりました・・・!!
先ほどのシェーダーが壁一面に広がっています。

29g511552242228

動いたので、UnityからWebGL出力

さて、エディター上では一通り、それなりの動きを見ることができました。
次はUnityからWebGL出力をします。これでブラウザ上で上手くシェーダーが動けばいいんですが・・・

書き出してしばし待機・・・

圧縮されてはいますが、動きました!

スクリーンショット 2015-11-25 18.48.59

デモはこちらになります。

モバイルブラウザでも動くの?

ちなみに、iOS9.1で動くのかも試してみましたが・・・
動きませんでした。まだ動かないかー。Android5.1でも動きませんでした。
しかも、こんなアラートも出ています。

IMG_0537

「まだUnityはWebGL出力をサポートしていないけどいいの?それでもいいならOKを押してくれたって構わないが・・」という相変わらずの内容です。当然OKを押しましたが、動きません。

モバイル対応の道はまだまだのようです。詳しい情報をご存知の方はお教えいただければと思います。

結果

GLSLで書いたシェーダーを、UnityのWebGL出力で、PCブラウザでは動かすことができました!(モバイルブラウザでは不可)
シェーダーを書くと、画面の表現力が一段増す感じがしますね。

【デモあり】Unity5のWebGLエクスポートをしてみました

GDC2015にて、Unity5の正式リリース発表がありました。
早速ダウンロードしてWebGLでエクスポートしてみました!
プラグイン不要ってすごい。

デモはこちら
URL : http://mankindinc.jp/wp01/games/carscenehtml5/

動いた・・・!

タップでCubeが出てくるようにしています。
ライトが非常に綺麗になりましたね。
あと、rigidbodyの処理が心なしか、上がってる感じがします・・・

WebGLということはiOSでも表示できるわけですよね。
iPhoneで実行してみると・・・

まだ対応してないようでした。

【3D】three.jsで400個のボックスを伸縮させるチュートリアル

iOS8にWebGLが搭載されたこともあり、three.jsにあらためて注目しています。

順を追ってコードのメモを書いていきます。

ボックス生成

for(i=0;i<20;i++){
	for(j=0;j<20;j++){
		var geometry = new THREE.BoxGeometry(10,10,10);
		var material = new THREE.MeshNormalMaterial();
		var cube = new THREE.Mesh(geometry , material);
		cube.castShadow = true;
		cube.position.set(i*10,0,j*10);
		scene.add(cube);
	}
}

iが縦軸、jが横軸になるように、2回ループを使って配置していきます。

座標を10ずつずらしているのは、ボックスのサイズが10だからです。

図示するとこんな感じです。

メインループの呼び方

(function mainLoop() {
requestAnimationFrame(mainLoop);

/*
ここが毎フレーム呼ばれる
*/

renderer.render(scene, camera);
})();

そうです「即時関数」です。即時関数で再帰的にメインループを呼びますので、ぐるぐるループして呼ばれます。

その際「requestAnimationFrame()」でフレームごとに呼ぶのがポイントですね。

処理が重たいなら、ここでフレーム調整するといいと思います。

メインループ内(1) → 全てのボックスに対して、forループで処理

var ob = scene['children'];

for(i=0;i<ob.length;i++){
	if(ob[i].scale.y>max){
		incFlg[i] = 1;
	}else if(ob[i].scale.y<min){
		incFlg[i] = 0;
	}
	if(incFlg[i] == 1){
		ob[i].scale.y -= yInc[i];
		ob[i].position.y -= yInc[i]*5;
	}else{
		ob[i].scale.y += yInc[i];
		ob[i].position.y += yInc[i]*5;
	}
}

先に「var ob = scene[‘children’];」でobにsceneの子を代入したうえで、ループをまわしていきます。

フラグ(incFlg)がある程度までいったら、今度はサイズを減らしていきます。

カメラの軌道は・・・

rad = angle * (Math.PI / 180)/4;
camerax =centerx + radian * Math.cos(rad);
cameray =centery + radian * Math.sin(rad);
camera.position.set(cameray,200,camerax);
angle+=3;

カメラの軌道に関して、ここで説明するのは割愛させていただきます。

こちらのコードを公式的に理解するか、根本から理解したい場合は「ジェネラティブ・アート -Processingによる実践ガイド」をおすすめします。この本は、軌道の描き方や三角関数などを原理から分かりやすく説明してくれています。Processingの本なのですが、実はどんなものにも応用できます。JavaScriptの類書でも、この本より分かりやすく刺激的な本はなさそうです。

完成コードはこちら

var main = function () {
var scene = new THREE.Scene();
//---------------------------------------------
//properties
var radius = 100;
var angle = 0;
var angleArr = [];
var angleInc = [];
var roteArr = [];
var radArr = [];
var rad = 0;
var radiuschange = -0.1;
var count = 1500;
var saikoro = 0;

var width = 800,
	height = 600;

//---------------------------------------------
//mesh

for(i=0;i<20;i++){
	for(j=0;j<20;j++){
		var geometry = new THREE.BoxGeometry(10,10,10);
		var material = new THREE.MeshNormalMaterial();
		var cube = new THREE.Mesh(geometry , material);
		cube.castShadow = true;
		cube.position.set(i*10,0,j*10);
		scene.add(cube);
	}
}

//---------------------------------------------
//light

//---------------------------------------------
//camera
var camera = new THREE.PerspectiveCamera(65,width/height+0.1,1,2000);
camera.position.set(200,220,200);
camera.lookAt(cube.position);

//---------------------------------------------
//renderer
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width,height);
renderer.setClearColor(0xeeeeee,1);
renderer.shadowMapEnabled = true;
document.getElementById('game').appendChild(renderer.domElement);
renderer.render(scene,camera);

/*---------------------------------------------
//helper
var axis = new THREE.AxisHelper(1000);
axis.position.set(0,0,0);
scene.add(axis);
*/

var controls = new THREE.OrbitControls(camera,renderer.domElement);

var ob = scene['children'];
var inc = 0;
console.log(ob[1]);
var y = 1;
var noise = 0;
var max = 20;
var min = 1;
var incFlg = [];
var ypos = 0;
yInc = [];
var camerax = 0
var cameray = 0;
var centerx = 150;
var centery = 150;
var rad = 0;
var angle = 0;
var radian = 100;
var cameraAngFlg = 0;

for(i=0;i<ob.length;i++){
	yInc[i] = Math.random()/10;
	incFlg[i] = 0;
}
//---------------------------------------------
//---------------------------------------------
//mainloop
(function mainLoop() {
requestAnimationFrame(mainLoop);

for(i=0;i<ob.length;i++){
	if(ob[i].scale.y>max){
		incFlg[i] = 1;
	}else if(ob[i].scale.y<min){
		incFlg[i] = 0;
	}
	if(incFlg[i] == 1){
		ob[i].scale.y -= yInc[i];
		ob[i].position.y -= yInc[i]*5;
	}else{
		ob[i].scale.y += yInc[i];
		ob[i].position.y += yInc[i]*5;
	}
}

//くわしくは「ジェネラティブ・アート -Processingによる実践ガイド」を参照
rad = angle * (Math.PI / 180)/4;
camerax =centerx + radian * Math.cos(rad);
cameray =centery + radian * Math.sin(rad);
camera.position.set(cameray,200,camerax);
angle+=3;

renderer.render(scene, camera);
controls.update();
})();
//end mainloop
//---------------------------------------------
//---------------------------------------------

};

function r(n){
	Math.floor(Math.random() * (n + 1) );
}

window.addEventListener( 'DOMContentLoaded', main, false );

iOS8がWebGLに対応したので早速3Dモデルを動かしてみた【three.js x iOS8】

iOS8から、ついに解禁となったWebGL(3Dの描画エンジンへJavaScriptからアクセスできる機能)。

そこで、早速以前作ったプログラム(three.js+WebGL)を動かしてみました。

機種はiPhone5(無印)です。iOS8の方はお試しあれ!(PCでも見れます)

結果・・・

動いた!!!

元プログラムのURL : http://mankindinc.jp/wp01/programming/webgl/1.html

three.jsでロゴも動かしてみました。

three.jsのサンプルロゴも「Mankind,inc.」にして動かしてみました

うむ。three.jsも動きますね。

更に、Unity5ではWebGLへの対応も決定しています。
夢が広がります。