将三维世界的二维透视定位在三维世界上。

问题描述 投票:1回答:1

所以我有这个方法在一个2D世界上画出3D世界中所有的矩形,我能够把3D对象定位在2D画布上,但现在我想把2D画布放置在合适的位置,而不是矩形,(这样一来,我就只能画一个带图像的大矩形,而不是每一个矩形。

办法是这样的

createBackground(objects) {
    const buffer = document.createElement("canvas").getContext("2d");

    const bufferRect = this.createEntity();
    const display = this.display;

    let {
      zAxis,
      canvas: {
        width,
        height,
      },
      currentCamera
    } = display;
    const cameraMatrix = currentCamera.matrix;

    zAxis--;
    const halfZ = zAxis / 2;
    let { coords: [x, y], area: [w, h] } = objects[objects.length - 1];

    const worldViewProjection = mat4.create();
    let [bX, bY, lX, lY] = [x+w, y+h, x-w, y-h]; //big x, little x

    for(let i = objects.length-1; i--;){
      const object = objects[i];
      const {coords: [_x, _y], area: [_w, _h]} = objects[i];

      if(_x > bX){
         bX = _x+_w;
      } else if(_x < lX) {
        lX = _x-_w;
      }
      if(_y > bY){
         bY = _y+_h;
      } else if(_y < lY) {
        lY = _y-_h;
      }

    }

    buffer.canvas.width = width;
    buffer.canvas.height = height;

    for (let i = objects.length; i--;) {
      const {
        coords: [_x, _y],
        area: [_w, _h]
      } = objects[i];
      mat4.multiply(worldViewProjection, this.display.currentCamera.matrix, objects[i].matrix);
      const points = [
        [-_w / 2, -_h / 2, 0],
        [ _w / 2,  _h / 2, 0],
      ].map(p => {
        const ndc = vec3.transformMat4([], p, worldViewProjection);
        return [
          (ndc[0] *  0.5 + 0.5) * width,
          (ndc[1] * -0.5 + 0.5) * height,
        ];
      });
      const ww = points[1][0] - points[0][0];
      const hh = points[1][1] - points[0][1];

      buffer.strokeStyle = 'red';
      buffer.strokeRect(...points[0], ww, hh);
    }

    bufferRect.move((bX+lX)/2, (bY+lY)/2);

    bufferRect.setSize(Math.abs(bX)+Math.abs(lX)-5, Math.abs(bY)+Math.abs(lY)-5);

    const texture = display.textureFromImage(buffer.canvas);

    bufferRect.attachImage(texture);

  }

问题就出在这里,我试图将矩形定位在中间,并将其大小设置为最左边的矩形和最右边的矩形之间的距离,有些则是上下的距离。

bufferRect.move((bX+lX)/2, (bY+lY)/2);

bufferRect.setSize(Math.abs(bX)+Math.abs(lX)-5, Math.abs(bY)+Math.abs(lY)-5);

这里是完整的代码

const FRAGMENT_SHADER = ` precision highp float; varying highp vec2 vTextureCoord; varying lowp vec4 vColor; uniform sampler2D uSampler; uniform bool aUseText; void main(void) { if( aUseText ){ gl_FragColor = texture2D(uSampler, vTextureCoord); } else { gl_FragColor = vColor; } } `;
const VERTEX_SHADER = ` attribute vec4 aVertexPosition; attribute vec4 aVertexColor; attribute vec2 aTextureCoord; uniform mat4 uModelViewMatrix; uniform mat4 uProjectionMatrix; uniform mat3 uTextMatrix; uniform float uPointSize; varying lowp vec4 vColor; varying highp vec2 vTextureCoord; void main(void) { gl_PointSize = uPointSize; gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; vColor = aVertexColor; vTextureCoord = (vec3(aTextureCoord, 1)*uTextMatrix).xy; } `;

function onResize(element, callback) {
  let elementHeight = element.height,
    elementWidth = element.width;

  setInterval(function() {
    if (element.height !== elementHeight || element.width !== elementWidth) {
      elementHeight = element.height;
      elementWidth = element.width;
      callback();
    }
  }, 16);
}


mat4.moveToVec3 = function(out, v) {
  out[12] = v[0];
  out[13] = v[1];
  out[14] = v[2];
};

class WebglEntity {
  constructor() {
    this.matrix = mat4.create();
    this.coords = vec3.create();
  }
  translate(newCoords) {
    const {
      matrix,
      coords
    } = this;
    mat4.translate(matrix, matrix, newCoords);
    vec3.copy(coords, [matrix[12], matrix[13], matrix[14]]);

    return this;
  }
  move(newCoords) {
    const {
      matrix,
      coords
    } = this;
    vec3.copy(coords, newCoords);
    mat4.moveToVec3(matrix, coords);

    return this;
  }
}

class Texture {

  constructor() {

    this.matrix = mat3.create();

    this.image = undefined;
    this.width = undefined;
    this.height = undefined;
    this.rotation = 0;
    this.y = 0;
    this.x = 0;

    let onload = function() {};

    Object.defineProperty(this, "onload", {
      get() {
        return onload;
      },

      set(value) {

        if (this.loaded) {
          value();
        } else {
          onload = value;
        }
      }
    });

    this.loaded = false;
  }

  setup(image, y, width, height, matrix, rotation) {

    this.image = image;
    this.y = y;
    this.width = width;
    this.height = height;
    this.rotation = 0;
    this.x = 0;

    if (matrix) {
      this.matrix = matrix;
      if (rotation) {
        this.rotation = rotation;

      }
    }

    this.loaded = true;
  }

  static from(texture) {

    const newTexture = new Texture();

    const {
      image,
      y,
      width,
      height,
      matrix,
      rotation
    } = texture;

    newTexture.setup(image, y, width, height, mat3.clone(matrix), rotation);

    return newTexture;
  }

  scale(w, h) {

    const matrix = this.matrix;

    mat3.scale(matrix, matrix, [w, h]);

  }

  rotate(rad) {

    const matrix = this.matrix;
    this.rotation = (this.rotation + rad) % (Math.PI * 2);

    mat3.rotate(matrix, matrix, rad);

  }
}
class Camera extends WebglEntity {
  constructor(fieldOfView, aspect, zNear, zFar) {
    super();

    this.projection = mat4.perspective(mat4.create(), fieldOfView, aspect, zNear, zFar);

  }
  lookAt(lookAt) {
    const {
      matrix,
      projection,
      coords
    } = this;
    mat4.lookAt(matrix, coords, lookAt, [0, 1, 0]);
    mat4.multiply(matrix, projection, matrix);
    return this;
  }
}
class Rect extends WebglEntity {

  constructor() {

    super();

    this.positionsBuffer = undefined;
    this.fragColorPos = undefined;
    this.strokeColorPos = undefined;
    this.strokePositionBuffer = undefined;
    this.vertexAttribInfo = undefined;
    this.vertextColorAttribInfo = undefined;
    this.vertexCount = undefined;
    this.textureInfo = undefined;
    this.strokeSize = 1;
    this.fillers = {
      fill: false,
      texture: false,
      stroke: false
    };


  }

  setup(matrix, positionsBuffer, strokePositionBuffer, vertexAttribInfo, vertextColorAttribInfo, vertexCount) {

    this.matrix = matrix;

    this.positionsBuffer = positionsBuffer;
    this.strokePositionBuffer = strokePositionBuffer;

    this.vertexAttribInfo = vertexAttribInfo;
    this.vertextColorAttribInfo = vertextColorAttribInfo;

    this.vertexCount = vertexCount;

    return this;
  }

  scale(scale) {

    const matrix = this.matrix;

    mat4.scale(matrix, matrix, scale);

    return this;

  }

  attachImage(newTexture) {
    this.fillers.texture = true;

    if (this.multiTextures) {
      this.textureInfo.push(newTexture);
      return;
    }

    this.textureInfo = newTexture;
    this.fillers.TRIANGLE_STRIP = true;

    return this;

  }

}

class Display {

  constructor(gl, programInfo, zAxis, texture) {
    this.gl = gl;
    this.programInfo = programInfo;

    this.canvas = gl.canvas;

    this.currentCamera = new Camera(45 * Math.PI / 180, gl.canvas.width / gl.canvas.height, 0.1, 100.0);

    this.currentCamera.translate([0, 0, zAxis]);

    this.currentCamera.lookAt([0, 0, 0]);

    this.zAxis = zAxis;
    this.drawZAxis = 0;

    texture.textAttribInfo = {
      numComponents: 2,
      type: gl.FLOAT,
      normalize: false,
      stride: 0,
      offset: 0
    };

    this.texture = texture;
    this.spriteSheets = [];

    const context = texture.context;
    const canvas = texture.canvas;

    this.images = {}; /*all the images with their src as their key*/

    onResize(texture.canvas, () => {

      const images = Object.values(this.images);
      for (let i = images.length; i--;) {

        const {
          image,
          y
        } = images[i];

        context.drawImage(image, 0, y);
      }

      const internalFormat = gl.RGBA;

      gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, internalFormat, gl.UNSIGNED_BYTE, canvas);

      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

    });

  }


  setSize(width, height) {

    const canvas = this.gl.canvas;

    canvas.width = width;
    canvas.height = height;

  }

  clear(color) {

    const gl = this.gl;

    gl.clearColor(0.1, 0.1, 0.3, 1);

    gl.clearDepth(1.0);
    gl.enable(gl.DEPTH_TEST);
    gl.depthFunc(gl.LEQUAL);

    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);


  }
  fillRect(rect, color) {
    const {
      createStaticDrawBuffer,
      gl,
      parseColor
    } = this;

    rect.fillers.fill = true;

    if (color) {
      rect.fragColorPos = createStaticDrawBuffer(gl, [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]);

    }
  }
  textureFromImage(image, texture) {

    const {
      images,
      texture: {
        canvas
      }
    } = this;

    texture = texture ? texture : new Texture();

    const {
      width,
      height
    } = image;

    const y = canvas.height;

    if (canvas.width < width) {

      canvas.width = width;

    }

    texture.setup(image, y, width, height, 0);

    canvas.height += height;

    images[images.length] = texture;

    return texture;

  }

  createRectPos(w, h) {

    const rect = [w / 2, h / 2, -w / 2, h / 2, w / 2, -h / 2, -w / 2, -h / 2];


    return {
      rect,
      stroke: undefined
    };
  }

  getRectInfo(x, y, rect, stroke) {

    return this.createSquareBuffer(rect, stroke, [x, y, this.drawZAxis]);
  }

  createStaticDrawBuffer(gl, data) {

    const buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);

    return buffer;
  }

  createSquareBuffer(positions, strokePosition, coords) {
    const {
      gl,
      createStaticDrawBuffer
    } = this;

    const positionsBuffer = createStaticDrawBuffer(gl, positions);
    const strokePositionBuffer = createStaticDrawBuffer(gl, strokePosition);
    const modelViewMatrix = mat4.create();

    mat4.translate(modelViewMatrix, modelViewMatrix, coords);

    return [modelViewMatrix, positionsBuffer, strokePositionBuffer, this.createAttribInfo(2, gl.FLOAT, false, 0, 0), this.createAttribInfo(4, gl.FLOAT, false, 0, 0), positions.length / 2];
  }

  createAttribInfo(numComponents, type, normalize, stride, offset) {

    return {
      numComponents,
      type,
      normalize,
      stride,
      offset
    };
  }

  enableAttrib(buffer, attrib, gl, {
    numComponents,
    type,
    normalize,
    stride,
    offset
  }) {

    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.vertexAttribPointer(attrib, numComponents, type, normalize, stride, offset);
    gl.enableVertexAttribArray(attrib);

  }

  drawTexture(texture, gl, canvas, enableAttrib, createStaticDrawBuffer, textAttribInfo, vertexCount, textureCoord, textMatrix, useText) {

    const _width = canvas.width;
    const _height = canvas.height;

    const {
      x,
      y,
      width,
      height,
      matrix
    } = texture;

    const realX = x / _width;
    const realWidth = realX + width / _width;
    const realHeight = y / _height;
    const realY = (y + height) / _height;

    const fragTextPos = createStaticDrawBuffer(gl, [realWidth, realHeight, realX, realHeight, realWidth, realY, realX, realY, ]);

    gl.uniformMatrix3fv(textMatrix, false, matrix);
    enableAttrib(fragTextPos, textureCoord, gl, textAttribInfo);
    gl.uniform1f(useText, true);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertexCount);

    gl.uniform1f(useText, false);
    gl.disableVertexAttribArray(textureCoord);
  }

  drawBuffer(buffer) {

    const {
      gl,
      drawTexture,
      enableAttrib,
      createStaticDrawBuffer,
      currentCamera,
      texture: {
        context,
        canvas,
        textAttribInfo
      },
      programInfo: {
        uniformLocations,
        program,
        attribLocations: {
          vertexPosition,
          vertexColor,
          textureCoord
        }
      }
    } = this;

    const cameraMatrix = currentCamera.matrix;

    const {
      positionsBuffer,
      fragColorPos,
      strokeColorPos,
      strokePositionBuffer,
      matrix,
      vertexAttribInfo,
      vertextColorAttribInfo,
      vertexCount,
      fragTextPos,
      fillers: {
        fill,
        stroke,
        texture
      },
      strokeSize,
      textureInfo,
      multiTextures
    } = buffer;

    gl.uniformMatrix4fv(uniformLocations.projectionMatrix, false, cameraMatrix);
    gl.uniformMatrix4fv(uniformLocations.modelViewMatrix, false, matrix);

    if (fill) {

      enableAttrib(positionsBuffer, vertexPosition, gl, vertexAttribInfo);
      enableAttrib(fragColorPos, vertexColor, gl, vertextColorAttribInfo);
      gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertexCount);
      gl.disableVertexAttribArray(vertexColor);

    }


    if (texture) {

      const _width = canvas.width;
      const _height = canvas.height;

      drawTexture(textureInfo, gl, canvas, enableAttrib, createStaticDrawBuffer, textAttribInfo, vertexCount, textureCoord, uniformLocations.textMatrix, uniformLocations.useText);

    }
  }

  static loadShader(gl, program, type, source) {

    const shader = gl.createShader(type);
    gl.shaderSource(shader, source);
    gl.compileShader(shader);
    gl.attachShader(program, shader);

  }

  static async create(canvas, width, height, zAxis = 6) {
    canvas.width = width;
    canvas.height = height;

    const gl = canvas.getContext("webgl");

    const shaderProgram = gl.createProgram();

    Display.loadShader(gl, shaderProgram, gl.VERTEX_SHADER, VERTEX_SHADER);
    Display.loadShader(gl, shaderProgram, gl.FRAGMENT_SHADER, FRAGMENT_SHADER);

    gl.linkProgram(shaderProgram);


    const programInfo = {
      program: shaderProgram,
      attribLocations: {
        vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
        vertexColor: gl.getAttribLocation(shaderProgram, 'aVertexColor'),
        textureCoord: gl.getAttribLocation(shaderProgram, 'aTextureCoord'),


      },
      uniformLocations: {
        projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
        modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
        textMatrix: gl.getUniformLocation(shaderProgram, 'uTextMatrix'),
        sampler: gl.getUniformLocation(shaderProgram, 'uSampler'),
        useText: gl.getUniformLocation(shaderProgram, 'aUseText'),
        pointSize: gl.getUniformLocation(shaderProgram, 'uPointSize'),
      },
    };

    gl.useProgram(programInfo.program);

    gl.uniform1f(programInfo.uniformLocations.pointSize, 1.0);

    gl.enable(gl.BLEND);
    gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);

    const textureBuffer = gl.createTexture();

    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, textureBuffer);
    gl.uniform1i(programInfo.uniformLocations.uSampler, 0);

    const textureCanvas = document.createElement("canvas");
    textureCanvas.width = 0;
    textureCanvas.height = 0;

    let texture = {
      canvas: textureCanvas,
      buffer: textureBuffer,
      context: textureCanvas.getContext("2d"),
    };

    return new Display(gl, programInfo, zAxis, texture);
  }
}

