Статья по теме:
Демо JavaScript:
<html> <head> <meta charset="utf-8"> </head> <body> <canvas id='tutorial' width='500' height='500'></canvas> <script> var vertices = []; var vertices1 = []; var a = []; var context, imData, em; const Point3D = function (x, y, z) { this.x = x; this.y = y; this.z = z; }; function Pixel(col) { imgData = em.createImageData(1, 1); imgData.data[0] = col.r; imgData.data[1] = col.g; imgData.data[2] = col.b; imgData.data[3] = col.a; } function Grafic() { // big pyramid vertices.push(new Point3D(180, 65, 0)); //A vertices.push(new Point3D(490, 340, 550)); //B vertices.push(new Point3D(180, 65, 0)); vertices.push(new Point3D(50, 184, 550)); //C vertices.push(new Point3D(180, 65, 0)); vertices.push(new Point3D(350, 181, 550)); //D //BC vertices.push(new Point3D(490, 340, 550)); vertices.push(new Point3D(50, 184, 550)); //CD vertices.push(new Point3D(50, 184, 550)); vertices.push(new Point3D(350, 181, 550)); //DB vertices.push(new Point3D(350, 181, 550)); vertices.push(new Point3D(490, 340, 550)); // в алгоритме Z-буфера рассматриваю грани //(ABC) Z_buf(vertices[0], vertices[1], vertices[3], { r: 165, g: 12, b: 15, a: 250 }); //(ACD) Z_buf(vertices[0], vertices[3], vertices[5], { r: 255, g: 25, b: 9, a: 255 }); //(ABD) Z_buf(vertices[0], vertices[1], vertices[5], { r: 2, g: 2, b: 5, a: 255 }); //(BCD) Z_buf(vertices[1], vertices[3], vertices[5], { r: 229, g: 102, b: 153, a: 255 }); em.strokeStyle = "black"; em.beginPath(); em.moveTo(vertices[0].x, vertices[0].y); for (var i = 1; i < vertices.length; i++) { em.lineTo(vertices[i].x, vertices[i].y); em.stroke(); } em.closePath(); // short pyramid vertices1.push(new Point3D(200, 140, 0)); //A vertices1.push(new Point3D(360, 330, 200)); //B vertices1.push(new Point3D(200, 140, 0)); vertices1.push(new Point3D(150, 240, 200)); //C vertices1.push(new Point3D(200, 140, 0)); vertices1.push(new Point3D(360, 245, 200)); //D //BC vertices1.push(new Point3D(360, 330, 200)); vertices1.push(new Point3D(150, 240, 200)); //CD vertices1.push(new Point3D(150, 240, 200)); vertices1.push(new Point3D(360, 245, 200)); //DB vertices1.push(new Point3D(360, 245, 200)); vertices1.push(new Point3D(360, 330, 200)); //(ABC) Z_buf(vertices1[0], vertices1[1], vertices1[3], { r: 15, g: 102, b: 105, a: 255 }); //(ACD) Z_buf(vertices1[0], vertices1[3], vertices1[5], { r: 250, g: 255, b: 9, a: 255 }); //(ABD) Z_buf(vertices1[0], vertices1[1], vertices1[5], { r: 225, g: 2, b: 105, a: 255 }); //(BCD) Z_buf(vertices1[1], vertices1[3], vertices1[5], { r: 220, g: 212, b: 15, a: 255 }); em.strokeStyle = "black"; em.beginPath(); em.moveTo(vertices1[0].x, vertices1[0].y); for (var i = 1; i < vertices1.length; i++) { em.lineTo(vertices1[i].x, vertices1[i].y); em.stroke(); } em.closePath(); } // алгоритм Z-буфера реализую с помощью растеризации треугольника function Z_buf(b1, b2, b3, col) { var foc=500000; var dx13, dx12, dx23; var dz13, dz12, dz23; var kz, bz; // упорядочиваем координаты по значению y if (b2.y < b1.y) { [b1.y, b2.y] = [b2.y, b1.y]; [b1.x, b2.x] = [b2.x, b1.x]; [b1.z, b2.z] = [b2.z, b1.z]; } if (b3.y < b1.y) { [b1.y, b3.y] = [b3.y, b1.y]; [b1.x, b3.x] = [b3.x, b1.x]; [b1.z, b3.z] = [b3.z, b1.z]; } if (b3.y < b2.y) { [b2.y, b3.y] = [b3.y, b2.y]; [b2.x, b3.x] = [b3.x, b2.x]; [b2.z, b3.z] = [b3.z, b2.z]; } // нахождение приращения if (b3.y != b1.y) { dx13 = Math.trunc(b3.x - b1.x); dx13 /= (b3.y - b1.y); dz13 = Math.trunc(b3.z - b1.z); dz13 /= (b3.y - b1.y); } else { dx13 = 0; dz13 = 0 } if (b2.y != b1.y) { dx12 = Math.trunc(b2.x - b1.x); dx12 /= (b2.y - b1.y); dz12 = Math.trunc(b2.z - b1.z); dz12 /= (b2.y - b1.y); } else { dx12 = 0; dz12 = 0 } if (b3.y != b2.y) { dx23 = Math.trunc(b3.x - b2.x); dx23 /= (b3.y - b2.y); dz23 = Math.trunc(b3.z - b2.z); dz23 /= (b3.y - b2.y); } else { dx23 = 0; dz23 = 0; } var wx1 = Math.trunc(b1.x); var wx2 = wx1; var _dx13 = dx13; var wz1 = Math.trunc(b1.z); var wz2 = wz1; var _dz13 = dz13; // упорядочиваем приращения if (dx13 > dx12) { [dx13, dx12] = [dx12, dx13]; } if (dz13 > dz12) { [dz13, dz12] = [dz12, dz13]; } // первый полутреугольник for (var i = b1.y; i < b2.y; i++) { kz =(wz1 - wz2) / (wx1 - wx2); bz = wz2 - (kz * wx2); for (var j = Math.trunc(wx1); j <= Math.trunc(wx2); j++) { z_gr = j * kz + bz; // находим значения z через уравнение прямой z=kz*x+bz //алгоритм z-буфера if (a[i][j] > z_gr) { a[i][j] = z_gr; //x_gr y_gr - перспективная проекция ->получится при очень большом foc x_gr = Math.round(foc * j / (z_gr + foc)); y_gr = Math.round(foc * i / (z_gr + foc)); Pixel(col); em.putImageData(imgData, x_gr, y_gr); } } wx1 += dx13; wx2 += dx12; wz1 += dz13; wz2 += dz12; } //случай когда верхнего треугольника нет if (b1.y == b2.y) { wx1 = Math.trunc(b1.x); wx2 = Math.trunc(b2.x); wz1 = Math.trunc(b1.z); wz2 = Math.trunc(b2.z); } // упорядочиваем приращения if (_dx13 < dx23) { [_dx13, dx23] = [dx23, _dx13]; } if (_dz13 < dz23) { [_dz13, dz23] = [dz23, _dz13]; } //второй полутреугольник //совершаем те же действия, что и для первого полутреугольника for (var i = b2.y; i <= b3.y; i++) { kz =(wz1 - wz2) / (wx1 - wx2); bz = wz2 - (kz * wx2); for (var j = Math.trunc(wx1); j <= Math.trunc(wx2); j++) { z_gr = j * kz + bz; if (a[i][j] > z_gr) { a[i][j] = z_gr; x_gr = Math.round(foc * j / (z_gr + foc)); y_gr = Math.round(foc * i / (z_gr + foc)); Pixel(col); em.putImageData(imgData, x_gr, y_gr); } } wx1 += _dx13; wx2 += dx23; wz1 += _dz13; wz2 += dz23; } } context = document.getElementById("tutorial"); em = context.getContext('2d'); em.strokeStyle = "#4682B4"; em.strokeRect(0, 0, 500, 500); for (var i = 0; i < 1500; i++) { a[i] = []; for (var j = 0; j < 1500; j++) { a[i][j] = 10000000; } } Grafic(); </script> </body> </html><code lang="javascript">