在支持HTML5的浏览器上运行WebGL程序的方法

春天来了!你看,融化的冰水把小溪弄醒了。 "丁粳、丁粳 ",它就像大自然的神奇歌手,唱着清脆悦耳的歌,向前奔流……

前提条件和预期结果

目前只有少数的浏览器支持 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复制内容到剪贴板
  1. //TODO#1Newmethodtocreateatexture
  2. functioncreateCubeTexture(text){
  3. ...
  4. }

在这里使用 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); 是非常重要的,用来确保写文本时不会前后颠倒。剩下的就很容易理解了:

XML/HTML Code复制内容到剪贴板
  1. //TODO#2Assignthecreatedtexturefordisplay
  2. cubeTexture=createCubeTexture("HelloWorld!");

源码

// File #1: webgl-demo.htm

XML/HTML Code复制内容到剪贴板
  1. <html>
  2. <head>
  3. <title>WebGL-HelloWorld!</title>
  4. <metahttp-equiv="Content-Type"content="text/html;charset=utf-8">
  5. <scriptsrc="sylvester.js"type="text/javascript"></script>
  6. <scriptsrc="glUtils.js"type="text/javascript"></script>
  7. <scriptsrc="webgl-demo.js"type="text/javascript"></script>
  8. <!--Fragmentshaderprogram-->
  9. <scriptid="shader-fs"type="x-shader/x-fragment">
  10. varyinghighpvec2vTextureCoord;
  11. varyinghighpvec3vLighting;
  12. uniformsampler2DuSampler;
  13. voidmain(void){
  14. highpvec4texelColor=texture2D(uSampler,vec2(vTextureCoord.s,vTextureCoord.t));
  15. gl_FragColor=vec4(texelColor.rgb*vLighting,texelColor.a);
  16. }
  17. </script>
  18. <!--Vertexshaderprogram-->
  19. <scriptid="shader-vs"type="x-shader/x-vertex">
  20. attributehighpvec3aVertexNormal;
  21. attributehighpvec3aVertexPosition;
  22. attributehighpvec2aTextureCoord;
  23. uniformhighpmat4uNormalMatrix;
  24. uniformhighpmat4uMVMatrix;
  25. uniformhighpmat4uPMatrix;
  26. varyinghighpvec2vTextureCoord;
  27. varyinghighpvec3vLighting;
  28. voidmain(void){
  29. gl_Position=uPMatrix*uMVMatrix*vec4(aVertexPosition,1.0);
  30. vTextureCoord=aTextureCoord;
  31. //Applylightingeffect
  32. highpvec3ambientLight=vec3(0.6,0.6,0.6);
  33. highpvec3directionalLightColor=vec3(0.5,0.5,0.75);
  34. highpvec3directionalVector=vec3(0.85,0.8,0.75);
  35. highpvec4transformedNormal=uNormalMatrix*vec4(aVertexNormal,1.0);
  36. highpfloatdirectional=max(dot(transformedNormal.xyz,directionalVector),0.0);
  37. vLighting=ambientLight+(directionalLightColor*directional);
  38. }
  39. </script>
  40. </head>
  41. <bodyonload="start()">
  42. <canvasid="glcanvas"width="640"height="480">
  43. Yourbrowserdoesn'tappeartosupporttheHTML5<code><canvas></code>element.
  44. </canvas>
  45. </body>
  46. </html>

// File #02: webgl-demo.js