class Entity extends Rect {

  constructor() {

    super();

    this.velocity = vec2.create();
    this.area = undefined;
    this.mass = 2;

    this.updateFillers = {};
    this.delete = false;
    this.draw = true;
  }

  setup(w, h, ...args) {
    this.area = vec2.fromValues(w, h);
    super.setup(...args);

    return this;
  }

  fill(...args) {
    this.updateFillers.fill = args;
  }

  attachImage(image) {
    super.attachImage(image);
  }

  move(x, y) {

    super.move([x, y, this.coords[2]]);

    return this;

  }

  setSize(w, h) {

    if (typeof w == "object") {
      h = w[1];
      w = w[0];
    }

    const area = this.area;
    const [_w, _h] = area;
    super.scale([w / _w, h / _h, 1]);

    area[0] = w;
    area[1] = h;

    return this;

  }

}

class Engine {
  constructor(time_step, update, render, allowedSkippedFrames) {
    this.accumulated_time = 0;
    this.animation_frame_request = undefined, this.time = undefined, this.time_step = time_step, this.updated = false;
    this.update = update;
    this.render = render;
    this.allowedSkippedFrames = allowedSkippedFrames;
    this.run = this.run.bind(this);
    this.end = false;
  }
  run(time_stamp) {
    const {
      accumulated_time,
      time,
      time_step,
      updated,
      update,
      render,
      allowedSkippedFrames,
      end
    } = this;
    this.accumulated_time += time_stamp - time;
    this.time = time_stamp;
    update(time_stamp);

    render(time_stamp);

    if (end) {
      return;
    }
    this.animation_frame_request = requestAnimationFrame(this.run);
  }
  start() {
    this.accumulated_time = this.time_step;
    this.time = performance.now();
    this.animation_frame_request = requestAnimationFrame(this.run);
  }
  stop() {
    this.end = true;
    cancelAnimationFrame(this.animation_frame_request);
  }
}

