Open imuncle opened 4 years ago
3D立体画是裸眼3D的一种,最初是由一些极具创意的画家在街道或平地上创作的,后来又有了在纸上的绘制的作品。我的第一幅3D立体画是高二上画的:
然后上大学后许久没有画过了。
近期武汉新型冠状病毒肺炎疫情严重,只能呆在家,闲来无事想写一个能直接生成3D立体画的程序。
思路很简单,我们的目的是在相机的图像中将需要显示的物体放置在纸面上,还是放出这个世界坐标和图像坐标的关系:
首先定义一个相机:
// 模拟相机类 var Camera = function() { var arr = [[1000, 0, 400], [0, 1000, 300], [0, 0, 1]]; this.intrinsic = new Matrix(arr); // 相机内参 var arr1 = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]]; this.extrinsic = new Matrix(arr1); //相机外参 var arr2 = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]; this.rotate = new Matrix(arr2); // 相机旋转矩阵 this.camera_angle = 0; this.camera_distance = 100; this.camera_height = 100; this.camera_move = 0; }
然后我还创建了一个矩阵相乘类:
// 简单的矩阵库,只支持矩阵乘法 var Matrix = function(matrix) { this.matrix = matrix; this.rows = this.matrix.length; // 行数y this.cols = this.matrix[0].length; // 列数x } // 矩阵相乘 Matrix.prototype.dot = function(matrix) { var self_matrix = this; var cols = matrix.cols; var rows = this.rows; var result = new Array(rows); for(var i = 0; i < rows; i++) { result[i] = new Array(cols); } for(var i = 0; i < cols; i++) { for(var j = 0; j < rows; j++) { var sum = 0; for(var k = 0; k < self_matrix.cols; k++) { sum += self_matrix.matrix[j][k] * matrix.matrix[k][i]; } result[j][i] = sum; } } var new_matrix = new Matrix(result); return new_matrix; }
最关键的是接下来这个paper类,我模拟了一个A4纸,A4四个角点的世界坐标是已知给定的,根据相机内参和外参计算出其在图像中的坐标,就可以显示A4纸了。
// 纸张以中心旋转 Paper.prototype.rotate = function() { var arr = [[Math.cos(this.paper_angle), -Math.sin(this.paper_angle)], [Math.sin(this.paper_angle), Math.cos(this.paper_angle)]]; var rotate = new Matrix(arr); var corners = [ [[-105], [-148.5]], [[105], [-148.5]], [[105], [148.5]], [[-105], [148.5]] ]; for(var i = 0; i < corners.length; i++) { var point = new Matrix(corners[i]); var result = rotate.dot(point); this.corners[i][0] = result.matrix[0][0] + 105; this.corners[i][1] = result.matrix[1][0] + 148.5; } } // 在canvas上显示纸张 Paper.prototype.show = function(context1) { this.rotate(); var arr = [[1, 0, 0, -this.height/2 + self.camera.camera_move], [0, 1, 0, self.camera.camera_height], [0, 0, 1, self.camera.camera_distance]]; self.camera.extrinsic.set(arr); var arr1 = [[1, 0, 0], [0, Math.cos(self.camera.camera_angle), -Math.sin(self.camera.camera_angle)], [0, Math.sin(self.camera.camera_angle), Math.cos(self.camera.camera_angle)]]; self.camera.rotate.set(arr1); self.camera.extrinsic = self.camera.rotate.dot(self.camera.extrinsic); context1.beginPath(); var point = new Matrix([[this.corners[3][0]], [0], [this.corners[3][1]], [1]]); var result = self.camera.intrinsic.dot(self.camera.extrinsic).dot(point); context1.moveTo(result.matrix[0][0] / result.matrix[2][0], result.matrix[1][0] / result.matrix[2][0]); // 从左下角逆时针 for(var i = 0; i < this.corners.length; i++) { point = new Matrix([[this.corners[i][0]], [0], [this.corners[i][1]], [1]]); result = self.camera.intrinsic.dot(self.camera.extrinsic).dot(point); context1.lineTo(result.matrix[0][0] / result.matrix[2][0], result.matrix[1][0] / result.matrix[2][0]); } context1.strokeStyle = 'red'; context1.lineWidth = 1; context1.stroke(); }
最后是生成图像的代码,逻辑很简单,遍历A4中的每个像素点,投影到图像坐标系,获取到对应的像素值。
Generate.prototype.show = function(context1, context2) { var context1 = self.canvas1.getContext('2d'); var context2 = self.canvas2.getContext('2d'); context2.clearRect(0, 0, self.canvas2.width, self.canvas2.height); var imageData = context2.getImageData(0, 0, self.canvas2.width, self.canvas2.height); var img_data = imageData.data; for(var i = 0; i < self.paper.width; i++) { for(var j = 0; j < self.paper.height; j++) { var point = [j, i]; point = self.paper.rotate_point(point); var temp_point = new Matrix([[point[0]], [0], [point[1]], [1]]); var result = self.camera.intrinsic.dot(self.camera.extrinsic).dot(temp_point); var pixel = context1.getImageData(result.matrix[0][0] / result.matrix[2][0], result.matrix[1][0] / result.matrix[2][0], 1, 1); var data = pixel.data; img_data[4 * (i + j * self.paper.width)] = data[0]; img_data[4 * (i + j * self.paper.width) + 1] = data[1]; img_data[4 * (i + j * self.paper.width) + 2] = data[2]; img_data[4 * (i + j * self.paper.width) + 3] = data[3]; } } context2.putImageData(imageData, 0, 0); }
最后,我把代码上传到了github上:https://github.com/imuncle/3Ddraw
在线体验:https://imuncle.github.io/3Ddraw/
强啊!以后就不需要手画了,直接打印出来
老板牛批
3D立体画是裸眼3D的一种,最初是由一些极具创意的画家在街道或平地上创作的,后来又有了在纸上的绘制的作品。我的第一幅3D立体画是高二上画的:
然后上大学后许久没有画过了。
近期武汉新型冠状病毒肺炎疫情严重,只能呆在家,闲来无事想写一个能直接生成3D立体画的程序。
思路很简单,我们的目的是在相机的图像中将需要显示的物体放置在纸面上,还是放出这个世界坐标和图像坐标的关系:
首先定义一个相机:
然后我还创建了一个矩阵相乘类:
最关键的是接下来这个paper类,我模拟了一个A4纸,A4四个角点的世界坐标是已知给定的,根据相机内参和外参计算出其在图像中的坐标,就可以显示A4纸了。
最后是生成图像的代码,逻辑很简单,遍历A4中的每个像素点,投影到图像坐标系,获取到对应的像素值。
最后,我把代码上传到了github上:https://github.com/imuncle/3Ddraw
在线体验:https://imuncle.github.io/3Ddraw/