tag:blogger.com,1999:blog-85771304397554711552024-03-06T21:01:57.425+01:00WebGL FactoryTutorials and how to's on WebGL and 3D math.Denis Tosichttp://www.blogger.com/profile/14992340631798182965noreply@blogger.comBlogger15125tag:blogger.com,1999:blog-8577130439755471155.post-20559627662308701182011-06-19T14:05:00.001+02:002011-06-19T14:05:47.915+02:00On Vacations...<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheZAKZ01c4y9hnIxdkhrqS-uuP0x2x058eMhvrm36v5-YhwuG0TVp2S4SPLVrtl9g7mhgN9seITbBVuHWvLwA7105Kr0aY-HM8KEKn-BnDOHRDGPgn0AGevc_pKBcs6bwOpwp6Zap1vtJa/s1600/ist2_841836-vacation-time.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheZAKZ01c4y9hnIxdkhrqS-uuP0x2x058eMhvrm36v5-YhwuG0TVp2S4SPLVrtl9g7mhgN9seITbBVuHWvLwA7105Kr0aY-HM8KEKn-BnDOHRDGPgn0AGevc_pKBcs6bwOpwp6Zap1vtJa/s320/ist2_841836-vacation-time.jpg" width="318" title="Vacation"/></a></div><div class="separator" style="clear: both; text-align: center;"><br />
</div>... for the next few weeks. Cheers! :D<div class="blogger-post-footer"><a href="http://webglfactory.blogspot.com/"> WebGL Factory</a><br/>
d copyright, 2011. Share it, but link back to this blog.<br/>
Denis Tosic<br/></div>Denis Tosichttp://www.blogger.com/profile/14992340631798182965noreply@blogger.com0tag:blogger.com,1999:blog-8577130439755471155.post-29540553180343673562011-06-10T21:33:00.001+02:002011-06-10T21:35:36.647+02:00Ro.meFor does of you who still did not see it:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/ReH7zzj5GPc?feature=player_embedded' frameborder='0'></iframe></div><div class="separator" style="clear: both; text-align: center;"><i>Look ma', birdy!</i></div><br />
And here is the link to the interactive version of <a href="http://www.ro.me/" title="ro.me demo">ro.me</a>. Thumbs UP!<div class="blogger-post-footer"><a href="http://webglfactory.blogspot.com/"> WebGL Factory</a><br/>
d copyright, 2011. Share it, but link back to this blog.<br/>
Denis Tosic<br/></div>Denis Tosichttp://www.blogger.com/profile/14992340631798182965noreply@blogger.com0tag:blogger.com,1999:blog-8577130439755471155.post-19231067832598704932011-06-05T21:44:00.000+02:002011-06-05T21:44:34.141+02:00Render text with canvas 2D<div style="text-align: justify;">Hello everyone, and welcome to my 4th tutorial about WebGL! This week we shall try something more interesting than spinning a box - Instead of explaining what we are going to do, just take a look at the video below:</div><div style="text-align: justify;"><br />
</div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/ZxltTg1BPmg?feature=player_embedded' frameborder='0'></iframe></div><div class="separator" style="clear: both; text-align: center;"><i>You can even put dirty content on it!</i></div><div class="separator" style="clear: both; text-align: center;"><i></i></div><a name='more'></a><div style="text-align: justify;"><i><br />
</i></div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">The code is not so different then the one from the previous <a href="http://webglfactory.blogspot.com/2011/05/adding-textures.html" title="Adding multiple textures tutorial">tutorial</a>. But this time, instead of using some static images, let's write our own simple textures - in real time! As before, you download the source code for this <a href="http://dl.dropbox.com/u/3947885/tutorials/Tutorial%203%20-%20WebGL%20Factory.rar" title="Render text to texture | WebGL Factory">tutorial here</a> . I am going to skip the introduction part and head straight over to the important things - how to render a texture with text content.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Step no.1 - create another canvas where we will render our text. That canvas is then going to be copied to a texture which will be used on our box. The new thing here is - we don't need WebGL to create this texture, instead we can use anoter context - the <b>2D </b>context. To get access to the 2D API, we use the same function as for WebGL, just with the '2d' parameter. That would then look like this:</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>context2D = document.getElementById('textCanvas').getContext('2d');</b></span></div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Our entry function has also a few new lines (all <b>context2D </b>calls refer to the <b>textCanvas</b>, all <b>gl </b>calls to the <b>3d scene</b>):</div><br />
<ul><li><b>setUpTextArea() </b>- set's up the text format, colors, positioning etc.</li>
<li><b>writeTextToCanvas(Sring text, int textureIndex) - </b>renders the text to a texture</li>
</ul><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> ....</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> initBuffers();</span><br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <b><span class="Apple-style-span" style="color: red;"> setUpTextArea();</span></b></span><br />
<b><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> for (var ii = 0; ii< 6; ++ii){</span></b><br />
<b><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> writeTextToCanvas('Type here!', ii);</span></b><br />
<b><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> }</span></b><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> gl.clearColor(0.5, 0.5, 0.5, 1.0);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> gl.enable(gl.DEPTH_TEST);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">.... </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: inherit;">So, the setUpTextArea() function does some the </span>initial<span class="Apple-style-span" style="font-family: inherit;"> housekeeping. It's pretty straight forward once you understand the mehtods:<br />
</span><br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> <b> function setUpTextArea()</b>{</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> context2D.font = 'normal 35px Verdana';</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> context2D.fillStyle = 'rgba(0,0,0,255)';</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> context2D.fillRect(0,0, 300, 300);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> context2D.lineWidth = 5;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> context2D.strokeStyle = 'rgba(0,0,0,255)';</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> context2D.textAlign = 'center';</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> context2D.textBaseline = 'middle';</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br />
</span><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-family: inherit;">The font property is I believe pretty obvious - play around with it to see the different looks! The <b>fillStyle</b> property is the actual drawing color - here we set it up to non-transparent black. LineWidth, textAlign and textBaseLine specify the formating properties (<a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#2dcontext" title="Canvas specification">2d rendering context API</a>). <b>strokeStyle </b>specifies the outline or shadow of the text - here we have set it also to black. All good? Let's continue.</span></div><span class="Apple-style-span" style="font-family: inherit;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: inherit;">The next function, <b>writeTextToCanvas()</b>, is actually writing the text for us. It looks like this:<br />
</span><br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>function writeTextToCanvas(text, idx){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: red;">context2D.save();</span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: red;">context2D.clearRect ( 0 , 0 , 300 , 300);</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>context2D.fillStyle = faceColors[idx];</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: red;">context2D.fillRect(0,0, 300, 300);</span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>context2D.fillStyle = 'rgba(255, 255, 255, 255)';</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: red;">context2D.strokeText(text, 150, 150);</span></span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>context2D.fillText(text, 150, 150);</span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> </span></span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>setTextureFromCanvas(context2D.canvas, textures[idx], idx);</span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: red;">context2D.restore();</span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br />
var faceColors = ['rgba(0, 0, 255, 255)', 'rgba(255, 0, 0, 255)',</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> 'rgba(0, 255, 0, 255)', 'rgba(0, 0, 0, 255)',</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> 'rgba(255, 0, 255, 255)', 'rgba(0, 255, 255, 255)'];</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<i><span class="Apple-style-span" style="font-family: inherit;">(Update: 300 is the width and height of the canvas, that's why it's hard coded. I was quite in a rush writing this, no hard feelings!)</span></i><br />
<i><span class="Apple-style-span" style="font-family: inherit;"><br />
</span></i><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-family: inherit;">The </span><b style="font-family: inherit;">save() </b><span class="Apple-style-span" style="font-family: inherit;">and </span><b style="font-family: inherit;">restore()</b><span class="Apple-style-span" style="font-family: inherit;"> functions are something like push() and pop() in OpenGL - they are used for state management. At the </span>beginning<span class="Apple-style-span" style="font-family: inherit;"> of this function I save my current set-up (which was the </span>initialization<span class="Apple-style-span" style="font-family: inherit;">), and at the end, once I have my texture created, I restore it again - this way I am always at my starting point when I want to create a texture. Now, a couple of things are important here:</span></div><div style="text-align: justify;"></div><ul><li><b>strokeText() - </b>writes the outline of the specified text starting at the specified position (with the previously set alignment etc.)</li>
<li><b>clearRectangle() </b>- erases an previously draw objects from the canvas (from point (0,0) to (300,300))</li>
<li><b>fillRectangle()</b> - fills the rectangle with the specified color (from point (0,0) to (300,300))</li>
<li><b>fillText()</b> - writes the text itself (in essence the same function as <b>strokeText()</b>)</li>
<li><b>setTextureFromCanvas() </b>- renders the current canvas content to a WebGL Texture object (parameters canvas, a texture object to render on, and an index as a reference to a cube face)</li>
</ul><div style="text-align: justify;">Now, you might think that the last one is quite complicated? Well, it's not at all! In fact, it's almost the same function we used in the previous tutorial, except that instead from an image we are now loading our content from a canvas element! Pretty simple, right? In case you don't believe me, here is the sample code:<br />
<br />
</div><div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><b>function setTextureFromCanvas</b>(canvas, textTexture, idx) {</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> <span class="Apple-style-span" style="color: red;"><b>gl.activeTexture(gl.TEXTURE0 + idx);</b></span></span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.bindTexture(gl.TEXTURE_2D, textTexture);</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, <b><span class="Apple-style-span" style="color: red;">canvas</span></b>);</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if (isPowerOfTwo(<b><span class="Apple-style-span" style="color: red;">canvas.width</span></b>) && isPowerOfTwo(<b><span class="Apple-style-span" style="color: red;">canvas.height</span></b>)){</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}else{</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> }</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; white-space: pre;"> </span></div><div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.bindTexture(gl.TEXTURE_2D, textTexture);</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.uniform1i(shaderTextureIndices[idx], idx); </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; white-space: pre;"> </span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span></div></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span></div><div style="text-align: justify;">The big difference between the function from the previous tutorial is and this is that we use a canvas instead of an image. Also this function get's called dynamically as we write the text value - it generates the text and automatically binds the new texture to the shader (the same one as in the previous tutorial). That's why we set the current <b>activeTexture </b>at the begging of this function. All clear? Hope it is, if not drop me a line. And don't complain about my web page style - I'm not a designer :)</div><div class="blogger-post-footer"><a href="http://webglfactory.blogspot.com/"> WebGL Factory</a><br/>
d copyright, 2011. Share it, but link back to this blog.<br/>
Denis Tosic<br/></div>Denis Tosichttp://www.blogger.com/profile/14992340631798182965noreply@blogger.com5tag:blogger.com,1999:blog-8577130439755471155.post-2251202465124026342011-06-04T14:37:00.006+02:002011-06-04T22:52:36.640+02:00How to create a view matrixWhen I started with 3D I had this really handy function in the GLUT library called <b><a href="http://www.opengl.org/resources/faq/technical/viewing.htm" title="gluLookAt | OpenGL FAQ">gluLookAt</a></b>(...) which helps you position the viewer (camera) inside your scene.What this matrix does is to transform points from world space into camera space - or in other words in it answers this question: <i>"What would be the coordinate of a world point if the camera was the origin of a new world coordinate system?". </i>WebGL does not have it in its API, so let's create our own from scratch then.<br />
<br />
<a name='more'></a><br />
<br />
You need three things to construct a view matrix:<br />
<span class="Apple-style-span" style="background-color: #b9e4ea;"></span><br />
<div class="Notes"></div><ul><li><b>eye </b>of the camera - position of the camera specified in world coordinates.</li>
<li><b>target </b>point <b>- </b>indicates the target of the camera (where we want to look).</li>
<li><b>up </b>vector - defines which direction is <i>up</i>. </li>
</ul><br />
You also need to remember that the up vector must not be parallel to the line of sightvector from the eye to the target point!<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwWAuSYqjiM-NH_cCFxrIzdvtYWUYx9TBzx5lMgX54ll9bBPz8ZD8LFSxnXOAuZcVcVLx9ZKSHQlQT06uUa-MpCOkzTxSnkgNZNwwXbshoVvMa0QO6TOrIxsl8oSeh4Qnyx3V-Ln9NO1M/s1600/camera.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="Eye, target and up vector in the scene." border="0" height="247" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwWAuSYqjiM-NH_cCFxrIzdvtYWUYx9TBzx5lMgX54ll9bBPz8ZD8LFSxnXOAuZcVcVLx9ZKSHQlQT06uUa-MpCOkzTxSnkgNZNwwXbshoVvMa0QO6TOrIxsl8oSeh4Qnyx3V-Ln9NO1M/s400/camera.jpg" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><i>Eye, target and up vector.</i></div><br />
And here is the pseudo-code for constructing the view matrix:<br />
<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <b><i>Matrix lookAt(Vector3 eye, Vector3 target, Vector3 up)</i></b> {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> Vector3 v<span class="Apple-style-span" style="font-size: xx-small;">z</span>= normalize(eye - target);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> Vector3 v<span class="Apple-style-span" style="font-size: xx-small;">x</span> = normalize(crossProduct(up, vz));</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // v<span class="Apple-style-span" style="font-size: xx-small;">y </span>doesn't need to be normalized because it's a cross</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // product of 2 normalized vectors</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> Vector3 v<span class="Apple-style-span" style="font-size: xx-small;">y</span> = crossProduct(vz, vx);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> Matrix inverseViewMatrix = new Matrix(new Vector4(v<span class="Apple-style-span" style="font-size: xx-small;">x</span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">, 0),</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">new Vector4(v<span class="Apple-style-span" style="font-size: xx-small;">y</span>, 0),</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> new Vector4(v<span class="Apple-style-span" style="font-size: xx-small;">z</span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">, 0),</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">new Vector4(eye, 1));</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> <b><i>return inverseViewMatrix.getInverse();</i></b></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<br />
First of all two important assumptions I have in this code:<br />
<br />
<ul><li>eye, target and up are 3-component vectors - this is not the general case</li>
<li>I assume that the matrix mode is row-major - the matrix mode only affects in which order you multiply matrices and vectors, the result stays the same</li>
</ul>Depending on your specific application or convention, fit this code to your needs. I guess everything should be straight forward except maybe the return line - why do we need the inverse? Well, you remember we want our camera to be the origin of the coordinate system? To achieve that we have to apply the inverse of the computed matrix to all our objects so they get transform to camera space. If you want more info on this topic, visit check out this <a href="http://robertokoci.com/world-view-projection-matrix-unveiled/" titel="World and view matrix tutorial">tutorial</a>.<div class="blogger-post-footer"><a href="http://webglfactory.blogspot.com/"> WebGL Factory</a><br/>
d copyright, 2011. Share it, but link back to this blog.<br/>
Denis Tosic<br/></div>Denis Tosichttp://www.blogger.com/profile/14992340631798182965noreply@blogger.com6tag:blogger.com,1999:blog-8577130439755471155.post-89688745404426235162011-05-29T14:08:00.002+02:002011-05-29T16:14:18.720+02:00Adding textures<div><div style="text-align: justify;">Hello Reader! For this week I have prepared a bit more interesting tutorial then last week. I will try to explain how to add textures to an object (yes, it's a plural) and give some more insight into attributes and shaders.</div></div><i><br />
</i><br />
<div style="text-align: center;"><i><span class="Apple-style-span" style="font-style: normal;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/KR3LThKkBnc?feature=player_embedded' frameborder='0'></iframe></span></i></div><div style="text-align: center;"><i>Anyone lost a 1d6 dice?</i></div><br />
<a name='more'></a><br />
<br />
Enough with the chit-chat, let's get started! As before, you can download the code from <a href="http://dl.dropbox.com/u/3947885/tutorials/Tutorial%202%20-%20WebGL%20Factory.rar" title="Adding textures | WebGL Factory">here</a>.<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function tutorial2(){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>initWebGL();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>createShaderProgram();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if (!shaderProgram){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>return;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>initBuffers();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: red;"><i>initTextures();</i></span></span><br />
<span class="Apple-tab-span" style="font-family: 'Courier New', Courier, monospace; white-space: pre;"> </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.clearColor(0.5, 0.5, 0.5, 1.0);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.enable(gl.DEPTH_TEST);</span><br />
<span class="Apple-tab-span" style="font-family: 'Courier New', Courier, monospace; white-space: pre;"> </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>requestAnimationFrame = window.mozRequestAnimationFrame;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if (!requestAnimationFrame)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>requestAnimationFrame= window.webkitRequestAnimationFrame;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if (!requestAnimationFrame){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>alert ('Ooops');</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>return;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>animate();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<br />
<div style="text-align: justify;">The entry code didn't change at all - we just added a new function,<i> initTextures()</i>, which is loading some images, and creating <b>WebGLTexture </b>objects. These objects is what WebGL uses to represent textures in JavaScipt (it won't work with normal images). The initTextures() function is actually quite simple - it loads six images for the six cube faces and after each of them gets loaded it creates a WebGLTexture from it.</div><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function initTextures() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>var image1 = new Image();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>image1.onload = function () {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>frontTexture = <span class="Apple-style-span" style="color: red;">createTextureFromImage</span>(</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> image1, 0, shaderProgram.front);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>image1.src = <i>someImageSource</i>;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>...</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>var image6 = new Image();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>image6.onload = function () {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>leftTexture = <span class="Apple-style-span" style="color: red;">createTextureFromImage</span>(</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> image6, 5, shaderProgram.left);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>image6.src = <i>someImageSource</i>;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<br />
<br />
<div style="text-align: justify;">This is the place where half of the magic happens. The first line just creates a texture object (not much to explain here). After we have created the texture, we need to assign an bind an image to it - and that's what the next few line do. First of all, we tell webgl that the texture we are working on is the newly created texture (webgl operates on one texture at a time). After that we are saying to flip our image - the reason for this is solely human error: Images are defined with the y-axis pointing upwards (thank you, paint and photoshop), while the screen coordinates are defined with the y-axis pointing downwards. That is the only reason why we call this function - otherwise the images would be upside down (set the value to <i>false</i> and try it out).</div><br />
The next function, <b>gl.texImage2D</b><i>, </i>is a kicker though. Simply speaking, it does the following: <span class="Apple-style-span" style="font-family: inherit;"><span class="Apple-style-span" style="font-size: 14px; line-height: 20px;">upload our image into the texture object in the graphics card</span><span class="Apple-style-span" style="font-size: 14px; line-height: 20px;">. The parameters are:</span></span><br />
<br />
<ul><li><span class="Apple-style-span" style="font-size: 14px; line-height: 20px;">the type of image we’re using (can also be <i>gl.TEXTURE_CUBE</i>) </span></li>
<li><span class="Apple-style-span" style="font-size: 14px; line-height: 20px;">the level of detail (mip-mapping)</span></li>
<li><span class="Apple-style-span" style="font-size: 14px; line-height: 20px;">the format in which we want it to be stored on the graphics card - </span><span class="Apple-style-span" style="font-size: 14px; line-height: 20px;"><a href="http://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml" title="texImage2D API">twice</a></span></li>
<li><span class="Apple-style-span" style="font-size: 14px; line-height: 20px;">the type of each channel of the image - unsigned byte for RGB</span></li>
<li><span class="Apple-style-span" style="font-size: 14px; line-height: 20px;">the image we want to load</span></li>
</ul><span class="Apple-style-span" style="font-size: 14px; line-height: 20px;"><br />
</span><br />
<ul></ul><div style="text-align: justify;">After this we need to do still one thing - specify how our image is going to be mapped onto the texture object. This is necessary because the texture object we are texturing on does not have to have the image's size (or proportions). Regarding that, I want you to remember one thing - WebGL works supercool with images that have width and height a power of two (POT) - it knows how to scale them (there are different parameters and I will wirte a how-to for that soon). For the other case (non-POT), we simply say "<i>Just</i> <i>WRAP the texture object without thinking</i>". After that we simply decrease a "texture left to load"counter and tidy up thing by binding null to the current texture. That's it!</div><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function createTextureFromImage(image, offset, uniform) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>var texture = gl.createTexture();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.bindTexture(gl.TEXTURE_2D, texture);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA,</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> gl.UNSIGNED_BYTE, image);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if (isPowerOfTwo(image.width) &&</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> isPowerOfTwo(image.height)){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.texParameteri(gl.TEXTURE_2D,</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> gl.TEXTURE_MAG_FILTER, gl.NEAREST);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.texParameteri(gl.TEXTURE_2D,</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> gl.TEXTURE_MIN_FILTER, gl.NEAREST);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}else{</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.texParameteri(gl.TEXTURE_2D,</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> gl.TEXTURE_MIN_FILTER, gl.LINEAR);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S,</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> gl.CLAMP_TO_EDGE);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T,</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> gl.CLAMP_TO_EDGE);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}<span class="Apple-style-span" style="white-space: pre;"> </span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>texturesToLoad--;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.bindTexture(gl.TEXTURE_2D, null);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>return texture;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<br />
<div style="text-align: justify;">Now that we have created and assigned values to our texture objects, we want to use them on our cube, don't we? Well, to do this we need to modify our shaders a bit. Let's first take a look at the vertex shader. We defined two new attributes here - atextureCoord and aFace. The first one specifies the texture coordinate per cube vertex - e.g. vertex (0,0,0) shall have the texture coordinate (0,0) while vertex (0,1,0) shall have the coordinate (0,1) (the exact numbers you can see in the, but don't break your head on them!). So, the lower left corner of the texture get's mapped to the lower left corner of the cube, the upper left corner to the upper left vertex of the cube (both front face - z = 0).</div><br />
The seond attribute tells us which face this vertex belongs to (0-5). This is important for our fragment shader, so we can use different textures for different faces - front face vertices have the face 0, back face vertices 1 and so on.<br />
<br />
<b>Vertext shader:</b><br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">attribute vec3 aVertexPosition;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> <span class="Apple-style-span" style="color: red;"> </span></span><span class="Apple-style-span" style="color: red;">attribute vec2 aTextureCoord;</span></span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>attribute float aFace;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>uniform mat4 uMVMatrix;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>uniform mat4 uPMatrix;<br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: red;">varying vec2 vTextureCoord;</span></span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>varying float vFace;<br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: red;"> </span>void main(void) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl_Position = uPMatrix * uMVMatrix *</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> vec4(aVertexPosition, 1.0);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: red;">vTextureCoord = aTextureCoord;</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: red;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span class="Apple-style-span" style="color: red;">vFace = aFace;</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: red;"> </span>}</span><br />
<br />
<div style="text-align: justify;">Following up with the fragment shader - here we only check the value of the face attribute (passed as a varying, remember that?). Depending on the value (0-5), we use the corresponding texture. But wait, there are no texture uniforms, just some sampler stuff?! Well, shaders call textures <b>sampler</b>, that's all :) The function <b>texture2D(sampler, vTextureCoord)</b><i> </i> get's the correct pixel value from the sampler at coordinate vTextureCoord and assigns it the gl_FragColor.</div><br />
<b>Fragment shader:</b><br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: red;">uniform sampler2D front;</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>uniform sampler2D back;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>uniform sampler2D top;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>uniform sampler2D bottom;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>uniform sampler2D right;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>uniform sampler2D left;<br />
</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>varying vec2 vTextureCoord;</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>varying float vFace;<br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>void main(void) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> <span class="Apple-style-span" style="color: red;"> </span></span><span class="Apple-style-span" style="color: red;">if (vFace < 0.1)</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: red;">gl_FragColor = texture2D(front, vTextureCoord);</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>else if (vFace < 1.1)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl_FragColor = texture2D(back, vTextureCoord);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>else if (vFace < 2.1)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl_FragColor = texture2D(top, vTextureCoord);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>else if (vFace < 3.1)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl_FragColor = texture2D(bottom, vTextureCoord);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>else if (vFace < 4.1)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl_FragColor = texture2D(right, vTextureCoord);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>else</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl_FragColor = texture2D(left, vTextureCoord);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<div><i><br />
</i></div><div>Since our shader code has changed, we need to update our createShaderProgram() function also. We add these lines:<br />
<br />
<span class="Apple-style-span" style="color: red;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">shaderProgram.textureLookUpAttribute =</span></span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> gl.getAttribLocation(shaderProgram, "aFace");</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.enableVertexAttribArray(shaderProgram.textureLookUpAttribute);</span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> </span></span><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;">shaderProgram.front=</span><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;">gl.getUniformLocation(shaderProgram,</span><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;">"front");</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> ....</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> shaderProgram.left =gl.getUniformLocation(shaderProgram,</span><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;">"left");</span><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> </span><br />
<span class="Apple-style-span" style="color: red;"><br />
</span><br />
<div style="text-align: justify;">We enable the aFace attribute which tells, and get the uniform locations for each sampler in our fragment shader. Makes sense, doesn't it? The last thing left to is to bind the WebGLTexture objects with our shader program. that looks like this (we do it 6 time for six sampler):</div><br />
<div style="text-align: left;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: red;">gl.activeTexture(gl.TEXTURE0 + <i>face</i>);</span></span></div><div style="text-align: left;"><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.bindTexture(gl.TEXTURE_2D, <i>faceTexture</i>);</span></div><div style="text-align: left;"><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.uniform1i(shaderProgram.front, <i>face</i>); </span></div><span class="Apple-style-span" style="color: red;"><br />
</span><br />
<div style="text-align: justify;">Before I explain what they do, I first need to tell you a bit more about textures in WebGL. You can define as much WebGLTexture objects as you wish, but at any given moment you can only use a maximum of 32 - that is the limit of samplers you can have in you shader code. In respect to that, the function <i><b>gl.activeTexture(gl.TEXTURE0 + face) </b></i>says: '<i>I want to work with TEXTURE0 + offset (0-31) now!</i>' - you can not work with more then one a a time. After that we call the bind function which is passing the value of <i>faceTexture</i> to the currently active texture. Last thing to do is to bind the currently active texture (gl.TEXTURE0 + <i>face</i>) to our uniform <i>face. </i>And that's it!</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">One P.S. though - I have not provided you with the number values of the buffer for the attributes. I consider that just a waste of space and unnecessary distraction. In 99,9% of cases you will not specify does values, neither even know - they will be generated by some 3D max or maya object exporter, and the only thing left to do for you is to load then into buffers. In the source code they are specified by hand, take a look at them if you wish. But as said - you will probably never see them again :)<br />
<br />
P.P.S<br />
<br />
<i>Many thanks to </i><a href="http://learningwebgl.com/" style="font-style: italic;">learningwebgl.com</a><i> for providing the base for this tutorial!</i></div><br />
</div><div class="blogger-post-footer"><a href="http://webglfactory.blogspot.com/"> WebGL Factory</a><br/>
d copyright, 2011. Share it, but link back to this blog.<br/>
Denis Tosic<br/></div>Denis Tosichttp://www.blogger.com/profile/14992340631798182965noreply@blogger.com8tag:blogger.com,1999:blog-8577130439755471155.post-62285190009396174332011-05-25T10:17:00.002+02:002011-05-29T16:24:04.605+02:00How to convert world to screen coordinates and vice versa<div style="text-align: justify;">This is a thing every 3D developer should know: for a given point in 3D what is the position of that point in 2D, e.g. on the screen in pixel coordinates? This problem appears quite often (starting with mouse picking) and it' always handy to have the solution at hand :)</div><div style="text-align: justify;"><br />
<a name='more'></a><br />
</div><div style="text-align: justify;">The first thing you should remember once and for all regarding screen coordinates - the upper left corner of the screen is (0,0) and lower right is (width, height) - no arguing! Now, lets say we got a 3D point in world coordinate space at (x, y, z) - this is not relative to the camera, but absolute (so the camera can have coordinates (c<span class="Apple-style-span" style="font-size: xx-small;">x</span>, c<span class="Apple-style-span" style="font-size: xx-small;">y</span>, c<span class="Apple-style-span" style="font-size: xx-small;">z</span>)). The camera defines the viewMatrix, and I suppose you also have defined a projectionMatrix (you better have!). The last thing you need is the width and height of the are you are rendering on (not the whole screen!). If you have all these things, then it's pretty easy:</div><div><br />
</div><div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function point2D get2dPoint(Point3D point3D, Matrix viewMatrix,</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> Matrix projectionMatrix, </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">int</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">width, </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">int</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">height) {</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> Matrix4 viewProjectionMatrix = projectionMatrix * viewMatrix;</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> //transform world to clipping coordinates</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> point3D = viewProjectionMatrix.multiply(point3D);</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> int winX = (int) Math.round((( point3D.getX() + 1 ) / 2.0) *</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> width );</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> //we calculate -point3D.getY() because the screen Y axis is</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> //oriented top->down </span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> int winY = (int) Math.round((( 1 - point3D.getY() ) / 2.0) *</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> height );</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return new Point2D(winX, winY);</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span></div></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span></div><div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: inherit;">And that's it! Depending on your development evironment, you might have a viewProjection matrix already computed, or your world coordinates are relative to the camera position - don't forget to modify the code according to that!</span></div></div><div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: inherit;"><br />
</span></div></div><div><div style="text-align: justify;">For the ones of you who want or need to know the way back from 2D to 3D - well, it's just doing the inverted opperations:</div></div><div><br />
</div><div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><br />
<div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function Point3D get3dPoint(Point2D point2D, int width,</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> int height, Matrix viewMatrix, Matrix projectionMatrix) {</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> double x = 2.0 * winX / clientWidth - 1;</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> double y = - 2.0 * winY / clientHeight + 1;</span></div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> Matrix4 viewProjectionInverse = inverse(projectionMatrix *</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> viewMatrix);</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><br />
<div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> Point3D point3D = new Point3D(x, y, 0);<span class="Apple-tab-span" style="white-space: pre;"> </span></span></div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return viewProjectionInverse.multiply(point3D);</span></div></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span></div><div><div style="text-align: justify;">The only unclear thing here is the z coordinate - I have set it to 0. The reason is that from the arguments passed to our function we can not figure out the Z coordinate exactly - because every number would be correct! Think quick - how many points from our 3D world can you map on a point of glass when you look through the window? Indefinitely many! To sum up: there is no unique mapping between a 2D and a 3D point, but there is between a 3D and a 2D point. </div></div><div><div style="text-align: justify;"><br />
</div></div><div><div style="text-align: justify;">Hope I made some things clearer.. you can find more info on the net, but as a more detailed overview I can recommend you this <a href="http://trac.bookofhook.com/bookofhook/trac.cgi/wiki/MousePicking" title="picking">picking tutorial</a>. </div></div><div class="blogger-post-footer"><a href="http://webglfactory.blogspot.com/"> WebGL Factory</a><br/>
d copyright, 2011. Share it, but link back to this blog.<br/>
Denis Tosic<br/></div>Denis Tosichttp://www.blogger.com/profile/14992340631798182965noreply@blogger.com38tag:blogger.com,1999:blog-8577130439755471155.post-81545303488761527992011-05-24T08:37:00.002+02:002011-05-24T08:39:22.030+02:00How to add a blog to technorati.com(Ok, this one is for my personal use, but some of you might like it too :)<br />
<div><br />
<div>First of all, <a href="http://technorati.com/">Technorati.com</a> is a blog search engine for... well, searching blogs. So if you want to make your blog a bit more visible on the net, submit it here. </div></div><div><ol><li>Go to <a href="http://technorati.com/">technorati.com</a> and sign in/join</li>
<li>Go to your profile (click on user name in the upper right corner)</li>
<li>To add your blog go "claim blog"</li>
<li>Fill out what's to fill out</li>
<li>You will get an email with a verification code like: E9CCZCBVNHQG</li>
<li>Put this one in a new post on your blog (not sure if it has to be a new one)</li>
<li>go back to your account at <a href="http://technorati.com/">technorati.com</a> and verify your blog</li>
<li><i><b>"<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 16px;">It may take quite some time for evaluation."</span></b></i></li>
</ol><div>That's it!</div></div><div class="blogger-post-footer"><a href="http://webglfactory.blogspot.com/"> WebGL Factory</a><br/>
d copyright, 2011. Share it, but link back to this blog.<br/>
Denis Tosic<br/></div>Denis Tosichttp://www.blogger.com/profile/14992340631798182965noreply@blogger.com0tag:blogger.com,1999:blog-8577130439755471155.post-30886162463657347792011-05-22T15:25:00.001+02:002011-05-22T19:01:45.625+02:00Tutorials 0 and 1 available!You can find the first two tutorials on the tutorials page!<br />
One of them is an introduction to WebGL, and the other one will help you create you first simple WebGL app.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/cMn00QwvVrE?feature=player_embedded' frameborder='0'></iframe></div><br />
<br />
Check it out <a href="http://webglfactory.blogspot.com/p/tutorials.html">here</a>!<div class="blogger-post-footer"><a href="http://webglfactory.blogspot.com/"> WebGL Factory</a><br/>
d copyright, 2011. Share it, but link back to this blog.<br/>
Denis Tosic<br/></div>Denis Tosichttp://www.blogger.com/profile/14992340631798182965noreply@blogger.com0tag:blogger.com,1999:blog-8577130439755471155.post-945944601937290512011-05-22T10:58:00.007+02:002011-05-29T16:20:05.878+02:00A simple renderIt's time to finally start drawing something - and let's make it easy today. I will try to show you some basic stuff - how to initialize things, how to define shaders, how to bind uniforms and attributes (I hope you have read <a href="http://webglfactory.blogspot.com/search/label/Tutorial%200">Tutorial 0</a>) and how to make it move, or "animate" :). Here you can download the source for <a href="http://dl.dropbox.com/u/3947885/tutorials/Tutorial%201%20-%20WebGL%20Factory.rar" title="tutorial 1">tutorial 1</a>. Now let's get started!<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/cMn00QwvVrE?feature=player_embedded' frameborder='0'></iframe></div><div class="separator" style="clear: both; text-align: center;"><i><br />
</i></div><div class="separator" style="clear: both; text-align: center;"><i>This is how it will look in the end.</i></div><br />
<a name='more'></a><br />
<br />
First of all, you need to specify <img alt="" border="0" height="1" src="http://www.assoc-amazon.com/e/ir?t=web091-20&l=bil&camp=213689&creative=392969&o=1&a=0321637631" style="border: none !important; margin: 0px !important; padding: 0px !important;" width="1" />a canvas - the area you want to draw the scene on. So, open up a text editor (I recommend <a href="http://notepad-plus-plus.org/download" title="notepad++">notepad++</a>), set up some basic HTML stuff and add the following into the <body> tag:<br />
<br />
<span class="Apple-tab-span" style="white-space: pre;"> <span class="Apple-style-span" style="color: red;"> </span></span><span class="Apple-style-span" style="color: red;"><canvas id ='canvas' width="500" height="500"></canvas></span><br />
<br />
<b><Canvas></b> is the new hot thing introduced in HTML5 which allows you to draw 3D stuff using WebGL - without it all super cool graphics code is useless. Now to help myself (and yourself) I will use a math library from Google called <a href="http://code.google.com/p/threedlibrary/" title="TDL"> TDL </a> just to hide some math operations - I will explain these things later on in the math section, so no worries. Sorry for not being able to run this demo on my page, blogger doesn't let me, but you can still run it on your local PC :)<br />
<br />
Now, let's take a look at the entry function:<br />
<br />
/*--------------------------------------------------------------------------------------------------------*/<br />
<br />
<span class="Apple-tab-span" style="white-space: pre;"> <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function tutorial1(){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>initWebGL();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>createShaderProgram();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if (!shaderProgram){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>return;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>initBuffers();<span class="Apple-tab-span" style="white-space: pre;"> </span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.clearColor(0.5, 0.5, 0.5, 1.0);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.enable(gl.DEPTH_TEST);</span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>animationFrame = window.mozRequestAnimationFrame;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if (!animationFrame)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>animationFrame = window.webkitRequestAnimationFrame<span class="Apple-tab-span" style="white-space: pre;"> </span>;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if (!animationFrame){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>alert ('Ooops');</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>return;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>animate();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<br />
/*--------------------------------------------------------------------------------------------------------*/<br />
<br />
<div style="text-align: justify;">As you can see, it' not that complicated and actually pretty straight forward. First we initialize the WebGL context which is nothing more the getting an access point to the WebGL API - let's call it '<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">gl</span>'. If this access point does not exist, the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">gl </span>variable will not get initialized, the browser does not support WebGL and we're done (I hope you got a WebGL ready browser!). After getting the WebGL context, we set some basic variables: the gl.viewport function defines the area of the canvas we will draw on (no, you don't have to draw on the whole canvas), which in our case is going to be the whole canvas - stretching from upper left (0,0) do lower right (width, height). After that we create our projectionMatrix (the one that converts from world to screen coordinates) - for now just take it as it is, I will explain in the math section another time.</div><br />
/*--------------------------------------------------------------------------------------------------------*/<br />
<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function initWebGL(){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> canvas = document.getElementById('canvas');</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> var names = ["webgl", "experimental-webgl", "webkit-3d",</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> "moz-webgl"];</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> <span class="Apple-tab-span" style="white-space: pre;"> </span>for (var ii = 0; ii < names.length; ++ii) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> try {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> <span class="Apple-style-span" style="color: red;">gl = canvas.getContext(names[ii]);</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> } catch(e) {}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> if (gl) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> <span class="Apple-tab-span" style="white-space: pre;"> </span> gl.viewportWidth = canvas.width;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> gl.viewportHeight = canvas.height;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">pMatrix = new Float32Array(16);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> matrix4.perspective(pMatrix, </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">tdl.math.degToRad(45),</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> gl.viewportWidth / gl.viewportHeight,</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">0.1, </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">100.0);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> return;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>alert('Oops, something went wrong...');</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
/*--------------------------------------------------------------------------------------------------------*/<br />
<br />
<div style="text-align: justify;">So far so good, we got access to the WebGL API, so let's code some shaders. As I explained before, shaders are the actual programs that draw things on your screen, and we need two of them - a vertex and a fragment shader. A vertex and a fragment shader together are called a program - now take a guess what the function <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">createShaderProgram </span>does? Exactly, it create a shader program from a vertex and fragmnet shader code. There are a few steps that you need to do to create a valid shader program:</div><br />
<ol><li>write the vertex and fragment shader code as normal text</li>
<li>for each of them you need to</li>
<ol><li>create a gl shader object with gl.createShader(shadertype) - shadertype is either <b>gl.VERTEX_SHADER</b> or <b>gl.FRAGMENT_SHADER</b></li>
<li>load the source code into the shader object with gl.shaderSource(shader, shaderSource)</li>
<li>compile the shader with gl.compileShader(shader)</li>
<li>check that everything went well :)</li>
</ol><li>create a shader program object with gl.createProgram();</li>
<li>attach both the vertex and fragment shader with gl.attachShader(..) </li>
<li>link the program with gl.linkProgram(shaderProgram)</li>
<li>use it with gl.useProgram(shaderProgram) - this function is particulary important if you have more then one shader and want to switch between the. Once you create and link all your shader you just need to specify which shader to use at the given moment</li>
</ol>/*--------------------------------------------------------------------------------------------------------*/<br />
<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function createShaderProgram(){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>var vertexShader = getShader(vertexShaderSource, 'vertex');</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if (!vertexShader)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>return;</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; white-space: pre;"> </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>var fragmentShader = getShader(fragmentShaderSource, 'fragment');</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if (!fragmentShader)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>return;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: red;">shaderProgram = gl.createProgram();</span></span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.attachShader(shaderProgram, vertexShader);</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.attachShader(shaderProgram, fragmentShader);</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.linkProgram(shaderProgram);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>alert(gl.getProgramInfoLog(shader));</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>return;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.useProgram(shaderProgram);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>shaderProgram.vertexPositionAttribute =</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> gl.getAttribLocation(shaderProgram,</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> "aVertexPosition");</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);</span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>return;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
/*--------------------------------------------------------------------------------------------------------*/<br />
<span class="Apple-style-span" style="font-family: inherit;"><br />
</span><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-family: inherit;">There are still two functions left I need to explain to you - </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">getAttribLocation</span><span class="Apple-style-span" style="font-family: inherit;">() and </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">enableVertexAttribArray</span><span class="Apple-style-span" style="font-family: inherit;">(). </span><span class="Apple-style-span" style="font-family: inherit;">Now the first, </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">getAttribLocation</span><span class="Apple-style-span" style="font-family: inherit;">(program, attributeName) return you something like a pointer to the attribute variable in our shader code - and we bind our vertexBuffer to that 'pointer' so WebGL knows where to read the data from. In this we are getting the vertex position attribute, and later in the code we will bind it. </span>The second function only specifies that the shader program shall enable the usage of this attribute - by default they are all disables, and give you an answer ahead, their number as typed is limited and predefined.</div><span class="Apple-style-span" style="font-family: inherit;"><br />
</span><br />
/*--------------------------------------------------------------------------------------------------------*/<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>function getShader(shaderCode, shaderType) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>var shader;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if (shaderType == 'vertex') {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: red;">shader = gl.createShader(gl.VERTEX_SHADER);<span class="Apple-tab-span" style="white-space: pre;"> </span></span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>} else if (shaderType == 'fragment') {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: red;">shader = gl.createShader(gl.FRAGMENT_SHADER);</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>} else {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>alert('Unsupported shader type');</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>return null;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: red;">gl.shaderSource(shader, shaderCode);</span></span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.compileShader(shader);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>alert(gl.getShaderInfoLog(shader));</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>return null;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>return shader;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
/*--------------------------------------------------------------------------------------------------------*/<br />
<br />
We have set up our webgl and we got our shader ready - we are now want to create some things we want to render. Now, usually a 3D artist or designer will provide you with the models, but for let's keep it simple and create our own objects. So let's create a triangle and a square. The function <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">initBuffers()</span> does exactly that. Just a reminder, buffers are the objects that hold our geometry data - the stuff that gets bind to the attribute variables in the shader code.<br />
<br />
When it comes to buffers, there are more things we need to do:<br />
<br />
<ol><li>create a buffer </li>
<li>set the properties of the buffer - number of items, and size of each item <br />
(for a triangle it's 3 vertices with 3 floats, for a square it's 4 vertices with 3 floats)</li>
<li>bind it to one of two glBuffers - for now we are only using <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">gl.ARRAY_BUFFER</span></li>
<li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-family: 'Times New Roman';">set the values in the glbuffer with gl.bufferData(...)</span></span></li>
</ol>The Float32Array is a new data type for holding floating point numbers, optimized for HTML 5. For now, I will not go into more detail with them... And now that we have set up our buffers and bound them correctly and we move on to the render function!<br />
<br />
/*--------------------------------------------------------------------------------------------------------*/<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function initBuffers() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> <span class="Apple-style-span" style="color: red;"> </span></span><span class="Apple-style-span" style="color: red;">triangleVertexPositionBuffer = gl.createBuffer();</span></span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);</span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> </span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>var triangleVertices = [</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>0.0, 1.0, 0.0,</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>-1.0, -1.0, 0.0,</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>1.0, -1.0, 0.0</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>];</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> <span class="Apple-style-span" style="color: red;"> </span></span><span class="Apple-style-span" style="color: red;">gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleVertices),</span></span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> gl.STATIC_DRAW);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>triangleVertexPositionBuffer.itemSize = 3;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>triangleVertexPositionBuffer.numItems = 3;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>squareVertexPositionBuffer = gl.createBuffer();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);</span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>var squareVertices = [</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>1.0, 1.0, 0.0,</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>-1.0, 1.0, 0.0,</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>1.0, -1.0, 0.0,</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>-1.0, -1.0, 0.0</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>];</span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(squareVertices),</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> gl.STATIC_DRAW);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>squareVertexPositionBuffer.itemSize = 3;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>squareVertexPositionBuffer.numItems = 4;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
/*--------------------------------------------------------------------------------------------------------*/<br />
<br />
This little beauty below is the function that makes tell the shader to draw something on the screen. Let's take a really good look at it.<br />
<br />
<div style="text-align: justify;">The function <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">gl.clear(...) </span>clears of the screen - if you're a painter then it's like you draw something, look at it, and then repaint the canvas to the inital color. And again, you draw something, look at it, and then repaint the canvas to the inital color. And so on. You can see there is some matrix math in the code - if you don't understand what translation and rotation does - no problems, ignore it in this lesson. Just keep in mind that these matrices define how we move our objects in space - translate them, rotate them or scale them.</div><br />
<div style="text-align: justify;">Now, the first big important function is <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>gl.bindBuffer()</b></span>. It tell WebGL which buffer to take as the input for our shaders. Here it's first the triangleBuffer. After we have bound the buffer, we set the attribute pointer (remeber the getAttribLocation() from before that retured us the 'pointer'?). This function takes a few more parameters then just the data pointer: like the item size, item type, offset etc, so take it with a grain of salt.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">After that we have defined a color variable which will be the color of our object - in the triangle case we defined (1, 0, 0, 1) which in RGBA values is normal red. Now we call the function setUniforms which bind the uniform values (remeber lesson 0?) to the shader.At the end we call gl.drawArrays(..) which executes the shader code. This function uses the data that is currently bound to the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">gl.ARRAY_BUFFER</span>, and also takes some parameters on how to interpret that data (triangle list or triangle strip, offset and number of elements). This same process is repeted for the square also, just with different values bound.</div><div style="text-align: justify;"><br />
</div>/*--------------------------------------------------------------------------------------------------------*/<br />
<br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-tab-span" style="white-space: pre;"> <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function render() {</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>var mvMatrix = new Float32Array(16);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>matrix4.identity(mvMatrix);<span class="Apple-tab-span" style="white-space: pre;"> </span></span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>matrix4.translate(mvMatrix, [-1.5, 0.0, -7.0]);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>matrix4.rotateY(mvMatrix, rotAngle);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: red;">gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);</span></span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> </span></span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>var color = new Float32Array(4); </span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>color[0] = 1.; color[1] = 0.; color[2] = 0.; color[3] = 1.; </span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-tab-span" style="white-space: pre;"> </span>setUniforms(shaderProgram, pMatrix, mvMatrix, color);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>matrix4.rotateY(mvMatrix, -rotAngle);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>matrix4.translate(mvMatrix, [3.0, 0.0, 0.0]);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>matrix4.rotateY(mvMatrix, - 2 *rotAngle);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>color[0] = Math.abs(Math.sin(rotAngle) + Math.cos(rotAngle));</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>color[1] = Math.abs(Math.sin(rotAngle)); </span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>setUniforms(shaderProgram, pMatrix, mvMatrix, color);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> <span class="Apple-style-span" style="color: red;"> </span></span><span class="Apple-style-span" style="color: red;">gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);</span></span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">/*--------------------------------------------------------------------------------------------------------*/</div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><br />
</div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">I hope that by now you understand the basic principles of WebGL. If you do, then rest will be a piece of cake. The setUniforms functions binds javascript values to uniform variables in the shader. So, it's basically the same as binding attributes, except that these are not attributes :) But from a instruction perspective it's the same: get the 'pointer' for the uniform (called location) and bind the values - that's it!</div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><br />
</div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">/*--------------------------------------------------------------------------------------------------------*/</div><br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function setUniforms(shaderProgram, pMatrix, mvMatrix, color) {</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>var pMatrixUniform = gl.getUniformLocation(shaderProgram,</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> "uPMatrix");</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>var mvMatrixUniform = gl.getUniformLocation(shaderProgram,</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> "uMVMatrix");</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>var colorUniform = gl.getUniformLocation(shaderProgram,</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> "color");</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.uniformMatrix4fv(pMatrixUniform, false, pMatrix);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.uniformMatrix4fv(mvMatrixUniform, false, mvMatrix);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gl.uniform4fv(colorUniform, color);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">/*--------------------------------------------------------------------------------------------------------*/</div><br />
<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
<div style="text-align: justify;">All the code explained until now will render you a scene - BUT it will not animate it, since we are making only one draw call. To make our scene move, we shall use a timer, and a requestAnimationFram object. Since this still depends on the browser, it is initialized in the entry function. It is basically an optimized timer for rendering, so don't get confused by it. So, on every frame of the screen this object calls the function animate - that's it.</div><div style="text-align: justify;">According to that, you should see this sample at constant 60FPS.</div><br />
<br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">/*--------------------------------------------------------------------------------------------------------*/</div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"></div><br />
<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function updateTimer() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>var timeNow = new Date().getTime();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if (lastTime != 0) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>var elapsed = timeNow - lastTime;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>rotAngle += (9 * elapsed) / 10000.0;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>lastTime = timeNow;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>function animate() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: red;">requestAnimationFrame(animate);</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>render();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>updateTimer();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">/*--------------------------------------------------------------------------------------------------------*/</div><div><br />
</div><div>Finally, here is our shader code - and quite simillar to the one from the introduction lesson. The only big difference: </div><div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"> #ifdef GL_ES</div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-tab-span" style="white-space: pre;"> </span> <span class="Apple-tab-span" style="white-space: pre;"> </span> precision highp float</div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-tab-span" style="white-space: pre;"> </span> <span class="Apple-tab-span" style="white-space: pre;"> </span> #endif</div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">This defines the float type precision - just c/p. It is also possible to specify low or medium precision, but I don't think it woul impact the rendering quality or this example to much :)</div></div><div><br />
</div><br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>var fragmentShaderSource =<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
'#ifdef GL_ES\n ' +<br />
'precision highp float;\n ' +<br />
'#endif\n ' +<br />
'uniform vec4 color;\n ' +<br />
<br />
'void main(void) {\n ' +<br />
'<span class="Apple-tab-span" style="white-space: pre;"> </span>gl_FragColor = color;\n ' +<br />
'}';<br />
<br />
<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>var vertexShaderSource =<br />
'attribute vec3 aVertexPosition;\n ' +<br />
'uniform mat4 uMVMatrix;\n ' +<br />
'uniform mat4 uPMatrix;\n ' +<br />
<br />
'void main(void) {\n ' +<br />
' gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition,<br />
1.0);\n ' +<br />
'}';<br />
<br />
I suppose that at this point everything should be more or less clear - I hope you were not bored to death reading this. Any questions or suggestion - put it in the comments or send me an email (it's on the right).<div class="blogger-post-footer"><a href="http://webglfactory.blogspot.com/"> WebGL Factory</a><br/>
d copyright, 2011. Share it, but link back to this blog.<br/>
Denis Tosic<br/></div>Denis Tosichttp://www.blogger.com/profile/14992340631798182965noreply@blogger.com0tag:blogger.com,1999:blog-8577130439755471155.post-2674745411469465152011-05-22T09:48:00.007+02:002011-05-29T16:19:16.301+02:00WebGL pipeline<div style="text-align: justify;">Welcome to my first tutorial about WebGL! For this one I suggest you grab a nice cup of coffee, lay back, a just scroll down a bit... I promise, it's not too hard!<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://www.chromeexperiments.com/img/globe.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="210" src="http://www.chromeexperiments.com/img/globe.jpg" width="320" alt="webgl globe"/></a></div><div class="separator" style="clear: both; text-align: center;"><i>WebGL planet?!</i></div><br />
<a name='more'></a><br />
<br />
So, before we start making our hands dirty, let's take a closer look at how WebGL works. The first thing that you have to understand is that any WebGL app can be divided into 3 parts: the vertex shader, the fragment shader and the JavaScript code. Now, what are vertex and fragments shaders? Does are "mini" programs that run on the GPU and make things appear on the screen - without them nothing gets drawn! </div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Their tasks are fairly simple - the vertex shader has to set the value of a variable named <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">gl_Position</span>, and the fragment shader has to set <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">gl_FragColor</span>. <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">gl_Position </span>says on which pixel of the screen (5x10, 354x785 etc.) to draw on, and <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">gl_FragColor </span>defines what color it should be. Note: The vertext shader gets executed first, and the fragmnet shader second - ALWAYS! </div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">You might ask yourself how do they achieve this? I assume you have some basic knowledge about 3D math - if not, next week I will write a tutorial on that, so please be patient. The vertex shader converts 3d coordinates of your objects (or any geometry) from 3D coordinates to 2D coordinates of the screen. using transformation matrices - one to convert from local to space to world space (the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">worldMatrix</span>), and another one to convert from world coordinates into screen coordinates (the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">viewProjection </span>matrix). The fragment shader just puts the correct color on those screen coordinates - maybe from a textures, maybe it's a plain color, maybe even a shadow. Fairly simple, right?</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">And where is JavaScript in this whole process? Well, JavaScript actually just sets up the variables in those mini programs and calls their execution. So, you need a worldMatrix in you vertex shader? JavaScript binds it! You need a color or a texture in you fragment shader? JavaScript binds it! You want to render a sphere instead of a cube? JavaScript says "render a sphere now"! To get a better understanding of this process, take a look at the following picture (taken from a Google IO presentaion):</div><br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0XOhAkMrge4HiTlVZWF4ifgIewaD80lvMXPpIrlDgskElCXRxK9V5snCViBpx1Uxw1xkQDKI2qwPql946sd5-5CMKu_JMmdhCcdyPbO5jbLEfUy5rQ5gatvG4cS9uLzHAUHJWb3Oj-wE/s1600/webglpipeline.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;" title="WebGL pipeline"><img alt="webgl pipeline" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0XOhAkMrge4HiTlVZWF4ifgIewaD80lvMXPpIrlDgskElCXRxK9V5snCViBpx1Uxw1xkQDKI2qwPql946sd5-5CMKu_JMmdhCcdyPbO5jbLEfUy5rQ5gatvG4cS9uLzHAUHJWb3Oj-wE/s1600/webglpipeline.png" /></a></div><br />
<div style="text-align: center;"><i>Yes, that's WebGL. Cool. Hah.</i></div><div style="text-align: center;"><i><br />
</i></div><div style="text-align: justify;">So there you have it - a vertex and a fragment shader that calculate all the stuff, <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">gl_Postion </span>and <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">gl_FragColor </span>as outputs and - OH! - buffers/attributes, uniforms and varying?! If you have never seen shader programs before you are going to feel a bit out of the line here (at least I did :O). Let's take a step at a time, it's not that complicated. Buffers are WebGL objects that contain information about the geometry you want to render - vertex positions, normals, whatever... You can have a few BIIIG buffers to hold all the data together (one buffer holding vertex coordinates, normal, texture mapping etc), or you can have a lot of smaller ones that hold chuncks of it - one buffer for the vertices, one buffer for normals... Inside a shader, those buffers are refered to as attributes because that is what they are - properties or attributes of the geometry you want to show on your screen.That is what gets bound when you says "I want a sphere instead of a cube" - the buffer values of a sphere get bound to the vertex shader beacuse does are the attributes that differentiate a sphere and a cube. Got it? Sure hope so :)</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Now, the little red box that says uniforms are for example our <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">world </span>and <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">viewprojection </span>matrices or a texture - stuff that is necessary to calculate the correct position and color of a object, but is not property of it. So why are does not attribute also? Well, consider you want to draw a cube 1000 times in different positions and in different colors - all you need is one cube geometry (which is held in buffer objects) and lot of uniforms to describe the different position and colors of 1000 cubes. What would be the alternative? 1000 cube different cubes geometries and that can take up a looot of memory if you want to render 1 000 000 instead of 1000 cubes. Not so bad does uniforms, aren't they?</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">No they last thing here are vaying types. As you can see in the picture, they basically serve for passing values from the vertex to the fragment shader (remember, does are different functions!). So, you have calculated something in the vertex shader, you pass it to the fragment shader by specifying that variable as varying. If you wouldn't do that, you would have to recompute the same thing again, which does not make sense really, does it?</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Let's take a quick look at a veeery simple vertex and fragment shader:</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"> <b> <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: #6aa84f;">/** </span></span></b></div><div style="text-align: justify;"><span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;"><b> * The vertex shader</b></span></div><div style="text-align: justify;"><span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;"><b> */</b></span></div><div style="text-align: justify;"><span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;"><br />
</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;"> //Attribute describing the position</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> attribute vec3 aVertexPosition; </span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: #6aa84f;">//Unfirom to convert to world space</span></span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> uniform mat4 worldMatrix;</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: #6aa84f;"> //uniform to convert to screen space</span></span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> uniform mat4 viewProjectionMatrix;<span class="Apple-tab-span" style="white-space: pre;"> </span></span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> void main(void) {</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: #6aa84f;">//calculate which pixel I want to color</span></span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span"><span class="Apple-tab-span" style="white-space: pre;"> </span> gl_Position = </span>viewProjectionMatrix <span class="Apple-style-span">* </span>worldMatrix <span class="Apple-style-span">* vec4(aVertexPosition, 1.0);</span></span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></div><div style="text-align: justify;"></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <b><span class="Apple-style-span" style="color: #6aa84f;">/** </span></b></span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: justify;"><span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;"><b> * The fragment shader</b></span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: justify;"><span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;"><b> */</b></span></div><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;"> //a uniform saying which color</span></div><div style="text-align: justify;"></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> uniform vec4 color;<span class="Apple-tab-span" style="white-space: pre;"> </span></span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> void main(void) {</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: #6aa84f;">//put that color on the pixel, YEAH!</span></span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> gl_FragColor = color;</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span></div><br />
<div style="text-align: justify;"><br />
</div>So that would be it for this lesson! Oh, one more thing. If you got issues on your Chrome or Firefox browser with WebGL (like, erm, it's not working) do the following:<br />
<br />
- for Chrome: add "--ignore-blacklist-gpu" to your run arguments for chrome, to force WebGL working<br />
- for Firefox: got to about:config, search webgl, and set force-webgl-enabled to true.<br />
<br />
That's it, your ready to go!!!<div class="blogger-post-footer"><a href="http://webglfactory.blogspot.com/"> WebGL Factory</a><br/>
d copyright, 2011. Share it, but link back to this blog.<br/>
Denis Tosic<br/></div>Denis Tosichttp://www.blogger.com/profile/14992340631798182965noreply@blogger.com1tag:blogger.com,1999:blog-8577130439755471155.post-24215256762532116462011-05-16T11:20:00.003+02:002011-05-16T12:36:43.395+02:00Page rank #1!I just wanted to share a little fact I got quite proud of:<br />
<br />
If you type into Google "webgl factory", this blog is listed as number one - after just one week of existence! It is also valued at 300$ :)<br />
<br />
Thank's to all my visitors, WebGL samples are already on the way!<div class="blogger-post-footer"><a href="http://webglfactory.blogspot.com/"> WebGL Factory</a><br/>
d copyright, 2011. Share it, but link back to this blog.<br/>
Denis Tosic<br/></div>Denis Tosichttp://www.blogger.com/profile/14992340631798182965noreply@blogger.com0tag:blogger.com,1999:blog-8577130439755471155.post-49779198861617744552011-05-11T20:36:00.003+02:002011-05-14T14:12:23.590+02:00IO WOW!<div style="text-align: justify;">Ok, I admit it - I have been wrong about the presentations (and I don't mean the eye candy part). I have just watch the "WebGL performance and techniques" session by Gregg Tavares and I am absolutely speechless. 40000 objects at 40FPS? Just consider the amount of multiplications that JavaScript does not have to execute - more then a few million. I mean, I am working now for a few years for computer graphics now, and I thought I got some good ideas, but Google once again proved me unworthy. I definitely recommend all graphics fans the presentation - it's a 201 session, and even if you're a newcomer it pay's out watching it. </div><br />
<br />
<div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/rfQ8rKGTVlg?feature=player_embedded' frameborder='0'></iframe></div><br />
<strike>I still got a few sessions to check out, so I will keep you posted. In the meanwhile, enjoy this on Thumbs up!</strike><br />
<strike><br />
</strike><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-family: inherit;">Now, this is not the only session involving WebGL and/or game development. <span class="Apple-style-span" style="line-height: 18px;">Ray Cromwell and Philip Rogers also hosted a very interesting <a href="http://www.google.com/events/io/2011/sessions/kick-ass-game-programming-with-google-web-toolkit.html">session </a>on this topic using GWT and Java. Google extend the GWT library with a quite useful development frame - the </span><a href="http://code.google.com/p/play-framework-gwt/" style="line-height: 18px;">ForPlay</a><span class="Apple-style-span" style="line-height: 18px;">. Keep in mind that this is still an alpha build, but never the less it's worth to check it out. ForPlay includes a lot things ranging from optimized draw procedures to physics. </span></span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: inherit;"><span class="Apple-style-span" style="line-height: 18px;"><br />
</span></span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: inherit;"><span class="Apple-style-span" style="line-height: 18px;">The really cool thing about it is it's platform "</span><span class="Apple-style-span" style="line-height: 18px;">independence</span><span class="Apple-style-span" style="line-height: 18px;">" - ForPlay makes it easy to develop a game in Java and the export it to different platforms - desktop Java, web browser or Android. So basically, you make one game and you got it right away ready for 3 platforms (maybe Flash will be included later?). I believe that especially social game developers could find a nice starting point or advacement in this.</span></span></div><span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: x-small;"><span class="Apple-style-span" style="line-height: 18px;"><br />
</span></span><br />
<div class="separator" style="clear: both; text-align: center;"><object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://0.gvt0.com/vi/F_sbusEUz5w/0.jpg" height="266" width="320"><param name="movie" value="http://www.youtube.com/v/F_sbusEUz5w&fs=1&source=uds" /><param name="bgcolor" value="#FFFFFF" /><embed width="320" height="266" src="http://www.youtube.com/v/F_sbusEUz5w&fs=1&source=uds" type="application/x-shockwave-flash"></embed></object></div><div class="separator" style="clear: both; text-align: justify;"><br />
</div><div class="separator" style="clear: both; text-align: justify;"><br />
</div><div class="separator" style="clear: both; text-align: justify;">The last session that I recommend watching (in respect to WebGL) is <a href="http://www.google.com/events/io/2011/sessions/building-game-development-tools-with-app-engine-gwt-and-webgl.html">Building game development tools</a> by Lilli Thompson. This one is not about games or engines itself, but about the tools needed and/or used by game developers and designers. Punchline here is that a lot of those tools can be ported to the browser - no installation, no portability or offsite work issues - all thanks to WebGL. The ease of development is provided by GWT and Java (of course :)). Consider the power is such approach in respect to CAD software, image or video effects editors. Or the commercial aspect in "renting" specific services on a weekly or monthly basis, incorporated with the <audio> and <video> tags capabilities. Enjoy the video!</div><div class="separator" style="clear: both; text-align: justify;"><br />
</div><div style="text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/NNmoEOpGJdk?feature=player_embedded' frameborder='0'></iframe></div><br />
<span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; line-height: 18px;"><br />
</span><div class="blogger-post-footer"><a href="http://webglfactory.blogspot.com/"> WebGL Factory</a><br/>
d copyright, 2011. Share it, but link back to this blog.<br/>
Denis Tosic<br/></div>Denis Tosichttp://www.blogger.com/profile/14992340631798182965noreply@blogger.com0tag:blogger.com,1999:blog-8577130439755471155.post-81761970814377374312011-05-09T20:10:00.002+02:002011-05-09T20:15:56.143+02:00The Wows and Hows of Google<div style="text-align: justify;">Tomorrow starts a two day paradise for developers - <a href="http://www.google.com/events/io/2011/">Google IO 2011</a>. Wish could be there myself, but as far as I heard the tickets were sold out in about an hour or so - and it's not like a got a few hundred dollars or euros spare to over there ... Well, time to stop dreaming - what have the Googlers prepared for us this years? Well, a lot of stuff - Android, Chrome, HTML5, GWT and of course my all time favorite WebGL. Here are some links to the sessions descriptions:</div><br />
<ul><li><a href="http://www.google.com/events/io/2011/sessions/building-game-development-tools-with-app-engine-gwt-and-webgl.html">Game development</a></li>
<li><a href="http://www.google.com/events/io/2011/sessions/high-performance-gwt-best-practices-for-writing-smaller-faster-apps.html">High performance GWT</a></li>
<li><a href="http://www.google.com/events/io/2011/sessions/webgl-techniques-and-performance.html">WebGL tips and tricks</a></li>
<li><a href="http://www.google.com/events/io/2011/sessions/programming-well-with-others-social-skills-for-geeks.html">How to be nice to other developers</a></li>
<li>Lunch breaks (link broken due to lack of pizza)</li>
</ul><div style="text-align: justify;"><br />
So what can we expect? To be honest, I guess we're going to see a lot of eye candies, a few super big and super small numbers and a guest appearance by Bing!... maybe. It's probably going to be more interesting the Khronos's WebGL specs, and I hold thumbs that their going to show us average people some nice hacks, but a glossary about WebGL performance enhancements I think we won't see.</div><div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Nevertheless, I think the presentations will be worth watching - some of the presenters do know how to make a good show after all. Well, stay tuned, in a few days we will know more.</div></div><div><br />
<br />
</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjb3lEnf7SNx5IYVtYP5ILTC7jh_srW7ouH11IiwMMDPJ1YB4i-sosEEaIulAKIbw_nrXxHeWzwiMYvKSruJBOG_c1pka51FuChgThciFJM5qwQBXQarJOskiwxSbS-afg8SRhcfOlLGds/s1600/firstgooglelesson.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="204" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjb3lEnf7SNx5IYVtYP5ILTC7jh_srW7ouH11IiwMMDPJ1YB4i-sosEEaIulAKIbw_nrXxHeWzwiMYvKSruJBOG_c1pka51FuChgThciFJM5qwQBXQarJOskiwxSbS-afg8SRhcfOlLGds/s320/firstgooglelesson.gif" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><i>Lesson #1 in this years Google IO</i></div><div class="separator" style="clear: both; text-align: center;"><i><br />
</i></div><div class="blogger-post-footer"><a href="http://webglfactory.blogspot.com/"> WebGL Factory</a><br/>
d copyright, 2011. Share it, but link back to this blog.<br/>
Denis Tosic<br/></div>Denis Tosichttp://www.blogger.com/profile/14992340631798182965noreply@blogger.com0tag:blogger.com,1999:blog-8577130439755471155.post-56355905662583910602011-05-08T20:01:00.000+02:002011-05-08T20:01:10.540+02:00Opening Day!<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYhjzPPVO6oMJfwXJcoPuTBs09xW9Tx_puEnwW18W7nRJsXQSvCMaS6Fu499-kRH9DtKXKKDeVGFclAVRRgKY4xKNaUJEYAjrJt8VC__niQYP9bPElK9s7lj_ozpQCibOnaO4ytsr-y28/s1600/HTML5.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYhjzPPVO6oMJfwXJcoPuTBs09xW9Tx_puEnwW18W7nRJsXQSvCMaS6Fu499-kRH9DtKXKKDeVGFclAVRRgKY4xKNaUJEYAjrJt8VC__niQYP9bPElK9s7lj_ozpQCibOnaO4ytsr-y28/s200/HTML5.png" width="200" /></a></div><div style="text-align: justify;"> </div><div style="text-align: justify;">I have been developing in WebGL for quite a while now, and I think it's time for me to share what I have learned. And why would I do that? Because I am a software developer, so it's kind of written in my genes to have a geeky blog about something. And let's face the truth - graphics are much more eye candy then Fortran or, God forbid, database tables... </div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Silliness aside, I believe that WebGL is coming on the BIG door. A lot of things will be moving to the web because of HTML5, and a lot of opportunities will arise - and you, dear readers, want to be sure that you will be able to utilize them (the picture below shows the average effect of WebGL on the end user). Of course, HTML5 - which I believe is now officialy just HTML - is not just WebGL, but this blog will mostly be about WebGL and themes regarding graphics problems. I will try to provide as much lessons, tutorials, examples and explanations as I can - starting with WebGL dummy problems. My end goal is to teach at least one of you how to make your own <a href="http://bodybrowser.googlelabs.com/body.html#">BodyBrowser</a> :) For the ones of you who don't feel like making their hands dirty - you can still stop by and enjoy the view!</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><br />
</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3zcOnC2fRf4Cmxkp7U9FWqQutGZSThWVCCL5vA_dArVGYDe5m-t-yKsQqibqOibnsTjHo_IfcjNjadpylEtDXg_BC2p3oGy_VSvUggs6ZROB-6f5JflTzLn_CtvzsXmSXB7EMjukB52U/s1600/MyFirstWebGLEncounter.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="92" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3zcOnC2fRf4Cmxkp7U9FWqQutGZSThWVCCL5vA_dArVGYDe5m-t-yKsQqibqOibnsTjHo_IfcjNjadpylEtDXg_BC2p3oGy_VSvUggs6ZROB-6f5JflTzLn_CtvzsXmSXB7EMjukB52U/s400/MyFirstWebGLEncounter.jpg" width="400" /></a></div><div style="text-align: center;"><i>The first time I saw a spinning teapot on my screen.</i></div><div style="text-align: center;"><i><br />
</i></div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">For this week there will be no lessons - after all it's my first time (ha-ha) and I don't want to drive you away after the introduction class. Next time I will try to prepare some basic introduction and example of how WebGL works, with as little theory as possible. I plan also to open a separate math and data structure sections, but that will come later, once the basics are covered. Also, I will try to put in here and there GWT and Java posts, and of course some personal, philosophic debates with myself - hope you'll forgive me that!</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">To close up this entry, if you still did not see some WebGL samples by now, visit <a href="http://www.chromeexperiments.com/webgl">Chrome experiments</a> to get a glimpse of what I will be trying to explain here. And by the way, don't forget to have a WebGL supporting browser :)</div><div class="blogger-post-footer"><a href="http://webglfactory.blogspot.com/"> WebGL Factory</a><br/>
d copyright, 2011. Share it, but link back to this blog.<br/>
Denis Tosic<br/></div>Denis Tosichttp://www.blogger.com/profile/14992340631798182965noreply@blogger.com0tag:blogger.com,1999:blog-8577130439755471155.post-64429993263072222112011-01-01T21:58:00.004+01:002011-05-27T10:21:28.930+02:00Image storage :)<div class="separator" style="clear: both; text-align: left;">TODO: remove</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_dwswdPgGPxcL3BHdVkaQfyeX4rw49aDHktvg4pdMi0wtsxiKlwEre7SmKI1taEC3BZEjmUNwX2p50F9LQDsnyRxfjnOrIYhJEi1Fugkuv4JPylWAn16ehaicAjLrlx1ab5J504MlIbU/s1600/delicious.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_dwswdPgGPxcL3BHdVkaQfyeX4rw49aDHktvg4pdMi0wtsxiKlwEre7SmKI1taEC3BZEjmUNwX2p50F9LQDsnyRxfjnOrIYhJEi1Fugkuv4JPylWAn16ehaicAjLrlx1ab5J504MlIbU/s1600/delicious.png" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEix4qYesgo_gscJUdWmFmIz1kOeNhocDXSmpxyYmQHbNzjnC1C-zIHDeaKRVFwUAmwUuA5wZc1iGu1JNxiroUDmX7fKYUCvr5VejisnyCv8BX8Y5Nb32VqMTy3fIS4RaxLWF3yzJU-2zCw/s1600/digg.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEix4qYesgo_gscJUdWmFmIz1kOeNhocDXSmpxyYmQHbNzjnC1C-zIHDeaKRVFwUAmwUuA5wZc1iGu1JNxiroUDmX7fKYUCvr5VejisnyCv8BX8Y5Nb32VqMTy3fIS4RaxLWF3yzJU-2zCw/s1600/digg.png" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRoXR79m6jyQUt535frhAwjnkYNtpga8_GVsx2vVkuC6zUpb6-Nji-ecgQ21lJVWDszYyrJYNsfLoI9UvaCwvIAFzmONJZUdhwmrAoj3Vj_NCwSkm8mMLVJQLCFp1iaZDOSNUEuEawvKo/s1600/facebook.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRoXR79m6jyQUt535frhAwjnkYNtpga8_GVsx2vVkuC6zUpb6-Nji-ecgQ21lJVWDszYyrJYNsfLoI9UvaCwvIAFzmONJZUdhwmrAoj3Vj_NCwSkm8mMLVJQLCFp1iaZDOSNUEuEawvKo/s1600/facebook.png" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixGSH8egXUl81mVsro8W-m5-O46250Tt6yHnD6gmirx_YETab4tPTSBmhevKQsHMM5KvfPtUFj1dEvXxalJ1tq5nBU-Vl8fIcVoHbzn39SbDSS8HJL4lukmxRuMH1v9Rrcb8_5Y6WkJgA/s1600/favorites.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixGSH8egXUl81mVsro8W-m5-O46250Tt6yHnD6gmirx_YETab4tPTSBmhevKQsHMM5KvfPtUFj1dEvXxalJ1tq5nBU-Vl8fIcVoHbzn39SbDSS8HJL4lukmxRuMH1v9Rrcb8_5Y6WkJgA/s1600/favorites.png" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEij7JCazPTw8emmlTdEelwadtFTNMYpeYLWazBlevlACOHMyNpIjND5O1w2iKSdE8uFdwuw1203EbJMU5xxlIbA8sng5lEFc7xgwIuAePlc_KmSkILSkcvUG8jOehu9lO0R-bDimEdZVwg/s1600/more.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEij7JCazPTw8emmlTdEelwadtFTNMYpeYLWazBlevlACOHMyNpIjND5O1w2iKSdE8uFdwuw1203EbJMU5xxlIbA8sng5lEFc7xgwIuAePlc_KmSkILSkcvUG8jOehu9lO0R-bDimEdZVwg/s1600/more.png" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwRYP9ofLMSqwh_BvPsmz3qD1pCndFwdMzdEHMxT1sUgVFuVDVes9x2ohmoMYpQ4rFeh4ZuFd8cuCCOVj1TaytMLuCn28L1ReZZu79wI8o9ektGj-mmkI-ei0mCG5g2GP-LcjXlZ0KsOQ/s1600/stumbleupon.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwRYP9ofLMSqwh_BvPsmz3qD1pCndFwdMzdEHMxT1sUgVFuVDVes9x2ohmoMYpQ4rFeh4ZuFd8cuCCOVj1TaytMLuCn28L1ReZZu79wI8o9ektGj-mmkI-ei0mCG5g2GP-LcjXlZ0KsOQ/s1600/stumbleupon.png" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCZ6rU73gWcCWwfEdUr_K3Af0tYDJoKtW5qqtK3QxJmfgHKdnryjNYlcR7Dg4LjD9N-RzjXEy3jV0hw9vfI6hk6N6qd_DhgSFPOBVD3pkGDrkM-ekLYRf86rU3XDNGFxBal45aztC3b9U/s1600/twitter.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCZ6rU73gWcCWwfEdUr_K3Af0tYDJoKtW5qqtK3QxJmfgHKdnryjNYlcR7Dg4LjD9N-RzjXEy3jV0hw9vfI6hk6N6qd_DhgSFPOBVD3pkGDrkM-ekLYRf86rU3XDNGFxBal45aztC3b9U/s1600/twitter.png" /></a></div><div class="blogger-post-footer"><a href="http://webglfactory.blogspot.com/"> WebGL Factory</a><br/>
d copyright, 2011. Share it, but link back to this blog.<br/>
Denis Tosic<br/></div>Denis Tosichttp://www.blogger.com/profile/14992340631798182965noreply@blogger.com