webgl实现canvas三维多彩方形碎片粒子飞舞动画效果代码

代码语言:html

所属分类:粒子

代码描述:webgl实现canvas三维多彩方形碎片粒子飞舞动画效果代码

代码标签: 三维 多彩 方形 碎片 粒子 飞舞 动画 效果

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<html lang="en">
<head>

    <meta charset="UTF-8">





    <style>
        body {
            background: #66f;
            margin: 0;
            overflow: hidden;
        }
        canvas {
            height: 100vh;
            width: 100vw;
            touch-action: none;
        }
        .osc {
            left: 0px;
            position: fixed;
            top: 0px;
        }

        .button {
            position: fixed;
            z-index: 10;
            right: 0;
            bottom: 0;
        }
        .controls {
            position: fixed;
            z-index: 10;
            left: 0;
            bottom: 0;
        }
        .playpause {
            background: #AAB;
            padding: 10px;
        }
        .playpause label {
            display: block;
            box-sizing: border-box;

            width: 0;
            height: 20px;

            cursor: pointer;

            border-color: transparent transparent transparent #202020;
            transition: 100ms all ease;
            will-change: border-width;

            border-style: double;
            border-width: 0px 0 0px 20px;
        }
        .playpause input[type='checkbox'] {
            visibility: hidden;
        }
        .playpause.checked label {
            border-style: double;
            border-width: 0px 0 0px 20px;
        }
        .playpause label {
            border-style: solid;
            border-width: 10px 0 10px 20px;
        }
        /* } */
    </style>



</head>

