As stated in the WebGL Programming Guide:

Traditional 3D graphics programs are usually developed in languages such as C or C++ and compiled into binary executable files for a particular platform. This means programs cannot run across platforms. In contrast, WebGL programs are made up of HTML and JavaScript that need only be placed on the Web to execute. WebGL is derived from OpenGL ES.

OpenGL is a low-level driver-level graphical interface, but this low-level interface is not called by JavaScript in the browser. When WebGL was introduced in 2010, it allowed engineers to use JavaScript to invoke the partially encapsulated OpenGL ES2.0 standard interface to provide hardware-level 3D graphics acceleration. So GLSL programs actually run on OpenGL drivers.

A, DOM

With that background, here’s how to run shader code on a web page, starting with DOM:


      
<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>

Copy the code

Ok, simple can not be a simple DOM structure, the entire page only needs a

node. Canvas is a feature provided by HTML5. You can regard it as a carrier, which is simply a blank sheet of paper. Canvas 2D is equivalent to acquiring the built-in 2D graphics interface, namely 2D brush. Canvas 3D is a graphical interface based on WebGL, which is equivalent to a 3D brush (of course you can also draw 2d things).

It also references several files from the toolkit provided in the WebGL Programming Guide:

<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>
Copy the code

The following is a list of their respective codes. Their functions are to encapsulate code, compatible versions, provide utility functions, etc., but we will not go into details here:

// 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='
      
'
+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); Copy the code
// 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){vart=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.blen dColor(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.po lygonOffset(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"= =typeoft[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"= =typeofr[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){vart=i[e]; tinstanceofWebGLBuffer? r.deleteBuffer(t):tinstanceofWebctxFramebuffer? r.deleteFramebuffer(t):tinstanceofWebctxProgram? r.deleteProgram(t):tinstanceofWebctxRenderbuffer? r.deleteRenderbuffer(t):tinstanceofWebctxShader? r.deleteShader(t):tinstanceof WebctxTexture&&r.deleteTexture(t)}}(),S(r),n=!1,o){vare=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){varA; e[A=R[g]]=function(e){return function(){return! n&&e.apply(r,arguments)}}(e[A])}function x(t){return"function"= =typeoft? t:function(e){t.handleEvent(e)}}return e.checkFramebufferStatus=(d=e.checkFramebufferStatus,function(){returnn? 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}}();
Copy the code
// cuon-utils.js
function initShaders(e,r,a){var t=createProgram(e,r,a);returnt? (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);returna? ((arguments.length<2||r)&&(a=WebGLDebugUtils.makeDebugContext(a)),a):null}
Copy the code
// 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};
Copy the code


Second, JavaScript,

Next is to write JS, yes is that fast.

// index.js

// Vertex shader code
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)); } `
// Chip shader code
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');
    // Set the width and height according to the actual situation
    canvas.width = 375;
    canvas.height = 667;
    
    // Get the WebGL context (getWebGLContext is the default of the tool library introduced earlier)
    var gl = getWebGLContext(canvas);
    if(! gl) {console.log('Failed to get the rendering context for WebGL');
        return;
    }
    
    // Initialize the shader (initShaders are functions defined by the tool library, passing in context, vertex/fragment shader code)
    if(! initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {console.log('Failed to intialize shaders.');
        return;
    }
    
    // Set vertex data (initVertexBuffers function see below)
    var n = initVertexBuffers(gl);
    if (n < 0) {
        console.log('Failed to set the vertex information');
        return;
    }
    
    // Empty the canvas
    gl.clearColor(0.0.0.0.0.0.1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    
    // Draw vertices (three points make a rectangle, four points can draw two triangles, form a rectangle, which is our canvas)
    gl.drawArrays(gl.TRIANGLE_FAN, 0.4);
}

// Initialize the vertex buffer
function initVertexBuffers(gl) {
    // Vertex coordinates (four points of the canvas)
    var verticesTexCoords = new Float32Array([
        1.0.1.0.1.0.1.0.1.0.1.0.1.0.1.0
    ]);

    // Number of vertices (4 points determine a rectangle)
    var n = 4;

    // Create a vertex buffer
    var vertexTexCoordBuffer = gl.createBuffer();
    if(! vertexTexCoordBuffer) {console.log('Failed to create the buffer object');
        return - 1;
    }

    // Bind buffer
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);

    // Get the size of each element of the array
    var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;

    // Get the location of a_Position and set the buffer
    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;
}
Copy the code

With this code in hand, we can add our effects by modifying the slice shader code.

By modifying the slice shader code (changing the color to red), you actually see a change after refreshing. What if we wanted to animate?

Very simply, we add a variable about time to the fragment shader code:

var FSHADER_SOURCE =`
    precision mediump float;
    varying vec2 uv;
    uniform float time;     // Change time

    void main() {
        gl_FragColor = vec4(0..0..0..1.);
    }
