env.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import envFragSource from "./shader-env.frag?raw";
  2. import envCubeFragSource from "./shader-env-cube.frag?raw";
  3. import envVertSource from "./shader-env.vert?raw";
  4. import { loadImage } from "@/util";
  5. import { createFPSCamera, createProgram, generateVao, useTex } from "@/util/gl";
  6. import { mat4, glMatrix } from "gl-matrix";
  7. import { setUniforms } from "./setUniform";
  8. const generatePreset = (gl: WebGL2RenderingContext) => {
  9. const skyCubeTex = gl.createTexture();
  10. const skyCubeTex1 = gl.createTexture();
  11. const updateSky1 = (images: HTMLImageElement[]) => {
  12. gl.bindTexture(gl.TEXTURE_CUBE_MAP, skyCubeTex1);
  13. const mapper = [2, 4, 0, 5, 1, 3];
  14. for (let i = 0; i < 6; i++) {
  15. gl.texImage2D(
  16. gl.TEXTURE_CUBE_MAP_POSITIVE_X + i,
  17. 0,
  18. gl.RGB,
  19. gl.RGB,
  20. gl.UNSIGNED_BYTE,
  21. images[mapper[i]]
  22. );
  23. }
  24. gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  25. gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  26. gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE);
  27. gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  28. gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  29. gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
  30. gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
  31. };
  32. const updateSky = (image: HTMLImageElement) => {
  33. gl.bindTexture(gl.TEXTURE_2D, skyCubeTex);
  34. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
  35. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  36. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  37. gl.bindTexture(gl.TEXTURE_2D, null);
  38. };
  39. return {
  40. skyCubeTex,
  41. skyCubeTex1,
  42. // async preset(urls: string[]) {
  43. // const images = await Promise.all(urls.map(loadImage));
  44. // updateSky(images);
  45. // },
  46. async preset(url: string | string[]) {
  47. if (Array.isArray(url)) {
  48. const images = await Promise.all(url.map(loadImage));
  49. updateSky1(images);
  50. } else {
  51. const image = await loadImage(url);
  52. updateSky(image);
  53. }
  54. },
  55. };
  56. };
  57. const getDrawVaring = (gl: WebGL2RenderingContext) => {
  58. const positions = new Float32Array([
  59. -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1,
  60. ]);
  61. const vao = generateVao(gl, { positions }, [
  62. {
  63. loc: 0,
  64. key: "positions",
  65. size: 2,
  66. type: gl.FLOAT,
  67. stride: 0,
  68. offset: 0,
  69. },
  70. ]);
  71. return {
  72. ...generatePreset(gl),
  73. vao,
  74. numArrays: positions.length / 2,
  75. };
  76. };
  77. export const init = (canvas: HTMLCanvasElement) => {
  78. let activeTex: "skyCubeTex1" | "skyCubeTex" = "skyCubeTex1";
  79. const gl = canvas.getContext("webgl2", { preserveDrawingBuffer: true })!;
  80. const program = createProgram(gl, envVertSource, envFragSource);
  81. const program1 = createProgram(gl, envVertSource, envCubeFragSource);
  82. const setSize = (size: number[]) => {
  83. mat4.perspective(
  84. projectionMat,
  85. glMatrix.toRadian(70),
  86. size[0] / size[1],
  87. 0.1,
  88. 100
  89. );
  90. gl.viewport(0, 0, size[0], size[1]);
  91. updateInv();
  92. redraw();
  93. };
  94. const projectionMat = mat4.create();
  95. const invProjectionViewMat = mat4.create();
  96. const viewMat = mat4.create();
  97. const varing = getDrawVaring(gl);
  98. const updateInv = () => {
  99. mat4.multiply(invProjectionViewMat, projectionMat, viewMat);
  100. mat4.invert(invProjectionViewMat, invProjectionViewMat);
  101. };
  102. const redraw = () => {
  103. gl.clear(gl.COLOR_BUFFER_BIT);
  104. gl.enable(gl.DEPTH_TEST);
  105. gl.depthFunc(gl.LEQUAL);
  106. const g = activeTex === "skyCubeTex" ? program : program1;
  107. gl.useProgram(g);
  108. gl.bindVertexArray(varing.vao);
  109. setUniforms(gl, g, {
  110. invProjectionViewMat,
  111. envTex:
  112. activeTex === "skyCubeTex1"
  113. ? useTex(gl, varing.skyCubeTex1!, gl.TEXTURE_CUBE_MAP, 1)
  114. : useTex(gl, varing.skyCubeTex!, gl.TEXTURE_2D),
  115. });
  116. gl.drawArrays(gl.TRIANGLES, 0, varing.numArrays);
  117. };
  118. setSize([canvas.width, canvas.height]);
  119. const fps = createFPSCamera(
  120. canvas.parentElement!,
  121. (nViewMat) => {
  122. mat4.copy(viewMat, nViewMat);
  123. updateInv();
  124. redraw();
  125. },
  126. [0, 1, 0],
  127. [0, 0, 0],
  128. { yaw: glMatrix.toRadian(-180) },
  129. 80
  130. );
  131. return {
  132. setSize,
  133. redraw,
  134. changeUrls(urls: string | string[]) {
  135. fps.recovery();
  136. return varing.preset(urls).then(() => {
  137. activeTex = Array.isArray(urls) ? "skyCubeTex1" : "skyCubeTex";
  138. redraw();
  139. });
  140. },
  141. destory: fps.destory,
  142. };
  143. };