This two last weeks were very exciting thanks to Adobe’s announcement : a new framework for 2D games : Starling, running on top of Stage3D (Molehill). Flash Player 11 is out henceforth, new 3D frameworks are coming : Adobe Proscenium, Aerys. And other big things like Unreal Engine 3 with Unreal Tournament 3 in our browser !
There’s a lot to digest ! Starling may be the next thing I will experiment…
Anyway, before all this annoucement, I was keeping an eye on JavaScript and its most awesome framework : ThreeJS created by Mr Doob. ThreeJS is a “lightweight 3D engine with a very low level of complexity — in other words, for dummies”. Dummy ? This is what I’m with 3D engine… in fact this is the first time that I try a 3D engine… so let’s start !
The three.js repository is on github with code source and many examples.
I started to learn ThreeJS with Ilmari Heikkinen’s slides that you can find here. It is really a good way to start.
Click here to see my experimentation.
I used three.js with WebGL render. My source code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
| <html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>three.js</title>
<style type="text/css">
body {
color: #808080;
font-family:Monospace;
font-size:13px;
text-align:center;
background-color: #ffffff;
margin: 0px;
overflow: hidden;
}
#info {
position: absolute;
top: 0px; width: 100%;
padding: 5px;
}
</style>
</head>
<body>
<div id="container"></div>
<div id="info"><a href="http://github.com/mrdoob/three.js" target="_blank">three.js</a></div>
<script type="text/javascript" src="Three.js"></script>
<script type="text/javascript" src="raf.js"></script>
<script type="text/javascript">
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(document.body.clientWidth, document.body.clientHeight);
document.body.appendChild(renderer.domElement);
renderer.setClearColorHex(0xEEEEEE, 1.0);
renderer.clear();
renderer.shadowCameraFov = 50;
renderer.shadowMapWidth = 1024;;
renderer.shadowMapHeight = 1024;
var fov = 45; // camera field-of-view in degrees
var width = renderer.domElement.width;
var height = renderer.domElement.height;
var aspect = width / height; // view aspect ratio
var near = 1; // near clip plane
var far = 10000; // far clip plane
var camera = new THREE.Camera(fov, aspect, near, far);
camera.position.z = -400;
camera.position.x = 200;
camera.position.y = 350;
const NBR_ELEMENTS = 250;
var scene = new THREE.Scene();
var light = new THREE.SpotLight();
light.castShadow = true;
light.position.set( 170, 330, -160 );
scene.addLight(light);
var tab = [];
var sprite;
for (var i = 0; i < NBR_ELEMENTS; ++i) {
sprite = new THREE.Mesh(new THREE.CubeGeometry(25, 25, 25), new THREE.MeshLambertMaterial({color: Math.random() * 0xffffff}));
sprite.castShadow = true;
scene.addChild(sprite);
if (i == 0)
sprite.position.x = Math.random() * 1000;
else
sprite.position.x = (Math.random() > 0.5) ? Math.random() * 1000 : -Math.random() * 1000;
sprite.position.y = (Math.random() > 0.5) ? Math.random() * 1000 : -Math.random() * 1000;
sprite.position.z = (Math.random() > 0.5) ? Math.random() * 1000 : -Math.random() * 1000;
tab.push(sprite);
}
var oneElement = false;
var diffx;
var diffy;
var diffz;
var i = 0;
renderer.shadowMapEnabled = true;
renderer.render(scene, camera);
var paused = false;
var last = new Date().getTime();
var down = false;
var sx = 0, sy = 0;
window.onmousedown = function (ev){
down = true; sx = ev.clientX; sy = ev.clientY;
};
window.onmouseup = function(){ down = false; };
window.onmousemove = function(ev) {
if (down) {
var dx = ev.clientX - sx;
var dy = ev.clientY - sy;
camera.position.x += dx;
camera.position.y += dy;
sx += dx;
sy += dy;
}
}
function animate(t) {
if (!paused) {
/*last = t;
sphere.position.x = Math.cos(t/600)*300;
sphere.position.z = Math.sin(t/600)*300;
sphere.rotation.y = t/800;*/
if (oneElement) {
if (tab[0].position.x < 1000) {
i = 0;
while (i < NBR_ELEMENTS) {
diffx = 0 - tab[i].position.x;
diffy = 0 - tab[i].position.y;
diffz = 0 - tab[i].position.z;
tab[i].position.x -= diffx * 0.05;
tab[i].position.y -= diffy * 0.05;
tab[i].position.z -= diffz * 0.05;
++i;
}
} else {
oneElement = false;
}
} else {
if (tab[0].position.x > 1) {
i = 0;
while (i < NBR_ELEMENTS) {
diffx = 0 - tab[i].position.x;
diffy = 0 - tab[i].position.y;
diffz = 0 - tab[i].position.z;
tab[i].position.x += diffx * 0.05;
tab[i].position.y += diffy * 0.05;
tab[i].position.z += diffz * 0.05;
++i;
}
} else {
oneElement = true;
}
}
renderer.clear();
renderer.render(scene, camera);
}
window.requestAnimationFrame(animate, renderer.domElement);
};
animate(new Date().getTime());
onmessage = function(ev) {
paused = (ev.data == 'pause');
};
</script>
</body>
</html> |
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>three.js</title>
<style type="text/css">
body {
color: #808080;
font-family:Monospace;
font-size:13px;
text-align:center;
background-color: #ffffff;
margin: 0px;
overflow: hidden;
}
#info {
position: absolute;
top: 0px; width: 100%;
padding: 5px;
}
</style>
</head>
<body>
<div id="container"></div>
<div id="info"><a href="http://github.com/mrdoob/three.js" target="_blank">three.js</a></div>
<script type="text/javascript" src="Three.js"></script>
<script type="text/javascript" src="raf.js"></script>
<script type="text/javascript">
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(document.body.clientWidth, document.body.clientHeight);
document.body.appendChild(renderer.domElement);
renderer.setClearColorHex(0xEEEEEE, 1.0);
renderer.clear();
renderer.shadowCameraFov = 50;
renderer.shadowMapWidth = 1024;;
renderer.shadowMapHeight = 1024;
var fov = 45; // camera field-of-view in degrees
var width = renderer.domElement.width;
var height = renderer.domElement.height;
var aspect = width / height; // view aspect ratio
var near = 1; // near clip plane
var far = 10000; // far clip plane
var camera = new THREE.Camera(fov, aspect, near, far);
camera.position.z = -400;
camera.position.x = 200;
camera.position.y = 350;
const NBR_ELEMENTS = 250;
var scene = new THREE.Scene();
var light = new THREE.SpotLight();
light.castShadow = true;
light.position.set( 170, 330, -160 );
scene.addLight(light);
var tab = [];
var sprite;
for (var i = 0; i < NBR_ELEMENTS; ++i) {
sprite = new THREE.Mesh(new THREE.CubeGeometry(25, 25, 25), new THREE.MeshLambertMaterial({color: Math.random() * 0xffffff}));
sprite.castShadow = true;
scene.addChild(sprite);
if (i == 0)
sprite.position.x = Math.random() * 1000;
else
sprite.position.x = (Math.random() > 0.5) ? Math.random() * 1000 : -Math.random() * 1000;
sprite.position.y = (Math.random() > 0.5) ? Math.random() * 1000 : -Math.random() * 1000;
sprite.position.z = (Math.random() > 0.5) ? Math.random() * 1000 : -Math.random() * 1000;
tab.push(sprite);
}
var oneElement = false;
var diffx;
var diffy;
var diffz;
var i = 0;
renderer.shadowMapEnabled = true;
renderer.render(scene, camera);
var paused = false;
var last = new Date().getTime();
var down = false;
var sx = 0, sy = 0;
window.onmousedown = function (ev){
down = true; sx = ev.clientX; sy = ev.clientY;
};
window.onmouseup = function(){ down = false; };
window.onmousemove = function(ev) {
if (down) {
var dx = ev.clientX - sx;
var dy = ev.clientY - sy;
camera.position.x += dx;
camera.position.y += dy;
sx += dx;
sy += dy;
}
}
function animate(t) {
if (!paused) {
/*last = t;
sphere.position.x = Math.cos(t/600)*300;
sphere.position.z = Math.sin(t/600)*300;
sphere.rotation.y = t/800;*/
if (oneElement) {
if (tab[0].position.x < 1000) {
i = 0;
while (i < NBR_ELEMENTS) {
diffx = 0 - tab[i].position.x;
diffy = 0 - tab[i].position.y;
diffz = 0 - tab[i].position.z;
tab[i].position.x -= diffx * 0.05;
tab[i].position.y -= diffy * 0.05;
tab[i].position.z -= diffz * 0.05;
++i;
}
} else {
oneElement = false;
}
} else {
if (tab[0].position.x > 1) {
i = 0;
while (i < NBR_ELEMENTS) {
diffx = 0 - tab[i].position.x;
diffy = 0 - tab[i].position.y;
diffz = 0 - tab[i].position.z;
tab[i].position.x += diffx * 0.05;
tab[i].position.y += diffy * 0.05;
tab[i].position.z += diffz * 0.05;
++i;
}
} else {
oneElement = true;
}
}
renderer.clear();
renderer.render(scene, camera);
}
window.requestAnimationFrame(animate, renderer.domElement);
};
animate(new Date().getTime());
onmessage = function(ev) {
paused = (ev.data == 'pause');
};
</script>
</body>
</html>
I used raf.js to make an “enter frame” on the page, you can find the script on Ilmari Heikkinen’s repository.