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 衍生而来。

WebGL Shader 环境搭建
WebGL Shader 环境搭建

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;
}
复制代码

有了这些代码之后,我们就可以通过修改片元着色器代码添加我们的效果了。

WebGL Shader 环境搭建

通过修改片元着色器代码(把颜色修改为红色),刷新后确实看到有所改变。那假设我们想要做动画呢?

很简单,我们在片元着色器代码中添加一个关于时间的变量:

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 真是个灾难:

WebGL Shader 环境搭建

三、加载材质

当然我们不能一直跟像素打交道,视频或者图片都可以以贴图的形式放在画布中进行加工。下面讲讲如何把贴图展示在画布中。首先简单的预加载我们想要的贴图资源:

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_Sampler0u_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 环境搭建

以上所述就是小编给大家介绍的《WebGL Shader 环境搭建》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Web Application Hacker's Handbook

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》 这本书的介绍吧!

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具