const engineMath = {
  randomBetween: function(min, max) {
    return min + Math.random() * (max - min);
  },

};

class Quixotic {

  constructor(display) {

    this.display = display;

    this.engine = undefined;

    this.render = undefined;
    this.update = undefined;
    this.frameRate = undefined;

    this.time = 0;
    this.speed = 1;
    this.world = {

      objects: {},
      objectsCollisionInfo: {},
      objectsArray: [],
      classesInfo: {}

    };

    this.timePassed = 0;

  }

  createEntity(Class, ...args) {
    const display = this.display;
    const {
      rect,
      stroke
    } = display.createRectPos(5, 5);

    let instance = new Entity();

    instance.setup(5, 5, ...display.getRectInfo(0, 0, rect, stroke, "#000"));
    this.world.objectsArray.push(instance);
    return instance;
  }

  createBackground(objects) {
    const buffer = document.createElement("canvas").getContext("2d");

    const bufferRect = this.createEntity();
    const display = this.display;

    let {
      zAxis,
      canvas: {
        width,
        height,
      },
      currentCamera
    } = display;
    const cameraMatrix = currentCamera.matrix;

    zAxis--;
    const halfZ = zAxis / 2;
    let {
      coords: [x, y],
      area: [w, h]
    } = objects[objects.length - 1];

    const worldViewProjection = mat4.create();
    let [bX, bY, lX, lY] = [x + w, y + h, x - w, y - h]; //big x, little x

    for (let i = objects.length - 1; i--;) {
      const object = objects[i];
      const {
        coords: [_x, _y],
        area: [_w, _h]
      } = objects[i];

      if (_x > bX) {
        bX = _x + _w;
      } else if (_x < lX) {
        lX = _x - _w;
      }
      if (_y > bY) {
        bY = _y + _h;
      } else if (_y < lY) {
        lY = _y - _h;
      }

    }

    buffer.canvas.width = width;
    buffer.canvas.height = height;

    for (let i = objects.length; i--;) {
      const {
        coords: [_x, _y],
        area: [_w, _h]
      } = objects[i];
      mat4.multiply(worldViewProjection, this.display.currentCamera.matrix, objects[i].matrix);
      const points = [
        [-_w / 2, -_h / 2, 0],
        [_w / 2, _h / 2, 0],
      ].map(p => {
        const ndc = vec3.transformMat4([], p, worldViewProjection);
        return [
          (ndc[0] * 0.5 + 0.5) * width,
          (ndc[1] * -0.5 + 0.5) * height,
        ];
      });
      const ww = points[1][0] - points[0][0];
      const hh = points[1][1] - points[0][1];

      buffer.strokeStyle = 'red';
      buffer.strokeRect(...points[0], ww, hh);
    }

    bufferRect.move((bX + lX) / 2, (bY + lY) / 2);

    bufferRect.setSize(Math.abs(bX) + Math.abs(lX) - 5, Math.abs(bY) + Math.abs(lY) - 5);

    const texture = display.textureFromImage(buffer.canvas);

    bufferRect.attachImage(texture);

  }