<body>
    <script id="vertexShader_particle" type="x-shader/x-vertex">
        attribute vec3 a_position;
        attribute vec3 a_particle;
        attribute vec2 a_reference;

        uniform float u_time;
        uniform mat4 u_m_model;
        uniform mat4 u_m_view;
        uniform mat4 u_m_MVP;
        uniform mat4 u_m_proj;

        uniform sampler2D b_position;
        uniform sampler2D b_velocity;

        varying vec3 v_colour;
        varying float v_fogDepth;
        varying float v_opacity;

        float random(vec2 st) {
            return fract(sin(dot(st,
                vec2(12.9898, 78.233)))*
                43758.5453123);
        }

        vec3 hsv2rgb(vec3 c) {
            vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
            vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
            return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
        }
        float hash21(vec2 p) {
            p = fract(p * vec2(233.34, 851.74));
            p += dot(p, p + 23.45);
            return fract(p.x * p.y);
        }

        mat3 fromQuat(vec4 q) {
            float x = q.x;
            float y = q.y;
            float z = q.z;
            float w = q.w;
            float x2 = q.x*2.;
            float y2 = q.y*2.;
            float z2 = q.z*2.;

            float xx = x * x2;
            float yx = y * x2;
            float yy = y * y2;
            float zx = z * x2;
            float zy = z * y2;
            float zz = z * z2;
            float wx = w * x2;
            float wy = w * y2;
            float wz = w * z2;

            return mat3(
                1. - yy -zz, yx -wz, zx + wy,
                yx + wz, 1. - xx - zz, zy - wx,
                zx - wy, zy + wx, 1. - xx - yy
            );
        }

        /**
        * Generates a look-at matrix with the given eye position, focal point, and up axis.
        * If you want a matrix that actually makes an object look at another object, you should use targetTo instead.
        *
        * @param {mat4} out mat4 frustum matrix will be written into
        * @param {vec3} eye Position of the viewer
        * @param {vec3} center Point the viewer is looking at
        * @param {vec3} up vec3 pointing up
        * @returns {mat4} out
        */
        mat4 lookAt(vec3 e, vec3 c, vec3 u) {

            // if (Math.abs(e.x - c.x) < EPSILON &&
            //     Math.abs(e.y - c.y) < EPSILON &&
            //     Math.abs(e.z - c.z) < EPSILON) {
            //   return new Mat4();
            // }

            vec3 off = normalize(e - c);

            vec3 or = vec3(
                u.y * off.z - u.z * off.y,
                u.z * off.x - u.x * off.z,
                u.x * off.y - u.y * off.x
            );
            or = normalize(or);

            vec3 tn = vec3(
                off.y * or.z - off.z * or.y,
                off.z * or.x - off.x * or.z,
                off.x * or.y - off.y * or.x
            );
            tn = normalize(tn);

            return mat4(
                or.x,
                tn.x,
                off.x,
                0,

                or.y,
                tn.y,
                off.y,
                0,

                or.z,
                tn.z,
                off.z,
                0,

                -(or.x * e.x + or.y * e.y + or.z * e.z),
                -(tn.x * e.x + tn.y * e.y + tn.z * e.z),
                -(off.x * e.x + off.y * e.y + off.z * e.z),
                1
            );
        }
        vec3 palette(in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d) {
            return a + b*cos(6.28318*(c*t+d));
        }

        void main() {
            // vec4 pos = vec4(a_particle, 1.);
            // vec4 mvPos = u_m_view * u_m_model * pos;
            // gl_Position = u_m_proj * mvPos;
            // float isq = ( 1. / -mvPos.z );
            // gl_PointSize = (100.) * isq;

            vec3 position = texture2D(b_position, a_reference).xyz;
            vec3 velocity = texture2D(b_velocity, a_reference).xyz;

            // vec4 quat = vec4(normalize(velocity), 0.);
            // mat3 mat = fromQuat(quat);
            // vec3 particle = a_particle * vec3(1, 1.+length(velocity*velocity*.05), 1) * .1 * mat;

            float vl = min(length(velocity*velocity)*.01, 5.);
            vec3 p = a_particle * vec3(1.-vl*.2, 1, 1.+vl);
            float vl1 = smoothstep(0., 20., length(velocity));
            p *= (.3 + vl1);

            mat4 look = lookAt(
                normalize(position),
                normalize(position+velocity),
                normalize(position)
            );
            vec3 particle = (vec4(p  * .2, 1) * look).xyz;
            // vec3 particle = (vec4(a_particle.yzx*vec3(1,1,1.+length(velocity*velocity)*.01), 1) * .2 * look).xyz;

            position += particle;

            vec4 pos = vec4(position, 1.);
            float l = length(pos);

            vec4 mvPos = u_m_view * u_m_model * pos;

            v_fogDepth = mvPos.z;

            float isq = (1. / -mvPos.z);

            float b = smoothstep(0., 80., l);
            float s = clamp(b*6., 0.1, 1.);
            v_opacity = b;

            gl_Position = u_m_proj * mvPos;
            float hash = hash21(a_reference);
            v_colour = palette(
                hash*.5+.3,
                vec3(0.5, 0.5, 0.5),
                vec3(0.5, 0.5, 0.5),
                vec3(1.0, 1.0, 1.0),
                vec3(0.5, 0.3, 0.2)
            );
            // v_colour = hsv2rgb(vec3(.6 + hash * .3 + vl1 * .1, 1., hash * .5 + .3));
            // if(length(a_reference) == 0.) v_colour = vec3(1,0,0);
        }
    </script>
    <script id="vertexShader_buffer" type="x-shader/x-vertex">
        attribute vec4 a_position;

        uniform mat4 u_modelViewMatrix;
        uniform mat4 u_projectionMatrix;

        void main() {
            gl_Position = a_position;
        }
    </script>
    <script id="fragmentShader_velocity" type="x-shader/x-fragment">
        #extension GL_OES_standard_derivatives: enable
        precision highp float;

        uniform vec2 u_resolution;
        uniform vec2 u_mouse;
        uniform float u_time;
        uniform sampler2D s_noise;
        uniform int u_frame;
        uniform float u_nsize;
        uniform float u_seed;

        uniform sampler2D b_velocity;
        uniform sampler2D b_position;

        #define PI 3.141592653589793
        #define HPI 1.5707963267948966
        #define TAU 6.283185307179586
        #define G 0.67408
        mat4 rotationMatrix(vec3 axis, float angle) {
            axis = normalize(axis);
            float s = sin(angle);
            float c = cos(angle);
            float oc = 1.0 - c;

            return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0,
                oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0,
                oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0,
                0.0, 0.0, 0.0, 1.0);
        }

        vec3 hash33(vec3 p) {
            return fract(vec3(
                sin(p.x) * 43543.454354,
                sin(p.y) * 7531.154354,
                sin(p.z) * 10053.75315
            ));
        }

        void main() {
            vec2 uv = gl_FragCoord.xy / u_resolution.xy;
            vec3 position = texture2D(b_position, uv).xyz;
            vec3 velocity = texture2D(b_velocity, uv).xyz;
            vec3 acceleration = vec3(position);
            vec3 f = position;

            float tm = u_time;

            float l = length(position);
            position = (vec4(position, 1.) * rotationMatrix(vec3(sin(tm * 25.), cos(tm * 10.), sin(tm) * cos(tm * 5.)), .5 + 10. / l)).xyz;

            vec3 spherical = vec3(1./max(l, .1), atan(position.y, position.x), acos(position.z / l));

            float a = sin(length(spherical.yz) * 5. + tm) * 5.;

            acceleration.x = spherical.x * sin(spherical.z) * cos(spherical.y) * a;
            acceleration.y = spherical.x * sin(spherical.z) * sin(spherical.y) * a;
            acceleration.z = spherical.x * cos(spherical.z) * a;

            f = normalize(f - acceleration) * -1.;
            f *= smoothstep(10., 40., l) * 2.;

            vec3 vel = velocity * .99 + (acceleration + f) * .5;

            gl_FragColor = vec4(vel, 1.0);
        }
    </script>
    <script id="fragmentShader_position" type="x-shader/x-fragment">
        #extension GL_OES_standard_derivatives: enable
        precision highp float;

        uniform vec2 u_resolution;
        uniform vec2 u_mouse;
        uniform float u_time;
        uniform float u_delta;
        uniform sampler2D s_noise;
        uniform bool u_nreset;

        uniform sampler2D b_prime;
        uniform sampler2D b_velocity;
        uniform sampler2D b_position;
        uniform sampler2D b_origin;

        vec2 getScreenSpace() {
            vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution.xy) / min(u_resolution.y, u_resolution.x);

            return uv;
        }

        vec3 hash33(vec3 p) {
            return fract(vec3(
                sin(p.x) * 43543.454354,
                sin(p.y) * 7531.154354,
                sin(p.z) * 10053.75315
            ));
        }

        void main() {
            vec2 uv = getScreenSpace();
            vec2 s = gl_FragCoord.xy / u_resolution.xy;

            vec3 position = texture2D(b_position, s).xyz;
            vec3 velocity = texture2D(b_velocity, s).xyz;

            vec3 pos = position + velocity * u_delta * .5;

            if (length(pos) > 100.) {
                pos = pos / length(pos) * 2.;
            }

            gl_FragColor = vec4(pos, 1.0);
        }
    </script>
    <script id="fragmentShader_particle" type="x-shader/x-fragment">
        #extension GL_OES_standard_derivatives: enable
        precision highp float;

        uniform vec2 u_resolution;
        uniform vec2 u_mouse;
        uniform float u_time;
        uniform sampler2D s_noise;
        uniform bool u_transition;
        uniform float u_transition_val;

        uniform sampler2D b_prime;
        uniform sampler2D b_position;

        varying vec3 v_colour;
        varying float v_fogDepth;
        varying float v_opacity;

        vec2 getScreenSpace() {
            vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution.xy) / min(u_resolution.y, u_resolution.x);

            return uv;
        }

        void main() {
            float fade = smoothstep(1., 0., u_transition_val);
            gl_FragColor = vec4(mix(vec3(1), v_colour, fade), 1.);
            //     vec2 uv = gl_PointCoord.xy - .5;
            //     vec2 s = gl_FragCoord.xy / u_resolution.xy;

            //     float l = length(uv);
            //     float c = smoothstep(.5, 0., l);
            //     float fog = smoothstep(-200., -1., v_fogDepth);
            //     float opacity = c*fog;
            //     if(c < .1) discard;

            //     float fade = smoothstep(1., 0., u_transition_val);

            //     gl_FragColor = vec4(
            //       mix(
            //         vec4(1.),
            //         mix(
            //           vec4(1),
            //           vec4(v_colour, opacity),
            //           c),
            //         fade
            //       )
            //     );
        }

    </script>
    <script id="fragmentShader_blur" type="x-shader/x-fragment">
        #extension GL_OES_standard_derivatives: enable
        precision highp float;

        uniform vec2 u_resolution;
        uniform vec2 u_mouse;
        uniform float u_time;
        uniform sampler2D s_noise;

        uniform sampler2D b_prime;
        uniform sampler2D b_blur;

        varying vec3 v_colour;

        vec2 getScreenSpace() {
            vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution.xy) / min(u_resolution.y, u_resolution.x);

            return uv;
        }

        void main() {
            vec2 uv = getScreenSpace();

            vec2 s = gl_FragCoord.xy / u_resolution.xy;

            vec4 n1 = texture2D(b_blur, s);
            vec4 n = clamp(texture2D(b_prime, s), 0., 1.);

            vec4 c = n1*.5 + n*.5;

            gl_FragColor = clamp(c, 0., 1.);
        }

    </script>
    <script id="fragmentShader_bloom" type="x-shader/x-fragment">
        #extension GL_OES_standard_derivatives: enable
        precision highp float;

        uniform vec2 u_resolution;
        uniform vec2 u_mouse;
        uniform float u_time;
        uniform sampler2D s_noise;
        uniform int u_bloomstep;

        uniform sampler2D b_prime;
        uniform sampler2D b_blur;
        uniform sampler2D b_bloom;

        varying vec3 v_colour;

        vec2 getScreenSpace() {
            vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution.xy) / min(u_resolution.y, u_resolution.x);

            return uv;
        }

        vec4 tex(sampler2D tex, vec2 co) {
            return clamp(texture2D(tex, co), 0., 1.);
        }

        void main() {
            vec2 uv = getScreenSpace();

            vec2 s = gl_FragCoord.xy / u_resolution.xy;
            vec2 p = 1./u_resolution.xy;


            // vec4 n1 = texture2D(b_blur, s);
            // vec4 n = texture2D(b_prime, s);

            vec4 n1;
            vec4 c;
            vec4 n = texture2D(b_prime, s);
            vec4 n2 = n;
            if (u_bloomstep == 0) {
                n1 = tex(b_blur, s);
                n1 += tex(b_blur, s + vec2(0, p.y));
                n1 += tex(b_blur, s + vec2(0, p.y*2.));
                n1 += tex(b_blur, s + vec2(0, p.y*3.));
                n1 += tex(b_blur, s + vec2(0, p.y*-1.));
                n1 += tex(b_blur, s + vec2(0, p.y*-2.));
                n1 += tex(b_blur, s + vec2(0, p.y*-3.));
                n1 /= 7.;
                c = n1;
            } else if (u_bloomstep == 1) {
                n1 = tex(b_bloom, s);
                n1 += tex(b_bloom, s + vec2(p.x, 0.));
                n1 += tex(b_bloom, s + vec2(p.x*2., 0));
                n1 += tex(b_bloom, s + vec2(p.x*3., 0));
                n1 += tex(b_bloom, s + vec2(p.x*-1., 0));
                n1 += tex(b_bloom, s + vec2(p.x*-2., 0));
                n1 += tex(b_bloom, s + vec2(p.x*-3., 0));
                n1 /= 7.;
                c = pow(n1, vec4(2.));
            }

            gl_FragColor = clamp(n1, 0., 1.);
        }

    </script>
    <script id="fragmentShader_buffer" type="x-shader/x-fragment">
        #extension GL_OES_standard_derivatives: enable
        precision highp float;

        uniform vec2 u_resolution;
        uniform vec2 u_mouse;
        uniform float u_time;
        uniform sampler2D s_noise;
        uniform bool u_transition;
        uniform float u_transition_val;

        uniform sampler2D b_prime;
        uniform sampler2D b_blur;
        uniform sampler2D b_bloom;

        varying vec3 v_colour;

        vec2 getScreenSpace() {
            vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution.xy) / min(u_resolution.y, u_resolution.x);

            return uv;
        }

        void main() {
            vec2 uv = getScreenSpace();

            vec2 s = gl_FragCoord.xy / u_resolution.xy;

            vec2 p = .5/u_resolution.xy;

            // vec4 bloom = texture2D(b_bloom, s);
            vec4 n = texture2D(b_blur, s);

            float fade = smoothstep(1., 0., u_transition_val);

            gl_FragColor = vec4(
                mix(
                    vec3(1),
                    n.rgb, fade) * (1. + (1.-fade)), 1.);
        }

    </script>
    <div class="controls">
        <div class="playpause checked">
            <label>
                <input checked="checked" type="checkbox" value="None" id="playpause" name="check">
            </label>
        </div>
    </div>


    <script type="module">
      import { Vec2, Vec3, Mat2, Mat3, Mat4, Quat } from 'https://cdn.skypack.dev/wtc-math';

