WebGL Shader 环境搭建
栏目: JavaScript · 发布时间: 6年前
内容简介:正如《WebGL 编程指南》中所说的:传统的三维图形程序通常使用 C 或 C++ 等语言开发,并为特定的平台被编译成二进制的可执行文件。这意味着程序不能跨平台运行。相比之下,WebGL 程序由 HTML 和 JavaScript 组成,只需要放在 Web 上即可执行。WebGL 由 OpenGL ES 衍生而来。OpenGL 是底层的驱动级的图形接口,但是这种底层接口是浏览器中的 JavaScript 无法调用的。2010 年 WebGL 被推出来了之后,它允许工程师使用 JavaScript 去调用部分
正如《WebGL 编程指南》中所说的:
传统的三维图形程序通常使用 C 或 C++ 等语言开发,并为特定的平台被编译成二进制的可执行文件。这意味着程序不能跨平台运行。相比之下,WebGL 程序由 HTML 和 JavaScript 组成,只需要放在 Web 上即可执行。WebGL 由 OpenGL ES 衍生而来。
OpenGL 是底层的驱动级的图形接口,但是这种底层接口是浏览器中的 JavaScript 无法调用的。2010 年 WebGL 被推出来了之后,它允许工程师使用 JavaScript 去调用部分封装过的 OpenGL ES2.0 标准接口去提供硬件级别的 3D 图形加速功能。所以 GLSL 程序真正运行的时候还是跑在 OpenGL 驱动上的。
一、DOM
介绍了背景之后,下面说下怎么在网页上跑起 shader 代码来,先看 DOM 怎么写:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>webgl</title> <style> body {margin: 0; overflow: hidden;} </style> </head> <body onload="main()"> <canvas id="album" width="300" height="300"> Please use a browser that supports "canvas" </canvas> <script src="./js/lib/webgl-utils.js"></script> <script src="./js/lib/webgl-debug.js"></script> <script src="./js/lib/cuon-utils.js"></script> <script src="./js/lib/cuon-matrix.js"></script> <script src="./index.js"></script> </body> </html> 复制代码
Ok,简单的不能再简单的 DOM 结构了,整个页面只需要一个 <canvas>
的节点即可。Canvas 是 HTML5 提供的一个特性,你可以把它当做一个载体,简单的说就是一张白纸。而 Canvas 2D 相当于获取了内置的二维图形接口,也就是二维画笔。Canvas 3D 是获取基于 WebGL 的图形接口,相当于三维画笔(当然你也可以画二维的东西)。
其中还引用了这么几个文件,这几个文件是来自《WebGL 编程指南》里提供的 工具 库:
<script src="./js/lib/webgl-utils.js"></script> <script src="./js/lib/webgl-debug.js"></script> <script src="./js/lib/cuon-utils.js"></script> <script src="./js/lib/cuon-matrix.js"></script> 复制代码
下面分别把他们的代码列出来,它们的作用就是封装代码、兼容版本、提供工具函数等,这里先不细究:
// webgl-utils.js WebGLUtils=function(){var o=function(e,n){for(var t=["webgl","experimental-webgl","webkit-3d","moz-webgl"],i=null,o=0;o<t.length;++o){try{i=e.getContext(t[o],n)}catch(e){}if(i)break}return i};return{create3DContext:o,setupWebGL:function(e,n,t){t=t||function(e){var n=document.getElementsByTagName("body")[0];if(n){var t=window.WebGLRenderingContext?'It doesn\'t appear your computer can support WebGL.<br/><a href="http://get.webgl.org">Click here for more information.</a>':'This page requires a browser that supports WebGL.<br/><a href="http://get.webgl.org">Click here to upgrade your browser.</a>';e&&(t+="<br/><br/>Status: "+e),n.innerHTML='<div style="margin: auto; width:500px;z-index:10000;margin-top:20em;text-align:center;">'+t+"</div>"}},e.addEventListener&&e.addEventListener("webglcontextcreationerror",function(e){t(e.statusMessage)},!1);var i=o(e,n);return i||(window.WebGLRenderingContext,t("")),i}}}(),window.requestAnimationFrame||(window.requestAnimationFrame=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(e,n){window.setTimeout(e,1e3/60)}),window.cancelAnimationFrame||(window.cancelAnimationFrame=window.cancelRequestAnimationFrame||window.webkitCancelAnimationFrame||window.webkitCancelRequestAnimationFrame||window.mozCancelAnimationFrame||window.mozCancelRequestAnimationFrame||window.msCancelAnimationFrame||window.msCancelRequestAnimationFrame||window.oCancelAnimationFrame||window.oCancelRequestAnimationFrame||window.clearTimeout); 复制代码
// webgl-debug.js WebGLDebugUtils=function(){var a={enable:{0:!0},disable:{0:!0},getParameter:{0:!0},drawArrays:{0:!0},drawElements:{0:!0,2:!0},createShader:{0:!0},getShaderParameter:{1:!0},getProgramParameter:{1:!0},getVertexAttrib:{1:!0},vertexAttribPointer:{2:!0},bindTexture:{0:!0},activeTexture:{0:!0},getTexParameter:{0:!0,1:!0},texParameterf:{0:!0,1:!0},texParameteri:{0:!0,1:!0,2:!0},texImage2D:{0:!0,2:!0,6:!0,7:!0},texSubImage2D:{0:!0,6:!0,7:!0},copyTexImage2D:{0:!0,2:!0},copyTexSubImage2D:{0:!0},generateMipmap:{0:!0},bindBuffer:{0:!0},bufferData:{0:!0,2:!0},bufferSubData:{0:!0},getBufferParameter:{0:!0,1:!0},pixelStorei:{0:!0,1:!0},readPixels:{4:!0,5:!0},bindRenderbuffer:{0:!0},bindFramebuffer:{0:!0},checkFramebufferStatus:{0:!0},framebufferRenderbuffer:{0:!0,1:!0,2:!0},framebufferTexture2D:{0:!0,1:!0,2:!0},getFramebufferAttachmentParameter:{0:!0,1:!0,2:!0},getRenderbufferParameter:{0:!0,1:!0},renderbufferStorage:{0:!0,1:!0},clear:{0:!0},depthFunc:{0:!0},blendFunc:{0:!0,1:!0},blendFuncSeparate:{0:!0,1:!0,2:!0,3:!0},blendEquation:{0:!0},blendEquationSeparate:{0:!0,1:!0},stencilFunc:{0:!0},stencilFuncSeparate:{0:!0,1:!0},stencilMaskSeparate:{0:!0},stencilOp:{0:!0,1:!0,2:!0},stencilOpSeparate:{0:!0,1:!0,2:!0,3:!0},cullFace:{0:!0},frontFace:{0:!0}},r=null;function o(e){if(null==r)for(var t in r={},e)"number"==typeof e[t]&&(r[e[t]]=t)}function n(){if(null==r)throw"WebGLDebugUtils.init(ctx) not called"}function f(e){n();var t=r[e];return void 0!==t?t:"*UNKNOWN WebGL ENUM (0x"+e.toString(16)+")"}function u(e,t,r){var n=a[e];return void 0!==n&&n[t]?f(r):r.toString()}function S(e){var t=e.getParameter(e.MAX_VERTEX_ATTRIBS),r=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,r);for(var n=0;n<t;++n)e.disableVertexAttribArray(n),e.vertexAttribPointer(n,4,e.FLOAT,!1,0,0),e.vertexAttrib1f(n,0);e.deleteBuffer(r);var a=e.getParameter(e.MAX_TEXTURE_IMAGE_UNITS);for(n=0;n<a;++n)e.activeTexture(e.TEXTURE0+n),e.bindTexture(e.TEXTURE_CUBE_MAP,null),e.bindTexture(e.TEXTURE_2D,null);for(e.activeTexture(e.TEXTURE0),e.useProgram(null),e.bindBuffer(e.ARRAY_BUFFER,null),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,null),e.bindFramebuffer(e.FRAMEBUFFER,null),e.bindRenderbuffer(e.RENDERBUFFER,null),e.disable(e.BLEND),e.disable(e.CULL_FACE),e.disable(e.DEPTH_TEST),e.disable(e.DITHER),e.disable(e.SCISSOR_TEST),e.blendColor(0,0,0,0),e.blendEquation(e.FUNC_ADD),e.blendFunc(e.ONE,e.ZERO),e.clearColor(0,0,0,0),e.clearDepth(1),e.clearStencil(-1),e.colorMask(!0,!0,!0,!0),e.cullFace(e.BACK),e.depthFunc(e.LESS),e.depthMask(!0),e.depthRange(0,1),e.frontFace(e.CCW),e.hint(e.GENERATE_MIPMAP_HINT,e.DONT_CARE),e.lineWidth(1),e.pixelStorei(e.PACK_ALIGNMENT,4),e.pixelStorei(e.UNPACK_ALIGNMENT,4),e.pixelStorei(e.UNPACK_FLIP_Y_WEBGL,!1),e.pixelStorei(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),e.UNPACK_COLORSPACE_CONVERSION_WEBGL&&e.pixelStorei(e.UNPACK_COLORSPACE_CONVERSION_WEBGL,e.BROWSER_DEFAULT_WEBGL),e.polygonOffset(0,0),e.sampleCoverage(1,!1),e.scissor(0,0,e.canvas.width,e.canvas.height),e.stencilFunc(e.ALWAYS,0,4294967295),e.stencilMask(4294967295),e.stencilOp(e.KEEP,e.KEEP,e.KEEP),e.viewport(0,0,e.canvas.clientWidth,e.canvas.clientHeight),e.clear(e.COLOR_BUFFER_BIT|e.DEPTH_BUFFER_BIT|e.STENCIL_BUFFER_BIT);e.getError(););}return{init:o,mightBeEnum:function(e){return n(),void 0!==r[e]},glEnumToString:f,glFunctionArgToString:u,makeDebugContext:function(t,a){o(t),a=a||function(e,t,r){for(var n,a="",i=0;i<r.length;++i)a+=(0==i?"":", ")+u(t,i,r[i]);n="WebGL error "+f(e)+" in "+t+"("+a+")",window.console&&window.console.log&&window.console.log(n)};var i={};function e(r,n){return function(){var e=r[n].apply(r,arguments),t=r.getError();return 0!=t&&(i[t]=!0,a(t,n,arguments)),e}}var r={};for(var n in t)"function"==typeof t[n]?r[n]=e(t,n):r[n]=t[n];return r.getError=function(){for(var e in i)if(i[e])return i[e]=!1,e;return t.NO_ERROR},r},makeLostContextSimulatingContext:function(r){var e={},a=1,n=!1,i=[],t=void 0,o=void 0,f=void 0,u={};function c(e,t){var r=e[t];return function(){if(!n)return function(e){for(var t=0;t<e.length;++t){var r=e[t];if((n=r)instanceof WebGLBuffer||n instanceof WebGLFramebuffer||n instanceof WebGLProgram||n instanceof WebGLRenderbuffer||n instanceof WebGLShader||n instanceof WebGLTexture)return r.__webglDebugContextLostId__==a}var n;return!0}(arguments)?r.apply(e,arguments):void(u[e.INVALID_OPERATION]=!0)}}for(var l in r)"function"==typeof r[l]?e[l]=c(r,l):e[l]=r[l];function b(e){return{statusMessage:e}}e.loseContext=function(){if(!n){for(n=!0,++a;r.getError(););!function(){for(var e=Object.keys(u),t=0;t<e.length;++t)delete glErrorShdow_[e]}(),u[r.CONTEXT_LOST_WEBGL]=!0,setTimeout(function(){t&&t(b("context lost"))},0)}},e.restoreContext=function(){if(n){if(!o)throw"You can not restore the context without a listener";setTimeout(function(){if(function(){for(var e=0;e<i.length;++e){var t=i[e];t instanceof WebGLBuffer?r.deleteBuffer(t):t instanceof WebctxFramebuffer?r.deleteFramebuffer(t):t instanceof WebctxProgram?r.deleteProgram(t):t instanceof WebctxRenderbuffer?r.deleteRenderbuffer(t):t instanceof WebctxShader?r.deleteShader(t):t instanceof WebctxTexture&&r.deleteTexture(t)}}(),S(r),n=!1,o){var e=o;o=f,f=void 0,e(b("context restored"))}},0)}},e.getError=function(){if(!n)for(;e=r.getError();)u[e]=!0;for(var e in u)if(u[e])return delete u[e],e;return r.NO_ERROR};for(var s=["createBuffer","createFramebuffer","createProgram","createRenderbuffer","createShader","createTexture"],g=0;g<s.length;++g)e[A=s[g]]=function(t){return function(){if(n)return null;var e=t.apply(r,arguments);return e.__webglDebugContextLostId__=a,i.push(e),e}}(r[A]);var E=["getActiveAttrib","getActiveUniform","getBufferParameter","getContextAttributes","getAttachedShaders","getFramebufferAttachmentParameter","getParameter","getProgramParameter","getProgramInfoLog","getRenderbufferParameter","getShaderParameter","getShaderInfoLog","getShaderSource","getTexParameter","getUniform","getUniformLocation","getVertexAttrib"];for(g=0;g<E.length;++g)e[A=E[g]]=function(e){return function(){return n?null:e.apply(r,arguments)}}(e[A]);var d,m,T,R=["isBuffer","isEnabled","isFramebuffer","isProgram","isRenderbuffer","isShader","isTexture"];for(g=0;g<R.length;++g){var A;e[A=R[g]]=function(e){return function(){return!n&&e.apply(r,arguments)}}(e[A])}function x(t){return"function"==typeof t?t:function(e){t.handleEvent(e)}}return e.checkFramebufferStatus=(d=e.checkFramebufferStatus,function(){return n?r.FRAMEBUFFER_UNSUPPORTED:d.apply(r,arguments)}),e.getAttribLocation=(m=e.getAttribLocation,function(){return n?-1:m.apply(r,arguments)}),e.getVertexAttribOffset=(T=e.getVertexAttribOffset,function(){return n?0:T.apply(r,arguments)}),e.isContextLost=function(){return n},e.registerOnContextLostListener=function(e){t=x(e)},e.registerOnContextRestoredListener=function(e){n?f=x(e):o=x(e)},e},resetToInitialState:S}}(); 复制代码
// cuon-utils.js function initShaders(e,r,a){var t=createProgram(e,r,a);return t?(e.useProgram(t),e.program=t,!0):(console.log("Failed to create program"),!1)}function createProgram(e,r,a){var t=loadShader(e,e.VERTEX_SHADER,r),o=loadShader(e,e.FRAGMENT_SHADER,a);if(!t||!o)return null;var l=e.createProgram();if(!l)return null;if(e.attachShader(l,t),e.attachShader(l,o),e.linkProgram(l),!e.getProgramParameter(l,e.LINK_STATUS)){var n=e.getProgramInfoLog(l);return console.log("Failed to link program: "+n),e.deleteProgram(l),e.deleteShader(o),e.deleteShader(t),null}return l}function loadShader(e,r,a){var t=e.createShader(r);if(null==t)return console.log("unable to create shader"),null;if(e.shaderSource(t,a),e.compileShader(t),!e.getShaderParameter(t,e.COMPILE_STATUS)){var o=e.getShaderInfoLog(t);return console.log("Failed to compile shader: "+o),e.deleteShader(t),null}return t}function getWebGLContext(e,r){var a=WebGLUtils.setupWebGL(e);return a?((arguments.length<2||r)&&(a=WebGLDebugUtils.makeDebugContext(a)),a):null} 复制代码
// cuon-matrix.js var Matrix4=function(t){var e,r,n;if(t&&"object"==typeof t&&t.hasOwnProperty("elements")){for(r=t.elements,n=new Float32Array(16),e=0;e<16;++e)n[e]=r[e];this.elements=n}else this.elements=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1])};Matrix4.prototype.setIdentity=function(){var t=this.elements;return t[0]=1,t[4]=0,t[8]=0,t[12]=0,t[1]=0,t[5]=1,t[9]=0,t[13]=0,t[2]=0,t[6]=0,t[10]=1,t[14]=0,t[3]=0,t[7]=0,t[11]=0,t[15]=1,this},Matrix4.prototype.set=function(t){var e,r,n;if((r=t.elements)!==(n=this.elements)){for(e=0;e<16;++e)n[e]=r[e];return this}},Matrix4.prototype.concat=function(t){var e,r,n,o,i,s,a,u;if(r=this.elements,n=this.elements,r===(o=t.elements))for(o=new Float32Array(16),e=0;e<16;++e)o[e]=r[e];for(e=0;e<4;e++)i=n[e],s=n[e+4],a=n[e+8],u=n[e+12],r[e]=i*o[0]+s*o[1]+a*o[2]+u*o[3],r[e+4]=i*o[4]+s*o[5]+a*o[6]+u*o[7],r[e+8]=i*o[8]+s*o[9]+a*o[10]+u*o[11],r[e+12]=i*o[12]+s*o[13]+a*o[14]+u*o[15];return this},Matrix4.prototype.multiply=Matrix4.prototype.concat,Matrix4.prototype.multiplyVector3=function(t){var e=this.elements,r=t.elements,n=new Vector3,o=n.elements;return o[0]=r[0]*e[0]+r[1]*e[4]+r[2]*e[8]+e[12],o[1]=r[0]*e[1]+r[1]*e[5]+r[2]*e[9]+e[13],o[2]=r[0]*e[2]+r[1]*e[6]+r[2]*e[10]+e[14],n},Matrix4.prototype.multiplyVector4=function(t){var e=this.elements,r=t.elements,n=new Vector4,o=n.elements;return o[0]=r[0]*e[0]+r[1]*e[4]+r[2]*e[8]+r[3]*e[12],o[1]=r[0]*e[1]+r[1]*e[5]+r[2]*e[9]+r[3]*e[13],o[2]=r[0]*e[2]+r[1]*e[6]+r[2]*e[10]+r[3]*e[14],o[3]=r[0]*e[3]+r[1]*e[7]+r[2]*e[11]+r[3]*e[15],n},Matrix4.prototype.transpose=function(){var t,e;return e=(t=this.elements)[1],t[1]=t[4],t[4]=e,e=t[2],t[2]=t[8],t[8]=e,e=t[3],t[3]=t[12],t[12]=e,e=t[6],t[6]=t[9],t[9]=e,e=t[7],t[7]=t[13],t[13]=e,e=t[11],t[11]=t[14],t[14]=e,this},Matrix4.prototype.setInverseOf=function(t){var e,r,n,o,i;if(r=t.elements,n=this.elements,(o=new Float32Array(16))[0]=r[5]*r[10]*r[15]-r[5]*r[11]*r[14]-r[9]*r[6]*r[15]+r[9]*r[7]*r[14]+r[13]*r[6]*r[11]-r[13]*r[7]*r[10],o[4]=-r[4]*r[10]*r[15]+r[4]*r[11]*r[14]+r[8]*r[6]*r[15]-r[8]*r[7]*r[14]-r[12]*r[6]*r[11]+r[12]*r[7]*r[10],o[8]=r[4]*r[9]*r[15]-r[4]*r[11]*r[13]-r[8]*r[5]*r[15]+r[8]*r[7]*r[13]+r[12]*r[5]*r[11]-r[12]*r[7]*r[9],o[12]=-r[4]*r[9]*r[14]+r[4]*r[10]*r[13]+r[8]*r[5]*r[14]-r[8]*r[6]*r[13]-r[12]*r[5]*r[10]+r[12]*r[6]*r[9],o[1]=-r[1]*r[10]*r[15]+r[1]*r[11]*r[14]+r[9]*r[2]*r[15]-r[9]*r[3]*r[14]-r[13]*r[2]*r[11]+r[13]*r[3]*r[10],o[5]=r[0]*r[10]*r[15]-r[0]*r[11]*r[14]-r[8]*r[2]*r[15]+r[8]*r[3]*r[14]+r[12]*r[2]*r[11]-r[12]*r[3]*r[10],o[9]=-r[0]*r[9]*r[15]+r[0]*r[11]*r[13]+r[8]*r[1]*r[15]-r[8]*r[3]*r[13]-r[12]*r[1]*r[11]+r[12]*r[3]*r[9],o[13]=r[0]*r[9]*r[14]-r[0]*r[10]*r[13]-r[8]*r[1]*r[14]+r[8]*r[2]*r[13]+r[12]*r[1]*r[10]-r[12]*r[2]*r[9],o[2]=r[1]*r[6]*r[15]-r[1]*r[7]*r[14]-r[5]*r[2]*r[15]+r[5]*r[3]*r[14]+r[13]*r[2]*r[7]-r[13]*r[3]*r[6],o[6]=-r[0]*r[6]*r[15]+r[0]*r[7]*r[14]+r[4]*r[2]*r[15]-r[4]*r[3]*r[14]-r[12]*r[2]*r[7]+r[12]*r[3]*r[6],o[10]=r[0]*r[5]*r[15]-r[0]*r[7]*r[13]-r[4]*r[1]*r[15]+r[4]*r[3]*r[13]+r[12]*r[1]*r[7]-r[12]*r[3]*r[5],o[14]=-r[0]*r[5]*r[14]+r[0]*r[6]*r[13]+r[4]*r[1]*r[14]-r[4]*r[2]*r[13]-r[12]*r[1]*r[6]+r[12]*r[2]*r[5],o[3]=-r[1]*r[6]*r[11]+r[1]*r[7]*r[10]+r[5]*r[2]*r[11]-r[5]*r[3]*r[10]-r[9]*r[2]*r[7]+r[9]*r[3]*r[6],o[7]=r[0]*r[6]*r[11]-r[0]*r[7]*r[10]-r[4]*r[2]*r[11]+r[4]*r[3]*r[10]+r[8]*r[2]*r[7]-r[8]*r[3]*r[6],o[11]=-r[0]*r[5]*r[11]+r[0]*r[7]*r[9]+r[4]*r[1]*r[11]-r[4]*r[3]*r[9]-r[8]*r[1]*r[7]+r[8]*r[3]*r[5],o[15]=r[0]*r[5]*r[10]-r[0]*r[6]*r[9]-r[4]*r[1]*r[10]+r[4]*r[2]*r[9]+r[8]*r[1]*r[6]-r[8]*r[2]*r[5],0===(i=r[0]*o[0]+r[1]*o[4]+r[2]*o[8]+r[3]*o[12]))return this;for(i=1/i,e=0;e<16;e++)n[e]=o[e]*i;return this},Matrix4.prototype.invert=function(){return this.setInverseOf(this)},Matrix4.prototype.setOrtho=function(t,e,r,n,o,i){var s,a,u,h;if(t===e||r===n||o===i)throw"null frustum";return a=1/(e-t),u=1/(n-r),h=1/(i-o),(s=this.elements)[0]=2*a,s[1]=0,s[2]=0,s[3]=0,s[4]=0,s[5]=2*u,s[6]=0,s[7]=0,s[8]=0,s[9]=0,s[10]=-2*h,s[11]=0,s[12]=-(e+t)*a,s[13]=-(n+r)*u,s[14]=-(i+o)*h,s[15]=1,this},Matrix4.prototype.ortho=function(t,e,r,n,o,i){return this.concat((new Matrix4).setOrtho(t,e,r,n,o,i))},Matrix4.prototype.setFrustum=function(t,e,r,n,o,i){var s,a,u,h;if(t===e||n===r||o===i)throw"null frustum";if(o<=0)throw"near <= 0";if(i<=0)throw"far <= 0";return a=1/(e-t),u=1/(n-r),h=1/(i-o),(s=this.elements)[0]=2*o*a,s[1]=0,s[2]=0,s[3]=0,s[4]=0,s[5]=2*o*u,s[6]=0,s[7]=0,s[8]=(e+t)*a,s[9]=(n+r)*u,s[10]=-(i+o)*h,s[11]=-1,s[12]=0,s[13]=0,s[14]=-2*o*i*h,s[15]=0,this},Matrix4.prototype.frustum=function(t,e,r,n,o,i){return this.concat((new Matrix4).setFrustum(t,e,r,n,o,i))},Matrix4.prototype.setPerspective=function(t,e,r,n){var o,i,s,a;if(r===n||0===e)throw"null frustum";if(r<=0)throw"near <= 0";if(n<=0)throw"far <= 0";if(t=Math.PI*t/180/2,0===(s=Math.sin(t)))throw"null frustum";return i=1/(n-r),a=Math.cos(t)/s,(o=this.elements)[0]=a/e,o[1]=0,o[2]=0,o[3]=0,o[4]=0,o[5]=a,o[6]=0,o[7]=0,o[8]=0,o[9]=0,o[10]=-(n+r)*i,o[11]=-1,o[12]=0,o[13]=0,o[14]=-2*r*n*i,o[15]=0,this},Matrix4.prototype.perspective=function(t,e,r,n){return this.concat((new Matrix4).setPerspective(t,e,r,n))},Matrix4.prototype.setScale=function(t,e,r){var n=this.elements;return n[0]=t,n[4]=0,n[8]=0,n[12]=0,n[1]=0,n[5]=e,n[9]=0,n[13]=0,n[2]=0,n[6]=0,n[10]=r,n[14]=0,n[3]=0,n[7]=0,n[11]=0,n[15]=1,this},Matrix4.prototype.scale=function(t,e,r){var n=this.elements;return n[0]*=t,n[4]*=e,n[8]*=r,n[1]*=t,n[5]*=e,n[9]*=r,n[2]*=t,n[6]*=e,n[10]*=r,n[3]*=t,n[7]*=e,n[11]*=r,this},Matrix4.prototype.setTranslate=function(t,e,r){var n=this.elements;return n[0]=1,n[4]=0,n[8]=0,n[12]=t,n[1]=0,n[5]=1,n[9]=0,n[13]=e,n[2]=0,n[6]=0,n[10]=1,n[14]=r,n[3]=0,n[7]=0,n[11]=0,n[15]=1,this},Matrix4.prototype.translate=function(t,e,r){var n=this.elements;return n[12]+=n[0]*t+n[4]*e+n[8]*r,n[13]+=n[1]*t+n[5]*e+n[9]*r,n[14]+=n[2]*t+n[6]*e+n[10]*r,n[15]+=n[3]*t+n[7]*e+n[11]*r,this},Matrix4.prototype.setRotate=function(t,e,r,n){var o,i,s,a,u,h,p,c,l,f,m,M;return t=Math.PI*t/180,o=this.elements,i=Math.sin(t),s=Math.cos(t),0!==e&&0===r&&0===n?(e<0&&(i=-i),o[0]=1,o[4]=0,o[8]=0,o[12]=0,o[1]=0,o[5]=s,o[9]=-i,o[13]=0,o[2]=0,o[6]=i,o[10]=s,o[14]=0,o[3]=0,o[7]=0,o[11]=0):0===e&&0!==r&&0===n?(r<0&&(i=-i),o[0]=s,o[4]=0,o[8]=i,o[12]=0,o[1]=0,o[5]=1,o[9]=0,o[13]=0,o[2]=-i,o[6]=0,o[10]=s,o[14]=0,o[3]=0,o[7]=0,o[11]=0):0===e&&0===r&&0!==n?(n<0&&(i=-i),o[0]=s,o[4]=-i,o[8]=0,o[12]=0,o[1]=i,o[5]=s,o[9]=0,o[13]=0,o[2]=0,o[6]=0,o[10]=1,o[14]=0,o[3]=0,o[7]=0,o[11]=0):(1!==(a=Math.sqrt(e*e+r*r+n*n))&&(e*=u=1/a,r*=u,n*=u),h=1-s,p=e*r,c=r*n,l=n*e,f=e*i,m=r*i,M=n*i,o[0]=e*e*h+s,o[1]=p*h+M,o[2]=l*h-m,o[3]=0,o[4]=p*h-M,o[5]=r*r*h+s,o[6]=c*h+f,o[7]=0,o[8]=l*h+m,o[9]=c*h-f,o[10]=n*n*h+s,o[11]=0,o[12]=0,o[13]=0,o[14]=0),o[15]=1,this},Matrix4.prototype.rotate=function(t,e,r,n){return this.concat((new Matrix4).setRotate(t,e,r,n))},Matrix4.prototype.setLookAt=function(t,e,r,n,o,i,s,a,u){var h,p,c,l,f,m,M,y,x,v,w,A;return p=n-t,c=o-e,l=i-r,m=(c*=f=1/Math.sqrt(p*p+c*c+l*l))*u-(l*=f)*a,M=l*s-(p*=f)*u,y=p*a-c*s,v=(M*=x=1/Math.sqrt(m*m+M*M+y*y))*l-(y*=x)*c,w=y*p-(m*=x)*l,A=m*c-M*p,(h=this.elements)[0]=m,h[1]=v,h[2]=-p,h[3]=0,h[4]=M,h[5]=w,h[6]=-c,h[7]=0,h[8]=y,h[9]=A,h[10]=-l,h[11]=0,h[12]=0,h[13]=0,h[14]=0,h[15]=1,this.translate(-t,-e,-r)},Matrix4.prototype.lookAt=function(t,e,r,n,o,i,s,a,u){return this.concat((new Matrix4).setLookAt(t,e,r,n,o,i,s,a,u))},Matrix4.prototype.dropShadow=function(t,e){var r=new Matrix4,n=r.elements,o=t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]*e[3];return n[0]=o-e[0]*t[0],n[1]=-e[1]*t[0],n[2]=-e[2]*t[0],n[3]=-e[3]*t[0],n[4]=-e[0]*t[1],n[5]=o-e[1]*t[1],n[6]=-e[2]*t[1],n[7]=-e[3]*t[1],n[8]=-e[0]*t[2],n[9]=-e[1]*t[2],n[10]=o-e[2]*t[2],n[11]=-e[3]*t[2],n[12]=-e[0]*t[3],n[13]=-e[1]*t[3],n[14]=-e[2]*t[3],n[15]=o-e[3]*t[3],this.concat(r)},Matrix4.prototype.dropShadowDirectionally=function(t,e,r,n,o,i,s,a,u){var h=n*t+o*e+i*r;return this.dropShadow([t,e,r,-h],[s,a,u,0])};var Vector3=function(t){var e=new Float32Array(3);t&&"object"==typeof t&&(e[0]=t[0],e[1]=t[1],e[2]=t[2]),this.elements=e};Vector3.prototype.normalize=function(){var t=this.elements,e=t[0],r=t[1],n=t[2],o=Math.sqrt(e*e+r*r+n*n);return o?1==o||(o=1/o,t[0]=e*o,t[1]=r*o,t[2]=n*o):(t[0]=0,t[1]=0,t[2]=0),this};var Vector4=function(t){var e=new Float32Array(4);t&&"object"==typeof t&&(e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3]),this.elements=e}; 复制代码
二、JavaScript
接下来就是写 JS 了,是的就是这么快。接下来我们在 index.js 写进我们的代码,大家可以留意到 DOM 中绑定了一个事件 <body onload="main()">
:
// index.js // 顶点着色器代码 var VSHADER_SOURCE =` attribute vec4 a_Position; varying vec2 uv; void main() { gl_Position = vec4(vec2(a_Position), 0.0, 1.0); uv = vec2(0.5, 0.5) * (vec2(a_Position) + vec2(1.0, 1.0)); } ` // 片元着色器代码 var FSHADER_SOURCE =` precision mediump float; varying vec2 uv; void main() { gl_FragColor = vec4(0.,0.,0.,1.); } `; function main() { var canvas = document.getElementById('album'); // 这里的宽高按实际情况设置 canvas.width = 375; canvas.height = 667; // 获取 webgl 上下文(getWebGLContext 是前面引入的工具库预设的) var gl = getWebGLContext(canvas); if (!gl) { console.log('Failed to get the rendering context for WebGL'); return; } // 初始化着色器(initShaders 是工具库定义的函数,传入上下文,顶点/片元着色器代码) if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { console.log('Failed to intialize shaders.'); return; } // 设置顶点数据(initVertexBuffers 函数详见下面) var n = initVertexBuffers(gl); if (n < 0) { console.log('Failed to set the vertex information'); return; } // 清空画布 gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); // 绘制顶点 gl.drawArrays(gl.TRIANGLE_FAN, 0, 4); } // 初始化顶点缓冲区 function initVertexBuffers(gl) { // 顶点坐标(画布的四个点) var verticesTexCoords = new Float32Array([ 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0 ]); // 顶点数量(4个点决定一个矩形) var n = 4; // 创建顶点缓冲区 var vertexTexCoordBuffer = gl.createBuffer(); if (!vertexTexCoordBuffer) { console.log('Failed to create the buffer object'); return -1; } // 绑定缓冲区 gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer); gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW); // 获取数组每个元素的大小 var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT; // 获取 a_Position 的存储位置并设置缓冲区 var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); if (a_Position < 0) { console.log('Failed to get the storage location of a_Position'); return -1; } gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 2, 0); gl.enableVertexAttribArray(a_Position); return n; } 复制代码
有了这些代码之后,我们就可以通过修改片元着色器代码添加我们的效果了。
通过修改片元着色器代码(把颜色修改为红色),刷新后确实看到有所改变。那假设我们想要做动画呢?
很简单,我们在片元着色器代码中添加一个关于时间的变量:
var FSHADER_SOURCE =` precision mediump float; varying vec2 uv; uniform float time; // 变化时间 void main() { gl_FragColor = vec4(0.,0.,0.,1.); } `; 复制代码
然后在 main()
函数最后新增以下代码:
var t = 0; var time = gl.getUniformLocation(gl.program, 'time'); if (time < 0) { console.log('Failed to get the storage location of time'); return -1; } gl.uniform1f(time, .0); render(gl, time, t); 复制代码
在 index.js 中新增一个render 函数:
function render(gl, time, t) { t += 0.01; gl.uniform1f(time, t); // 每次都需要清空画布再绘制 gl.clear(gl.COLOR_BUFFER_BIT); gl.drawArrays(gl.TRIANGLE_FAN, 0, 4); requestAnimationFrame(render.bind(this, gl, time, t)); } 复制代码
添加完了之后,我们修改一下片元着色器,看看是否有效果:
var FSHADER_SOURCE =` precision mediump float; varying vec2 uv; uniform float time; // 变化时间 void main() { float r = uv.x; float g = uv.y; float b = abs(sin(time)); gl_FragColor = vec4(r,g,b,1.); } `; 复制代码
PS:渐变录成 GIF 真是个灾难:
三、加载材质
当然我们不能一直跟像素打交道,视频或者图片都可以以贴图的形式放在画布中进行加工。下面讲讲如何把贴图展示在画布中。首先简单的预加载我们想要的贴图资源:
function main() { var canvas = document.getElementById('album'); // 这里的宽高按实际情况设置 canvas.width = 375; canvas.height = 667; // 在这里把贴图资源预加载了 var imgList = []; preload(imgList, [ './images/img1.jpg', './images/img2.jpg' ], function() { // 加载完成后再开始初始化 WebGL // 获取 webgl 上下文 var gl = getWebGLContext(canvas); if (!gl) { console.log('Failed to get the rendering context for WebGL'); return; } // ... }) } // 图片预加载 function preload(imgList, arrayOfImages, callback) { var sum = arrayOfImages.length; var count = 0; arrayOfImages.forEach(function(value){ var image = new Image() image.src = value; image.onload = function() { imgList.push(image) if (++count == sum) { callback && callback() } } }) } 复制代码
接下来就是初始化材质内容,在资源预加载的回调函数底部增加如下代码:
function main() { var canvas = document.getElementById('album'); // 这里的宽高按实际情况设置 canvas.width = 375; canvas.height = 667; // 在这里把贴图资源预加载了(这里加载两张图,方便做转场) var imgList = []; preload(imgList, [ './images/img8.jpg', './images/img9.jpg' ], function() { // ... // 绘制顶点(这一步留到后面) // gl.drawArrays(gl.TRIANGLE_FAN, 0, 4); // 新增材质初始化函数 if (!initTextures(gl, imgList)) { console.log('Failed to intialize the texture.'); return; } var t = 0; var time = gl.getUniformLocation(gl.program, 'time'); if (time < 0) { console.log('Failed to get the storage location of time'); return -1; } gl.uniform1f(time, .0); render(gl, time, t); }) } // 初始化材质 function initTextures(gl, imgList) { var texture0 = gl.createTexture(); var texture1 = gl.createTexture(); if (!texture0 && !texture1) { console.log('Failed to create the texture object'); return false; } var u_Sampler0 = gl.getUniformLocation(gl.program, 'u_Sampler0'); if (!u_Sampler0) { console.log('Failed to get the storage location of u_Sampler0'); return false; } var u_Sampler1 = gl.getUniformLocation(gl.program, 'u_Sampler1'); if (!u_Sampler1) { console.log('Failed to get the storage location of u_Sampler1'); return false; } loadTexture(gl, texture0, u_Sampler0, imgList[0], 0); loadTexture(gl, texture1, u_Sampler1, imgList[1], 1); return true; } // 加载材质 function loadTexture(gl, texture, u_Sampler, image, index) { gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1) gl.activeTexture(gl['TEXTURE'+index]) gl.bindTexture(gl.TEXTURE_2D, texture) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); gl.uniform1i(u_Sampler, index); return true; } 复制代码
如果这个时候执行代码你会发现报错,因为我们新增了两个变量 u_Sampler0
和 u_Sampler1
用于存放两张材质图,所以需要在片元着色器中加上:
// 片元着色器代码 var FSHADER_SOURCE =` precision mediump float; varying vec2 uv; uniform float time; // 变化时间 uniform sampler2D u_Sampler0; uniform sampler2D u_Sampler1; void main() { vec4 color = mix(texture2D(u_Sampler0, uv), texture2D(u_Sampler1, uv), abs(sin(time))); gl_FragColor = vec4(color); } `; 复制代码
以上所述就是小编给大家介绍的《WebGL Shader 环境搭建》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Hyperledger Fabric环境搭建及环境测试(Mac环境)
- CV 环境很重要,各种环境搭建大全
- Openstack Queens 环境搭建(一)环境准备
- Python 环境搭建
- 1 - 搭建开发环境
- 搭建 Android 内核环境
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Web Application Hacker's Handbook
Dafydd Stuttard、Marcus Pinto / Wiley / 2011-9-27 / USD 50.00
The highly successful security book returns with a new edition, completely updated Web applications are the front door to most organizations, exposing them to attacks that may disclose personal infor......一起来看看 《The Web Application Hacker's Handbook》 这本书的介绍吧!