`;
Copy the code

Then add the following code at the end of the main() function:

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);Copy the code

Add a render function to index.js:

function render(gl, time, t) {
    t += 0.01;
    gl.uniform1f(time, t);  // Each render passes the latest time into the fragment shader
    
    // The canvas needs to be emptied each time
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.TRIANGLE_FAN, 0.4);

    requestAnimationFrame(render.bind(this, gl, time, t));
}
Copy the code

After adding it, let’s modify the slice shader (introducing a time change) to see if it works:

var FSHADER_SOURCE =`
    precision mediump float;
    varying vec2 uv;
    uniform float time;     // Change time

    void main() {
        float r = uv.x;
        float g = uv.y;
        float b = abs(sin(time));
        gl_FragColor = vec4(r,g,b,1.);
    }
`;
Copy the code

Photoshop: Gradient to GIF is a disaster:


Load the material

Of course we can’t work with pixels all the time, video or images can be processed on canvas in the form of textures. Here’s how to display the texture on the canvas. Start by simply preloading the tile resources we want:

function main() {
    var canvas = document.getElementById('album');
    // Set the width and height according to the actual situation
    canvas.width = 375;
    canvas.height = 667;
    
    // Preload the texture resources here
    var imgList = [];
    preload(imgList, [
        './images/img1.jpg'.'./images/img2.jpg'].function() {
        // Start initializing WebGL after loading is complete
        
        // Get the WebGL context
        var gl = getWebGLContext(canvas);
        if(! gl) {console.log('Failed to get the rendering context for WebGL');
            return;
        }
    // ...})}// Images are preloaded
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()
            }
        }
    })
}
Copy the code

The next step is to initialize the material content and add the WebGL code we wrote earlier to the bottom of the resource preloaded callback:

function main() {
    var canvas = document.getElementById('album');
    // Set the width and height according to the actual situation
    canvas.width = 375;
    canvas.height = 667;

    // Preload the map resources here.
    var imgList = [];
    preload(imgList, [
        './images/img8.jpg'.'./images/img9.jpg'].function() {

        // ...
        // Draw vertices (save this step for later)
        // gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);


        // Add a material initialization function
        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); })}// Initialize the material
function initTextures(gl, imgList) {

    // There are two images, so create two materials
    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;
    }

    // Load our material and initialize it (functions are defined below)
    loadTexture(gl, texture0, u_Sampler0, imgList[0].0);
    loadTexture(gl, texture1, u_Sampler1, imgList[1].1);

    return true;
}


// Load the material
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;
}
Copy the code

If you execute the code at this time, you will find an error, because we have added two new variables u_Sampler0 and u_Sampler1 to hold the two texture images, so we need to add:

// Chip shader code
var FSHADER_SOURCE =`
    precision mediump float;
    varying vec2 uv;
    uniform float time;     // Change time
    uniform sampler2D u_Sampler0; 
    uniform sampler2D u_Sampler1;

    void main() {
        // Use the built-in mix() function to interpolate linear gradients for both images
        vec4 color = mix(texture2D(u_Sampler0, uv), texture2D(u_Sampler1, uv), abs(sin(time)));
        gl_FragColor = vec4(color);
    }
`;
Copy the code

In fact, this is the general transition effect, we can also use some built-in functions to do more transitions:

// Chip shader code
var FSHADER_SOURCE =`
    precision mediump float;
    varying vec2 uv;
    uniform float time;     // Change time
    uniform sampler2D u_Sampler0; 
    uniform sampler2D u_Sampler1;

    void main() {
        // Use the built-in mix() function to interpolate linear gradients for both images
        vec4 color = mix(texture2D(u_Sampler0, uv), texture2D(u_Sampler1, uv), abs(sin(time)));
        gl_FragColor = vec4(color);
    }