  buildWorld({
    objects,
    classes,
    tileMap
  }) {

    const world = this.world;

    if (Array.isArray(objects)) {
      for (let i = objects.length - 1; i > -1; i--) {
        const object = objects[i];
        const {
          amount,
          position,
        } = object;

        const {
          rangeX,
          rangeY
        } = position;

        let _array = [];

        for (let j = amount; j--;) {
          const instance = this.createEntity();
          instance.move(engineMath.randomBetween(...rangeX), engineMath.randomBetween(...rangeY));

          _array.push(instance);
        }
        world.objects[name] = _array;
        world.objectsArray.push(..._array);
      }
    }
    return;

  }


  setup(game) {

    const {
      style: {
        backgroundColor,
        backgroundImage,
        stroke
      },
      world,
      engine: {
        frameRate,
        update,
        render
      },
      setup
    } = game;
    this.buildWorld(world);
    const {
      display,
      world: {
        objectsArray,
        objects
      }
    } = this;

    this.frameRate = frameRate;
    let lastUpdated = 0;
    this.update = (time) => {
      let deltaTime = time - lastUpdated;
      lastUpdated = time;
      const speed = this.speed;
      this.timePassed += deltaTime * speed;

      update(deltaTime / 1000, this);
    };
    let lastRendered = 0;
    this.render = (timeStamp) => {
      if (backgroundColor) display.clear(backgroundColor);
      const length = objectsArray.length;
      for (let i = length; i--;) {
        const object = objectsArray[length - i - 1];
        if (object.draw) {
          const updateFillers = Object.entries(object.updateFillers);
          const fillersLength = updateFillers.length;
          if (fillersLength) {
            for (let j = fillersLength; j--;) {
              const [func, args] = updateFillers[fillersLength - j - 1];
              display[func + "Rect"](object, ...args);
            }
            object.updateFillers = {};
          }
          display.drawBuffer(object);
        }
      }

      render(display, this);
    };
    setup(this, this.world);
    this.engine = new Engine(this.frameRate, this.update, this.render, 3);
    this.engine.start();
    return game;
  }

  static async create({
    display: {
      canvas,
      width,
      height,
      zAxis
    },
    homeURL
  }) {

    const display = await Display.create(canvas, width, height, zAxis);

    return new Quixotic(display);
  }

}

const fps = document.querySelector("#fps");
const minLength = innerWidth > innerHeight ? innerHeight : innerWidth;
const game = {

  create: {

    display: {

      canvas: document.querySelector("#canvas"),
      zAxis: 90,
      width: minLength,
      height: minLength,

    },

    homeURL: "/src"
  },

  style: {
    backgroundColor: "#111122"
  },

  world: {
    objects: [{
      name: "trees",

      array: true,
      amount: 5,
      position: {
        type: "random",
        rangeX: [-37.5, 37.5],
        rangeY: [-37.5, 37.5]

      }
    }]
  },

  engine: {

    frameRate: 1000 / 30,

    update: function(deltaTime, engine) {
      fps.innerText = 1 / deltaTime;
    },

    render: function(display) {}
  },

  setup: function(engine, {
    objectsArray
  }) {

    objectsArray.forEach(tree => {
      tree.fill("#00ff00")
    })
    engine.createBackground(objectsArray);
  }

};



