test 2D渲染器 WebGL WebGL2

cnblogs 2024-07-31 08:11:02 阅读 92

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 }

Renderer Code

使用栗子:

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

结果图:

未完 持续更新中...



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。