Javascript自上而下的相机-画布2D转换矩阵

时间:2018-06-24 09:59:19

标签: javascript matrix camera 2d

我正在尝试创建一个摄像机,以显示包含在平面2d世界中的物品。 忽略碰撞,剔除,世界边界/界限等,我似乎很难为相机的“视口”制定变换,以便将世界中的对象渲染到应有的位置(即,在“世界到相机的坐标”中) “)。

我希望相机:

  • 可以在世界各地移动(翻译)
  • 能够围绕目标旋转(旋转)
  • 能够缩放(缩放)。

我在下面的示例中尝试使用println(基本,不通过requestAnimFramce或类似的东西重绘,只是一个快照)。

很快,这就是它想要做的:

  1. 创造世界
  2. 使用随机放置的块元素填充它
  3. 创建具有多个参数(例如位置,旋转等)的相机
  4. 渲染摄像机的“视口”

代码:

MyObject obj = new MyObject();
myMethod(obj); // Doesn't work
System.out.print(obj); // Works (why?)

static void myMethod(String str) {
    // Do things
}

似乎,我正在使用mat2d(转换->旋转->缩放)使用正确的转换顺序:

  • glMatrix
  • <!DOCTYPE html> <html lang="en-gb"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Camera</title> <style> *{ /*! normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css */ button,hr,input{overflow:visible}progress,sub,sup{vertical-align:baseline}[type=checkbox],[type=radio],legend{box-sizing:border-box;padding:0}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{color:inherit;display:table;max-width:100%;white-space:normal}textarea{overflow:auto}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none} } *{ box-sizing:border-box; margin:0; padding:0; font-family:Arial; } body{ background:#e4e4e4; padding:15px; font-size:14px; overflow:hidden; } h1{ text-align:center; font-size:18px; margin:0 0 15px 0; position:absolute; top:20px; left:50%; transform:translate(-50%,0); z-index:99999; } #map{ height:100%; width:100%; position:absolute; top:0; left:0; overflow:hidden; z-index:1; } #target{ position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); z-index:99; border:1px solid #999; padding:2px; } .block{ background:#ccc; border:1px solid #333; position:absolute; z-index:101; transform:translate(-50%,-50%); } </style> </head> <body> <h1>Camera</h1> <script src="/js/g.js"></script> <script src="/js/gl-matrix-min.js"></script> <script> (function(w,d){ var Block = function(conf){ this.id = conf.id || Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 20); this.dimensions = conf.dimensions || [50,50]; this.position = conf.position || [0,0]; this.init(); }; Block.prototype = { init: function(){ this.shape = this.generateShape(); }, generateShape: function(){ var canvas = d.createElement('canvas'); canvas.width = this.dimensions[0]; canvas.height = this.dimensions[1]; var ctx = canvas.getContext('2d'); ctx.fillStyle = 'rgba(0,0,0,.4)'; ctx.rect(0, 0, this.dimensions[0], this.dimensions[1]); ctx.fill(); return canvas; }, draw: function(ctx){ ctx.save(); ctx.translate( this.position[0], this.position[1] ); ctx.drawImage( this.shape, 0, 0 ); ctx.restore(); } }; var World = function(conf){ this.dimensions = conf.dimensions || [10000,10000]; this.blocks = {}; this.init(); }; World.prototype = { init: function(){ this.grid = this.generateGrid(); this.initBlocks(); }, generateGrid: function(){ var canvas = d.createElement('canvas'); canvas.width = this.dimensions[0]; canvas.height = this.dimensions[1]; var ctx = canvas.getContext('2d'); var grid_spacing = 50; ctx.lineWidth = 1; ctx.strokeStyle = 'rgba(200,200,200,0.7)'; ctx.beginPath(); for( var x=0; x<this.dimensions[0]; x = x + grid_spacing ){ ctx.moveTo(0, x); ctx.lineTo(canvas.width, x); } for( var y=0; y<this.dimensions[1]; y = y + grid_spacing ){ ctx.moveTo(y, 0); ctx.lineTo(y, canvas.height); } ctx.closePath(); ctx.stroke(); return canvas; }, initBlocks: function(){ var nb_blocks_min = 80, max_blocks_extra = 100; var nb_blocks = Math.floor(Math.random() * max_blocks_extra) + nb_blocks_min; var block_min_size_x = 80, block_extra_size_x = 150, block_min_size_y = 80, block_extra_size_y = 150; var block_id; for( var i=0; i<nb_blocks; i++ ){ block_id = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 20); this.blocks[block_id] = new Block({ id: block_id, dimensions: [ Math.floor(Math.random() * block_extra_size_x) + block_min_size_x, Math.floor(Math.random() * block_extra_size_y) + block_min_size_y, ], position: [ Math.floor(Math.random() * this.dimensions[0]) + 0, Math.floor(Math.random() * this.dimensions[1]) + 0, ] }); } }, draw: function(ctx){ // draw grid ctx.drawImage(this.grid, 0, 0); // draw blocks var block, block_el; for( var block_id in this.blocks ){ block = this.blocks[block_id]; block.draw(ctx); } }, }; var world = new World({}); var Camera = function(conf){ this.position = conf.position; this.translate = vec2.create(); this.scale = vec2.create(); vec2.set(this.scale, conf.scale, conf.scale); this.rotation = conf.rotation || 0; this.matrix = mat2d.create(); this.renderable_minimap = conf.renderable_minimap || false; this.canvas_id = conf.canvas_id; this.width = conf.width; this.height = conf.height; this.world = conf.world; this.init(); }; Camera.prototype = { init: function(){ this.canvas = d.createElement('canvas'); this.canvas.id = this.canvas_id; this.canvas.width = this.width; this.canvas.height = this.height; d.querySelector('body').appendChild(this.canvas); this.ctx = this.canvas.getContext('2d'); }, setMatrix: function(){ mat2d.identity(this.matrix); this.translate = vec2.set(this.translate, this.position[0], this.position[1]); this.matrix = mat2d.translate(this.matrix, this.matrix, this.translate); this.matrix = mat2d.rotate(this.matrix, this.matrix, this.rotation); this.matrix = mat2d.scale(this.matrix, this.matrix, this.scale); this.ctx.setTransform( this.matrix[0], this.matrix[1], this.matrix[2], this.matrix[3], this.matrix[4], this.matrix[5] ); }, draw: function(){ this.ctx.clearRect(0, 0, this.width, this.height); this.ctx.save(); this.setMatrix(); this.world.draw(this.ctx); this.ctx.restore(); }, }; var map = new Camera({ // position: [world.dimensions[0]/2, world.dimensions[1]/2], position: [0, 0], rotation: Math.PI/3, scale: .1, canvas_id: 'map', height: w.innerHeight, width: w.innerWidth, world: world, renderable_minimap: true, }); map.draw(); })(window,document); </script> </body> </html>
  • this.translate = vec2.set(this.translate, this.position[0], this.position[1]);
  • this.matrix = mat2d.translate(this.matrix, this.matrix, this.translate);

我怀疑我缺少世界->浏览器转换吗?还是其他?

编辑:经过几次尝试,我想到了:

https://destination.example/accounts/record_activity

转换似乎好得多(即使尚未应用缩放)。我仍然对应用位移的方式不满意。 总体而言,位移->位置处理还能改善什么?

0 个答案:

没有答案