Quixotic.create(game.create)
  .then(engine => {

    engine.setup(game);
  });
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  background-color: #111c31;
  overflow: hidden;
  align-items: space-around;
  display: grid;
  height: 100%;
  width: 100%;
}

#canvas {
  background-color: #152646;
  /* justify-self: center; */
}

#fps {
  position: fixed;
  color: white;
  right: 0;
}

canvas {
  position: fixed
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"></script>

<canvas id="canvas" width="300" height="300"></canvas>
<p id="fps"></p>
javascript canvas webgl
1个回答
0
投票

你计算一个透视投影的非线性坐标。为此,矩形的角由视图矩阵和投影矩阵变换。这样做似乎很好,但你在绘制红色矩形时没有改变投影和视图矩阵。因此,顶点坐标被投影矩阵和视图矩阵变换了两次。第一次是在计算非线性坐标时,第二次是在顶点着色器中。

当你绘制红色矩形时,你必须设置一个与红色矩形的单位相匹配的正交投影矩阵,而视图矩阵必须为 身份矩阵. 红色的矩形被绘制到一个协调系统的平面上,其中一个大小为5x5的区域被映射到视口上。中心在(0,0)。因此,正交投影矩阵为

[2/5,0,0,0, 0,2/5,0,0, 0,0,1,0, 0,0,0,1]

绘制绿色物体后,改变视图矩阵和投影矩阵。

gl.uniformMatrix4fv(uniformLocations.projectionMatrix, false, cameraMatrix);
gl.uniformMatrix4fv(uniformLocations.modelViewMatrix, false, matrix);

if (fill) {

    enableAttrib(positionsBuffer, vertexPosition, gl, vertexAttribInfo);
    enableAttrib(fragColorPos, vertexColor, gl, vertextColorAttribInfo);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertexCount);
    gl.disableVertexAttribArray(vertexColor);
}

const ortho_mat = [2/5,0,0,0, 0,2/5,0,0, 0,0,1,0, 0,0,0,1];
const identity_mat = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];
gl.uniformMatrix4fv(uniformLocations.projectionMatrix, false, ortho_mat);
gl.uniformMatrix4fv(uniformLocations.modelViewMatrix, false, identity_mat);

// [...]

请看这个例子。

const FRAGMENT_SHADER = ` precision highp float; varying highp vec2 vTextureCoord; varying lowp vec4 vColor; uniform sampler2D uSampler; uniform bool aUseText; void main(void) { if( aUseText ){ gl_FragColor = texture2D(uSampler, vTextureCoord); } else { gl_FragColor = vColor; } } `;
const VERTEX_SHADER = ` attribute vec4 aVertexPosition; attribute vec4 aVertexColor; attribute vec2 aTextureCoord; uniform mat4 uModelViewMatrix; uniform mat4 uProjectionMatrix; uniform mat3 uTextMatrix; uniform float uPointSize; varying lowp vec4 vColor; varying highp vec2 vTextureCoord; void main(void) { gl_PointSize = uPointSize; gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; vColor = aVertexColor; vTextureCoord = (vec3(aTextureCoord, 1)*uTextMatrix).xy; } `;

function onResize(element, callback) {
  let elementHeight = element.height,
    elementWidth = element.width;

  setInterval(function() {
    if (element.height !== elementHeight || element.width !== elementWidth) {
      elementHeight = element.height;
      elementWidth = element.width;
      callback();
    }
  }, 16);
}


mat4.moveToVec3 = function(out, v) {
  out[12] = v[0];
  out[13] = v[1];
  out[14] = v[2];
};

class WebglEntity {
  constructor() {
    this.matrix = mat4.create();
    this.coords = vec3.create();
  }
  translate(newCoords) {
    const {
      matrix,
      coords
    } = this;
    mat4.translate(matrix, matrix, newCoords);
    vec3.copy(coords, [matrix[12], matrix[13], matrix[14]]);

    return this;
  }
  move(newCoords) {
    const {
      matrix,
      coords
    } = this;
    vec3.copy(coords, newCoords);
    mat4.moveToVec3(matrix, coords);

    return this;
  }
}

class Texture {

  constructor() {

    this.matrix = mat3.create();

    this.image = undefined;
    this.width = undefined;
    this.height = undefined;
    this.rotation = 0;
    this.y = 0;
    this.x = 0;

    let onload = function() {};

    Object.defineProperty(this, "onload", {
      get() {
        return onload;
      },

      set(value) {

        if (this.loaded) {
          value();
        } else {
          onload = value;
        }
      }
    });

    this.loaded = false;
  }

  setup(image, y, width, height, matrix, rotation) {

    this.image = image;
    this.y = y;
    this.width = width;
    this.height = height;
    this.rotation = 0;
    this.x = 0;

    if (matrix) {
      this.matrix = matrix;
      if (rotation) {
        this.rotation = rotation;

      }
    }

    this.loaded = true;
  }

  static from(texture) {

    const newTexture = new Texture();

    const {
      image,
      y,
      width,
      height,
      matrix,
      rotation
    } = texture;

    newTexture.setup(image, y, width, height, mat3.clone(matrix), rotation);

    return newTexture;
  }

  scale(w, h) {

    const matrix = this.matrix;

    mat3.scale(matrix, matrix, [w, h]);

  }

  rotate(rad) {

    const matrix = this.matrix;
    this.rotation = (this.rotation + rad) % (Math.PI * 2);

    mat3.rotate(matrix, matrix, rad);

  }
}
class Camera extends WebglEntity {
  constructor(fieldOfView, aspect, zNear, zFar) {
    super();

    this.projection = mat4.perspective(mat4.create(), fieldOfView, aspect, zNear, zFar);

  }
  lookAt(lookAt) {
    const {
      matrix,
      projection,
      coords
    } = this;
    mat4.lookAt(matrix, coords, lookAt, [0, 1, 0]);
    mat4.multiply(matrix, projection, matrix);
    return this;
  }
}
class Rect extends WebglEntity {

  constructor() {

    super();

    this.positionsBuffer = undefined;
    this.fragColorPos = undefined;
    this.strokeColorPos = undefined;
    this.strokePositionBuffer = undefined;
    this.vertexAttribInfo = undefined;
    this.vertextColorAttribInfo = undefined;
    this.vertexCount = undefined;
    this.textureInfo = undefined;
    this.strokeSize = 1;
    this.fillers = {
      fill: false,
      texture: false,
      stroke: false
    };


  }