import gifJs from 'https://cdn.skypack.dev/gif.js';

console.clear();

// Determine whether a number is a power of 2
function powerOf2(v) {
  return v && !(v & (v - 1));
}
// Return the next greatest power of 2
function nextPow2( v ) {
  v--;
  v |= v >> 1;
  v |= v >> 2;
  v |= v >> 4;
  v |= v >> 8;
  v |= v >> 16;
  v++;
  return v;
}
// Update a provided image to the nearest power of 2 in size.
const pow2Image = (c) => {
  const newWidth = powerOf2(c.width) ? c.width : nextPow2(c.width);
  const newHeight = powerOf2(c.height) ? c.height : nextPow2(c.height);
  const _c = document.createElement('canvas');
  const ctx = _c.getContext('2d');
  _c.width = newWidth;
  _c.height = newHeight;
  ctx.drawImage(c, 0, 0, newWidth, newHeight);
  return _c;
}
const asyncImageLoad = function(img, src) {
  return new Promise((resolve, reject) => {
    img.onload = () => resolve(img);
    img.onerror = reject;
    img.src = src;
  })
}
const glEnumToString = (function() {
  const haveEnumsForType = {};
  const enums = {};

  function addEnums(gl) {
    const type = gl.constructor.name;
    if (!haveEnumsForType[type]) {
      for (const key in gl) {
        if (typeof gl[key] === 'number') {
          const existing = enums[gl[key]];
          enums[gl[key]] = existing ? `${existing} | ${key}` : key;
        }
      }
      haveEnumsForType[type] = true;
    }
  }

  return function glEnumToString(gl, value) {
    addEnums(gl);
    return enums[value] || (typeof value === 'number' ? `0x${value.toString(16)}` : value);
  };
}());
const addExtensions = (ctx) => {
  // Set up the extensions
  ctx.getExtension('OES_standard_derivatives');
  ctx.getExtension('EXT_shader_texture_lod');
  ctx.getExtension('OES_texture_float');
  ctx.getExtension('WEBGL_color_buffer_float');
  ctx.getExtension('OES_texture_float_linear');
  ctx.getExtension('EXT_color_buffer_float');
}
function createContext(c, opt_attribs, params) {
  const ctx = c.getContext("webgl", params) || this._el.getContext("experimental-webgl", params);
  
  addExtensions(ctx);
  
  return ctx;
}

