春天来了!你看,融化的冰水把小溪弄醒了。 "丁粳、丁粳 ",它就像大自然的神奇歌手,唱着清脆悦耳的歌,向前奔流……
前提条件和预期结果
目前只有少数的浏览器支持 WebGL ,请看我的另外一篇文章:Can I use WebGL?.
下面的例子是在 Windows 下的 Chrome 16/23 以及 Android 下的 Firefox 17 进行测试。如果你使用的是非兼容浏览器访问则会弹出一个警告。
图1:包含 Hello world 文本的动画的 WebGL 立方体
在兼容 HTML5 的浏览器上,你将会看到如下图所示的带动画效果的立方体:
图2: 示例运行的屏幕截图
该代码基于 Lighting in WebGL - How to simulate lighting effects in your WebGL context - 非常感谢这篇好代码教程。在该实例初始运行时,动画的立方体是通过一个静态的 Bitmap 图形对象渲染的。
下面的代码演示如何在程序中动态的渲染文本:
XML/HTML Code复制内容到剪贴板
- //TODO#1Newmethodtocreateatexture
- functioncreateCubeTexture(text){
- ...
- }
在这里使用 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); 是非常重要的,用来确保写文本时不会前后颠倒。剩下的就很容易理解了:
XML/HTML Code复制内容到剪贴板
- //TODO#2Assignthecreatedtexturefordisplay
- cubeTexture=createCubeTexture("HelloWorld!");
源码
// File #1: webgl-demo.htm
XML/HTML Code复制内容到剪贴板
- <html>
- <head>
- <title>WebGL-HelloWorld!</title>
- <metahttp-equiv="Content-Type"content="text/html;charset=utf-8">
- <scriptsrc="sylvester.js"type="text/javascript"></script>
- <scriptsrc="glUtils.js"type="text/javascript"></script>
- <scriptsrc="webgl-demo.js"type="text/javascript"></script>
- <!--Fragmentshaderprogram-->
- <scriptid="shader-fs"type="x-shader/x-fragment">
- varyinghighpvec2vTextureCoord;
- varyinghighpvec3vLighting;
- uniformsampler2DuSampler;
- voidmain(void){
- highpvec4texelColor=texture2D(uSampler,vec2(vTextureCoord.s,vTextureCoord.t));
- gl_FragColor=vec4(texelColor.rgb*vLighting,texelColor.a);
- }
- </script>
- <!--Vertexshaderprogram-->
- <scriptid="shader-vs"type="x-shader/x-vertex">
- attributehighpvec3aVertexNormal;
- attributehighpvec3aVertexPosition;
- attributehighpvec2aTextureCoord;
- uniformhighpmat4uNormalMatrix;
- uniformhighpmat4uMVMatrix;
- uniformhighpmat4uPMatrix;
- varyinghighpvec2vTextureCoord;
- varyinghighpvec3vLighting;
- voidmain(void){
- gl_Position=uPMatrix*uMVMatrix*vec4(aVertexPosition,1.0);
- vTextureCoord=aTextureCoord;
- //Applylightingeffect
- highpvec3ambientLight=vec3(0.6,0.6,0.6);
- highpvec3directionalLightColor=vec3(0.5,0.5,0.75);
- highpvec3directionalVector=vec3(0.85,0.8,0.75);
- highpvec4transformedNormal=uNormalMatrix*vec4(aVertexNormal,1.0);
- highpfloatdirectional=max(dot(transformedNormal.xyz,directionalVector),0.0);
- vLighting=ambientLight+(directionalLightColor*directional);
- }
- </script>
- </head>
- <bodyonload="start()">
- <canvasid="glcanvas"width="640"height="480">
- Yourbrowserdoesn'tappeartosupporttheHTML5<code><canvas></code>element.
- </canvas>
- </body>
- </html>
// File #02: webgl-demo.js
XML/HTML Code复制内容到剪贴板
- varcanvas;
- vargl;
- varcubeVerticesBuffer;
- varcubeVerticesTextureCoordBuffer;
- varcubeVerticesIndexBuffer;
- varcubeVerticesIndexBuffer;
- varcubeRotation=0.0;
- varlastCubeUpdateTime=0;
- varcubeImage;
- varcubeTexture;
- varmvMatrix;
- varshaderProgram;
- varvertexPositionAttribute;
- varvertexNormalAttribute;
- vartextureCoordAttribute;
- varperspectiveMatrix;
- //
- //start
- //
- //Calledwhenthecanvasiscreatedtogettheballrolling.
- //
- functionstart(){
- canvas=document.getElementById("glcanvas");
- initWebGL(canvas);//InitializetheGLcontext
- //OnlycontinueifWebGLisavailableandworking
- if(gl){
- gl.clearColor(0.0,0.0,0.0,1.0);//Cleartoblack,fullyopaque
- gl.clearDepth(1.0);//Cleareverything
- gl.enable(gl.DEPTH_TEST);//Enabledepthtesting
- gl.depthFunc(gl.LEQUAL);//Nearthingsobscurefarthings
- //Initializetheshaders;thisiswhereallthelightingforthe
- //verticesandsoforthisestablished.
- initShaders();
- //Here'swherewecalltheroutinethatbuildsalltheobjects
- //we'llbedrawing.
- initBuffers();
- //Next,loadandsetupthetextureswe'llbeusing.
- //TODO#2Start
- cubeTexture=createCubeTexture("HelloWorld!");
- //TODO#2End
- //Setuptodrawthesceneperiodically.
- setInterval(drawScene,15);
- }
- }
- //
- //initWebGL
- //
- //InitializeWebGL,returningtheGLcontextornullif
- //WebGLisn'tavailableorcouldnotbeinitialized.
- //
- functioninitWebGL(){
- gl=null;
- try{
- gl=canvas.getContext("experimental-webgl");
- }
- catch(e){
- }
- //Ifwedon'thaveaGLcontext,giveupnow
- if(!gl){
- alert("UnabletoinitializeWebGL.Yourbrowsermaynotsupportit.");
- }
- }
- //
- //initBuffers
- //
- //Initializethebufferswe'llneed.Forthisdemo,wejusthave
- //oneobject--asimpletwo-dimensionalcube.
- //
- functioninitBuffers(){
- //Createabufferforthecube'svertices.
- cubeVerticesBuffer=gl.createBuffer();
- //SelectthecubeVerticesBufferastheonetoapplyvertex
- //operationstofromhereout.
- gl.bindBuffer(gl.ARRAY_BUFFER,cubeVerticesBuffer);
- //Nowcreateanarrayofverticesforthecube.
- varvertices=[
- //Frontface
- -1.0,-1.0,1.0,
- 1.0,-1.0,1.0,
- 1.0,1.0,1.0,
- -1.0,1.0,1.0,
- //Backface
- -1.0,-1.0,-1.0,
- -1.0,1.0,-1.0,
- 1.0,1.0,-1.0,
- 1.0,-1.0,-1.0,
- //Topface
- -1.0,1.0,-1.0,
- -1.0,1.0,1.0,
- 1.0,1.0,1.0,
- 1.0,1.0,-1.0,
- //Bottomface
- -1.0,-1.0,-1.0,
- 1.0,-1.0,-1.0,
- 1.0,-1.0,1.0,
- -1.0,-1.0,1.0,
- //Rightface
- 1.0,-1.0,-1.0,
- 1.0,1.0,-1.0,
- 1.0,1.0,1.0,
- 1.0,-1.0,1.0,
- //Leftface
- -1.0,-1.0,-1.0,
- -1.0,-1.0,1.0,
- -1.0,1.0,1.0,
- -1.0,1.0,-1.0
- ];
- //NowpassthelistofverticesintoWebGLtobuildtheshape.We
- //dothisbycreatingaFloat32ArrayfromtheJavaScriptarray,
- //thenuseittofillthecurrentvertexbuffer.
- gl.bufferData(gl.ARRAY_BUFFER,newFloat32Array(vertices),gl.STATIC_DRAW);
- //Setupthenormalsforthevertices,sothatwecancomputelighting.
- cubeVerticesNormalBuffer=gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER,cubeVerticesNormalBuffer);
- varvertexNormals=[
- //Front
- 0.0,0.0,1.0,
- 0.0,0.0,1.0,
- 0.0,0.0,1.0,
- 0.0,0.0,1.0,
- //Back
- 0.0,0.0,-1.0,
- 0.0,0.0,-1.0,
- 0.0,0.0,-1.0,
- 0.0,0.0,-1.0,
- //Top
- 0.0,1.0,0.0,
- 0.0,1.0,0.0,
- 0.0,1.0,0.0,
- 0.0,1.0,0.0,
- //Bottom
- 0.0,-1.0,0.0,
- 0.0,-1.0,0.0,
- 0.0,-1.0,0.0,
- 0.0,-1.0,0.0,
- //Right
- 1.0,0.0,0.0,
- 1.0,0.0,0.0,
- 1.0,0.0,0.0,
- 1.0,0.0,0.0,
- //Left
- -1.0,0.0,0.0,
- -1.0,0.0,0.0,
- -1.0,0.0,0.0,
- -1.0,0.0,0.0
- ];
- gl.bufferData(gl.ARRAY_BUFFER,newFloat32Array(vertexNormals),
- gl.STATIC_DRAW);
- //Mapthetextureontothecube'sfaces.
- cubeVerticesTextureCoordBuffer=gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER,cubeVerticesTextureCoordBuffer);
- vartextureCoordinates=[
- //Front
- 0.0,0.0,
- 1.0,0.0,
- 1.0,1.0,
- 0.0,1.0,
- //Back
- 0.0,0.0,
- 1.0,0.0,
- 1.0,1.0,
- 0.0,1.0,
- //Top
- 0.0,0.0,
- 1.0,0.0,
- 1.0,1.0,
- 0.0,1.0,
- //Bottom
- 0.0,0.0,
- 1.0,0.0,
- 1.0,1.0,
- 0.0,1.0,
- //Right
- 0.0,0.0,
- 1.0,0.0,
- 1.0,1.0,
- 0.0,1.0,
- //Left
- 0.0,0.0,
- 1.0,0.0,
- 1.0,1.0,
- 0.0,1.0
- ];
- gl.bufferData(gl.ARRAY_BUFFER,newFloat32Array(textureCoordinates),
- gl.STATIC_DRAW);
- //Buildtheelementarraybuffer;thisspecifiestheindices
- //intothevertexarrayforeachface'svertices.
- cubeVerticesIndexBuffer=gl.createBuffer();
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,cubeVerticesIndexBuffer);
- //Thisarraydefineseachfaceastwotriangles,usingthe
- //indicesintothevertexarraytospecifyeachtriangle's
- //position.
- varcubeVertexIndices=[
- 0,1,2,0,2,3,//front
- 4,5,6,4,6,7,//back
- 8,9,10,8,10,11,//top
- 12,13,14,12,14,15,//bottom
- 16,17,18,16,18,19,//right
- 20,21,22,20,22,23//left
- ]
- //NowsendtheelementarraytoGL
- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
- newUint16Array(cubeVertexIndices),gl.STATIC_DRAW);
- }
- //
- //initTextures
- //
- //Initializethetextureswe'llbeusing,theninitiatealoadof
- //thetextureimages.ThehandleTextureLoaded()callbackwillfinish
- //thejob;itgetscalledeachtimeatexturefinishesloading.
- //
- //TODO#1Start
- functioncreateCubeTexture(text){
- //createahiddencanvastodrawthetexture
- varcanvas=document.createElement('canvas');
- canvas.id="hiddenCanvas";
- canvas.width=512;
- canvas.height=512;
- canvas.style.display="none";
- varbody=document.getElementsByTagName("body")[0];
- body.appendChild(canvas);
- //drawtexture
- varcubeImage=document.getElementById('hiddenCanvas');
- varctx=cubeImage.getContext('2d');
- ctx.beginPath();
- ctx.rect(0,0,ctx.canvas.width,ctx.canvas.height);
- ctx.fillStyle='white';
- ctx.fill();
- ctx.fillStyle='black';
- ctx.font="65pxArial";
- ctx.textAlign='center';
- ctx.fillText(text,ctx.canvas.width/2,ctx.canvas.height/2);
- ctx.restore();
- //createnewtexture
- vartexture=gl.createTexture();
- gl.bindTexture(gl.TEXTURE_2D,texture);
- gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_NEAREST);
- gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,true);
- handleTextureLoaded(cubeImage,texture)
- returntexture;
- }
- //TODO#1End
- functionhandleTextureLoaded(image,texture){
- gl.bindTexture(gl.TEXTURE_2D,texture);
- gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,image);
- gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_NEAREST);
- gl.generateMipmap(gl.TEXTURE_2D);
- gl.bindTexture(gl.TEXTURE_2D,null);
- }
- //
- //drawScene
- //
- //Drawthescene.
- //
- functiondrawScene(){
- //Clearthecanvasbeforewestartdrawingonit.
- gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
- //Establishtheperspectivewithwhichwewanttoviewthe
- //scene.Ourfieldofviewis45degrees,withawidth/height
- //ratioof640:480,andweonlywanttoseeobjectsbetween0.1units
- //and100unitsawayfromthecamera.
- perspectiveMatrix=makePerspective(45,640.0/480.0,0.1,100.0);
- //Setthedrawingpositiontothe"identity"point,whichis
- //thecenterofthescene.
- loadIdentity();
- //Nowmovethedrawingpositionabittowherewewanttostart
- //drawingthecube.
- mvTranslate([0.0,0.0,-6.0]);
- //Savethecurrentmatrix,thenrotatebeforewedraw.
- mvPushMatrix();
- mvRotate(cubeRotation,[1,0,1]);
- //Drawthecubebybindingthearraybuffertothecube'svertices
- //array,settingattributes,andpushingittoGL.
- gl.bindBuffer(gl.ARRAY_BUFFER,cubeVerticesBuffer);
- gl.vertexAttribPointer(vertexPositionAttribute,3,gl.FLOAT,false,0,0);
- //Setthetexturecoordinatesattributeforthevertices.
- gl.bindBuffer(gl.ARRAY_BUFFER,cubeVerticesTextureCoordBuffer);
- gl.vertexAttribPointer(textureCoordAttribute,2,gl.FLOAT,false,0,0);
- //Bindthenormalsbuffertotheshaderattribute.
- gl.bindBuffer(gl.ARRAY_BUFFER,cubeVerticesNormalBuffer);
- gl.vertexAttribPointer(vertexNormalAttribute,3,gl.FLOAT,false,0,0);
- //Specifythetexturetomapontothefaces.
- gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(gl.TEXTURE_2D,cubeTexture);
- gl.uniform1i(gl.getUniformLocation(shaderProgram,"uSampler"),0);
- //Drawthecube.
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,cubeVerticesIndexBuffer);
- setMatrixUniforms();
- gl.drawElements(gl.TRIANGLES,36,gl.UNSIGNED_SHORT,0);
- //Restoretheoriginalmatrix
- mvPopMatrix();
- //Updatetherotationforthenextdraw,ifit'stimetodoso.
- varcurrentTime=(newDate).getTime();
- if(lastCubeUpdateTime){
- vardelta=currentTime-lastCubeUpdateTime;
- cubeRotation+=(30*delta)/1000.0;
- }
- lastCubeUpdateTime=currentTime;
- }
- //
- //initShaders
- //
- //Initializetheshaders,soWebGLknowshowtolightourscene.
- //
- functioninitShaders(){
- varfragmentShader=getShader(gl,"shader-fs");
- varvertexShader=getShader(gl,"shader-vs");
- //Createtheshaderprogram
- shaderProgram=gl.createProgram();
- gl.attachShader(shaderProgram,vertexShader);
- gl.attachShader(shaderProgram,fragmentShader);
- gl.linkProgram(shaderProgram);
- //Ifcreatingtheshaderprogramfailed,alert
- if(!gl.getProgramParameter(shaderProgram,gl.LINK_STATUS)){
- alert("Unabletoinitializetheshaderprogram.");
- }
- gl.useProgram(shaderProgram);
- vertexPositionAttribute=gl.getAttribLocation(shaderProgram,"aVertexPosition");
- gl.enableVertexAttribArray(vertexPositionAttribute);
- textureCoordAttribute=gl.getAttribLocation(shaderProgram,"aTextureCoord");
- gl.enableVertexAttribArray(textureCoordAttribute);
- vertexNormalAttribute=gl.getAttribLocation(shaderProgram,"aVertexNormal");
- gl.enableVertexAttribArray(vertexNormalAttribute);
- }
- //
- //getShader
- //
- //Loadsashaderprogrambyscouringthecurrentdocument,
- //lookingforascriptwiththespecifiedID.
- //
- functiongetShader(gl,id){
- varshaderScript=document.getElementById(id);
- //Didn'tfindanelementwiththespecifiedID;abort.
- if(!shaderScript){
- returnnull;
- }
- //Walkthroughthesourceelement'schildren,buildingthe
- //shadersourcestring.
- vartheSource="";
- varcurrentChild=shaderScript.firstChild;
- while(currentChild){
- if(currentChild.nodeType==3){
- theSource+=currentChild.textContent;
- }
- currentChildcurrentChild=currentChild.nextSibling;
- }
- //Nowfigureoutwhattypeofshaderscriptwehave,
- //basedonitsMIMEtype.
- varshader;
- if(shaderScript.type=="x-shader/x-fragment"){
- shader=gl.createShader(gl.FRAGMENT_SHADER);
- }elseif(shaderScript.type=="x-shader/x-vertex"){
- shader=gl.createShader(gl.VERTEX_SHADER);
- }else{
- returnnull;//Unknownshadertype
- }
- //Sendthesourcetotheshaderobject
- gl.shaderSource(shader,theSource);
- //Compiletheshaderprogram
- gl.compileShader(shader);
- //Seeifitcompiledsuccessfully
- if(!gl.getShaderParameter(shader,gl.COMPILE_STATUS)){
- alert("Anerroroccurredcompilingtheshaders:"+gl.getShaderInfoLog(shader));
- returnnull;
- }
- returnshader;
- }
- //
- //Matrixutilityfunctions
- //
- functionloadIdentity(){
- mvMatrix=Matrix.I(4);
- }
- functionmultMatrix(m){
- mvMatrixmvMatrix=mvMatrix.x(m);
- }
- functionmvTranslate(v){
- multMatrix(Matrix.Translation($V([v[0],v[1],v[2]])).ensure4x4());
- }
- functionsetMatrixUniforms(){
- varpUniform=gl.getUniformLocation(shaderProgram,"uPMatrix");
- gl.uniformMatrix4fv(pUniform,false,newFloat32Array(perspectiveMatrix.flatten()));
- varmvUniform=gl.getUniformLocation(shaderProgram,"uMVMatrix");
- gl.uniformMatrix4fv(mvUniform,false,newFloat32Array(mvMatrix.flatten()));
- varnormalMatrix=mvMatrix.inverse();
- normalMatrixnormalMatrix=normalMatrix.transpose();
- varnUniform=gl.getUniformLocation(shaderProgram,"uNormalMatrix");
- gl.uniformMatrix4fv(nUniform,false,newFloat32Array(normalMatrix.flatten()));
- }
- varmvMatrixStack=[];
- functionmvPushMatrix(m){
- if(m){
- mvMatrixStack.push(m.dup());
- mmvMatrix=m.dup();
- }else{
- mvMatrixStack.push(mvMatrix.dup());
- }
- }
- functionmvPopMatrix(){
- if(!mvMatrixStack.length){
- throw("Can'tpopfromanemptymatrixstack.");
- }
- mvMatrix=mvMatrixStack.pop();
- returnmvMatrix;
- }
- functionmvRotate(angle,v){
- varinRadians=angle*Math.PI/180.0;
- varm=Matrix.Rotation(inRadians,$V([v[0],v[1],v[2]])).ensure4x4();
- multMatrix(m);
- }
本文在支持HTML5的浏览器上运行WebGL程序的方法到此结束。幻想者头脑里只有空中楼阁,实干家胸中才有摩天大厦。小编再次感谢大家对我们的支持!