  setup(matrix, positionsBuffer, strokePositionBuffer, vertexAttribInfo, vertextColorAttribInfo, vertexCount) {

    this.matrix = matrix;

    this.positionsBuffer = positionsBuffer;
    this.strokePositionBuffer = strokePositionBuffer;

    this.vertexAttribInfo = vertexAttribInfo;
    this.vertextColorAttribInfo = vertextColorAttribInfo;

    this.vertexCount = vertexCount;

    return this;
  }

  scale(scale) {

    const matrix = this.matrix;

    mat4.scale(matrix, matrix, scale);

    return this;

  }

  attachImage(newTexture) {
    this.fillers.texture = true;

    if (this.multiTextures) {
      this.textureInfo.push(newTexture);
      return;
    }

    this.textureInfo = newTexture;
    this.fillers.TRIANGLE_STRIP = true;

    return this;

  }

}

class Display {

  constructor(gl, programInfo, zAxis, texture) {
    this.gl = gl;
    this.programInfo = programInfo;

    this.canvas = gl.canvas;

    this.currentCamera = new Camera(45 * Math.PI / 180, gl.canvas.width / gl.canvas.height, 0.1, 100.0);

    this.currentCamera.translate([0, 0, zAxis]);

    this.currentCamera.lookAt([0, 0, 0]);

    this.zAxis = zAxis;
    this.drawZAxis = 0;

    texture.textAttribInfo = {
      numComponents: 2,
      type: gl.FLOAT,
      normalize: false,
      stride: 0,
      offset: 0
    };

    this.texture = texture;
    this.spriteSheets = [];

    const context = texture.context;
    const canvas = texture.canvas;

    this.images = {}; /*all the images with their src as their key*/

    onResize(texture.canvas, () => {

      const images = Object.values(this.images);
      for (let i = images.length; i--;) {

        const {
          image,
          y
        } = images[i];

        context.drawImage(image, 0, y);
      }

      const internalFormat = gl.RGBA;

      gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, internalFormat, gl.UNSIGNED_BYTE, canvas);

      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

    });

  }


  setSize(width, height) {

    const canvas = this.gl.canvas;

    canvas.width = width;
    canvas.height = height;

  }

  clear(color) {

    const gl = this.gl;

    gl.clearColor(0.1, 0.1, 0.3, 1);

    gl.clearDepth(1.0);
    gl.enable(gl.DEPTH_TEST);
    gl.depthFunc(gl.LEQUAL);

    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);


  }
  fillRect(rect, color) {
    const {
      createStaticDrawBuffer,
      gl,
      parseColor
    } = this;

    rect.fillers.fill = true;

    if (color) {
      rect.fragColorPos = createStaticDrawBuffer(gl, [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]);

    }
  }
  textureFromImage(image, texture) {

    const {
      images,
      texture: {
        canvas
      }
    } = this;

    texture = texture ? texture : new Texture();

    const {
      width,
      height
    } = image;

    const y = canvas.height;

    if (canvas.width < width) {

      canvas.width = width;

    }

    texture.setup(image, y, width, height, 0);

    canvas.height += height;

    images[images.length] = texture;

    return texture;

  }

  createRectPos(w, h) {

    const rect = [w / 2, h / 2, -w / 2, h / 2, w / 2, -h / 2, -w / 2, -h / 2];


    return {
      rect,
      stroke: undefined
    };
  }

  getRectInfo(x, y, rect, stroke) {

    return this.createSquareBuffer(rect, stroke, [x, y, this.drawZAxis]);
  }

  createStaticDrawBuffer(gl, data) {

    const buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);

    return buffer;
  }

  createSquareBuffer(positions, strokePosition, coords) {
    const {
      gl,
      createStaticDrawBuffer
    } = this;

    const positionsBuffer = createStaticDrawBuffer(gl, positions);
    const strokePositionBuffer = createStaticDrawBuffer(gl, strokePosition);
    const modelViewMatrix = mat4.create();

    mat4.translate(modelViewMatrix, modelViewMatrix, coords);

    return [modelViewMatrix, positionsBuffer, strokePositionBuffer, this.createAttribInfo(2, gl.FLOAT, false, 0, 0), this.createAttribInfo(4, gl.FLOAT, false, 0, 0), positions.length / 2];
  }

  createAttribInfo(numComponents, type, normalize, stride, offset) {

    return {
      numComponents,
      type,
      normalize,
      stride,
      offset
    };
  }

  enableAttrib(buffer, attrib, gl, {
    numComponents,
    type,
    normalize,
    stride,
    offset
  }) {

    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.vertexAttribPointer(attrib, numComponents, type, normalize, stride, offset);
    gl.enableVertexAttribArray(attrib);

  }

   drawTexture(texture, gl, canvas, enableAttrib, createStaticDrawBuffer, textAttribInfo, vertexCount, textureCoord, textMatrix, useText) {

    const _width = canvas.width;
    const _height = canvas.height;

    const {
      x,
      y,
      width,
      height,
      matrix
    } = texture;

    const realX = x / _width;
    const realWidth = realX + width / _width;
    const realHeight = y / _height;
    const realY = (y + height) / _height;

    const fragTextPos = createStaticDrawBuffer(gl, [realWidth, realHeight, realX, realHeight, realWidth, realY, realX, realY, ]);

    gl.uniformMatrix3fv(textMatrix, false, matrix);
    enableAttrib(fragTextPos, textureCoord, gl, textAttribInfo);
    gl.uniform1f(useText, true);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertexCount);

    gl.uniform1f(useText, false);
    gl.disableVertexAttribArray(textureCoord);
  }

  drawBuffer(buffer) {

    const {
      gl,
      drawTexture,
      enableAttrib,
      createStaticDrawBuffer,
      currentCamera,
      texture: {
        context,
        canvas,
        textAttribInfo
      },
      programInfo: {
        uniformLocations,
        program,
        attribLocations: {
          vertexPosition,
          vertexColor,
          textureCoord
        }
      }
    } = this;

    const cameraMatrix = currentCamera.matrix;

    const {
      positionsBuffer,
      fragColorPos,
      strokeColorPos,
      strokePositionBuffer,
      matrix,
      vertexAttribInfo,
      vertextColorAttribInfo,
      vertexCount,
      fragTextPos,
      fillers: {
        fill,
        stroke,
        texture
      },
      strokeSize,
      textureInfo,
      multiTextures
    } = buffer;

    gl.uniformMatrix4fv(uniformLocations.projectionMatrix, false, cameraMatrix);
    gl.uniformMatrix4fv(uniformLocations.modelViewMatrix, false, matrix);

    if (fill) {

      enableAttrib(positionsBuffer, vertexPosition, gl, vertexAttribInfo);
      enableAttrib(fragColorPos, vertexColor, gl, vertextColorAttribInfo);
      gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertexCount);
      gl.disableVertexAttribArray(vertexColor);

    }

    const ortho_mat = [2/5,0,0,0, 0,2/5,0,0, 0,0,1,0, 0,0,0,1];
    const identity_mat = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];
    gl.uniformMatrix4fv(uniformLocations.projectionMatrix, false, ortho_mat);
    gl.uniformMatrix4fv(uniformLocations.modelViewMatrix, false, identity_mat);


    if (texture) {

      const _width = canvas.width;
      const _height = canvas.height;

      drawTexture(textureInfo, gl, canvas, enableAttrib, createStaticDrawBuffer, textAttribInfo, vertexCount, textureCoord, uniformLocations.textMatrix, uniformLocations.useText);

    }
  }

  static loadShader(gl, program, type, source) {

    const shader = gl.createShader(type);
    gl.shaderSource(shader, source);
    gl.compileShader(shader);
    gl.attachShader(program, shader);

  }

  static async create(canvas, width, height, zAxis = 6) {
    canvas.width = width;
    canvas.height = height;

    const gl = canvas.getContext("webgl");

    const shaderProgram = gl.createProgram();

    Display.loadShader(gl, shaderProgram, gl.VERTEX_SHADER, VERTEX_SHADER);
    Display.loadShader(gl, shaderProgram, gl.FRAGMENT_SHADER, FRAGMENT_SHADER);

    gl.linkProgram(shaderProgram);


    const programInfo = {
      program: shaderProgram,
      attribLocations: {
        vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
        vertexColor: gl.getAttribLocation(shaderProgram, 'aVertexColor'),
        textureCoord: gl.getAttribLocation(shaderProgram, 'aTextureCoord'),


      },
      uniformLocations: {
        projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
        modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
        textMatrix: gl.getUniformLocation(shaderProgram, 'uTextMatrix'),
        sampler: gl.getUniformLocation(shaderProgram, 'uSampler'),
        useText: gl.getUniformLocation(shaderProgram, 'aUseText'),
        pointSize: gl.getUniformLocation(shaderProgram, 'uPointSize'),
      },
    };

    gl.useProgram(programInfo.program);

    gl.uniform1f(programInfo.uniformLocations.pointSize, 1.0);

    gl.enable(gl.BLEND);
    gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);

    const textureBuffer = gl.createTexture();

    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, textureBuffer);
    gl.uniform1i(programInfo.uniformLocations.uSampler, 0);

    const textureCanvas = document.createElement("canvas");
    textureCanvas.width = 0;
    textureCanvas.height = 0;

    let texture = {
      canvas: textureCanvas,
      buffer: textureBuffer,
      context: textureCanvas.getContext("2d"),
    };

    return new Display(gl, programInfo, zAxis, texture);
  }
}