XML/HTML Code复制内容到剪贴板
  1. varcanvas;
  2. vargl;
  3. varcubeVerticesBuffer;
  4. varcubeVerticesTextureCoordBuffer;
  5. varcubeVerticesIndexBuffer;
  6. varcubeVerticesIndexBuffer;
  7. varcubeRotation=0.0;
  8. varlastCubeUpdateTime=0;
  9. varcubeImage;
  10. varcubeTexture;
  11. varmvMatrix;
  12. varshaderProgram;
  13. varvertexPositionAttribute;
  14. varvertexNormalAttribute;
  15. vartextureCoordAttribute;
  16. varperspectiveMatrix;
  17. //
  18. //start
  19. //
  20. //Calledwhenthecanvasiscreatedtogettheballrolling.
  21. //
  22. functionstart(){
  23. canvas=document.getElementById("glcanvas");
  24. initWebGL(canvas);//InitializetheGLcontext
  25. //OnlycontinueifWebGLisavailableandworking
  26. if(gl){
  27. gl.clearColor(0.0,0.0,0.0,1.0);//Cleartoblack,fullyopaque
  28. gl.clearDepth(1.0);//Cleareverything
  29. gl.enable(gl.DEPTH_TEST);//Enabledepthtesting
  30. gl.depthFunc(gl.LEQUAL);//Nearthingsobscurefarthings
  31. //Initializetheshaders;thisiswhereallthelightingforthe
  32. //verticesandsoforthisestablished.
  33. initShaders();
  34. //Here'swherewecalltheroutinethatbuildsalltheobjects
  35. //we'llbedrawing.
  36. initBuffers();
  37. //Next,loadandsetupthetextureswe'llbeusing.
  38. //TODO#2Start
  39. cubeTexture=createCubeTexture("HelloWorld!");
  40. //TODO#2End
  41. //Setuptodrawthesceneperiodically.
  42. setInterval(drawScene,15);
  43. }
  44. }
  45. //
  46. //initWebGL
  47. //
  48. //InitializeWebGL,returningtheGLcontextornullif
  49. //WebGLisn'tavailableorcouldnotbeinitialized.
  50. //
  51. functioninitWebGL(){
  52. gl=null;
  53. try{
  54. gl=canvas.getContext("experimental-webgl");
  55. }
  56. catch(e){
  57. }
  58. //Ifwedon'thaveaGLcontext,giveupnow
  59. if(!gl){
  60. alert("UnabletoinitializeWebGL.Yourbrowsermaynotsupportit.");
  61. }
  62. }
  63. //
  64. //initBuffers
  65. //
  66. //Initializethebufferswe'llneed.Forthisdemo,wejusthave
  67. //oneobject--asimpletwo-dimensionalcube.
  68. //
  69. functioninitBuffers(){
  70. //Createabufferforthecube'svertices.
  71. cubeVerticesBuffer=gl.createBuffer();
  72. //SelectthecubeVerticesBufferastheonetoapplyvertex
  73. //operationstofromhereout.
  74. gl.bindBuffer(gl.ARRAY_BUFFER,cubeVerticesBuffer);
  75. //Nowcreateanarrayofverticesforthecube.
  76. varvertices=[
  77. //Frontface
  78. -1.0,-1.0,1.0,
  79. 1.0,-1.0,1.0,
  80. 1.0,1.0,1.0,
  81. -1.0,1.0,1.0,
  82. //Backface
  83. -1.0,-1.0,-1.0,
  84. -1.0,1.0,-1.0,
  85. 1.0,1.0,-1.0,
  86. 1.0,-1.0,-1.0,
  87. //Topface
  88. -1.0,1.0,-1.0,
  89. -1.0,1.0,1.0,
  90. 1.0,1.0,1.0,
  91. 1.0,1.0,-1.0,
  92. //Bottomface
  93. -1.0,-1.0,-1.0,
  94. 1.0,-1.0,-1.0,
  95. 1.0,-1.0,1.0,
  96. -1.0,-1.0,1.0,
  97. //Rightface
  98. 1.0,-1.0,-1.0,
  99. 1.0,1.0,-1.0,
  100. 1.0,1.0,1.0,
  101. 1.0,-1.0,1.0,
  102. //Leftface
  103. -1.0,-1.0,-1.0,
  104. -1.0,-1.0,1.0,
  105. -1.0,1.0,1.0,
  106. -1.0,1.0,-1.0
  107. ];
  108. //NowpassthelistofverticesintoWebGLtobuildtheshape.We
  109. //dothisbycreatingaFloat32ArrayfromtheJavaScriptarray,
  110. //thenuseittofillthecurrentvertexbuffer.
  111. gl.bufferData(gl.ARRAY_BUFFER,newFloat32Array(vertices),gl.STATIC_DRAW);
  112. //Setupthenormalsforthevertices,sothatwecancomputelighting.
  113. cubeVerticesNormalBuffer=gl.createBuffer();
  114. gl.bindBuffer(gl.ARRAY_BUFFER,cubeVerticesNormalBuffer);
  115. varvertexNormals=[
  116. //Front
  117. 0.0,0.0,1.0,
  118. 0.0,0.0,1.0,
  119. 0.0,0.0,1.0,
  120. 0.0,0.0,1.0,
  121. //Back
  122. 0.0,0.0,-1.0,
  123. 0.0,0.0,-1.0,
  124. 0.0,0.0,-1.0,
  125. 0.0,0.0,-1.0,
  126. //Top
  127. 0.0,1.0,0.0,
  128. 0.0,1.0,0.0,
  129. 0.0,1.0,0.0,
  130. 0.0,1.0,0.0,
  131. //Bottom
  132. 0.0,-1.0,0.0,
  133. 0.0,-1.0,0.0,
  134. 0.0,-1.0,0.0,
  135. 0.0,-1.0,0.0,
  136. //Right
  137. 1.0,0.0,0.0,
  138. 1.0,0.0,0.0,
  139. 1.0,0.0,0.0,
  140. 1.0,0.0,0.0,
  141. //Left
  142. -1.0,0.0,0.0,
  143. -1.0,0.0,0.0,
  144. -1.0,0.0,0.0,
  145. -1.0,0.0,0.0
  146. ];
  147. gl.bufferData(gl.ARRAY_BUFFER,newFloat32Array(vertexNormals),
  148. gl.STATIC_DRAW);
  149. //Mapthetextureontothecube'sfaces.
  150. cubeVerticesTextureCoordBuffer=gl.createBuffer();
  151. gl.bindBuffer(gl.ARRAY_BUFFER,cubeVerticesTextureCoordBuffer);
  152. vartextureCoordinates=[
  153. //Front
  154. 0.0,0.0,
  155. 1.0,0.0,
  156. 1.0,1.0,
  157. 0.0,1.0,
  158. //Back
  159. 0.0,0.0,
  160. 1.0,0.0,
  161. 1.0,1.0,
  162. 0.0,1.0,
  163. //Top
  164. 0.0,0.0,
  165. 1.0,0.0,
  166. 1.0,1.0,
  167. 0.0,1.0,
  168. //Bottom
  169. 0.0,0.0,
  170. 1.0,0.0,
  171. 1.0,1.0,
  172. 0.0,1.0,
  173. //Right
  174. 0.0,0.0,
  175. 1.0,0.0,
  176. 1.0,1.0,
  177. 0.0,1.0,
  178. //Left
  179. 0.0,0.0,
  180. 1.0,0.0,
  181. 1.0,1.0,
  182. 0.0,1.0
  183. ];
  184. gl.bufferData(gl.ARRAY_BUFFER,newFloat32Array(textureCoordinates),
  185. gl.STATIC_DRAW);
  186. //Buildtheelementarraybuffer;thisspecifiestheindices
  187. //intothevertexarrayforeachface'svertices.
  188. cubeVerticesIndexBuffer=gl.createBuffer();
  189. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,cubeVerticesIndexBuffer);
  190. //Thisarraydefineseachfaceastwotriangles,usingthe
  191. //indicesintothevertexarraytospecifyeachtriangle's
  192. //position.
  193. varcubeVertexIndices=[
  194. 0,1,2,0,2,3,//front
  195. 4,5,6,4,6,7,//back
  196. 8,9,10,8,10,11,//top
  197. 12,13,14,12,14,15,//bottom
  198. 16,17,18,16,18,19,//right
  199. 20,21,22,20,22,23//left
  200. ]
  201. //NowsendtheelementarraytoGL
  202. gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
  203. newUint16Array(cubeVertexIndices),gl.STATIC_DRAW);
  204. }
  205. //
  206. //initTextures
  207. //
  208. //Initializethetextureswe'llbeusing,theninitiatealoadof
  209. //thetextureimages.ThehandleTextureLoaded()callbackwillfinish
  210. //thejob;itgetscalledeachtimeatexturefinishesloading.
  211. //
  212. //TODO#1Start
  213. functioncreateCubeTexture(text){
  214. //createahiddencanvastodrawthetexture
  215. varcanvas=document.createElement('canvas');
  216. canvas.id="hiddenCanvas";
  217. canvas.width=512;
  218. canvas.height=512;
  219. canvas.style.display="none";
  220. varbody=document.getElementsByTagName("body")[0];
  221. body.appendChild(canvas);
  222. //drawtexture
  223. varcubeImage=document.getElementById('hiddenCanvas');
  224. varctx=cubeImage.getContext('2d');
  225. ctx.beginPath();
  226. ctx.rect(0,0,ctx.canvas.width,ctx.canvas.height);
  227. ctx.fillStyle='white';
  228. ctx.fill();
  229. ctx.fillStyle='black';
  230. ctx.font="65pxArial";
  231. ctx.textAlign='center';
  232. ctx.fillText(text,ctx.canvas.width/2,ctx.canvas.height/2);
  233. ctx.restore();
  234. //createnewtexture
  235. vartexture=gl.createTexture();
  236. gl.bindTexture(gl.TEXTURE_2D,texture);
  237. gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.LINEAR);
  238. gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_NEAREST);
  239. gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,true);
  240. handleTextureLoaded(cubeImage,texture)
  241. returntexture;
  242. }
  243. //TODO#1End
  244. functionhandleTextureLoaded(image,texture){
  245. gl.bindTexture(gl.TEXTURE_2D,texture);
  246. gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,image);
  247. gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.LINEAR);
  248. gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_NEAREST);
  249. gl.generateMipmap(gl.TEXTURE_2D);
  250. gl.bindTexture(gl.TEXTURE_2D,null);
  251. }
  252. //
  253. //drawScene
  254. //
  255. //Drawthescene.
  256. //
  257. functiondrawScene(){
  258. //Clearthecanvasbeforewestartdrawingonit.
  259. gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
  260. //Establishtheperspectivewithwhichwewanttoviewthe
  261. //scene.Ourfieldofviewis45degrees,withawidth/height
  262. //ratioof640:480,andweonlywanttoseeobjectsbetween0.1units
  263. //and100unitsawayfromthecamera.
  264. perspectiveMatrix=makePerspective(45,640.0/480.0,0.1,100.0);
  265. //Setthedrawingpositiontothe"identity"point,whichis
  266. //thecenterofthescene.
  267. loadIdentity();
  268. //Nowmovethedrawingpositionabittowherewewanttostart
  269. //drawingthecube.
  270. mvTranslate([0.0,0.0,-6.0]);
  271. //Savethecurrentmatrix,thenrotatebeforewedraw.
  272. mvPushMatrix();
  273. mvRotate(cubeRotation,[1,0,1]);
  274. //Drawthecubebybindingthearraybuffertothecube'svertices
  275. //array,settingattributes,andpushingittoGL.
  276. gl.bindBuffer(gl.ARRAY_BUFFER,cubeVerticesBuffer);
  277. gl.vertexAttribPointer(vertexPositionAttribute,3,gl.FLOAT,false,0,0);
  278. //Setthetexturecoordinatesattributeforthevertices.
  279. gl.bindBuffer(gl.ARRAY_BUFFER,cubeVerticesTextureCoordBuffer);
  280. gl.vertexAttribPointer(textureCoordAttribute,2,gl.FLOAT,false,0,0);
  281. //Bindthenormalsbuffertotheshaderattribute.
  282. gl.bindBuffer(gl.ARRAY_BUFFER,cubeVerticesNormalBuffer);
  283. gl.vertexAttribPointer(vertexNormalAttribute,3,gl.FLOAT,false,0,0);
  284. //Specifythetexturetomapontothefaces.
  285. gl.activeTexture(gl.TEXTURE0);
  286. gl.bindTexture(gl.TEXTURE_2D,cubeTexture);
  287. gl.uniform1i(gl.getUniformLocation(shaderProgram,"uSampler"),0);
  288. //Drawthecube.
  289. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,cubeVerticesIndexBuffer);
  290. setMatrixUniforms();
  291. gl.drawElements(gl.TRIANGLES,36,gl.UNSIGNED_SHORT,0);
  292. //Restoretheoriginalmatrix
  293. mvPopMatrix();
  294. //Updatetherotationforthenextdraw,ifit'stimetodoso.
  295. varcurrentTime=(newDate).getTime();
  296. if(lastCubeUpdateTime){
  297. vardelta=currentTime-lastCubeUpdateTime;
  298. cubeRotation+=(30*delta)/1000.0;
  299. }
  300. lastCubeUpdateTime=currentTime;
  301. }
  302. //
  303. //initShaders
  304. //
  305. //Initializetheshaders,soWebGLknowshowtolightourscene.
  306. //
  307. functioninitShaders(){
  308. varfragmentShader=getShader(gl,"shader-fs");
  309. varvertexShader=getShader(gl,"shader-vs");
  310. //Createtheshaderprogram
  311. shaderProgram=gl.createProgram();
  312. gl.attachShader(shaderProgram,vertexShader);
  313. gl.attachShader(shaderProgram,fragmentShader);
  314. gl.linkProgram(shaderProgram);
  315. //Ifcreatingtheshaderprogramfailed,alert
  316. if(!gl.getProgramParameter(shaderProgram,gl.LINK_STATUS)){
  317. alert("Unabletoinitializetheshaderprogram.");
  318. }
  319. gl.useProgram(shaderProgram);
  320. vertexPositionAttribute=gl.getAttribLocation(shaderProgram,"aVertexPosition");
  321. gl.enableVertexAttribArray(vertexPositionAttribute);
  322. textureCoordAttribute=gl.getAttribLocation(shaderProgram,"aTextureCoord");
  323. gl.enableVertexAttribArray(textureCoordAttribute);
  324. vertexNormalAttribute=gl.getAttribLocation(shaderProgram,"aVertexNormal");
  325. gl.enableVertexAttribArray(vertexNormalAttribute);
  326. }
  327. //
  328. //getShader
  329. //
  330. //Loadsashaderprogrambyscouringthecurrentdocument,
  331. //lookingforascriptwiththespecifiedID.
  332. //
  333. functiongetShader(gl,id){
  334. varshaderScript=document.getElementById(id);
  335. //Didn'tfindanelementwiththespecifiedID;abort.
  336. if(!shaderScript){
  337. returnnull;
  338. }
  339. //Walkthroughthesourceelement'schildren,buildingthe
  340. //shadersourcestring.
  341. vartheSource="";
  342. varcurrentChild=shaderScript.firstChild;
  343. while(currentChild){
  344. if(currentChild.nodeType==3){
  345. theSource+=currentChild.textContent;
  346. }
  347. currentChildcurrentChild=currentChild.nextSibling;
  348. }
  349. //Nowfigureoutwhattypeofshaderscriptwehave,
  350. //basedonitsMIMEtype.
  351. varshader;
  352. if(shaderScript.type=="x-shader/x-fragment"){
  353. shader=gl.createShader(gl.FRAGMENT_SHADER);
  354. }elseif(shaderScript.type=="x-shader/x-vertex"){
  355. shader=gl.createShader(gl.VERTEX_SHADER);
  356. }else{
  357. returnnull;//Unknownshadertype
  358. }
  359. //Sendthesourcetotheshaderobject
  360. gl.shaderSource(shader,theSource);
  361. //Compiletheshaderprogram
  362. gl.compileShader(shader);
  363. //Seeifitcompiledsuccessfully
  364. if(!gl.getShaderParameter(shader,gl.COMPILE_STATUS)){
  365. alert("Anerroroccurredcompilingtheshaders:"+gl.getShaderInfoLog(shader));
  366. returnnull;
  367. }
  368. returnshader;
  369. }
  370. //
  371. //Matrixutilityfunctions
  372. //
  373. functionloadIdentity(){
  374. mvMatrix=Matrix.I(4);
  375. }
  376. functionmultMatrix(m){
  377. mvMatrixmvMatrix=mvMatrix.x(m);
  378. }
  379. functionmvTranslate(v){
  380. multMatrix(Matrix.Translation($V([v[0],v[1],v[2]])).ensure4x4());
  381. }
  382. functionsetMatrixUniforms(){
  383. varpUniform=gl.getUniformLocation(shaderProgram,"uPMatrix");
  384. gl.uniformMatrix4fv(pUniform,false,newFloat32Array(perspectiveMatrix.flatten()));
  385. varmvUniform=gl.getUniformLocation(shaderProgram,"uMVMatrix");
  386. gl.uniformMatrix4fv(mvUniform,false,newFloat32Array(mvMatrix.flatten()));
  387. varnormalMatrix=mvMatrix.inverse();
  388. normalMatrixnormalMatrix=normalMatrix.transpose();
  389. varnUniform=gl.getUniformLocation(shaderProgram,"uNormalMatrix");
  390. gl.uniformMatrix4fv(nUniform,false,newFloat32Array(normalMatrix.flatten()));
  391. }
  392. varmvMatrixStack=[];
  393. functionmvPushMatrix(m){
  394. if(m){
  395. mvMatrixStack.push(m.dup());
  396. mmvMatrix=m.dup();
  397. }else{
  398. mvMatrixStack.push(mvMatrix.dup());
  399. }
  400. }
  401. functionmvPopMatrix(){
  402. if(!mvMatrixStack.length){
  403. throw("Can'tpopfromanemptymatrixstack.");
  404. }
  405. mvMatrix=mvMatrixStack.pop();
  406. returnmvMatrix;
  407. }
  408. functionmvRotate(angle,v){
  409. varinRadians=angle*Math.PI/180.0;
  410. varm=Matrix.Rotation(inRadians,$V([v[0],v[1],v[2]])).ensure4x4();
  411. multMatrix(m);
  412. }

本文在支持HTML5的浏览器上运行WebGL程序的方法到此结束。幻想者头脑里只有空中楼阁,实干家胸中才有摩天大厦。小编再次感谢大家对我们的支持!

标签: WebGL