`;
Copy the code

Here’s another complicated one:

// Chip shader code
var FSHADER_SOURCE =`
    precision mediump float;
    varying vec2 uv;
    uniform float time;     // Change time
    uniform sampler2D u_Sampler0; 
    uniform sampler2D u_Sampler1;

    void main() {
        float m = smoothstep(0.5.0., uv.x - abs(sin(time))*1.5);
        gl_FragColor = mix(texture2D(u_Sampler0, (uv - 0.5) * (1.0 - m) + 0.5), texture2D(u_Sampler1, (uv - 0.5) * m + 0.5), m);
    }
`;
Copy the code

Let’s do another scaled one:

// Chip shader code
var FSHADER_SOURCE =`
    precision mediump float;
    varying vec2 uv;
    uniform float time;     // Change time
    uniform sampler2D u_Sampler0; 
    uniform sampler2D u_Sampler1;

    void main() {
        st -= . 5;
        st *= 1.-smoothstep(0..1..abs(sin(time)));
        st += . 5;

        vec4 color = mix(texture2D(u_Sampler0, st), texture2D(u_Sampler1, uv), abs(sin(time)));
    }
`;
Copy the code

Here is the complete indexJS code:

// Vertex shader code
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)); } `
// Chip shader code
var FSHADER_SOURCE =` precision mediump float; varying vec2 uv; uniform float time; Uniform sampler2D u_Sampler0; uniform sampler2D u_Sampler1; Void main() {float m = smoothstep(-0.5, 0., uv.x - abs(sin(time))*1.5); Gl_FragColor = mix(texture2D(u_Sampler0, (UV-0.5) * (1.0-m) + 0.5), texture2D(u_Sampler1, (UV-0.5) * m + 0.5), texture2D(u_Sampler1, (UV-0.5) * m + 0.5), m); } `;


function main() {
    var canvas = document.getElementById('album');
    // Set the width and height according to the actual situation
    canvas.width = 375;
    canvas.height = 667;

    // Preload the texture resources here
    var imgList = [];
    preload(imgList, [
        './images/img8.jpg'.'./images/img9.jpg'].function() {

        // Get the WebGL context (getWebGLContext is the default of the tool library introduced earlier)
        var gl = getWebGLContext(canvas);
        if(! gl) {console.log('Failed to get the rendering context for WebGL');
            return;
        }
        
        // Initialize the shader (initShaders are functions defined by the tool library, passing in context, vertex/fragment shader code)
        if(! initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {console.log('Failed to intialize shaders.');
            return;
        }
        
        // Set vertex data (initVertexBuffers function see below)
        var n = initVertexBuffers(gl);
        if (n < 0) {
            console.log('Failed to set the vertex information');
            return;
        }
        
        // Empty the canvas
        gl.clearColor(0.0.0.0.0.0.1.0);
        gl.clear(gl.COLOR_BUFFER_BIT);

        // Draw vertices
        // gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);

        // Set the texture
        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); })}// Initialize the vertex buffer
function initVertexBuffers(gl) {
    // Vertex coordinates
    var verticesTexCoords = new Float32Array([
        1.0.1.0.1.0.1.0.1.0.1.0.1.0.1.0
    ]);

    // Number of vertices
    var n = 4;

    // Create a vertex buffer
    var vertexTexCoordBuffer = gl.createBuffer();
    if(! vertexTexCoordBuffer) {console.log('Failed to create the buffer object');
        return - 1;
    }

    // Bind buffer
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);

    // Get the size of each element of the array
    var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;

    // Get the location of a_Position and set the buffer
    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;
}

function render(gl, time, t) {
    t += 0.01;
    gl.uniform1f(time, t);
    
    // The canvas needs to be emptied each time
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.TRIANGLE_FAN, 0.4);
    
    requestAnimationFrame(render.bind(this, gl, time, t));
}

// Images are preloaded
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()
            }
        }
    })
}

// Initialize the material
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;
}

// Load the material
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;
}
Copy the code