class Entity extends Rect {

  constructor() {

    super();

    this.velocity = vec2.create();
    this.area = undefined;
    this.mass = 2;

    this.updateFillers = {};
    this.delete = false;
    this.draw = true;
  }

  setup(w, h, ...args) {
    this.area = vec2.fromValues(w, h);
    super.setup(...args);

    return this;
  }

  fill(...args) {
    this.updateFillers.fill = args;
  }

  attachImage(image) {
    super.attachImage(image);
  }

  move(x, y) {

    super.move([x, y, this.coords[2]]);

    return this;

  }

  setSize(w, h) {

    if (typeof w == "object") {
      h = w[1];
      w = w[0];
    }

    const area = this.area;
    const [_w, _h] = area;
    super.scale([w / _w, h / _h, 1]);

    area[0] = w;
    area[1] = h;

    return this;

  }

}

class Engine {
  constructor(time_step, update, render, allowedSkippedFrames) {
    this.accumulated_time = 0;
    this.animation_frame_request = undefined, this.time = undefined, this.time_step = time_step, this.updated = false;
    this.update = update;
    this.render = render;
    this.allowedSkippedFrames = allowedSkippedFrames;
    this.run = this.run.bind(this);
    this.end = false;
  }
  run(time_stamp) {
    const {
      accumulated_time,
      time,
      time_step,
      updated,
      update,
      render,
      allowedSkippedFrames,
      end
    } = this;
    this.accumulated_time += time_stamp - time;
    this.time = time_stamp;
    update(time_stamp);

    render(time_stamp);

    if (end) {
      return;
    }
    this.animation_frame_request = requestAnimationFrame(this.run);
  }
  start() {
    this.accumulated_time = this.time_step;
    this.time = performance.now();
    this.animation_frame_request = requestAnimationFrame(this.run);
  }
  stop() {
    this.end = true;
    cancelAnimationFrame(this.animation_frame_request);
  }
}

const engineMath = {
  randomBetween: function(min, max) {
    return min + Math.random() * (max - min);
  },

};

class Quixotic {

  constructor(display) {

    this.display = display;

    this.engine = undefined;

    this.render = undefined;
    this.update = undefined;
    this.frameRate = undefined;

    this.time = 0;
    this.speed = 1;
    this.world = {

      objects: {},
      objectsCollisionInfo: {},
      objectsArray: [],
      classesInfo: {}

    };

    this.timePassed = 0;

  }