const quatToMat4 = (q) => {
    if(q.array) q = q.array; // This just transforms a provided vector into to an array.
    
    if(q instanceof Array && q.length >= 4) {
      const [x, y, z, w] = q;
      const [x2, y2, z2] = q.map(x => x * 2.);
      
      const xx = x * x2,
            yx = y * x2,
            yy = y * y2,
            zx = z * x2,
            zy = z * y2,
            zz = z * z2,
            wx = w * x2,
            wy = w * y2,
            wz = w * z2;
      
      return new Mat4(
        1 - yy -zz, yx -wz, zx + wy, 0, 
        yx + wz, 1 - xx - zz, zy - wx, 0, 
        zx - wy, zy + wx, 1 - xx - yy, 0,
        0, 0, 0, 1
      );
    }
  }

class Renderer {
  static #defaultOptions = {
    width: 512,
    height: 512,
    pxRatio: Math.min(window.devicePixelRatio, 2),
    clearing: true,
    depthTesting: true,
    premultipliedAlpha: true
  }
    
  static BLENDING_DEBUG      = -1;
  static BLENDING_NORMAL      = 1;
  static BLENDING_ADDITIVE    = 2;
  static BLENDING_SUBTRACTIVE = 4;
  static BLENDING_MULTIPLY    = 8;
  static BLENDING_OFF         = 16;

  isWebgl2 = false;

  #blending;
  #blendingEnabled = false;
  #buffers = [];

  constructor(canvas, options) {
    options = Object.assign({}, Renderer.#defaultOptions, options);
    this.width = options.width;
    this.height = options.height;
    this.pxRatio = options.pxRatio;
    this.clearing = options.clearing;
    this.depthTesting = options.depthTesting;
    this.canvas = canvas || document.createElement('canvas');
    this.canvas.width = this.width * this.pxRatio;
    this.canvas.height = this.height * this.pxRatio;
    this.premultipliedAlpha = options.premultipliedAlpha;
    
    this.ctx = this.canvas.getContext("webgl", options) || this.canvas.getContext("experimental-webgl", options);
    
    this.ctx.viewportWidth = this.canvas.width;
    this.ctx.viewportHeight = this.canvas.height;
    
    this.uniformResolution = new Uniform(this.ctx, 'resolution', Uniform.TYPE_V2, [this.canvas.width, this.canvas.height]);
    
    this.addExtensions();
  }
  resize(w, h, ratio) {
    this.width = w;
    this.height = h;
    this.pxRatio = ratio || this.pxRatio;
    this.canvas.width = this.width * this.pxRatio;
    this.canvas.height = this.height * this.pxRatio;
    
    this.ctx.viewportWidth = this.canvas.width;
    this.ctx.viewportHeight = this.canvas.height;
    
    this.uniformResolution = new Uniform(this.ctx, 'resolution', Uniform.TYPE_V2, [this.canvas.width, this.canvas.height]);
  }
  setViewport(dimensions) {
    let w = this.width*this.pxRatio;
    let h = this.height*this.pxRatio;
    if(dimensions) {
      w = dimensions[0];
      h = dimensions[1];
    }
    this.ctx.viewport(0, 0, w, h);
    this.uniformResolution = new Uniform(this.ctx, 'resolution', Uniform.TYPE_V2, [w, h]);
  }
  addExtensions() {
    this.ctx.getExtension('OES_standard_derivatives');
    this.ctx.getExtension('EXT_shader_texture_lod');
    this.ctx.getExtension('OES_texture_float');
    this.ctx.getExtension('WEBGL_color_buffer_float');
    this.ctx.getExtension('OES_texture_float_linear');
    this.ctx.getExtension('EXT_color_buffer_float');
  }
  linkBuffer(buffer) {
    let hasBuffer = false;
    this.#buffers.forEach((b) => {
      if(buffer === b) hasBuffer = true;
    });
    if(!hasBuffer) {
      this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, buffer.buffer);
      this.ctx.bufferData(
        this.ctx.ARRAY_BUFFER,
        buffer.data,
        buffer.drawType);
    }
    buffer.link(this.currentProgram.program);
  }
  setupProgram(program, buffers, attributes, uniforms) {
    this.currentProgram = program;
    this.ctx.useProgram(program.program);
    
    this.premultiplied = program.premultiplied;
    
    this.depthTesting = program.depthTesting;
    
    
    if(program.blending === Program.BLENDING_NORMAL && program.transparent === false ) {
      this.blending = Program.BLENDING_OFF;
    } else {
      this.blending = program.blending;
    }
    
    this.clearColour = program.clearColour;
    const a = this.clearColour[3];
    // console.log('prem', this.premultipliedAlpha)
    if(this.premultipliedAlpha) this.clearColour = this.clearColour.map((c, i) => c * a );
    
    this.ctx.clearColor(...this.clearColour);
    
    // TODO: Unlink unused buffers during this setup phase as well.
    buffers.forEach(buffer => {
      this.linkBuffer(buffer);
    });
      
    // this.ctx.enable(ctx.DEPTH_TEST);
    if(this.depthTesting) this.ctx.enable(ctx.DEPTH_TEST);
    else this.ctx.disable(ctx.DEPTH_TEST);
    
    uniforms.forEach(uniform => {
      uniform.bind(program.program);
    });
    this.uniformResolution.bind(program.program);
  }
  render(points, buffer) {
    this.ctx.bindFramebuffer(this.ctx.FRAMEBUFFER, buffer?.fb || null);
    if(this.clearing) {
      this.ctx.clear( this.ctx.COLOR_BUFFER_BIT );
      
      if(this.depthTesting) this.ctx.clear( this.ctx.DEPTH_BUFFER_BIT );
    }
    switch(this.currentProgram.renderType) {
      case Program.RENDER_TRIANGLES: 
        this.ctx.drawArrays(this.ctx.TRIANGLES, 0, points);
        break;
      case Program.RENDER_STRIP: 
        this.ctx.drawArrays(this.ctx.TRIANGLE_STRIP, 0, points);
        break;
      case Program.RENDER_LINES: 
        this.ctx.drawArrays(this.ctx.LINE_STRIP, 0, points);
        break;
      case Program.RENDER_LINELOOP: 
        this.ctx.drawArrays(this.ctx.LINE_LOOP, 0, points);
        break;
      case Program.RENDER_POINTS: 
        this.ctx.drawArrays(this.ctx.POINTS, 0, points);
        break;
    }
    
  }

  /* SETTERS AND GETTERS */
  get blending() {
    return this.#blending || Program.BLENDING_NORMAL;
  }
  set blending(blending) {
    
    if(blending === Renderer.BLENDING_DEBUG) {
      
      if(!this.breakLog) {
        console.log(blending, Renderer.BLENDING_OFF, this.premultiplied)
        this.breakLog = true;
      }
      this.#blending = blending;
      this.ctx.enable(this.ctx.BLEND);
        this.ctx.blendFuncSeparate( this.ctx.ONE, this.ctx.ONE_MINUS_SRC_ALPHA, this.ctx.ONE, this.ctx.ONE_MINUS_SRC_ALPHA );
      return;
    }
    
    this.#blending = blending;
    if(blending === Renderer.BLENDING_OFF) {
      this.ctx.disable(this.ctx.BLEND);
      this.#blendingEnabled = false;
      return;
    }
		if ( this.#blendingEnabled === false ) {
      this.ctx.enable(this.ctx.BLEND);
      // this.ctx.alphaFunc(this.ctx.GL_GREATER, 0.5);
      // this.ctx.enable(this.ctx.GL_ALPHA_TEST);
			this.#blendingEnabled = true;
		}
    
		if( this.premultiplied ) {
      switch (this.blending) {
        case Renderer.BLENDING_NORMAL:
          this.ctx.blendFuncSeparate( this.ctx.ONE, this.ctx.ONE_MINUS_SRC_ALPHA, this.ctx.ONE, this.ctx.ONE_MINUS_SRC_ALPHA );
          break;
        case Renderer.BLENDING_ADDITIVE: 
          this.ctx.blendFunc( this.ctx.ONE, this.ctx.ONE );
          break;
        case Renderer.BLENDING_SUBTRACTIVE: 
          this.ctx.blendFuncSeparate( this.ctx.ZERO, this.ctx.ZERO, this.ctx.ONE_MINUS_SRC_COLOR, this.ctx.ONE_MINUS_SRC_ALPHA );
          break;
        case Renderer.BLENDING_MULTIPLY:
          this.ctx.blendFuncSeparate( this.ctx.ZERO, this.ctx.SRC_COLOR, this.ctx.ZERO, this.ctx.SRC_ALPHA );
          break;
      }
    } else {
      switch (this.blending) {
        case Renderer.BLENDING_NORMAL: 
          this.ctx.blendFuncSeparate( this.ctx.SRC_ALPHA, this.ctx.ONE_MINUS_SRC_ALPHA, this.ctx.ONE, this.ctx.ONE_MINUS_SRC_ALPHA );
          break;
        case Renderer.BLENDING_ADDITIVE: 
          this.ctx.blendFunc( this.ctx.SRC_ALPHA, this.ctx.ONE );
          break;
        case Renderer.BLENDING_SUBTRACTIVE: 
          this.ctx.blendFunc( this.ctx.ZERO, this.ctx.ONE_MINUS_SRC_COLOR );
          break;
        case Renderer.BLENDING_MULTIPLY:
          this.ctx.blendFunc( this.ctx.ZERO, this.ctx.SRC_COLOR );
          break;
      }
    }
  }
}
class Buffer {
  static #defaultAttribute = {
        numComponents: 2,
        offset: 0,
        stride: 0
      };
  static #defaults = {
    attributes: [{
        name: 'position'
      }
    ],
    normalized: false,
    drawType: window.WebGLRenderingContext.STATIC_DRAW,
    type: window.WebGLRenderingContext.FLOAT
  }
  constructor(ctx, data, options) {
    this.ctx = ctx;
    this.name = name;
    options = Object.assign({}, Buffer.#defaults, options);
    this.attributes = options.attributes.map(a => Object.assign({}, Buffer.#defaultAttribute, a));
    
    this.normalized = options.normalized;
    this.drawType = options.drawType;
    this.type = options.type;
    if(data instanceof Array) data = new Float32Array(data);
    this.data = data;
    this.buffer = ctx.createBuffer();
  }

  link(program, hasBuffer = false) {
    let location = this.ctx.getAttribLocation(program, `a_${this.name}`);
    
    this.attributes.forEach(attribute => {
      const location = this.ctx.getAttribLocation(program, `a_${attribute.name}`);
      this.ctx.vertexAttribPointer(location, attribute.numComponents, this.type, this.normalized, attribute.stride, attribute.offset);
      this.ctx.enableVertexAttribArray(location);
    });
  }

  get length() {
    return this.data.length;
  }
}
class Program {
  
  static RENDER_TRIANGLES     = 0;
  static RENDER_STRIP         = 1;
  static RENDER_LINES         = 2;
  static RENDER_LINELOOP      = 4;
  static RENDER_POINTS        = 8;
  
  static #defaultOptions = {
    renderType: Program.RENDER_TRIANGLES,
    clearColour: [1.0, 1.0, 1.0, 1.0],
    blending: Renderer.BLENDING_OFF,
    premultiplied: true,
    transparent: false,
    depthTesting: true
  }
  
  #vShader
  #fShader
  #p
  #renderType
  
  constructor(ctx, vertexShaderSource, fragmentShaderSource, options = {}) {
    options = Object.assign({}, Program.#defaultOptions, options);
    
    this.ctx = ctx;
    
    this.renderType = options.renderType;
    
    this.clearColour = options.clearColour;
    this.blending = options.blending;
    this.premultiplied = options.premultiplied;
    this.transparent = options.transparent;
    this.depthTesting = options.depthTesting;
    
    // Create the shaders
    this.vShader = Program.createShaderOfType(this.ctx, this.ctx.VERTEX_SHADER, vertexShaderSource);
    this.fShader = Program.createShaderOfType(this.ctx, this.ctx.FRAGMENT_SHADER, fragmentShaderSource);
    
    // Create the program and link the shaders
    this.#p = this.ctx.createProgram();
    this.ctx.attachShader(this.#p, this.vShader);
    this.ctx.attachShader(this.#p, this.fShader);
    
    this.ctx.linkProgram(this.#p);
    
    // Check the result of linking
    var linked = this.ctx.getProgramParameter(this.#p, this.ctx.LINK_STATUS);
    if (!linked) {
      var error = this.ctx.getProgramInfoLog(this.#p);
      console.log('Failed to link program: ' + error);
      this.ctx.deleteProgram(this.#p);
      this.ctx.deleteShader(this.fShader);
      this.ctx.deleteShader(this.vShader);
    }
  }
  
  get program() {
    return this.#p;
  }

  /* SETTERS AND GETTERS */

  set renderType(value) {
    if([
      Program.RENDER_TRIANGLES,
      Program.RENDER_STRIP,
      Program.RENDER_LINES,
      Program.RENDER_LINELOOP,
      Program.RENDER_POINTS
    ].indexOf(value) > -1) this.#renderType = value;
  }
  get renderType() {
    return this.#renderType;
  }
  
  /**
   * Static Methods
   */

	/**
	 * Create a shader of a given type given a context, type and source.
	 *
   * @static
	 * @param  {WebGLContext} ctx The context under which to create the shader
	 * @param  {WebGLShaderType} type The shader type, vertex or fragment
	 * @param  {string} source The shader source.
	 * @return {WebGLShader} The created shader
	 */
  static createShaderOfType(ctx, type, source) {
    const shader = ctx.createShader(type);
    ctx.shaderSource(shader, source);
    ctx.compileShader(shader);
    
    // Check the compile status
    const compil.........完整代码请登录后点击上方下载按钮下载查看

网友评论0