1 import { Box, Matrix3, Vector2 } from './Utils.js'; 2 import { Shape, ShapeUtils, SplineCurve } from './TwoUtils.js'; 3 4 const BlendEquationAdd = [0, -1]; 5 6 const BlendDefault = [6, 7, -1, -1], 7 BlendAdd = [1, 1, -1, -1], 8 BlendSub = [0, 3, 0, 1], 9 BlendMultiply = [0, 2, 0, 6]; 10 11 const ModePoints = "POINTS", 12 ModeLineStrip = "LINE_STRIP", 13 ModeLineLoop = "LINE_LOOP", 14 ModeLines = "LINES", 15 ModeTriangleStrip = "TRIANGLE_STRIP", 16 ModeTriangleFan = "TRIANGLE_FAN", 17 ModeTriangles = "TRIANGLES"; 18 19 const FormatAlpha = 0, 20 FormatLuminance = 2, 21 FormatLuminanceAlpha = 4, 22 FormatRGB = 12, 23 FormatRGBA = 14; 24 25 const PixelStoreiFlipY = 2, 26 PixelStoreiPremultiplyAlpht = 3; 27 28 29 /* test defaultShaderCode.texture2_blend 30 const geometry = new GeometryRect(200, 200); //二維的矩形 31 32 const sstruet = new Structure({ 33 vertexCode: defaultShaderCode.texture2_blend.vertex, 34 fragmentCode: defaultShaderCode.texture2_blend.fragment, 35 36 attributes: { 37 aPos: new Attribute(2, geometry.vertices), 38 }, 39 40 uniforms: { 41 uPMat: renderer.projectionMatrix, 42 uMat: new Matrix3().translate(100, 100).toArray(), 43 uSampler: images[2], //不透明的背景圖 44 uSampler1: images[4], //透明的圓球 45 opacity: 1, 46 ratio: 0.5, 47 uSize: [geometry.width, geometry.height], 48 }, 49 50 indices: geometry.indices, 51 }); 52 53 renderer.append(sstruet).redraw(); 54 */ 55 56 /* test defaultShaderCode.texture2_after 57 const geometry = new GeometryRect(200, 200); //二維的矩形 58 59 const sstruet = new Structure({ 60 vertexCode: defaultShaderCode.texture2_after.vertex, 61 fragmentCode: defaultShaderCode.texture2_after.fragment, 62 63 attributes: { 64 aPos: new Attribute(2, geometry.vertices), 65 }, 66 67 uniforms: { 68 uPMat: renderer.projectionMatrix, 69 uMat: new Matrix3().translate(100, 100).toArray(), 70 uSampler: images[2], 71 uSampler1: images[3], 72 damp: 1, 73 uSize: [geometry.width, geometry.height], 74 }, 75 76 indices: geometry.indices, 77 }); 78 79 renderer.append(sstruet).redraw(); 80 */ 81 82 /* test defaultShaderCode.texture2_WaterRefract 83 const geometry = new GeometryRect(innerWidth, innerHeight); //二維的矩形 84 const sstruet = new Structure({ 85 vertexCode: defaultShaderCode.texture2_WaterRefract.vertex, 86 fragmentCode: defaultShaderCode.texture2_WaterRefract.fragment, 87 88 attributes: { 89 aPos: new Attribute(2, geometry.vertices), 90 }, 91 92 uniforms: { 93 uPMat: renderer.projectionMatrix, 94 uMat: new Matrix3().toArray(), 95 textureMatrix: new Matrix3().toArray(), 96 uSampler: images[5], //waterColor.jpg 97 uSampler1: images[5], //waterNormal.jpg 98 uColor: [0, 0.5, 0], //綠色 99 uTime: 0, 100 uSize: [geometry.width, geometry.height], 101 }, 102 103 indices: geometry.indices, 104 }); 105 106 function loop() { 107 sstruet.uniforms.uTime -= 0.05; 108 renderer.redraw(); 109 } 110 111 renderer.append(sstruet); 112 new AnimateLoop(loop).play(); 113 */ 114 115 116 const defaultShaderCode = { 117 color_v4: { 118 vertex: ` 119 attribute vec2 aPos; 120 uniform mat3 uPMat; 121 uniform mat3 uMat; 122 void main() { 123 gl_Position.xyz = uPMat * uMat * vec3(aPos, 1.0); 124 gl_Position.w = 1.0; 125 } 126 `, 127 fragment: ` 128 precision mediump float; //highp, mediump, lowp 129 uniform vec4 uColor; 130 void main() { 131 gl_FragColor = uColor; 132 } 133 `, 134 }, 135 texture1: { 136 vertex: `#version 300 es 137 in vec2 aPos; 138 uniform mat3 uPMat; 139 uniform mat3 uMat; 140 out vec2 vPos; 141 void main() { 142 vPos = aPos; 143 gl_Position.xyz = uPMat * uMat * vec3(aPos, 1.0); 144 gl_Position.w = 1.0; 145 } 146 `, 147 fragment: `#version 300 es 148 precision mediump float; //highp, mediump, lowp 149 uniform sampler2D uImage; 150 uniform vec2 uSize; 151 in vec2 vPos; 152 out vec4 outColor; 153 void main() { 154 outColor = texture(uImage, vPos / uSize); 155 } 156 `, 157 }, 158 texture1_sprite: { 159 vertex: `#version 300 es 160 in vec2 aPos; 161 uniform mat3 uPMat; 162 uniform mat3 uMat; 163 out vec2 vPos; 164 void main() { 165 vPos = aPos; 166 gl_Position.xyz = uPMat * uMat * vec3(aPos, 1.0); 167 gl_Position.w = 1.0; 168 } 169 `, 170 fragment: `#version 300 es 171 precision mediump float; //highp, mediump, lowp 172 uniform sampler2D uImage; 173 uniform float uLen; 174 uniform float uOffset; 175 uniform vec2 uSize; 176 in vec2 vPos; 177 out vec4 outColor; 178 void main() { 179 outColor = texture(uImage, vec2(vPos.x / (uSize.x * uLen) + 1.0 / uLen * uOffset, vPos.y / uSize.y)); 180 } 181 `, 182 }, 183 texture1_Instanced: { 184 vertex: `#version 300 es 185 in vec2 aPos; 186 in mat3 uIMat; 187 uniform mat3 uPMat; 188 uniform mat3 uMat; 189 out vec2 vPos; 190 void main() { 191 vPos = aPos; 192 gl_Position.xyz = uPMat * uMat * uIMat * vec3(aPos, 1.0); 193 gl_Position.w = 1.0; 194 } 195 `, 196 fragment: `#version 300 es 197 precision mediump float; //highp, mediump, lowp 198 uniform sampler2D uImage; 199 uniform vec2 uSize; 200 in vec2 vPos; 201 out vec4 outColor; 202 void main() { 203 outColor = texture(uImage, vPos / uSize); 204 } 205 `, 206 }, 207 texture1_Instanced_points: { 208 vertex: `#version 300 es 209 in vec2 aPos; 210 in mat3 uIMat; 211 uniform mat3 uPMat; 212 uniform mat3 uMat; 213 uniform float uSize; 214 void main() { 215 gl_Position.xyz = uPMat * uMat * uIMat * vec3(aPos, 1.0); 216 gl_Position.w = 1.0; 217 gl_PointSize = uSize; 218 } 219 `, 220 fragment: `#version 300 es 221 precision mediump float; //highp, mediump, lowp 222 uniform sampler2D uImage; 223 out vec4 outColor; 224 void main() { 225 outColor = texture(uImage, gl_PointCoord.xy); 226 } 227 `, 228 }, 229 texture1_Instanced_sprite: { 230 vertex: `#version 300 es 231 in vec2 aPos; 232 in mat3 uIMat; 233 uniform mat3 uPMat; 234 uniform mat3 uMat; 235 out vec2 vPos; 236 void main() { 237 vPos = aPos; 238 gl_Position.xyz = uPMat * uMat * uIMat * vec3(aPos, 1.0); 239 gl_Position.w = 1.0; 240 } 241 `, 242 fragment: `#version 300 es 243 precision mediump float; //highp, mediump, lowp 244 uniform sampler2D uImage; 245 uniform float uLen; 246 uniform float uOffset; 247 uniform vec2 uSize; 248 in vec2 vPos; 249 out vec4 outColor; 250 void main() { 251 outColor = texture(uImage, vec2(vPos.x / (uSize.x * uLen) + 1.0 / uLen * uOffset, vPos.y / uSize.y)); 252 } 253 `, 254 }, 255 texture1_fog: { 256 vertex: ` 257 attribute vec2 aPos; 258 uniform mat3 uPMat; 259 uniform mat3 uMat; 260 varying vec2 vPos; 261 void main() { 262 vPos = aPos; 263 gl_Position.xyz = uPMat * uMat * vec3(aPos, 1.0); 264 gl_Position.w = 1.0; 265 } 266 `, 267 fragment: ` 268 precision mediump float; //highp, mediump, lowp 269 uniform sampler2D uSampler; 270 uniform vec4 uFogColor; 271 uniform float uFogAmount; 272 uniform vec2 uSize; 273 varying vec2 vPos; 274 void main() { 275 gl_FragColor = mix(texture2D(uSampler, vPos / uSize), uFogColor, uFogAmount); 276 } 277 `, 278 }, 279 texture1_brightContrast: { //亮度對比度 280 vertex: ` 281 attribute vec2 aPos; 282 uniform mat3 uPMat; 283 uniform mat3 uMat; 284 varying vec2 vPos; 285 void main() { 286 vPos = aPos; 287 gl_Position.xyz = uPMat * uMat * vec3(aPos, 1.0); 288 gl_Position.w = 1.0; 289 } 290 `, 291 fragment: ` 292 precision mediump float; //highp, mediump, lowp 293 uniform sampler2D uSampler; 294 uniform float bright; 295 uniform float contrast; 296 uniform vec2 uSize; 297 varying vec2 vPos; 298 void main() { 299 gl_FragColor = texture2D(uSampler, vPos / uSize); 300 gl_FragColor.rgb += bright; 301 if(contrast > 0.0){ 302 gl_FragColor.rgb = (gl_FragColor.rgb - 0.5) / (1.0 - contrast) + 0.5; 303 } else { 304 gl_FragColor.rgb = (gl_FragColor.rgb - 0.5) * (1.0 + contrast) + 0.5; 305 } 306 } 307 `, 308 }, 309 texture1_color_ifv3: { 310 vertex: ` 311 attribute vec2 aPos; 312 uniform mat3 uPMat; 313 uniform mat3 uMat; 314 varying vec2 vPos; 315 void main() { 316 vPos = aPos; 317 gl_Position.xyz = uPMat * uMat * vec3(aPos, 1.0); 318 gl_Position.w = 1.0; 319 } 320 `, 321 fragment: ` 322 precision mediump float; //highp, mediump, lowp 323 uniform sampler2D uSampler; 324 uniform vec3 uColor; 325 uniform vec2 uSize; 326 varying vec2 vPos; 327 void main() { 328 vec4 tex = texture2D(uSampler, vPos / uSize); 329 gl_FragColor = vec4(dot(tex.xyz, vec3(0.299, 0.587, 0.114)) * uColor, tex.w); 330 } 331 `, 332 }, 333 texture2_blend: { 334 vertex: ` 335 attribute vec2 aPos; 336 uniform mat3 uPMat; 337 uniform mat3 uMat; 338 varying vec2 vPos; 339 void main() { 340 vPos = aPos; 341 gl_Position.xyz = uPMat * uMat * vec3(aPos, 1.0); 342 gl_Position.w = 1.0; 343 } 344 `, 345 fragment: ` 346 precision mediump float; //highp, mediump, lowp 347 uniform sampler2D uSampler; 348 uniform sampler2D uSampler1; 349 uniform float opacity; 350 uniform float ratio; 351 uniform vec2 uSize; 352 varying vec2 vPos; 353 void main() { 354 vec2 uv = vPos / uSize; 355 gl_FragColor = opacity * mix(texture2D(uSampler, uv), texture2D(uSampler1, uv), ratio); 356 } 357 `, 358 }, 359 texture2_after: { 360 vertex: ` 361 attribute vec2 aPos; 362 uniform mat3 uPMat; 363 uniform mat3 uMat; 364 varying vec2 vPos; 365 void main() { 366 vPos = aPos; 367 gl_Position.xyz = uPMat * uMat * vec3(aPos, 1.0); 368 gl_Position.w = 1.0; 369 } 370 `, 371 fragment: ` 372 precision mediump float; //highp, mediump, lowp 373 uniform sampler2D uSampler; 374 uniform sampler2D uSampler1; 375 uniform float damp; 376 uniform vec2 uSize; 377 varying vec2 vPos; 378 vec4 when_gt(vec4 x, float y) { 379 return max(sign(x - y), 0.0); 380 } 381 void main() { 382 vec2 uv = vPos / uSize; 383 vec4 tex = texture2D(uSampler, uv); 384 tex *= damp * when_gt(tex, 0.1); 385 gl_FragColor = max(texture2D(uSampler1, uv), tex); 386 } 387 `, 388 }, 389 texture2_WaterRefract: { //水折射 390 vertex: ` 391 attribute vec2 aPos; 392 uniform mat3 uPMat; 393 uniform mat3 uMat; 394 varying vec2 vPos; 395 396 uniform mat3 textureMatrix; 397 varying vec3 vUvRefraction; 398 399 void main() { 400 vPos = aPos; 401 vec3 pos = vec3(aPos, 1.0); 402 vUvRefraction = textureMatrix * pos; 403 gl_Position.xyz = uPMat * uMat * pos; 404 gl_Position.w = 1.0; 405 } 406 `, 407 fragment: ` 408 precision mediump float; //highp, mediump, lowp 409 uniform sampler2D uSampler; 410 uniform sampler2D uSampler1; 411 uniform vec3 uColor; 412 uniform float uTime; 413 uniform vec2 uSize; 414 varying vec2 vPos; 415 416 varying vec3 vUvRefraction; 417 418 float blendOverlay(float base, float blend) { 419 return(base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend))); 420 } 421 422 vec3 blendOverlay(vec3 base, vec3 blend) { 423 return vec3(blendOverlay(base.r, blend.r), blendOverlay(base.g, blend.g),blendOverlay(base.b, blend.b)); 424 } 425 426 void main() { 427 vec2 uv = vPos / uSize; 428 float waveStrength = 0.5; 429 float waveSpeed = 0.03; 430 431 // simple distortion (ripple) via dudv map (see https://www.youtube.com/watch?v=6B7IF6GOu7s) 432 433 vec2 distortedUv = texture2D(uSampler1, vec2(uv.x + uTime * waveSpeed, uv.y)).rg * waveStrength; 434 distortedUv = uv.xy + vec2(distortedUv.x, distortedUv.y + uTime * waveSpeed); 435 vec2 distortion = (texture2D(uSampler1, distortedUv).rg * 2.0 - 1.0) * waveStrength; 436 437 // new vUvRef coords 438 439 vec4 vUvRef = vec4(vUvRefraction, 1.0); 440 vUvRef.xy += distortion; 441 442 vec4 base = texture2DProj(uSampler, vUvRef); 443 444 gl_FragColor = vec4(blendOverlay(base.rgb, uColor), 1.0); 445 446 //#include <tonemapping_fragment> 447 //#include <colorspace_fragment> 448 } 449 `, 450 }, 451 } 452 453 //返回是否時可用畫素源 454 function isPixelSource(source) { 455 /* TypeArray: 456 Uint8Array 如果 type 是 gl.UNSIGNED_BYTE則必須使用 457 Uint16Array 如果 type 是 gl.UNSIGNED_SHORT_5_6_5, gl.UNSIGNED_SHORT_4_4_4_4, gl.UNSIGNED_SHORT_5_5_5_1, gl.UNSIGNED_SHORT 或ext.HALF_FLOAT_OES則必須使用 458 Uint32Array 如果type 是 gl.UNSIGNED_INT 或ext.UNSIGNED_INT_24_8_WEBGL則必須使用 459 Float32Array 如果type 是 gl.FLOAT則必須使用 460 */ 461 return ImageData.prototype.isPrototypeOf(source) || 462 ImageBitmap.prototype.isPrototypeOf(source) || 463 HTMLImageElement.prototype.isPrototypeOf(source) || 464 HTMLCanvasElement.prototype.isPrototypeOf(source) || 465 HTMLVideoElement.prototype.isPrototypeOf(source); 466 } 467 468 //翻轉 points: [x, y, x1, y1, ...] => [x1, y1, x, y, ...]; 469 function reversePoints(points = [0, 0, 0, 0]) { 470 for(let i = 0, j = points.length - 1; i < j; i += 2, j -= 2){ 471 points[i] = points[j-1]; 472 points[i+1] = points[j]; 473 points[j] = points[i+1]; 474 points[j-1] = points[i]; 475 } 476 return points; 477 } 478 479 //value 是否是2的冪 480 function isPowerOf2(value) { 481 return (value & (value - 1)) === 0; 482 } 483 484 //array 的某個元素如果超出 Uint16Array 範圍則立即返回true 485 function arrayNeedsUint32( array ) { 486 // assumes larger values usually on last 487 for ( let i = array.length - 1; i >= 0; -- i ) { 488 if ( array[ i ] >= 65535 ) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565 489 } 490 return false; 491 } 492 493 //平移頂點 494 function translateVertices(vertices, count, x, y) { 495 for(let i = 0; i < vertices.length; i += count){ 496 vertices[i] += x; 497 vertices[i + 1] += y; 498 } 499 } 500 501 //計算包圍盒 502 function computeBBox(vertices, count) { 503 let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; 504 for(let i = 0, x, y; i < vertices.length; i += count){ 505 x = vertices[i]; 506 y = vertices[i + 1]; 507 minX = Math.min(x, minX); 508 minY = Math.min(y, minY); 509 maxX = Math.max(x, maxX); 510 maxY = Math.max(y, maxY); 511 } 512 return {x: minX, y: minY, x1: maxX, y1: maxY} 513 } 514 515 /** 516 * @returns {WebGL2RenderingContext} 517 */ 518 function createWebGL2(contextOption = Renderer.contextOption, glOption = Renderer.glOption){ 519 const canvas = document.createElement("canvas"); 520 const gl = canvas.getContext("webgl2", contextOption); 521 //gl.viewport(0, 0, width, height); 522 523 const clearColor = glOption.clearColor || {r: 0.45, g: 0.45, b: 0.45, a: 1} 524 gl.clearColor(Math.min(clearColor.r, 1), Math.min(clearColor.g, 1), Math.min(clearColor.b, 1), Math.min(clearColor.a, 1)); //清除顏色: r, g, b, a: 0 - 1; 對應 .clear(COLOR_BUFFER_BIT) 525 //gl.clear(gl.COLOR_BUFFER_BIT); //COLOR_BUFFER_BIT //顏色緩衝區 gl.DEPTH_BUFFER_BIT //深度緩衝區 STENCIL_BUFFER_BIT //模板緩衝區 526 //gl.getParameter(gl.COLOR_CLEAR_VALUE); //要獲得當前的清除值,傳入 COLOR_CLEAR_VALUE, DEPTH_CLEAR_VALUE 或 STENCIL_CLEAR_VALUE 常量 527 528 //gl.enable(gl.DEPTH_TEST); //啟用深度 對應 .clear(DEPTH_BUFFER_BIT) 529 //gl.depthFunc(gl.LEQUAL); // Near things obscure far things 近覆蓋遠 530 //gl.clearDepth(1); // 設定深度緩衝區的值(0-1),預設為1 531 532 //gl.clearStencil(1) //設定模板緩衝區的值(0或1),預設0; 對應 .clear(STENCIL_BUFFER_BIT); 533 534 //gl.enable(gl.SCISSOR_TEST); //開啟剪裁 535 //gl.scissor(x, y, width, height); //設定剪裁區域 536 537 //gl.colorMask(true, true, true, false); //禁啟用: 紅色通道, 綠色通道, 藍色通道, 透明度(如果為false則不會繪製任何顏色即完全透明); 538 539 //混合 540 //gl.enable(gl.BLEND); // 啟用混合, 預設透明部分用背景色覆蓋 541 //gl.blendEquation(gl.FUNC_ADD); 542 543 //gl.blendFunc(gl.ONE, gl.ONE); //gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); 544 545 //gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); //將所有顏色乘以源 alpha 值, 將所有顏色乘以 1 減去源 alpha 值。 546 //gl.disable(gl.BLEND); //禁用混合 547 //gl.getParameter(gl.BLEND_SRC_RGB) == gl.SRC_COLOR; 548 //混合畫素的方法: 549 550 //gl.drawElements(gl.TRIANGLES, obj2d.geometry.indices.length, gl.UNSIGNED_SHORT, 0); //gl.UNSIGNED_BYTE, gl.UNSIGNED_SHORT 551 //gl.drawArrays(mode, 0, geo.vertices.length / geo.vertexCount); 552 return gl; 553 } 554 555 function initExtensions(gl) { 556 // 啟用了抗鋸齒 557 if(Renderer.contextOption.antialias === true && !gl.getContextAttributes().antialias && gl.getExtension('WEBGL_multisample_2d_canvas')) { 558 gl.sampleCoverage = true; 559 gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE); 560 } 561 562 //擴充套件 WEBGL_multi_draw 563 /* const WEBGL_multi_draw = gl.getExtension('WEBGL_multi_draw'); 564 if (WEBGL_multi_draw) { 565 var multiDrawElementsWEBGL = gl.multiDrawElementsWEBGL.bind(gl); 566 // 準備你要繪製的元素的資訊 567 var counts = []; //每個draw call的元素數量 568 var offsets = []; //每個draw call的起始偏移 569 // 呼叫 multiDrawElementsWEBGL 方法 570 multiDrawElementsWEBGL(gl.TRIANGLES, counts, gl.UNSIGNED_SHORT, offsets); 571 } */ 572 } 573 574 function createShader(gl, program, type, code) { 575 const shader = gl.createShader(type); //建立著色器 576 gl.shaderSource(shader, code); //繫結資料來源 577 gl.compileShader(shader); //編譯著色器 578 gl.attachShader(program, shader); //繫結著色器 579 if(gl.getShaderParameter(shader, gl.COMPILE_STATUS) === false){ 580 console.error(type, gl.getShaderInfoLog(shader), code); 581 gl.deleteShader(shader); 582 } 583 return shader; 584 } 585 586 function createProgram(gl, vertexShaderCode, fragmentShaderCode) { 587 if(!gl) return null; 588 const program = gl.createProgram(); 589 const vertexShader = createShader(gl, program, gl.VERTEX_SHADER, vertexShaderCode); 590 const fragmentShader = createShader(gl, program, gl.FRAGMENT_SHADER, fragmentShaderCode); 591 592 gl.linkProgram(program); //連線頂點著色器與片元著色器 593 if(gl.getProgramParameter(program, gl.LINK_STATUS) === false){ 594 console.error(gl.getProgramInfoLog(program)); 595 gl.deleteProgram(program); 596 return null; 597 } 598 599 return { 600 program: program, 601 vertexShader: vertexShader, 602 fragmentShader: fragmentShader, 603 }; 604 } 605 606 function compileUniform(gl, loc, n, v, t) { 607 //number 608 switch(typeof v[n]){ 609 case "number": 610 return () => gl.uniform1f(loc, v[n]); 611 612 case "object": 613 break; 614 615 default: return function(){}; 616 } 617 618 //vec2, vec3, vec4, Matrix3x3, Matrix4x4 619 if(Array.isArray(v[n]) === true){ 620 switch(v[n].length){ 621 case 2: 622 return () => gl.uniform2f(loc, v[n][0], v[n][1]); 623 624 case 3: 625 return () => gl.uniform3f(loc, v[n][0], v[n][1], v[n][2]); 626 627 case 4: 628 return () => gl.uniform4f(loc, v[n][0], v[n][1], v[n][2], v[n][3]); 629 630 case 9: 631 return () => gl.uniformMatrix3fv(loc, false, v[n]); 632 633 case 16: 634 return () => gl.uniformMatrix4fv(loc, false, v[n]); 635 } 636 } 637 638 //Material 639 /* if(Material.prototype.isPrototypeOf(v[n]) === true){ 640 const i = t.length, 641 obj = { 642 texture: gl.createTexture(), 643 source: v[n], 644 index: gl["TEXTURE"+i], 645 needupdate: false, 646 }; 647 648 t[i] = obj; 649 gl.activeTexture(obj.index); 650 gl.bindTexture(gl.TEXTURE_2D, obj.texture); 651 652 const material = v[n]; 653 obj.update = () => { 654 if(material.source !== null) updateTexture(gl, material); 655 } 656 Object.defineProperties(obj, { 657 source: {get: () => {return material.source;}}, 658 needupdate: {get: () => {return material.needupdate;}}, 659 }); 660 obj.update(); 661 return () => gl.uniform1i(loc, i); 662 } */ 663 664 //Attribute 665 if(Attribute.prototype.isPrototypeOf(v[n]) === true){ 666 switch(v[n].size){ 667 case 1: 668 return () => gl.uniform1fv(loc, v[n].value); 669 670 case 2: 671 return () => gl.uniform2fv(loc, v[n].value); 672 673 case 3: 674 return () => gl.uniform3fv(loc, v[n].value); 675 676 case 4: 677 return () => gl.uniform4fv(loc, v[n].value); 678 } 679 } 680 681 return function(){}; 682 } 683 684 function compileBuffer(gl, loc, att) { 685 const obj = { 686 vao: gl.createVertexArray(), 687 buffer: gl.createBuffer(), 688 size: att.size, 689 //loc: loc, //如果著色器中沒有用到這個變數就會返回-1 690 value: att.value, 691 } 692 693 gl.bindVertexArray(obj.vao); 694 gl.bindBuffer(gl.ARRAY_BUFFER, obj.buffer); //指定 buffer 695 gl.bufferData(gl.ARRAY_BUFFER, obj.value, gl.STATIC_DRAW); //上傳資料到指定的 buffer 696 gl.vertexAttribPointer(loc, att.size, gl.FLOAT, false, 0, 0); 697 gl.enableVertexAttribArray(loc); 698 699 return obj; 700 } 701 702 function resetBuffers(gl) { 703 gl.bindVertexArray(null); 704 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); 705 gl.bindBuffer(gl.ARRAY_BUFFER, null); 706 gl.bindTexture(gl.TEXTURE_2D, null); 707 } 708 709 function createBuffers(gl, vertices, indices) { 710 //索引 indices 711 var indexBuffer = null; 712 if(indices) { 713 indexBuffer = gl.createBuffer(); 714 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 715 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); 716 } 717 718 //頂點 vertices 719 const vertexBuffer = gl.createBuffer(); 720 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); //指定儲存單元 721 gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); //gl.STATIC_DRAW: 寫入一次,繪製多次(不能再次修改,可重複使用) 722 723 //紋理座標 uvs 724 //const uvBuffer = gl.createBuffer(); 725 //gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer); 726 //gl.bufferData(gl.ARRAY_BUFFER, uvs, gl.STATIC_DRAW); 727 728 //const loc = gl.getAttribLocation(pro, "aPosition"); 729 730 //gl.vertexAttribPointer(loc, vertexCount, gl.FLOAT, false, 0, 0); 731 732 return { 733 indexBuffer: indexBuffer, 734 vertexBuffer: vertexBuffer, 735 //uvBuffer: uvBuffer, 736 } 737 } 738 739 function deleteBuffers(gl, buffers) { 740 if(!buffers) return; 741 for(let n in buffers){ 742 if(buffers[n]) gl.deleteBuffer(buffers[n]); 743 } 744 } 745 746 function updateTexture(gl, tex) { 747 //畫素預處理 748 if(Array.isArray(tex.pixelStorei) === true) { 749 for(let i = 0, v; i < tex.pixelStorei.length; i++) { 750 v = Texture.pixelStoreis[tex.pixelStorei[i]]; 751 if(v !== undefined) gl.pixelStorei(gl[v], true); 752 } 753 } 754 755 if(ImageSource.prototype.isPrototypeOf(tex.source) === true){ 756 gl.texImage2D(gl.TEXTURE_2D, 0, gl[tex.format], tex.source.width, tex.source.height, 0, gl[tex.format], gl[tex.type], tex.source.data); 757 } else { 758 gl.texImage2D(gl.TEXTURE_2D, 0, gl[tex.format], gl[tex.format], gl[tex.type], tex.source); 759 } 760 761 //gl.getParameter(gl.MAX_TEXTURE_SIZE); 762 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); //LINEAR (default value)(線性的), NEAREST(最近的) 763 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); //REPEAT(重複), CLAMP_TO_EDGE(夾到邊緣), MIRRORED_REPEAT(像鏡子一樣的重複?) 764 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); 765 766 //mipmap 767 if(tex.mipmap === true){ // && isPowerOf2(tex.source.width) === true && isPowerOf2(tex.source.height) === true 768 gl.generateMipmap(gl.TEXTURE_2D); 769 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); 770 } else { 771 //gl.LINEAR, gl.NEAREST, gl.NEAREST_MIPMAP_NEAREST, gl.LINEAR_MIPMAP_NEAREST, gl.NEAREST_MIPMAP_LINEAR (default value), gl.LINEAR_MIPMAP_LINEAR. 772 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 773 } 774 } 775 776 function proHandler(gl, pros, renderer, object2d) { 777 const isMS = MaterialShader.prototype.isPrototypeOf(object2d.material); 778 779 const result = { 780 cache: null, 781 uniforms: { 782 uPMat: renderer.projectionMatrix, 783 uMat: object2d.modelMatrix, 784 uSize: null, 785 }, 786 } 787 788 let proName = "", pro = null; 789 790 switch(object2d.constructor.name){ 791 case "Object2D": 792 proName = "texture1"; 793 794 if(isMS === false){ 795 pro = pros[proName]; 796 } else { 797 pro = createProgram(gl, object2d.material.vertexCode, object2d.material.fragmentCode); 798 proName = ""; 799 } 800 801 result.uniforms.uImage = object2d.material.texture; 802 result.uniforms.uSize = [object2d.geometry.width, object2d.geometry.height]; 803 result.cache = new Cache(proName, pro, gl[ModeTriangles]); 804 break; 805 806 case "Sprite": 807 proName = "texture1_sprite"; 808 809 if(isMS === false){ 810 pro = pros[proName]; 811 } else { 812 pro = createProgram(gl, object2d.material.vertexCode, object2d.material.fragmentCode); 813 proName = ""; 814 } 815 816 result.uniforms.uImage = object2d.material.texture; 817 result.uniforms.uSize = [object2d.geometry.width, object2d.geometry.height]; 818 result.uniforms.uLen = object2d.len; 819 Object.defineProperty(result.uniforms, "uOffset", { 820 enumerable: true, //編譯時需要遍歷 uniforms 821 get: () => {return object2d.offset;}, 822 }); 823 result.cache = new Cache(proName, pro, gl[ModeTriangles]); 824 break; 825 826 case "Instanced": 827 proName = "texture1_Instanced"; 828 829 if(isMS === false){ 830 pro = pros[proName]; 831 } else { 832 pro = createProgram(gl, object2d.material.vertexCode, object2d.material.fragmentCode); 833 proName = ""; 834 } 835 836 result.uniforms.uImage = object2d.material.texture; 837 result.uniforms.uSize = [object2d.geometry.width, object2d.geometry.height]; 838 result.cache = new CacheInstanced(proName, pro, gl[ModeTriangles], gl, object2d); 839 break; 840 841 case "InstancedPoints": 842 proName = "texture1_Instanced_points"; 843 844 if(isMS === false){ 845 pro = pros[proName]; 846 } else { 847 pro = createProgram(gl, object2d.material.vertexCode, object2d.material.fragmentCode); 848 proName = ""; 849 } 850 851 result.uniforms.uImage = object2d.material.texture; 852 Object.defineProperty(result.uniforms, "uSize", { 853 enumerable: true, //編譯時需要遍歷 uniforms 854 get: () => {return object2d.pointSize;}, 855 }); 856 result.cache = new CacheInstanced(proName, pro, gl[ModePoints], gl, object2d); 857 break; 858 859 case "InstancedSprite": 860 proName = "texture1_Instanced_sprite"; 861 862 if(isMS === false){ 863 pro = pros[proName]; 864 } else { 865 pro = createProgram(gl, object2d.material.vertexCode, object2d.material.fragmentCode); 866 proName = ""; 867 } 868 869 result.uniforms.uImage = object2d.material.texture; 870 result.uniforms.uSize = [object2d.geometry.width, object2d.geometry.height]; 871 result.uniforms.uLen = object2d.len1; 872 Object.defineProperty(result.uniforms, "uOffset", { 873 enumerable: true, //編譯時需要遍歷 uniforms 874 get: () => {return object2d.offset;}, 875 }); 876 result.cache = new CacheInstanced(proName, pro, gl[ModeTriangles], gl, object2d); 877 break; 878 } 879 880 if(isMS) Object.assign(result.uniforms, object2d.material.uniforms); 881 882 return result; 883 } 884 885 886 /* glsl 一些內建函式: 887 abs 返回一個數的絕對值。 888 acos 返回一個數的反餘弦。 889 asin 返回一個數的反正弦。 890 atan 返回一個數的反正切。 891 atan2 返回從X軸到點(y,x)的角度(以弧度為單位)。 892 cos 返回一個數的餘弦。 893 sin 返回一個數的正弦。 894 sqrt 返回一個數的平方根。 895 tan 返回一個數的正切。 896 round 將一個指定的數值表示式舍入到最近的整數並將其返回。 897 random 返回一個0和1之間的偽隨機數。 898 parseFloat 返回從字串轉換而來的浮點數。 899 parseInt 返回從字串轉換而來的整數。 900 pow 返回一個指定冪次的底表示式的值。 901 step(float a, float x): a < x ? x : a; 902 clamp(float x, float min, float max): min > x ? min : min < x && max > x ? x : max; 返回三個值中的中間值 903 mix(vac4 color1, vec4 color2, float weight) 返回兩種顏色的混合, color2 為 weight, color1 為 1 - weight 904 mod(float x, float y): x % y 905 906 exp 返回e的x次冪。 907 log 返回x的自然對數, 908 exp2 返回2的x次冪。 909 log2 返回x的2為底的對數, 910 911 inversesqrt 回1/xxx, 912 sign 返回數值的符號值。 913 914 texture2D(uSampler, gl_PointCoord.xy) gl_PointCoord 特殊變數能自動獲取 points 的紋理座標 915 */ 916 917 918 class Attribute { 919 920 /** 921 * @param {number} size 922 * @param {Array|TypeBufferArray} value 923 */ 924 constructor(size, value) { 925 this.size = size; 926 this.value = value; 927 } 928 929 } 930 931 932 /** Geometry 933 demo: 934 const width = 256, height = 256; 935 936 const geometry = new Geometry({ 937 aPosition: new Attribute(2, new Float32Array([ 938 width,0, 0,0, 0,height, 939 0,height, width,height, width,0, 940 ])), 941 }, width, height); 942 943 944 //頂點索引版本: 945 const geometry = new Geometry({ 946 aPosition: new Attribute(2, new Float32Array([ 947 0,0, width,0, width,height, 0,height, 948 ])) 949 }, width, height); 950 951 geometry.setIndex([1,0,3, 3,2,1]); 952 */ 953 class Geometry { 954 955 #type = "UNSIGNED_SHORT"; //索引面的型別(初始化時自動選擇設定); 可能的值有: UNSIGNED_SHORT|UNSIGNED_INT 956 get type() {return this.#type;} 957 958 #offset = 0; //繪製幾何體的偏移 959 get offset() {return this.#offset;} 960 961 #indices = null; 962 get indices() {return this.#indices;} 963 964 #w = 0; 965 #h = 0; 966 get width() {return this.#w;} 967 get height() {return this.#h;} 968 969 /** 970 * @param {{aPos: Attribute}} attributes 必須定義(面索用.setIndex()方法設定) 971 * @param {Box} bbox 可選(如果未定義則在構造器中自動計算一次) 972 */ 973 constructor(attributes, w = 0, h = 0) { 974 this.attributes = attributes; 975 this.#w = w; 976 this.#h = h; 977 } 978 979 /** 980 * 根據 this.attributes[attributeName] 的頂點設定邊界大小 981 * @param {string} attributeName 982 */ 983 computeSize(attributeName) { 984 const att = this.attributes[attributeName]; 985 if(Attribute.prototype.isPrototypeOf(att) === false) return; 986 const obj = computeBBox(att.value, att.size); 987 if(obj.x !== 0 || obj.y !== 0){ 988 translateVertices(att.value, att.size, -obj.x, -obj.y); 989 this.#w = Math.abs(obj.x1 - obj.x); 990 this.#h = Math.abs(obj.y1 - obj.y); 991 } else { 992 this.#w = obj.x1; 993 this.#h = obj.y1; 994 } 995 } 996 997 /** 998 * 設定頂點索引 999 * @param {[]|Uint16Array|Uint32Array} indices 1000 * @param {undefined|boolean} isu32 //如果 indices 已是型別陣列可以忽略此引數 1001 * @returns 1002 */ 1003 setIndex(indices, isu32) { 1004 if(this.#indices !== null) return console.warn("不支援更改索引面"); 1005 1006 switch(indices.constructor.name){ 1007 case "Array": 1008 break; 1009 1010 case "Uint32Array": 1011 this.#type = "UNSIGNED_INT"; 1012 this.#indices = indices; 1013 return; 1014 1015 default: 1016 case "Uint16Array": 1017 this.#type = "UNSIGNED_SHORT"; 1018 this.#indices = indices; 1019 return; 1020 } 1021 1022 if(typeof isu32 !== "boolean"){ 1023 isu32 = false; 1024 for(let i = 0; i < indices.length; i++){ 1025 if(indices[i] < 65535) continue; 1026 isu32 = true; 1027 break; 1028 } 1029 } 1030 1031 if(isu32 === false) { 1032 this.#type = "UNSIGNED_SHORT"; 1033 this.#indices = new Uint16Array(indices); 1034 } else { 1035 this.#type = "UNSIGNED_INT"; 1036 this.#indices = new Uint32Array(indices); 1037 } 1038 } 1039 1040 } 1041 1042 1043 //矩形 1044 class GeometryRect extends Geometry { 1045 1046 constructor(width, height) { 1047 super({aPos: new Attribute(2, new Float32Array([0,0, width,0, width,height, 0,height]))}, width, height); 1048 this.setIndex([1, 0, 3, 3, 2, 1], false); 1049 } 1050 1051 } 1052 1053 1054 //圓形 1055 class GeometryCircle extends Geometry { 1056 1057 constructor(radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2) { 1058 segments = Math.max(3, segments); 1059 1060 let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; 1061 1062 const vertices = [0, 0]; 1063 for ( let s = 0, i = 3, segment; s <= segments; s ++, i += 3 ) { 1064 segment = thetaStart + s / segments * thetaLength; 1065 const x = radius * Math.cos( segment ), 1066 y = radius * Math.sin( segment ); 1067 vertices.push(x, y); 1068 minX = Math.min(x, minX); 1069 minY = Math.min(y, minY); 1070 maxX = Math.max(x, maxX); 1071 maxY = Math.max(y, maxY); 1072 } 1073 1074 const indices = []; 1075 for ( let i = 1; i <= segments; i ++ ) { 1076 indices.push( i, i + 1, 0 ); 1077 } 1078 1079 if(minX !== 0 || minY !== 0){ 1080 translateVertices(vertices, 2, -minX, -minY); 1081 super({aPos: new Attribute(2, new Float32Array(vertices))}, Math.abs(maxX - minX), Math.abs(maxY - minY)); 1082 } else { 1083 super({aPos: new Attribute(2, new Float32Array(vertices))}, maxX, maxY); 1084 } 1085 1086 this.setIndex(indices); 1087 } 1088 1089 } 1090 1091 1092 //形狀 1093 class GeometryShape extends Geometry { 1094 1095 constructor(points, segments = 1) { 1096 let isu32 = false, minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; 1097 1098 points = new Shape(points).extractPoints(segments).shape; 1099 if(ShapeUtils.isClockWise(points) === false) points = points.reverse(); 1100 1101 // 1102 const indices = [], vertices = [], 1103 faces = ShapeUtils.triangulateShape(points, []); 1104 for(let i = 0, p, l = points.length; i < l; i ++){ 1105 p = points[i]; 1106 vertices.push(p.x, p.y); 1107 minX = Math.min(p.x, minX); 1108 minY = Math.min(p.y, minY); 1109 maxX = Math.max(p.x, maxX); 1110 maxY = Math.max(p.y, maxY); 1111 } 1112 1113 for(let i = 0, face, l = faces.length; i < l; i++){ 1114 face = faces[i]; 1115 indices.push(face[0], face[1], face[2]); 1116 if(isu32 === false && (face[0] >= 65535 || face[1] >= 65535 || face[2] >= 65535)) isu32 = true; 1117 } 1118 1119 // 1120 1121 if(minX !== 0 || minY !== 0){ 1122 translateVertices(vertices, 2, -minX, -minY); 1123 super({aPos: new Attribute(2, new Float32Array(vertices))}, Math.abs(maxX - minX), Math.abs(maxY - minY)); 1124 } else { 1125 super({aPos: new Attribute(2, new Float32Array(vertices))}, maxX, maxY); 1126 } 1127 1128 this.setIndex(indices, isu32); 1129 } 1130 1131 } 1132 1133 1134 //波浪矩形 1135 class GeometryRectWavy extends GeometryShape { 1136 1137 constructor(width, height, distance, divisions = 12) { 1138 const halfW = width / 2, halfH = height / 2; 1139 distance = distance === undefined ? Math.min(halfW, halfH) * 0.2 : Math.min(distance, Math.min(halfW, halfH) * 0.5); 1140 const points = [ 1141 //右上 1142 new Vector2(halfW + distance, 0), 1143 new Vector2(width, 0), 1144 new Vector2(width, halfH - distance), 1145 1146 //右下 1147 new Vector2(width, halfH + distance), 1148 new Vector2(width, height), 1149 new Vector2(halfW + distance, height), 1150 1151 //左下 1152 new Vector2(halfW - distance, height), 1153 new Vector2(0, height), 1154 new Vector2(0, halfH + distance), 1155 1156 //左上 1157 new Vector2(0, halfH - distance), 1158 new Vector2(0, 0), 1159 new Vector2(halfW - distance, 0), 1160 ]; 1161 1162 const points1 = [], curve = new SplineCurve(); 1163 for(let i = 0; i < points.length; i += 3) { 1164 curve.points = [points[i], points[i+1], points[i+2]]; 1165 const points2 = curve.getPoints(divisions); 1166 for(let i = 0; i < points2.length; i++){ 1167 points1.push(points2[i]); 1168 } 1169 } 1170 1171 super(points1, 1); 1172 } 1173 1174 } 1175 1176 1177 class ImageSource { 1178 1179 /** 1180 * ImageData 的構造器不是很友好, 這是它的替代品 1181 * @param {number} w 1182 * @param {number} h 1183 * @param {Uint8Array} d //步長為4的陣列, 分別是: r, g, b, a 1184 */ 1185 constructor(w, h, d = new Uint8Array(w * h * 4)) { 1186 this.width = w; 1187 this.height = h; 1188 this.data = d; 1189 } 1190 1191 } 1192 1193 1194 class Texture { 1195 1196 static pixelStoreis = [ 1197 "PACK_ALIGNMENT", //將畫素資料打包到記憶體中 1198 "UNPACK_ALIGNMENT", //從記憶體中解壓縮畫素資料 1199 "UNPACK_FLIP_Y_WEBGL", //翻轉紋理的y軸 1200 "UNPACK_PREMULTIPLY_ALPHA_WEBGL", //預乘阿爾法通道(將alpha通道與其他顏色通道相乘) 1201 "UNPACK_COLORSPACE_CONVERSION_WEBGL", //預設顏色空間轉換或不進行顏色空間轉換 1202 ]; 1203 1204 static formats = [ 1205 "ALPHA", "UNSIGNED_BYTE", //14: 1,1; //阿爾法 1206 "LUMINANCE", "UNSIGNED_BYTE", //12: 1,1; //不透明灰度圖 1207 "LUMINANCE_ALPHA", "UNSIGNED_BYTE", //10: 2,2; //透明灰度圖 1208 "RGB", "UNSIGNED_SHORT_5_6_5", //8: 3,2 1209 "RGBA", "UNSIGNED_SHORT_5_5_5_1", //6: 4,2 1210 "RGBA", "UNSIGNED_SHORT_4_4_4_4", //4: 4,3 1211 "RGB", "UNSIGNED_BYTE", //2: 3,3 //不透明 1212 "RGBA", "UNSIGNED_BYTE", //0: 4,4 //透明 1213 ]; 1214 1215 #f_t = 0; 1216 get format() {return Texture.formats[this.#f_t];} 1217 get type() {return Texture.formats[this.#f_t + 1];} 1218 1219 #needupdate = false; //如果材質屬性發生改變將此值設為true,渲染器會重繪材質的紋理(紋理不適合頻繁的修改, 用著色器實現動態紋理) 1220 get needupdate() { 1221 if(this.#needupdate === false) return false; 1222 this.#needupdate = false; 1223 return true; 1224 } 1225 1226 #source = null; 1227 get source() {return this.#source;} 1228 set source(v) { 1229 this.#source = v; 1230 this.#needupdate = true; 1231 } 1232 1233 #pixelStorei = null; 1234 get pixelStorei() {return this.#pixelStorei;} 1235 get isPremultiplyAlpht() { 1236 return this.#pixelStorei === null ? false : this.#pixelStorei.includes(PixelStoreiPremultiplyAlpht); 1237 } 1238 1239 #mipmap = false; 1240 get mipmap() {return this.#mipmap;} 1241 set mipmap(v) { 1242 if(typeof v !== "boolean" || v === this.#mipmap) return; 1243 this.#mipmap = v; 1244 this.#needupdate = true; 1245 } 1246 1247 constructor(source, format = FormatRGBA, pixelStorei = [PixelStoreiPremultiplyAlpht], mipmap = false) { 1248 this.#source = source; 1249 this.#f_t = format; 1250 this.#pixelStorei = pixelStorei; 1251 this.#mipmap = mipmap; 1252 } 1253 1254 setFormatAndType(v = FormatRGBA) { 1255 this.#f_t = v; 1256 this.#needupdate = true; 1257 } 1258 1259 setPixelStorei(key = PixelStoreiPremultiplyAlpht, enable = false) { 1260 if(key >= Texture.pixelStoreis.length || key < 0) return; 1261 if(enable === true){ 1262 if(this.#pixelStorei === null) this.#pixelStorei = []; 1263 this.#pixelStorei.push(key); 1264 this.#needupdate = true; 1265 } else if(this.#pixelStorei !== null){ 1266 const i = this.#pixelStorei.indexOf(key); 1267 if(i === -1) return; 1268 this.#pixelStorei.splice(i, 1); 1269 this.#needupdate = true; 1270 } 1271 } 1272 1273 } 1274 1275 1276 class MUS { 1277 1278 static blendESs = [ 1279 "FUNC_ADD", //source + destination (default value) 1280 "FUNC_SUBTRACT", //source - destination 1281 "FUNC_REVERSE_SUBTRACT", //destination - source 1282 "MIN", //Minimum of source and destination 1283 "MAX", //Maximum of source and destination 1284 ]; 1285 1286 static blendFSs = [ 1287 "ZERO", //所有顏色乘 0 1288 "ONE", //所有顏色乘 1 1289 "SRC_COLOR", //將所有顏色乘上源顏色 1290 "ONE_MINUS_SRC_COLOR", //每個源顏色所有顏色乘 1 1291 "DST_COLOR", //將所有顏色與目標顏色相乘 1292 "ONE_MINUS_DST_COLOR", //將所有顏色乘以 1 減去每個目標顏色, 1293 "SRC_ALPHA", //將所有顏色乘以源 alpha 值 1294 "ONE_MINUS_SRC_ALPHA", //將所有顏色乘以 1 減去源 alpha 值 1295 "DST_ALPHA", //將所有顏色與目標 alpha 值相乘 1296 "ONE_MINUS_DST_ALPHA", //將所有顏色乘以 1 減去目標 alpha 值 1297 "CONSTANT_COLOR", //將所有顏色乘以一個常數顏色 1298 "ONE_MINUS_CONSTANT_COLOR", //所有顏色乘以 1 減去一個常數顏色 1299 "CONSTANT_ALPHA", //將所有顏色乘以一個常數 1300 "ONE_MINUS_CONSTANT_ALPHA", //所有顏色乘以 1 減去一個常數 1301 "SRC_ALPHA_SATURATE", //將 RGB 顏色乘以源 alpha 值或 1 減去目標 alpha 值中的較小值。alpha 值乘以 1 1302 ]; 1303 1304 #blendEnable = false; 1305 get blendEnable() {return this.#blendEnable;} 1306 set blendEnable(v) { 1307 this.#blendEnable = typeof v === "boolean" ? v : false; 1308 } 1309 1310 #blendC = {r: 0, g: 0, b: 0, a: 0}; 1311 get blendC(){return this.#blendC;} 1312 1313 #blendES = [BlendEquationAdd[0], BlendEquationAdd[1]]; 1314 get blendES(){return this.#blendES;} //mode || modeRGB, modeAlpha; 值為: MUS.blendESs 的索引 1315 1316 #blendFS = [BlendDefault[0], BlendDefault[1], BlendDefault[2], BlendDefault[3]]; 1317 get blendFS(){return this.#blendFS;} //sfactor, dfactor || srcRGB, dstRGB, srcAlpha, dstAlpha; 值為: MUS.blendFSs 的索引 1318 1319 //如果紋理屬性發生改變將此值設為true,渲染器會重繪材質的紋理(紋理不適合頻繁的修改, 用著色器實現動態紋理) 1320 //注意還要設定對應材質的.needupdate = true 才有效 1321 #needupdate = false; 1322 get needupdate() { 1323 if(this.#needupdate === false) return false; 1324 this.#needupdate = false; 1325 return true; 1326 } 1327 set needupdate(v) { 1328 this.#needupdate = v; 1329 } 1330 1331 /** 1332 * 設定內建的混合組合 1333 * @param {Array} v //值為帶字首 Blend* 的常量 1334 */ 1335 setBlend(v){ 1336 switch(v){ 1337 default: return; 1338 case BlendDefault: 1339 case BlendAdd: 1340 case BlendSub: 1341 case BlendMultiply: 1342 break; 1343 } 1344 Object.assign(this.#blendFS, v); 1345 } 1346 1347 /** 1348 * gl.blendFunc(sfactor, dfactor) 1349 * @param {number} sfactor //值為: MUS.blendFSs 的索引 1350 * @param {number} dfactor //值為: MUS.blendFSs 的索引 1351 */ 1352 blendFunc(sfactor, dfactor) { 1353 this.#blendFS[0] = sfactor; 1354 this.#blendFS[1] = dfactor; 1355 this.#blendFS[2] = -1; 1356 this.#blendFS[3] = -1; 1357 } 1358 1359 /** 1360 * //gl.blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); 值為: MUS.blendFSs 的索引 1361 * @param {number} srcRGB 1362 * @param {number} dstRGB 1363 * @param {number} srcAlpha 1364 * @param {number} dstAlpha 1365 */ 1366 blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha) { 1367 this.#blendFS[0] = srcRGB; 1368 this.#blendFS[1] = dstRGB; 1369 this.#blendFS[2] = srcAlpha; 1370 this.#blendFS[3] = dstAlpha; 1371 } 1372 1373 } 1374 1375 1376 class Material extends MUS { 1377 1378 #texture = null; 1379 get texture() {return this.#texture;} 1380 1381 /** 1382 * @param {Texture} texture 1383 * @param {boolean} blendEnable 1384 */ 1385 constructor(texture, blendEnable = true) { 1386 super(); 1387 this.#texture = texture; 1388 this.blendEnable = blendEnable; 1389 } 1390 1391 } 1392 1393 1394 /** MaterialShader 1395 demo: 1396 const width = 256, height = 256; 1397 const geometry = new Geometry({ 1398 aPosition: new Attribute(2, new Float32Array([ 1399 width,0, 0,0, 0,height, 1400 0,height, width,height, width,0, 1401 ])), 1402 }, width, height); 1403 1404 const material = new MaterialShader({ 1405 vertexCode: `#version 300 es 1406 in vec2 aPosition; 1407 uniform mat3 projectionMatrix; 1408 uniform mat3 modelMatrix; 1409 void main() { 1410 gl_Position.xyz = projectionMatrix * modelMatrix * vec3(aPosition, 1.0); 1411 gl_Position.w = 1.0; 1412 } 1413 `, 1414 fragmentCode: `#version 300 es 1415 precision mediump float; //highp, mediump, lowp 1416 uniform vec4 uColor; 1417 out vec4 outColor; 1418 void main() { 1419 outColor = uColor; 1420 } 1421 `, 1422 uniforms: { 1423 projectionMatrix: renderer.projectionMatrix, 1424 modelMatrix: null, //這裡夠不到模型矩陣,先佔個位 1425 uColor: [1, 0, 0, 1], 1426 }, 1427 }); 1428 1429 const shader = new Object2D(geometry, material).translate(100, 300); 1430 material.uniforms.modelMatrix = shader.modelMatrix; 1431 renderer.append(shader).redraw(); 1432 1433 1434 //這麼做太麻煩了, 看下面這個: 1435 1436 const geometry1 = new GeometryRect(256, 256); 1437 const material1 = new MaterialShader({ 1438 vertexCode: `#version 300 es 1439 in vec2 aPos; //aPosition -> aPos 頂點屬性 1440 uniform mat3 uPMat; //projectionMatrix -> uPMat 投影矩陣 1441 uniform mat3 uMat; //modelMatrix -> uMat 模型矩陣 1442 void main() { 1443 gl_Position.xyz = uPMat * uMat * vec3(aPos, 1.0); 1444 gl_Position.w = 1.0; 1445 } 1446 `, 1447 fragmentCode: `#version 300 es 1448 precision mediump float; //highp, mediump, lowp 1449 uniform vec4 uColor; 1450 out vec4 outColor; 1451 void main() { 1452 outColor = uColor; 1453 } 1454 `, 1455 uniforms: { 1456 uColor: [1, 0, 0, 1], 1457 }, 1458 }); 1459 1460 const shader1 = new Object2D(geometry1, material1).translate(100+256+10, 300); 1461 renderer.append(shader1).redraw(); 1462 1463 //每個模型的內建變數不一樣(參考: defaultShaderCode), 這裡只針對 Object2D; 1464 //Object2D 內建了1個屬性, Geometry.attributes: {aPos: Attribute} 1465 //Object2D 內建了4個全域性屬性, .uniforms: {uPMat: [3x3], uMat: [3x3], uSize: [0, 0], uImage: Texture} 1466 //如果定義的著色器程式碼沒有使用這些內建屬性,渲染器在初始化它們時將自動丟棄掉 1467 */ 1468 class MaterialShader extends MUS { 1469 1470 constructor(option = {vertexCode: "", fragmentCode: "", uniforms: {uTime: 0}}) { 1471 super(); 1472 this.vertexCode = option.vertexCode; 1473 this.fragmentCode = option.fragmentCode; 1474 this.uniforms = option.uniforms; 1475 } 1476 1477 } 1478 1479 1480 /** Object2D 1481 demo: 1482 const geometry = new GeometryRect(renderer.domElement.width, renderer.domElement.height); 1483 const texture = new Texture(images[0], FormatRGB); 1484 const material = new Material(texture, false); 1485 const background = new Object2D(geometry, material); 1486 renderer.append(background).redraw(); 1487 */ 1488 class Object2D { 1489 1490 #geometry = null; 1491 get geometry() { 1492 return this.#geometry; 1493 } 1494 1495 #material = null; 1496 get material() { 1497 return this.#material; 1498 } 1499 1500 #mat3A = []; 1501 get modelMatrix() {return this.#mat3A;} 1502 1503 #mat3 = null; 1504 #bbox = new Box(); 1505 #cx = 0; 1506 #cy = 0; 1507 get x() {return this.#bbox.x;} 1508 get y() {return this.#bbox.y;} 1509 1510 /** 1511 * 渲染器的常規成員 (複用它們: Geometry, Material, Texture, 如果這麼做那麼它們大部分東西都是共享的包括GPU上的快取) 1512 * @param {Geometry} geometry 1513 * @param {Material} material 1514 */ 1515 constructor(geometry, material) { 1516 this.#geometry = geometry || null; 1517 this.#material = material || null; 1518 1519 this.#bbox.size(geometry.width, geometry.height); 1520 this.#mat3 = new Matrix3(this.#mat3A).makeTranslation(0, 0); 1521 this.#cx = this.#bbox.w / 2; 1522 this.#cy = this.#bbox.h / 2; 1523 } 1524 1525 translate(x, y) { 1526 this.#bbox.x += x; 1527 this.#bbox.y += y; 1528 this.#cx = this.#bbox.x + this.#bbox.w / 2; 1529 this.#cy = this.#bbox.y + this.#bbox.h / 2; 1530 this.#mat3.translate(x, y); 1531 return this; 1532 } 1533 1534 rotate(x) { 1535 this.#mat3.translate(-this.#cx, -this.#cy) 1536 .rotate(x).translate(this.#cx, this.#cy); 1537 return this; 1538 } 1539 1540 scale(x, y) { 1541 this.#mat3.translate(-this.#cx, -this.#cy) 1542 .scale(x, y).translate(this.#cx, this.#cy); 1543 return this; 1544 } 1545 1546 containsPoint(x, y) { 1547 return this.#bbox.containsPoint(x, y); 1548 } 1549 1550 } 1551 1552 1553 /** Sprite 1554 demo: 1555 const geometry = new GeometryRect(renderer.domElement.width, renderer.domElement.height); 1556 const texture = new Texture(images[3], FormatRGB); 1557 const material = new Material(texture, false); 1558 const sprite = new Sprite(geometry, material, 8, 0); 1559 renderer.append(sprite).redraw(); 1560 setInterval(() => { 1561 sprite.offset += 1; //sprite.offset += 0.1; 1562 renderer.redraw(); 1563 }, 600); 1564 */ 1565 class Sprite extends Object2D { 1566 1567 #len = 1; 1568 get len() {return this.#len;} 1569 1570 #offset = 0; 1571 get offset() {return this.#offset;} 1572 set offset(v) {this.#offset = v % this.#len;} 1573 1574 /** 1575 * 雪碧圖, 精靈 (暫只支援 x * 1 的雪碧圖), 設定它 .offset 實時生效! 1576 * @param {Geometry} geometry 1577 * @param {Material} material 1578 * @param {number} len //雪碧圖x軸長度(圖片的寬 / 每一格的寬) 1579 * @param {number} offset //雪碧圖x軸位置, 浮點值, 0.0 至 len - 1 個為一個迴圈(offset % len) 1580 */ 1581 constructor(geometry, material, len, offset = 0.0) { 1582 super(geometry, material); 1583 this.#len = Math.floor(Math.max(this.#len, len)); 1584 this.offset = offset; 1585 } 1586 1587 } 1588 1589 1590 /** Instanced 1591 demo: 1592 const instanced = new Instanced(geos[2], mats[2], 5).translate(100, 100); 1593 for(let i = 0; i < instanced.len; i++){ //設定每一個例項的旋轉 1594 instanced.rotateI(i, i / instanced.len); 1595 } 1596 1597 renderer.append(instanced).redraw(); 1598 */ 1599 class Instanced extends Object2D { 1600 1601 #frequentUpdate = false; 1602 get frequentUpdate() {return this.#frequentUpdate;} 1603 1604 #needupdate = null; 1605 #needupdateI = []; 1606 get needupdateI() {return this.#needupdateI;} 1607 get needupdate() {return this.#needupdate;} 1608 1609 #len = 0; 1610 get len() {return this.#len;} 1611 1612 #matrixData = null; 1613 get matrixData() {return this.#matrixData;} 1614 1615 #matrices = null; 1616 #matricesA = null; 1617 get matricesA() {return this.#matricesA;} 1618 1619 #bboxs = null; 1620 1621 /** 1622 * Object2D 的例項化版本 1623 * @param {Geometry} geometry 1624 * @param {Material} material 1625 * @param {number} len //例項的長度 1626 * @param {boolean} frequentUpdate //是否經常更新變換矩陣, 預設 false; (決定了變換矩陣資料在著色器中的快取型別) 1627 */ 1628 constructor(geometry, material, len, frequentUpdate) { 1629 super(geometry, material); 1630 1631 len = Math.max(1, Math.floor(len)); 1632 1633 const matrixLen = 3 * 3, sizeByte = matrixLen * 4; 1634 this.#matrixData = new Float32Array(len * matrixLen); 1635 this.#matrices = new Array(len); 1636 this.#matricesA = new Array(len); 1637 1638 this.#needupdate = new Array(len); 1639 this.#bboxs = new Array(len); 1640 const cx = geometry.width / 2, cy = geometry.height / 2; 1641 1642 for(let i = 0, val; i < len; i++){ 1643 val = new Float32Array(this.#matrixData.buffer, i * sizeByte, matrixLen); 1644 this.#matricesA[i] = val; 1645 this.#matrices[i] = new Matrix3(val).makeTranslation(0, 0); 1646 this.#needupdate[i] = false; 1647 this.#bboxs[i] = new Box(0, 0, cx, cy); 1648 } 1649 1650 this.#len = len; 1651 this.#frequentUpdate = typeof frequentUpdate === "boolean" ? frequentUpdate : false; 1652 } 1653 1654 translateI(i, x, y) { 1655 const bboxs = this.#bboxs[i]; 1656 bboxs.x += x; 1657 bboxs.y += y; 1658 bboxs.w = this.x + bboxs.x + this.geometry.width / 2; 1659 bboxs.h = this.y + bboxs.y + this.geometry.height / 2; 1660 this.#matrices[i].translate(x, y); 1661 if(this.#needupdate[i] === false){ 1662 this.#needupdate[i] = true; 1663 this.#needupdateI.push(i); 1664 } 1665 return this; 1666 } 1667 1668 rotateI(i, x) { 1669 const bboxs = this.#bboxs[i]; 1670 this.#matrices[i].translate(-bboxs.w, -bboxs.h) 1671 .rotate(x).translate(bboxs.w, bboxs.h); 1672 if(this.#needupdate[i] === false){ 1673 this.#needupdate[i] = true; 1674 this.#needupdateI.push(i); 1675 } 1676 return this; 1677 } 1678 1679 scaleI(i, x, y) { 1680 const bboxs = this.#bboxs[i]; 1681 this.#matrices[i].translate(-bboxs.w, -bboxs.h) 1682 .scale(x, y)(bboxs.w, bboxs.h); 1683 if(this.#needupdate[i] === false){ 1684 this.#needupdate[i] = true; 1685 this.#needupdateI.push(i); 1686 } 1687 return this; 1688 } 1689 1690 containsPointI(x, y) { 1691 for(let i = this.#len; i <= 0; i++){ 1692 if(this.#bboxs[i].containsPoint(x, y) === true) return i; 1693 } 1694 return -1; 1695 } 1696 1697 } 1698 1699 1700 /** InstancedPoints 1701 demo: 1702 const points = new InstancedPoints(geos[1], mats[1], 50000, false, 10); 1703 for(let i = 0; i < points.len; i++){ //每一個例項設定一個隨機位置 1704 points.translateI(i, UTILS.random(0, innerWidth - points.geometry.width), UTILS.random(0, innerHeight - points.geometry.height)); 1705 } 1706 1707 renderer.append(points).redraw(); 1708 */ 1709 class InstancedPoints extends Instanced { 1710 1711 /** 1712 * 幾乎與 Instanced 一樣, 就多了一個.pointSize 屬性, 修改此屬性實時生效! 1713 * @param {Geometry} geometry 1714 * @param {Material} material 1715 * @param {number} len 1716 * @param {boolean} frequentUpdate 1717 * @param {number} pointSize //每一個點的大小(可以是浮點數) 1718 */ 1719 constructor(geometry, material, len, frequentUpdate, pointSize) { 1720 super(geometry, material, len, frequentUpdate); 1721 this.pointSize = pointSize; 1722 } 1723 1724 } 1725 1726 1727 /** InstancedSprite 1728 demo: 1729 const geometry = new GeometryRect(renderer.domElement.width, renderer.domElement.height); 1730 const texture = new Texture(images[3], FormatRGB); 1731 const material = new Material(texture, false); 1732 const sprite = new InstancedSprite(geometry, material, 5, false, 8); 1733 for(let i = 0; i < sprite.len; i++){ //每一個例項設定一個隨機位置 1734 sprite.translateI(i, UTILS.random(0, innerWidth - sprite.geometry.width), UTILS.random(0, innerHeight - sprite.geometry.height)); 1735 } 1736 renderer.append(sprite).redraw(); 1737 setInterval(() => { 1738 sprite.offset += 1; //sprite.offset += 0.1; 1739 renderer.redraw(); 1740 }, 600); 1741 */ 1742 class InstancedSprite extends Instanced { 1743 1744 #len1 = 1; 1745 get len1() {return this.#len1;} 1746 1747 #offset = 0; 1748 get offset() {return this.#offset;} 1749 set offset(v) {this.#offset = v % this.#len1;} 1750 1751 /** 1752 * Sprite 雪碧圖的例項化版本, 設定它 .offset 實時生效! 1753 * @param {Geometry} geometry 1754 * @param {Material} material 1755 * @param {number} len 1756 * @param {boolean} frequentUpdate 1757 * @param {number} len1 //雪碧圖x軸長度(圖片的寬 / 每一格的寬) 1758 * @param {number} offset //雪碧圖x軸位置, 浮點值, 0.0 至 len1 - 1 個為一個迴圈(offset % len1) 1759 */ 1760 constructor(geometry, material, len, frequentUpdate, len1, offset = 0.0) { 1761 super(geometry, material, len, frequentUpdate); 1762 this.#len1 = Math.floor(Math.max(this.#len1, len1)); 1763 this.offset = offset; 1764 } 1765 1766 } 1767 1768 1769 1770 //此類對外部是完全隱藏, 保密的, 只由渲染器直接呼叫; 1771 class Cache { 1772 1773 constructor(proName, pro, mode) { 1774 this.proName = proName; 1775 this.pro = pro; 1776 this.mode = mode; 1777 this.geo = null; 1778 this.mat = null; 1779 this.texLocs = []; 1780 this.uniforms = []; 1781 this.isselect = false; 1782 } 1783 1784 initUniforms(gl, uniforms, matHad = false, texMap = new Map()) { 1785 for(let n in uniforms){ 1786 const loc = gl.getUniformLocation(this.pro.program, n); 1787 if(loc === null) continue; 1788 1789 if(Texture.prototype.isPrototypeOf(uniforms[n]) === false){ 1790 this.uniforms.push(compileUniform(gl, loc, n, uniforms)); 1791 continue; 1792 } 1793 1794 if(matHad === false){ 1795 let tex = texMap.get(uniforms[n]); 1796 if(tex === undefined){ 1797 tex = new CacheTexture(gl, this.mat.textures.length, n, uniforms); 1798 gl.activeTexture(tex.index); 1799 gl.bindTexture(gl.TEXTURE_2D, tex._texture); 1800 if(tex.texture.needupdate !== undefined) updateTexture(gl, tex.texture); 1801 texMap.set(uniforms[n], tex); 1802 } 1803 tex.len++; 1804 this.mat.textures.push(tex); 1805 //this.uniforms.push(() => gl.uniform1i(loc, i)); 1806 } 1807 } 1808 1809 if(matHad === false) this.mat.lenT = this.mat.textures.length; 1810 for(let i = 0; i < this.mat.lenT; i++){ 1811 this.texLocs[i] = gl.getUniformLocation(this.pro.program, this.mat.textures[i].name); 1812 } 1813 } 1814 1815 dispose(gl) { 1816 gl.deleteShader(this.pro.vertexShader); 1817 gl.deleteShader(this.pro.fragmentShader); 1818 gl.deleteProgram(this.pro.program); 1819 } 1820 1821 draw(gl) { 1822 if(this.geo.lenI === 0){ 1823 gl.drawArrays(this.mode, 0, this.geo.lenV); 1824 } else { 1825 gl.drawElements(this.mode, this.geo.lenI, gl[this.geo.value.type], 0); 1826 } 1827 } 1828 1829 } 1830 1831 1832 class CacheInstanced extends Cache { 1833 1834 constructor(proName, pro, mode, gl, instanced) { 1835 super(proName, pro, mode); 1836 1837 this.matrixLoc = gl.getAttribLocation(pro.program, "uIMat"); 1838 this.matrixBuffer = gl.createBuffer(); 1839 gl.bindBuffer(gl.ARRAY_BUFFER, this.matrixBuffer); 1840 gl.bufferData(gl.ARRAY_BUFFER, instanced.matrixData, gl[instanced.frequentUpdate === false ? "STATIC_DRAW" : "DYNAMIC_DRAW"]); 1841 1842 instanced.needupdate.fill(false); 1843 instanced.needupdateI.length = 0; 1844 1845 this.sizeByte = 3 * 3 * 4; 1846 this.instanced = instanced; 1847 } 1848 1849 dispose(gl) { 1850 gl.deleteBuffer(this.matrixBuffer); 1851 return super.dispose(gl); 1852 } 1853 1854 draw(gl) { 1855 gl.bindBuffer(gl.ARRAY_BUFFER, this.matrixBuffer); 1856 1857 const instanced = this.instanced; 1858 var n, v = instanced.needupdateI.length; 1859 1860 if(v !== 0){ //需要上傳矩陣 1861 if(v !== instanced.len){ //個別矩陣更新了 1862 let i = 0; 1863 for(n = 0; n < v; n++){ 1864 i = instanced.needupdateI[n]; 1865 instanced.needupdate[i] = false; 1866 gl.bufferSubData(gl.ARRAY_BUFFER, i * this.sizeByte, instanced.matricesA[i], 0, 9); //9 = 3 * 3 = instanced.matricesA[i].length; 1867 } 1868 } else { //所有矩陣都更新了 1869 instanced.needupdate.fill(false); 1870 gl.bufferSubData(gl.ARRAY_BUFFER, 0, instanced.matrixData); 1871 } 1872 instanced.needupdateI.length = 0; 1873 } 1874 1875 for(n = 0; n < 3; n++){ 1876 v = this.matrixLoc + n; 1877 gl.enableVertexAttribArray(v); 1878 gl.vertexAttribPointer(v, 3, gl.FLOAT, false, this.sizeByte, n * 12); //12 = 3 mat3 * 4 byte 1879 gl.vertexAttribDivisor(v, 1); 1880 } 1881 1882 if(this.geo.lenI === 0){ 1883 gl.drawArraysInstanced(this.mode, 0, this.geo.lenV, instanced.len); 1884 } else { 1885 gl.drawElementsInstanced(this.mode, this.geo.lenI, gl[this.geo.value.type], 0, instanced.len); 1886 } 1887 } 1888 1889 } 1890 1891 1892 class CacheGeometry { 1893 1894 constructor(gl, pro, geo) { 1895 this.lenV = 0; 1896 this.attributes = {}; 1897 for(let n in geo.attributes){ 1898 const loc = gl.getAttribLocation(pro.program, n); 1899 if(loc === -1) continue; 1900 const obj = compileBuffer(gl, loc, geo.attributes[n]); 1901 this.attributes[n] = obj; 1902 if(this.lenV === 0) this.lenV = obj.value.length / obj.size; 1903 } 1904 1905 this.lenI = 0; 1906 this.indexBuffer = null; 1907 if(geo.indices !== null) { 1908 this.indexBuffer = gl.createBuffer(); 1909 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); 1910 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, geo.indices, gl.STATIC_DRAW); 1911 this.lenI = geo.indices.length; 1912 } 1913 1914 this.len = 0; 1915 this.value = geo; 1916 } 1917 1918 dispose(gl) { 1919 if(this.indexBuffer !== null){ 1920 gl.deleteBuffer(this.indexBuffer); 1921 } 1922 for(let n in this.attributes){ 1923 gl.deleteVertexArray(this.attributes[n].vao); 1924 gl.deleteBuffer(this.attributes[n].buffer); 1925 } 1926 } 1927 1928 } 1929 1930 1931 class CacheMaterial { 1932 1933 constructor(mat, texs = []) { 1934 this.textures = texs; 1935 this.lenT = texs.length; 1936 this.len = 0; 1937 this.value = mat; 1938 } 1939 1940 dispose(gl) { 1941 1942 } 1943 1944 } 1945 1946 1947 class CacheTexture { 1948 1949 constructor(gl, i, n, v) { 1950 this._texture = gl.createTexture(); 1951 this.texture = v[n]; 1952 this.name = n; 1953 this.index = gl["TEXTURE"+i]; 1954 this.len = 0; 1955 } 1956 1957 dispose(gl) { 1958 gl.deleteTexture(this._texture); 1959 } 1960 1961 } 1962 1963 1964 /* Renderer WebGL2 2D渲染器 1965 constructor: 1966 空; 初始化選項都在 Renderer.contextOption .glOption 中自行修改 1967 1968 attribute: 1969 domElement: HTMLCanvasElement; //只讀 1970 projectionMatrix: Array; //只讀, 返回預設的3x3投影矩陣 1971 1972 method: 1973 append(object2d: Structure|InstancedPoints|Object2D...): this; //新增到渲染佇列並建立快取 1974 delete(object2d: Structure|InstancedPoints|Object2D...): this; //從渲染佇列刪除並釋放快取 1975 dispose(): undefined; //銷燬 WebGL2RenderingContext, 銷燬後此類無法在繼續使用 1976 setSize(width, height): undefined; //設定了: 畫布寬高, gl的視口, 投影矩陣; 1977 select(x, y: number): Object2D|null; //根據一個點來獲取 Object2D 1978 redraw(): undefined; //重繪畫布 1979 readPixels(box: Box, result: ImageSource): ImageSource; //擷取畫素; box.x,box.y: 距畫布左上角的偏移量; result: 如果未定義則會建立一個新的 ImageSource; 1980 1981 demo: 1982 const renderer = new Renderer(); 1983 renderer.setSize(innerWidth, innerHeight); 1984 document.body.appendChild(renderer.domElement); 1985 1986 const geos = [ 1987 new GeometryRect(256, 256), 1988 new GeometryCircle(125), 1989 new GeometryRectWavy(256, 256), 1990 ]; 1991 1992 const mats = [ 1993 new Material(new Texture(images[0], FormatRGB), false), 1994 new Material(new Texture(images[1], FormatRGBA), true), 1995 new Material(new Texture(images[2], FormatRGB), false), 1996 ]; 1997 1998 renderer.domElement.addEventListener("click", e => { 1999 const geo = geos[Math.floor(UTILS.random(0, geos.length))]; //隨機複用幾何體 2000 const mat = mats[Math.floor(UTILS.random(0, mats.length))]; //隨機複用材質 2001 const obj2d = new Object2D(geo, mat).translate(e.offsetX, e.offsetY); 2002 2003 renderer.append(obj2d).redraw(); 2004 setTimeout(() => renderer.delete(obj2d).redraw(), UTILS.random(5000, 15000)); 2005 }); 2006 2007 2008 //擷取畫素示例.readPixels(): 2009 //Renderer.contextOption.preserveDrawingBuffer 必須為 true, 預設為false(開啟可能會影響繪製效能), 否則結果會使一個黑圖 2010 renderer.domElement.addEventListener("click", e => { 2011 const geo = new GeometryRect(256, 256); 2012 geo.translate(e.offsetX, e.offsetY); 2013 2014 const source = renderer.readPixels(new Box(e.offsetX, e.offsetY, 256, 256)); 2015 const mat = new Material(source); 2016 mat.setPixelStorei(PixelStoreiFlipY, true); //設定紋理預處理: 翻轉畫素y; 2017 2018 renderer.append(new Object2D(geo, mat)).redraw(); 2019 }); 2020 2021 2022 //畫布截圖 2023 //renderer.domElement.toBlob(ElementUtils.downloadFile, "image/png"); 2024 */ 2025 class Renderer { 2026 2027 static contextOption = { 2028 alpha: false, //畫布css的背景啟用阿爾法 預設 true 2029 antialias: true, //抗鋸齒 預設 true 2030 depth: false, //深度緩衝 預設 true 2031 desynchronized: true, //從事件迴圈中取消畫布繪製週期的同步來減少延遲 2032 stencil: false, //模板緩衝 預設 false 2033 premultipliedAlpha: true, //預乘阿爾法通道 預設 true 2034 preserveDrawingBuffer: false, //true: 保留繪圖緩衝區 預設 false 2035 failIfMajorPerformanceCaveat: true, //指示在系統效能較低時是否建立上下文 2036 2037 powerPreference: "default", //指示哪種GPU配置適合於WebGL上下文。 2038 //可能的值是: 2039 //"default" 讓使用者代理決定哪個GPU配置最適合。這是預設值。 2040 //"high-performance" 將渲染效能優先於功耗。 2041 //"low-power" 將節能優先於渲染效能。 2042 2043 xrCompatible: false, 2044 } 2045 2046 static glOption = { 2047 clearColor: {r: 0.45, g: 0.45, b: 0.45, a: 1}, //gl的背景顏色(在畫布css顏色之上) 2048 } 2049 2050 #contextAttributes = {}; 2051 #gl = createWebGL2(); 2052 #pro = null; 2053 2054 #states = { 2055 pro: null, 2056 blendE: false, 2057 blendC: {r: -1, g: 0, b: 0, a: 0}, 2058 blendES: [-1, 0], 2059 blendFS: [-1, 0, 0, 0], 2060 } 2061 2062 #geometries = new Map(); 2063 #materials = new Map(); 2064 #textures = new Map(); 2065 #objects = []; 2066 #caches = []; 2067 2068 #projectionMatrix = new Matrix3(); 2069 #projectionMatrixA = []; 2070 get projectionMatrix() { 2071 return this.#projectionMatrixA; //#projectionMatrix.toArray(); 2072 } 2073 2074 get lengthObject() {return this.#objects.length;} 2075 get lengthGeometry() {return this.#geometries.size;} 2076 get lengthMaterial() {return this.#materials.size;} 2077 get lengthTexture() {return this.#textures.size;} 2078 2079 get domElement() {return this.#gl.canvas;} 2080 2081 constructor() { 2082 Object.assign(this.#contextAttributes, this.#gl.getContextAttributes()); 2083 this.#pro = { 2084 texture1: createProgram(this.#gl, defaultShaderCode.texture1.vertex, defaultShaderCode.texture1.fragment), 2085 texture1_sprite: createProgram(this.#gl, defaultShaderCode.texture1_sprite.vertex, defaultShaderCode.texture1_sprite.fragment), 2086 texture1_Instanced: createProgram(this.#gl, defaultShaderCode.texture1_Instanced.vertex, defaultShaderCode.texture1_Instanced.fragment), 2087 texture1_Instanced_points: createProgram(this.#gl, defaultShaderCode.texture1_Instanced_points.vertex, defaultShaderCode.texture1_Instanced_points.fragment), 2088 texture1_Instanced_sprite: createProgram(this.#gl, defaultShaderCode.texture1_Instanced_sprite.vertex, defaultShaderCode.texture1_Instanced_sprite.fragment), 2089 } 2090 initExtensions(this.#gl); 2091 } 2092 2093 /** 2094 * @param {Object2D|Sprite|Instanced|InstancedPoints|InstancedSprite} object2d 2095 * @returns {this} 2096 */ 2097 append(object2d) { 2098 if(arguments.length === 1){ 2099 if(this.#objects.includes(object2d) === true) return this; 2100 2101 const obj = proHandler(this.#gl, this.#pro, this, object2d), 2102 cache = obj.cache; 2103 if(obj === null) return this; 2104 2105 // 2106 cache.geo = this.#geometries.get(object2d.geometry); 2107 if(cache.geo === undefined){ 2108 cache.geo = new CacheGeometry(this.#gl, cache.pro, object2d.geometry); 2109 this.#geometries.set(object2d.geometry, cache.geo); 2110 } 2111 cache.geo.len++; 2112 2113 // 2114 cache.mat = this.#materials.get(object2d.material); 2115 const matHad = cache.mat !== undefined; 2116 if(matHad === false){ 2117 cache.mat = new CacheMaterial(object2d.material); 2118 if(object2d.material.needupdate !== undefined) this.#materials.set(object2d.material, cache.mat); 2119 } else { 2120 for(let i = 0; i < cache.mat.textures.length; i++) cache.mat.textures[i].len++; 2121 } 2122 cache.mat.len++; 2123 2124 // 2125 cache.initUniforms(this.#gl, obj.uniforms, matHad, this.#textures); 2126 this.#caches.push(cache); 2127 this.#objects.push(object2d); 2128 return this; 2129 } 2130 2131 for(let i = 0, arg = arguments; i < arg.length; i++) this.append(arg[i]); 2132 2133 return this; 2134 } 2135 2136 /** 2137 * @param {Object2D|Sprite|Instanced|InstancedPoints|InstancedSprite} object2d 2138 * @returns {this} 2139 */ 2140 delete(object2d) { 2141 if(arguments.length === 1){ 2142 const i = this.#objects.indexOf(object2d); 2143 if(i === -1) return this; 2144 2145 const gl = this.#gl, cache = this.#caches[i]; 2146 2147 this.#objects.splice(i, 1); 2148 this.#caches.splice(i, 1); 2149 2150 if(!cache.proName || this.#pro[cache.proName] !== cache.pro){ 2151 cache.dispose(gl); //是自定義的著色器 2152 } 2153 2154 cache.geo.len--; 2155 if(cache.geo.len === 0){ 2156 cache.geo.dispose(gl); 2157 this.#geometries.delete(object2d.geometry); 2158 } 2159 2160 cache.mat.len--; 2161 if(cache.mat.len === 0){ 2162 cache.mat.dispose(gl); 2163 this.#materials.delete(object2d.material); 2164 } 2165 2166 for(let i = 0, tex; i < cache.mat.textures.length; i++){ 2167 tex = cache.mat.textures[i]; 2168 tex.len--; 2169 if(tex.len === 0){ 2170 tex.dispose(gl); 2171 this.#textures.delete(tex.texture); 2172 } 2173 } 2174 2175 return this; 2176 } 2177 2178 for(let i = 0, arg = arguments; i < arg.length; i++) this.delete(arg[i]); 2179 2180 return this; 2181 } 2182 2183 redraw() { 2184 const len = this.#caches.length; 2185 if(len === 0) return; 2186 2187 const gl = this.#gl, states = this.#states; 2188 //gl.clear(gl.COLOR_BUFFER_BIT); 2189 2190 for(let i = 0, n, t, v, g, m; i < len; i++){ 2191 v = this.#caches[i]; 2192 2193 if(states.pro !== v.pro){ 2194 states.pro = v.pro; 2195 gl.useProgram(states.pro.program); 2196 } 2197 2198 if(g !== v.geo){ 2199 g = v.geo; 2200 for(n in g.attributes) gl.bindVertexArray(g.attributes[n].vao); 2201 if(g.lenI !== 0) gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, g.indexBuffer); 2202 } 2203 2204 if(m !== v.mat || m.value.needupdate === true){ 2205 m = v.mat; 2206 2207 if(states.blendE !== m.value.blendEnable){ 2208 states.blendE = m.value.blendEnable; 2209 gl[states.blendE === true ? "enable" : "disable"](gl.BLEND); 2210 } 2211 2212 if(states.blendE === true){ 2213 if(states.blendC.r !== m.value.blendC.r || 2214 states.blendC.g !== m.value.blendC.g || 2215 states.blendC.b !== m.value.blendC.b || 2216 states.blendC.a !== m.value.blendC.a){ 2217 Object.assign(states.blendC, m.value.blendC); 2218 gl.blendColor(states.blendC.r, states.blendC.g, states.blendC.b, states.blendC.a); 2219 } 2220 2221 if(states.blendES[0] !== m.value.blendES[0] || 2222 states.blendES[1] !== m.value.blendES[1]){ 2223 Object.assign(states.blendES, m.value.blendES); 2224 if(states.blendES[0] !== -1){ 2225 if(states.blendES[1] === -1){ 2226 gl.blendEquation(gl[MUS.blendESs[states.blendES[0]]]); 2227 } else { 2228 gl.blendEquationSeparate(gl[MUS.blendESs[states.blendES[0]]], gl[MUS.blendESs[states.blendES[1]]]); 2229 } 2230 } 2231 } 2232 2233 if(states.blendFS[0] !== m.value.blendFS[0] || 2234 states.blendFS[1] !== m.value.blendFS[1] || 2235 states.blendFS[2] !== m.value.blendFS[2] || 2236 states.blendFS[3] !== m.value.blendFS[3]){ 2237 Object.assign(states.blendFS, m.value.blendFS); 2238 if(states.blendFS[0] !== -1){ 2239 if(states.blendFS[2] === -1){ 2240 gl.blendFunc(gl[MUS.blendFSs[states.blendFS[0]]], gl[MUS.blendFSs[states.blendFS[1]]]); 2241 } else { 2242 gl.blendFuncSeparate(gl[MUS.blendFSs[states.blendFS[0]]], gl[MUS.blendFSs[states.blendFS[1]]], gl[MUS.blendFSs[states.blendFS[2]]], gl[MUS.blendFSs[states.blendFS[3]]]); 2243 } 2244 } 2245 } 2246 } 2247 2248 for(n = 0; n < m.lenT; n++){ 2249 t = m.textures[n]; 2250 gl.activeTexture(t.index); 2251 gl.bindTexture(gl.TEXTURE_2D, t._texture); 2252 if(t.texture.needupdate === true) updateTexture(gl, t.texture); 2253 gl.uniform1i(v.texLocs[n], n); 2254 } 2255 } else { 2256 2257 for(n = 0; n < m.lenT; n++){ 2258 gl.uniform1i(v.texLocs[n], n); //這玩意每個物件都會建立一個新的(即使材質或紋理都一樣,因為它指向的是自己所用的 program) 2259 } 2260 2261 } 2262 2263 t = v.uniforms.length; 2264 for(n = 0; n < t; n++) v.uniforms[n](); 2265 2266 v.draw(gl); 2267 //resetBuffers(gl); 2268 } 2269 } 2270 2271 dispose() { 2272 this.#geometries.clear(); 2273 this.#materials.clear(); 2274 this.#textures.clear(); 2275 if(this.#gl.isContextLost() === false){ 2276 this.#gl.loseContext(); 2277 } 2278 this.#gl = null; 2279 } 2280 2281 setSize(width = innerWidth, height = innerHeight) { 2282 width *= window.devicePixelRatio; 2283 height *= window.devicePixelRatio; 2284 this.domElement.width = width; 2285 this.domElement.height = height; 2286 this.#gl.viewport(0, 0, width, height); 2287 this.#projectionMatrix.projection(width, height); 2288 this.#projectionMatrix.toArray(this.#projectionMatrixA); 2289 } 2290 2291 select(x = 0, y = 0, targets = this.#objects) { 2292 const len = targets.length; 2293 for(let i = len, obj2d; i >= 0; i--){ 2294 obj2d = targets[i]; 2295 if(this.#caches[i].isselect === true && obj2d.geometry.containsPoint(x, y) === true) return obj2d; 2296 } 2297 return null; 2298 } 2299 2300 readPixels(box = new Box(0, 0, 100, 100), result = new ImageSource(box.w, box.h)) { 2301 const y = (1 - box.y / this.domElement.height) * this.domElement.height - box.h; 2302 this.#gl.readPixels(box.x, y, box.w, box.h, this.#gl.RGBA, this.#gl.UNSIGNED_BYTE, result.data); 2303 return result; 2304 } 2305 2306 } 2307 2308 2309 export { 2310 defaultShaderCode, 2311 2312 BlendEquationAdd, 2313 2314 BlendDefault, 2315 BlendAdd, 2316 BlendSub, 2317 BlendMultiply, 2318 2319 FormatAlpha, 2320 FormatLuminance, 2321 FormatLuminanceAlpha, 2322 FormatRGB, 2323 FormatRGBA, 2324 2325 PixelStoreiFlipY, 2326 PixelStoreiPremultiplyAlpht, 2327 2328 Attribute, 2329 Geometry, 2330 GeometryRect, 2331 GeometryCircle, 2332 GeometryShape, 2333 GeometryRectWavy, 2334 2335 ImageSource, 2336 Texture, 2337 Material, 2338 MaterialShader, 2339 2340 Object2D, 2341 Sprite, 2342 Instanced, 2343 InstancedPoints, 2344 InstancedSprite, 2345 2346 Renderer, 2347 }
使用栗子:
1 import { UTILS, AnimateLoop, TweenCache } from './lib/Utils.js'; 2 import { CanvasImageText } from './lib/ElementUtils.js'; 3 import { 4 FormatRGB, 5 FormatRGBA, 6 7 Attribute, 8 Geometry, 9 GeometryRect, 10 GeometryCircle, 11 GeometryShape, 12 GeometryRectWavy, 13 14 ImageSource, 15 Texture, 16 Material, 17 MaterialShader, 18 19 Object2D, 20 Sprite, 21 Instanced, 22 InstancedPoints, 23 InstancedSprite, 24 25 Renderer, 26 } from './lib/Two.js'; 27 28 29 //完整原始碼分享網址: https://share.weiyun.com/84cLfjst 30 31 32 const images = [ 33 "./img/girls.jpg", 34 "./img/ball.png", 35 "./img/water.jpg", 36 "./img/spriteX8.png", 37 ] 38 39 { //init images 40 let len = 0; 41 for(let i = 0; i < images.length; i++) { 42 const image = new Image(); 43 image.onload = () => { 44 if(len++ === images.length - 1) main(); 45 } 46 image.src = images[i]; 47 images[i] = image; 48 } 49 } 50 51 function main() { 52 //初始化渲染器 53 const renderer = new Renderer(); 54 renderer.setSize(); 55 document.body.appendChild(renderer.domElement); 56 console.log(renderer); 57 58 59 //共享的元件 60 const geos = [ 61 new GeometryRect(256, 256), 62 new GeometryCircle(125), 63 new GeometryRectWavy(256, 256), 64 ]; 65 const texs = [ 66 new Texture(images[0], FormatRGB), 67 new Texture(images[1], FormatRGBA), 68 new Texture(images[2], FormatRGB), 69 ]; 70 const mats = [ 71 new Material(texs[0], false), 72 new Material(texs[1], true), 73 new Material(texs[2], false), 74 ]; 75 76 77 const rotates = []; 78 renderer.domElement.addEventListener("click", e => { 79 const geo = geos[Math.floor(UTILS.random(0, geos.length))]; 80 const mat = mats[Math.floor(UTILS.random(0, mats.length))]; 81 82 const obj2d = new Object2D(geo, mat).translate(e.offsetX, e.offsetY); 83 renderer.append(obj2d); 84 rotates.push(obj2d); 85 86 setTimeout(() => { 87 renderer.delete(obj2d); 88 rotates.splice(rotates.indexOf(obj2d), 1); 89 }, UTILS.random(5000, 15000)); 90 }); 91 92 93 //建立背景和標題 94 const background = new Object2D(new GeometryRect(innerWidth, innerHeight), new Material(new Texture(images[0], FormatRGB), false)); 95 const imageTitle = new CanvasImageText().setFont(40).size(innerWidth, 50).fillText("點選畫布隨機生成 Object2D !", "green").image; 96 const title = new Object2D(new GeometryRect(imageTitle.width, imageTitle.height), new Material(new Texture(imageTitle, FormatRGBA), true)); 97 title.translate(0, 10); 98 99 100 //Object2D 的例項化版本 101 const instanced = new Instanced(geos[2], mats[2], 5).translate(100, 100); 102 for(let i = 0; i < instanced.len; i++){ 103 instanced.rotateI(i, i / instanced.len); 104 } 105 106 107 //例項粒子 108 const points = new InstancedPoints(geos[1], mats[1], 100, false, 10); 109 for(let i = 0; i < points.len; i++){ 110 points.translateI(i, UTILS.random(0, innerWidth - points.geometry.width), UTILS.random(0, innerHeight - points.geometry.height)); 111 } 112 113 114 //例項雪碧圖 115 116 117 //雪碧圖 118 const sprite = new Sprite(geos[2], new Material(new Texture(images[3])), 8).translate(100+256+10, 100); 119 const tween = new TweenCache({x: 0}, {x: 1}, 300, () => { //每 300 毫秒更換一次 120 sprite.offset += 1; 121 tween.reverse(); 122 tween.start(); 123 }, true); 124 125 126 renderer.append(background, instanced, points, sprite, title); 127 128 function loop() { 129 for(let i = 0, len = rotates.length; i < len; i++) rotates[i].rotate(i / len); 130 tween.update(); //sprite.offset += 1; //sprite.offset += 0.1; 131 renderer.redraw(); 132 } 133 134 new AnimateLoop(loop).play(); 135 } 136 137
//完整原始碼分享網址: https://share.weiyun.com/84cLfjst
結果圖:
未完 持續更新中...