  createEntity(Class, ...args) {
    const display = this.display;
    const {
      rect,
      stroke
    } = display.createRectPos(5, 5);

    let instance = new Entity();

    instance.setup(5, 5, ...display.getRectInfo(0, 0, rect, stroke, "#000"));
    this.world.objectsArray.push(instance);
    return instance;
  }

  createBackground(objects) {
    const buffer = document.createElement("canvas").getContext("2d");

    const bufferRect = this.createEntity();
    const display = this.display;

    let {
      zAxis,
      canvas: {
        width,
        height,
      },
      currentCamera
    } = display;
    const cameraMatrix = currentCamera.matrix;

    zAxis--;
    const halfZ = zAxis / 2;
    let {
      coords: [x, y],
      area: [w, h]
    } = objects[objects.length - 1];

    const worldViewProjection = mat4.create();
    let [bX, bY, lX, lY] = [x + w, y + h, x - w, y - h]; //big x, little x

    for (let i = objects.length - 1; i--;) {
      const object = objects[i];
      const {
        coords: [_x, _y],
        area: [_w, _h]
      } = objects[i];

      if (_x > bX) {
        bX = (_x + _w);
      } else if (_x < lX) {
        lX = _x - _w;
      }
      if (_y > bY) {
        bY = _y + _h;
      } else if (_y < lY) {
        lY = _y - _h;
      }

    }

    buffer.canvas.width = width;
    buffer.canvas.height = height;

    for (let i = objects.length; i--;) {
      const {
        coords: [_x, _y],
        area: [_w, _h]
      } = objects[i];
      mat4.multiply(worldViewProjection, this.display.currentCamera.matrix, objects[i].matrix);
      const points = [
        [-_w / 2, -_h / 2, 0],
        [_w / 2, _h / 2, 0],
      ].map(p => {
        const ndc = vec3.transformMat4([], p, worldViewProjection);
        return [
          (ndc[0] * 0.5 + 0.5) * width,
          (ndc[1] * -0.5 + 0.5) * height,
        ];
      });
      const ww = points[1][0] - points[0][0];
      const hh = points[1][1] - points[0][1];

      buffer.strokeStyle = 'red';
      buffer.strokeRect(...points[0], ww, hh);
    }

    bufferRect.move((bX + lX) / 2, (bY + lY) / 2);

    bufferRect.setSize(Math.abs(bX) + Math.abs(lX) - 5, Math.abs(bY) + Math.abs(lY) - 5);

    const texture = display.textureFromImage(buffer.canvas);

    bufferRect.attachImage(texture);

  }

  buildWorld({
    objects,
    classes,
    tileMap
  }) {

    const world = this.world;

    if (Array.isArray(objects)) {
      for (let i = objects.length - 1; i > -1; i--) {
        const object = objects[i];
        const {
          amount,
          position,
        } = object;

        const {
          rangeX,
          rangeY
        } = position;

        let _array = [];

        for (let j = amount; j--;) {
          const instance = this.createEntity();
          instance.move(engineMath.randomBetween(...rangeX), engineMath.randomBetween(...rangeY));

          _array.push(instance);
        }
        world.objects[name] = _array;
        world.objectsArray.push(..._array);
      }
    }
    return;

  }


  setup(game) {

    const {
      style: {
        backgroundColor,
        backgroundImage,
        stroke
      },
      world,
      engine: {
        frameRate,
        update,
        render
      },
      setup
    } = game;
    this.buildWorld(world);
    const {
      display,
      world: {
        objectsArray,
        objects
      }
    } = this;

    this.frameRate = frameRate;
    let lastUpdated = 0;
    this.update = (time) => {
      let deltaTime = time - lastUpdated;
      lastUpdated = time;
      const speed = this.speed;
      this.timePassed += deltaTime * speed;

      update(deltaTime / 1000, this);
    };
    let lastRendered = 0;
    this.render = (timeStamp) => {
      if (backgroundColor) display.clear(backgroundColor);
      const length = objectsArray.length;
      for (let i = length; i--;) {
        const object = objectsArray[length - i - 1];
        if (object.draw) {
          const updateFillers = Object.entries(object.updateFillers);
          const fillersLength = updateFillers.length;
          if (fillersLength) {
            for (let j = fillersLength; j--;) {
              const [func, args] = updateFillers[fillersLength - j - 1];
              display[func + "Rect"](object, ...args);
            }
            object.updateFillers = {};
          }
          display.drawBuffer(object);
        }
      }

      render(display, this);
    };
    setup(this, this.world);
    this.engine = new Engine(this.frameRate, this.update, this.render, 3);
    this.engine.start();
    return game;
  }

  static async create({
    display: {
      canvas,
      width,
      height,
      zAxis
    },
    homeURL
  }) {

    const display = await Display.create(canvas, width, height, zAxis);

    return new Quixotic(display);
  }

}

const fps = document.querySelector("#fps");
const minLength = innerWidth > innerHeight ? innerHeight : innerWidth;
const game = {

  create: {

    display: {

      canvas: document.querySelector("#canvas"),
      zAxis: 90,
      width: minLength,
      height: minLength,

    },

    homeURL: "/src"
  },

  style: {
    backgroundColor: "#111122"
  },

  world: {
    objects: [{
      name: "trees",

      array: true,
      amount: 5,
      position: {
        type: "random",
        rangeX: [-37.5, 37.5],
        rangeY: [-37.5, 37.5]

      }
    }]
  },

  engine: {

    frameRate: 1000 / 30,

    update: function(deltaTime, engine) {
      fps.innerText = 1 / deltaTime;
    },

    render: function(display) {}
  },

  setup: function(engine, {
    objectsArray
  }) {

    objectsArray.forEach(tree => {
      tree.fill("#00ff00")
    })
    engine.createBackground(objectsArray);
  }

};

Quixotic.create(game.create)
  .then(engine => {

    engine.setup(game);
  });
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background-color: #111c31; overflow: hidden; align-items: space-around; display: grid; height: 100%; width: 100%; }
#canvas { background-color: #152646; /* justify-self: center; */}
#fps { position: fixed; color: white; right: 0; }
canvas { position: fixed }
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"></script>
<canvas id="canvas" width="300" height="300"></canvas>
<p id="fps"></p>
© www.soinside.com 2019 - 2024. All rights reserved.