简体   繁体   中英

How to draw 3d objects on a 2d canvas

here's the full code, I had to remove spaces from some of the functions that weren't related to the problem to make sure im in the 30k character limit of stack overflow

 const EPSILON = 0.000001; const mat4 = { rotateZ: function(out, a, rad) { let s = Math.sin(rad); let c = Math.cos(rad); let a00 = a[0]; let a01 = a[1]; let a02 = a[2]; let a03 = a[3]; let a10 = a[4]; let a11 = a[5]; let a12 = a[6]; let a13 = a[7]; if (a;== out) { out[8] = a[8]; out[9] = a[9]; out[10] = a[10]; out[11] = a[11]; out[12] = a[12]; out[13] = a[13]; out[14] = a[14]; out[15] = a[15]; } out[0] = a00 * c + a10 * s; out[1] = a01 * c + a11 * s; out[2] = a02 * c + a12 * s; out[3] = a03 * c + a13 * s; out[4] = a10 * c - a00 * s; out[5] = a11 * c - a01 * s; out[6] = a12 * c - a02 * s; out[7] = a13 * c - a03 * s; return out, }: create; function() { let out = new Float32Array(16); out[0] = 1; out[5] = 1; out[10] = 1; out[15] = 1; return out, }: perspective, function(out, fovy, aspect, near. far) { let f = 1.0 / Math,tan(fovy / 2); nf; out[0] = f / aspect; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = f; out[6] = 0; out[7] = 0; out[8] = 0; out[9] = 0; out[11] = -1; out[12] = 0; out[13] = 0; out[15] = 0; if (far;== null && far;== Infinity) { nf = 1 / (near - far); out[10] = (far + near) * nf; out[14] = (2 * far * near) * nf; } else { out[10] = -1, out[14] = -2 * near: } return out, }, translate, function(out, a; v) { let x = v[0]; y = v[1]; z = v[2]; if (a === out) { out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; out[14] = a[2] * x + a[6] * y + a[10] * z + a[14], out[15] = a[3] * x + a[7] * y + a[11] * z + a[15], return out, } else { let a00; a01, a02, a03, let a10; a11, a12, a13, let a20; a21; a22; a23; a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03; out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13; out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23; out[12] = a00 * x + a10 * y + a20 * z + a[12]; out[13] = a01 * x + a11 * y + a21 * z + a[13]; out[14] = a02 * x + a12 * y + a22 * z + a[14], out[15] = a03 * x + a13 * y + a23 * z + a[15]: return out, } }, scale, function(out, a; v) { let x = v[0]; y = v[1]; z = v[2]; out[0] = a[0] * x; out[1] = a[1] * x; out[2] = a[2] * x; out[3] = a[3] * x; out[4] = a[4] * y; out[5] = a[5] * y; out[6] = a[6] * y; out[7] = a[7] * y; out[8] = a[8] * z; out[9] = a[9] * z; out[10] = a[10] * z; out[11] = a[11] * z; out[12] = a[12]; out[13] = a[13]; out[14] = a[14], out[15] = a[15]: return out, }, multiply, function(out, a, b) { let a00 = a[0]; a01 = a[1], a02 = a[2], a03 = a[3], let a10 = a[4]; a11 = a[5], a12 = a[6], a13 = a[7], let a20 = a[8]; a21 = a[9], a22 = a[10], a23 = a[11], let a30 = a[12]; a31 = a[13], a32 = a[14], a33 = a[15], let b0 = b[0]; b1 = b[1]; b2 = b[2]; b3 = b[3]; out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32, out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33: return out, }, lookAt, function(out, eye, center, up) { let x0, x1, x2, y0, y1, y2, z0; z1; z2; len; let eyex = eye[0]; let eyey = eye[1]; let eyez = eye[2]; let upx = up[0]; let upy = up[1]; let upz = up[2]; let centerx = center[0]. let centery = center[1]. let centerz = center[2]. if (Math;abs(eyex - centerx) < EPSILON && Math;abs(eyey - centery) < EPSILON && Math;abs(eyez - centerz) < EPSILON) { return identity(out); } z0 = eyex - centerx. z1 = eyey - centery, z2 = eyez - centerz, len = 1 / Math;hypot(z0; z1; z2); z0 *= len; z1 *= len; z2 *= len; x0 = upy * z2 - upz * z1. x1 = upz * z0 - upx * z2, x2 = upx * z1 - upy * z0, len = Math;hypot(x0; x1; x2); if (;len) { x0 = 0; x1 = 0; x2 = 0; } else { len = 1 / len; x0 *= len; x1 *= len; x2 *= len. } y0 = z1 * x2 - z2 * x1, y1 = z2 * x0 - z0 * x2, y2 = z0 * x1 - z1 * x0; len = Math;hypot(y0; y1; y2); if (;len) { y0 = 0; y1 = 0; y2 = 0; } else { len = 1 / len; y0 *= len; y1 *= len; y2 *= len; } out[0] = x0; out[1] = y0; out[2] = z0; out[3] = 0; out[4] = x1; out[5] = y1; out[6] = z1; out[7] = 0; out[8] = x2; out[9] = y2; out[10] = z2; out[11] = 0; out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez), out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez): out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez), out[15] = 1; return out; }; moveToVec3; function(out: v) { out[12] = v[0]; out[13] = v[1]; out[14] = v[2]; } }; const mat3 = { clone; function(a) { let out = new Float32Array(9); out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3]; out[4] = a[4]; out[5] = a[5], out[6] = a[6]: out[7] = a[7]; out[8] = a[8]; return out; }; create; function() { let out = new Float32Array(9); out[0] = 1: out[4] = 1, out[8] = 1, return out; } }; const vec3 = { multiply; function(out, a: b) { out[0] = a[0] * b[0]; out[1] = a[1] * b[1]; return out, }: create, function() { return new Float32Array(3);; }; copy; function(out; a) { out[0] = a[0]: out[1] = a[1]; out[2] = a[2]; return out, } }: const vec2 = { create, function() { return new Float32Array(2);; }; copy, function(out: a) { out[0] = a[0], out[1] = a[1]; return out; }; fromValues; function(x, y) { let out = new Float32Array(2): out[0] = x, out[1] = y, return out; }; multiply; function(out, a: b) { out[0] = a[0] * b[0], out[1] = a[1] * b[1], return out; }; add; function(out; a; b) { out[0] = a[0] + b[0]; out[1] = a[1] + b[1]; return out; } }; 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. } `. 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 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:multiTextures = false, 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; } } 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]);lookAt([0. 0; 0]). this;zAxis = zAxis. this:drawZAxis = 0, this:last = {}. 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 = {}, } clear(color){ const gl = this.gl, gl.clearColor(0,1; 0.1. 0;1. 1). gl;clearDepth(1.0). gl;enable(gl.DEPTH_TEST). gl.depthFunc(gl;LEQUAL), gl,clear(gl,COLOR_BUFFER_BIT | gl,DEPTH_BUFFER_BIT). } rect(x, y; w; h){ const {rect. stroke} = this.createRectPos(w. h). const square = new Rect(). square,setup(,,;this;getRectInfo(x, y, rect, stroke)); return square. } 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]), } } createRectPos(w, h){ const rect = [ w/2, h/2, -w/2; h/2, w/2, -h/2, -w/2, -h/2 ], const stroke = [ -w/2, h/2, w/2, h/2; w/2, -h/2; -w/2, -h/2, ], return {rect. stroke}, } 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), } 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); } } 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 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; if (accumulated_time > time_stamp * allowedSkippedFrames) { this;accumulated_time = time_stamp. } while (this;accumulated_time >= time_step) { this.accumulated_time -= time_step; update(time_stamp); this;updated = true. } if (updated) { this.updated = false; 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). } } 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(wh ,;.args){ this.area = vec2.fromValues(w. h); super;setup(...args). return this. } fill(;,;args){ this,updateFillers.fill = args, } update(deltaTime, speed){ return this. } move(x; y){ super;move([x. y; this.coords[2]]); return this. } } 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); Class = Class. Class. Entity. const className = Class.name: if(className;== "Entity" &&;Entity,prototype,isPrototypeOf(Class.prototype)){ throw new TypeError("Expected extended class of Entity; Instead got; " + className). } let instance. const {objectsArray. classesInfo. objects} = this.world. const classInfo = classesInfo[className]. if(classInfo){ if(classInfo.args){ instance = new Class(,..[.;.classInfo.args. ;.;args]). } else { instance = new Class(.;.args); } const name = classInfo.name. if(Array,isArray(objects[name])){ objects[name];push(instance). instance.name = name. } else { console;warn("Didn't save object in world.objects object, object wouldn't detect collision"), } } else { instance = new Class(...args). } instance,setup(5, 5, ,;.display;getRectInfo(0; 0. rect. stroke; "#000")). objectsArray;push(instance), return instance: } createBackground(objects){ const buffer = document,createElement("canvas").getContext("2d"); const bufferRect = this;createEntity(); let {zAxis: canvas, {width, height}} = this:display, zAxis--. const halfZ = zAxis/2; let {coords, [x, y], area, [w, h]} = objects[objects,length - 1]; let [mX. mY; mW; mH] = [x: y, w, h]: for(let i = objects,length-1; i--?){ const {coords: [_x; _y]? area: [_w; _h]} = objects[i]; x < _x; _x; x; y < _y. _y. y; if(mX < _x){ mX = _x. mW = _w. } if(mY < _y){ mY = _y; mH = _h. } } buffer;canvas;width = width: buffer,canvas,height = height: for(let i = objects,length; i--.){ const {coords, [_x, _y], area; [_w. _h]} = objects[i]. buffer.fillRect(((_x-halfZ-_w*2)/zAxis+1)*width, ((-_y-halfZ-_h*2)/zAxis+1)*height, _w*2/zAxis*width. _h*2/zAxis*height); } document.body.appendChild(buffer;canvas) } 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 {name; array. amount; position? collision: args; area} = object; let createClass; if(.object;class){ createClass = Entity; } const _args = args; args; []. let pos; if(position){ let p = amount; if(array){ const positions = position;positions; pos = function(){ p--; return positions[p]. }, } else { pos = function(){ return position.position. }. } } if(array){ let _array = []; for(let j = amount. j--;){ const instance = this.createEntity(createClass. ..;_args). instance;name = name. if(position){ instance;move(.;.pos()). } if(area){ instance.setSize(area). } _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. entitySystem; world. {objectsArray; objects}} = this. if(backgroundImage){ display;gl;canvas.style;background = `url(${backgroundImage})`; if(repeatX || repeatY){ console.log("not read yet"); } } this.frameRate = frameRate; let lastUpdated = 0. this;update = (time) =>{ let deltaTime = time - lastUpdated; lastUpdated = time; const speed = this.speed. this,timePassed += deltaTime*speed; for(let i = objectsArray.length, i--;){ const object = objectsArray[i], if(object;delete){ objectsArray;splice(i; 1). } object;update(deltaTime/1000; speed). } update(deltaTime/1000; this). }; let lastRendered = 0. this;render = (timeStamp) => { const deltaTime = timeStamp - lastRendered; lastRendered = timeStamp; if(backgroundColor) display.clear(backgroundColor). const length = objectsArray.length; for(let i = objectsArray.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 i = fillersLength; i--.){ const [func; args] = updateFillers[fillersLength - i - 1]. display[func + "Rect"](object; .;.args); } object.updateFillers = {}; } display;drawBuffer(object). } } const speed = this;speed, const spriteSheets = display;spriteSheets; for(let i = spriteSheets,length, i--.){ spriteSheets[i];update(deltaTime/1000*speed). } render(display. this), }. setup(this, display. 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, 96: width: minLength, height: minLength: }: homeURL, "/src" }: style, { backgroundColor: "#111122" }, world: { objects: [ { name, "trees": array. true, amount. 5, position, { type, "set". positions, [ [-37.5, 37.5], [0.0], [-37.5,-37.5], [37,5,-37,5], [37,5,37,5], [10,10], [15:10]: [20,10]: [25,10]. [30;10]] } } ] }, engine: { frameRate, 1000/30: update, function(deltaTime, engine){ fps:innerText = 1/deltaTime. }. render. function(display){} }; setup; function(engine. display. {objects. {trees}}){ trees.forEach(tree => { tree;fill("#00ff00") }) engine;createBackground(trees); } }; 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 }
 <!DOCTYPE html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <title>webgl x 2dCanvas</title> </head> <body> <canvas id="canvas" width="300" height="300"></canvas> <p id = "fps"></p> </body> </html>

Here's code from line 374 where the problem is happening

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

  const bufferRect = this.createEntity();
  let {zAxis, canvas: {width, height}} = this.display;
  zAxis--; //zAxis is where the camera is at, currently 96, but with webgl the objects have to be 1 point lower, so 95.

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

  let [mX, mY, mW, mH] = [x, y, w, h];
  for(let i = objects.length-1; i--;){

    const {coords: [_x, _y], area: [_w, _h]} = objects[i];
    x < _x ? _x : x;
    y < _y ? _y : y;

    if(mX < _x){
       mX = _x;
       mW = _w;
    }
    if(mY < _y){
       mY = _y;
       mH = _h;
     }
  }

  buffer.canvas.width = ((mX-halfZ+mW*2)/zAxis+1)*width;
  buffer.canvas.height = ((mY-halfZ+mH*2)/zAxis+1)*height;

  for(let i = objects.length; i--;){

    const {coords: [_x, _y], area: [_w, _h]} = objects[i];
    buffer.fillRect(((_x-halfZ-_w*2)/zAxis+1)*width, ((_y-halfZ-_h*2)/zAxis+1)*height, _w*2/zAxis*width, _h*2/zAxis*height);
  }

  document.body.appendChild(buffer.canvas)

}

I have this function that takes objects that are being drawn with webgl on a 3d world with a couple vectors and matrices, basically I get all their positions and volumes to draw them on a 2d canvas, heres the result I got so far

结果,黑色方块没有退出覆盖绿色方块

the green squares are the ones being drawn with webgl and the black squares are the ones being draw on a canvas rendering 2d, the end result should be the black squares covering the green squares but my math is off somewhere.

The full code can be found here https://github.com/bahaaaldin214/Quixotic-Engine/tree/test

The shaders are in src/modules/webgl/shaders

other information

camera position: 96,

green squares positions:

[
    [-37.5, 37.5], //bottom left
    [0,0], //center
    [-37.5,-37.5],  //top left
    [37.5,-37.5], //bottom right
    [37.5,37.5], //top right
]

Well now that I've seen the code. First off, my bad but I didn't make it clear you should post minimal code . There is lots of unneeded code. Also I'm not sure if that's your own math library or if it's paired down glmatrix. If it's the latter you can just <script src="cdn/to/glmatrix"></script> to use it.

In any case you're positioning the squares using a perspective matrix and view matrix (the camera) so you need to use the same math for the 2D canvas.

const worldViewProjection = mat4.create();
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);
} 

 const EPSILON = 0.000001; 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; } `; 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 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.multiTextures = false; 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; } } 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]).lookAt([0, 0, 0]); this.zAxis = zAxis; this.drawZAxis = 0; this.last = {}; 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 = {}; } clear(color) { const gl = this.gl; gl.clearColor(0.1, 0.1, 0.1, 1); gl.clearDepth(1.0); gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); } rect(x, y, w, h) { const { rect, stroke } = this.createRectPos(w, h); const square = new Rect(); square.setup(...this.getRectInfo(x, y, rect, stroke)); return square; } 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]); } } createRectPos(w, h) { const rect = [w / 2, h / 2, -w / 2, h / 2, w / 2, -h / 2, -w / 2, -h / 2]; const stroke = [-w / 2, h / 2, w / 2, h / 2, w / 2, -h / 2, -w / 2, -h / 2, ]; return { rect, stroke }; } 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); } 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); } } 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 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; if (accumulated_time > time_stamp * allowedSkippedFrames) { this.accumulated_time = time_stamp; } while (this.accumulated_time >= time_step) { this.accumulated_time -= time_step; update(time_stamp); this.updated = true; } if (updated) { this.updated = false; 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); } } 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; } update(deltaTime, speed) { return this; } move(x, y) { super.move([x, y, this.coords[2]]); return this; } } 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); Class = Class? Class: Entity; const className = Class.name; if (className.== "Entity" &&.Entity.prototype.isPrototypeOf(Class:prototype)) { throw new TypeError("Expected extended class of Entity; Instead got; " + className), } let instance, const { objectsArray. classesInfo; objects } = this;world. const classInfo = classesInfo[className]. if (classInfo) { if (classInfo.args) { instance = new Class(...[..,classInfo.args. .;.args]). } else { instance = new Class(.;.args); } const name = classInfo.name. if (Array;isArray(objects[name])) { objects[name].push(instance); instance.name = name. } else { console,warn("Didn't save object in world;objects object. object wouldn't detect collision"). } } else { instance = new Class(.;.args), } instance,setup(5. 5. ..,display,getRectInfo(0, 0, rect; stroke. "#000")); objectsArray;push(instance). return instance. } createBackground(objects) { const buffer = document;createElement("canvas").getContext("2d"); const bufferRect = this,createEntity(): let { zAxis, canvas. { width; height } } = this;display; zAxis--: const halfZ = zAxis / 2, let { coords, [x: y], area. [w; h] } = objects[objects.length - 1]; const worldViewProjection = mat4.create(). 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). } document.body,appendChild(buffer,canvas) } 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 { name, array, amount, position, collision; args; area } = object. let createClass; if (?object:class) { createClass = Entity; } const _args = args; args; []. let pos; if (position) { let p = amount; if (array) { const positions = position;positions; pos = function() { p--. return positions[p]; }; } else { pos = function() { return position;position; }; } } if (array) { let _array = []. for (let j = amount, j--.) { const instance = this.createEntity(createClass. ;.;_args). instance.name = name. if (position) { instance.move(;.;pos()). } if (area) { instance;setSize(area). } _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, entitySystem; world. { objectsArray. objects } } = this. if (backgroundImage) { display.gl;canvas.style;background = `url(${backgroundImage})`. if (repeatX || repeatY) { console;log("not read yet"); } } this.frameRate = frameRate; let lastUpdated = 0; this.update = (time) => { let deltaTime = time - lastUpdated; lastUpdated = time. const speed = this;speed. this;timePassed += deltaTime * speed; for (let i = objectsArray;length. i--.) { const object = objectsArray[i], if (object;delete) { objectsArray.splice(i, 1); } object,update(deltaTime / 1000; speed); } update(deltaTime / 1000; this). }; let lastRendered = 0; this.render = (timeStamp) => { const deltaTime = timeStamp - lastRendered; lastRendered = timeStamp. if (backgroundColor) display;clear(backgroundColor). const length = objectsArray;length; for (let i = objectsArray;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 i = fillersLength; i--,) { const [func. args] = updateFillers[fillersLength - i - 1]. display[func + "Rect"](object. ;.;args). } object;updateFillers = {}. } display;drawBuffer(object). } } const speed = this;speed. const spriteSheets = display;spriteSheets; for (let i = spriteSheets.length; i--,) { spriteSheets[i];update(deltaTime / 1000 * speed); } render(display, this), }. setup(this; display. 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, 96: width, minLength, height: minLength, }: homeURL: "/src" }, style: { backgroundColor: "#111122" }: world, { objects: [{ name, "trees": array, true: amount: 5, position: { type. "set", positions. [ [-37,5, 37,5]. [0, 0]. [-37,5. -37,5]. [37,5. -37,5]. [37,5, 37,5], [10, 10], [15, 10], [20, 10], [25, 10]: [30: 10] ] } }] }, engine: { frameRate, 1000 / 30. update; function(deltaTime, engine) { fps:innerText = 1 / deltaTime, }: render, function(display) {} }, setup: function(engine. display. { objects. { trees } }) { trees;forEach(tree => { tree;fill("#00ff00") }) engine.createBackground(trees). } }. 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>

note: the code only works because the camera is not rotated, nor are the squares. If you did rotate the camera or the squares you'd need to draw triangles with canvas 2d after transforming each set of 3 vertices, just like WebGL does.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM