范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

JavaScript实战3D建模软件开发

  Subsurfer 是使用 HTML5 Canvas 控件和 WebGL 用 Ja vaScript 编写的 3D 建模应用程序。它实现了 Catmull-Clark 细分曲面算法。该程序的一个独特功能是编辑窗口使用自定义 JavaScript 代码在 2D 画布上下文中实现 3D 投影。视图窗口使用 WebGL 的 3D 画布上下文。Subsurfer 是用 Notepad++ 编写并在 Chrome 中调试的,源代码可以从这里下载。 1、介绍
  Subsurfer 中的建模基于立方体,每个模型都以立方体开始。顶部的按钮选择当前工具。使用实体工具,你可以右键单击实体并更改其某些属性,例如颜色。使用滑块工具完成模型的平移、缩放和旋转。上下文菜单和颜色选择器在Canvas控件中实现。此 3D 投影和所有模型编辑均在 2D 环境中完成。
  模型是通过将连续的细分曲面应用于实体,结合挤出和切面的分裂来开发的。该界面是按键命令和使用实体、小平面、边和顶点工具的右键单击菜单的组合。在这里,我们看到了立方体表面细分的连续应用。
  复选框控制查看选项。在这里,我们看到选中了 清除 和 轮廓选项的相同模型。
  在这里,我们看到一个被挤压的刻面。挤压是一个右键菜单项和一个击键命令。使用 Facet 工具选择 Facet。你可以单击一个构面,单击并滚动以选择多个构面,或拖动一个框以净选构面。
  挤出刻面时的一件重要事情是避免有共同的内壁。当挤压法线指向同一方向的多个相邻面时,可能会发生这种情况。共享内墙会混淆 Catmull-Clark 算法,结果看起来不正确。为避免这种情况,在拉伸相邻面时,除非它们的法线朝向不同的方向,否则最好使用"挤出组"命令。
  边循环影响曲面细分将如何塑造模型。可以使用 Bevel 命令(Facet 工具)或使用 Split 命令(Edge 工具)添加循环边。可以使用边缘工具的右键单击菜单选项来选择边缘循环。
  Subsurfer 中的每个面都是四边形。Catmull-Clark 算法可以很好地处理四边形,并且它们可以更容易地实现可以遍历模型以查找边缘循环和小平面循环的算法。
  顶点工具可以用来拖动顶点,就像面工具可以拖动面,边工具可以拖动边一样。拖动模型元素时,显示网格(网格复选框选项)很重要,这样您就会知道您正在拖动哪个 2 维。否则,结果可能是意外和不受欢迎的。
  Subsurfer 有一个编辑窗口(2D 画布上下文)和一个查看窗口(3D 画布上下文)。它们由"编辑"和"查看"复选框控制。在这里,我们在"编辑"窗口中看到了一个模型,在"视图"窗口中看到了它的 WebGL 等效模型。
  细分曲面建模生成具有平滑圆角曲线的形状。通过仔细的规划和耐心的编辑,可以通过小平面的挤压、分割、缩放和倾斜、边缘和顶点的平移以及平滑算法的连续应用来生成复杂的模型。
  这是编辑窗口中 spacepig 模型的网格视图。像所有 Subsurfer 模型一样,它最初是一个立方体。
  Subsurfer 支持一些内置纹理,例如木纹(如下所示)。 名为textures.png 的图像文件包含所有纹理。
  如果要从文件系统运行程序,浏览器安全设置将不允许网页加载纹理图像。HTML 页面和 PNG 图像都必须托管在同一台服务器上。如果你有合适的软件来设置它,您可以从 localhost 运行该程序。或者,你可以使用特殊的命令行选项运行 Chrome.exe ,以允许从文件系统加载纹理。需要执行的命令是" chrome.exe --allow-file-access-from-files"。在执行此操作之前,你必须关闭所有 Chrome 实例。
  包括各种纹理,包括下面看到的 mod 佩斯利。有一个挤压系列命令可以自动连续挤压刻面,这有助于创造幻觉的洛夫克拉夫特式噩梦。
  源命令(左侧按钮)打开一个新选项卡,显示当前模型网格的文本表示。
  Save 、 Open 和 Delete 按钮是使用 AJAX 调用实现和测试的,以将模型存储在服务器上并按名称检索它们。但是,出于本文的目的,我不想在我的服务器上进行任何点击,因此我更改了路径和名称,因此按钮不会做任何事情。你仍然可以使用提供的 AJAX 代码,但你必须实现自己的 SOAP Web 服务并更改客户端代码以匹配。
  但是,你仍然可以通过复制 Source 命令中的文本将模型保存在本地文件中,如上所示。如果想将本地保存的模型输入到 Subsurfer 中,请使用 输入 按钮。这是左侧的命令之一,但未在这些图片中显示。输入命令会弹出一个表单,你只需将网格文本粘贴到字段中,如下所示。即使对于大型模型,这似乎也很有效。你可能会遇到浏览器安全设置的问题,但对我来说效果很好。
  包括各种 WebGL 着色器,可以从右上角的下拉菜单中选择。WebGL 中的着色器是使用 GLSL 实现的。具有可选镜面反射的平面着色和 Phong(平滑)着色是最有用的。边缘锐利的物体应使用平面阴影。带有 Phong 阴影的立方体看起来很有趣。我还实现了一些不真实的自定义着色器,包括下图的节日彩虹着色器(这不是纹理,它是自定义着色器)。这个着色器对物体在空间中的位置很敏感,所以当物体旋转时颜色会以一种非常诡异的方式变化。
  程序中内置了一个帮助文件和一个击键列表(左侧的最后两个按钮),但是开始使用 Subsurfer 的最快方法是使用击键命令通过挤出刻面和平滑实体来进行实验,看看什么您可以制作各种奇怪而有趣的模型。挤出小平面的击键命令是" e",平滑实体的击键命令是"s"。您将希望选择 Facet 工具,以便您可以选择 facet。您可以使用 Facet 工具(和大多数其他工具)通过在窗口中单击鼠标右键并拖动来旋转模型。加号和减号键将放大或缩小。单击一个构面以将其选中。您还可以净选择构面并单击+拖动以选择区域。可以同时挤出多个面。但是,如果进行多次拉伸,请确保小平面不朝向完全相同的方向,否则您最终会得到共享的内壁,这会影响细分算法。如果挤压面向相同方向的相邻面,最好使用挤压组(击键" g")。 2、使用代码
  你可以从本地文件系统运行 HTML 文件。如上所述,如果在本地运行,则会遇到安全问题,并且纹理不会在 WebGL 中显示。
  要解决此问题,请关闭所有 Chrome 实例并使用以下命令启动 Chrome:" chrome.exe --allow-file-access-from-files"。
  此外, Save 、 Open 和 Delete 按钮被有效禁用。要保存模型,请使用 Source 命令(左侧按钮)复制网格规范。要将保存的模型输入到 Subsurfer,请使用 输入 命令并将网格文本粘贴到提供的表格中。
  挤出刻面时的一件重要事情是避免有共同的内壁。当挤压法线指向同一方向的多个相邻面时,可能会发生这种情况。内墙会弄乱 Catmull-Clark 算法的结果。为避免这种情况,在拉伸相邻面时,除非它们的法线朝向不同的方向,否则最好使用"挤出组"命令。 3、构建编辑视图
  应用程序中有大约 14000 行代码。WebGL 部分使用 James Coglan 的 Sylvester 矩阵数学库,根据许可协议使用。在本文中,我将介绍使该程序正常工作的一些基本元素。我可能会在以后的文章中更深入地介绍一些主题。
  本节介绍如何在 2D 绘图环境中生成编辑视图的 3D 投影。
  该程序使用 HTML5 Canvas 控件,该控件具有两个上下文。这是初始化程序 UI 的函数。它添加了两个Canvas控件并为一个获取 2D 上下文,为另一个获取 webgl (3D) 上下文。如果 webgl 不可用,它会回退到实验性 webgl。WebGL 功能似乎在所有主要浏览器上都得到了很好的支持。其余代码为用户输入设置侦听器并处理其他事务,例如将可用的着色器选项添加到listbox. function startModel() {     alertUser("");          filename = "";          setInterval(timerEvent, 10);      makeCube();                           canvas = document.createElement("canvas");     canvas2 = document.createElement("canvas");      document.body.appendChild(canvas);     document.body.appendChild(canvas2);      canvas.style.position = "fixed";     canvas2.style.position = "fixed";      ctx = canvas.getContext("2d");     gl = canvas2.getContext("webgl") || canvas2.getContext("experimental-webgl");          pos = new Point(0, 0); // last known position              lastClickPos = new Point(0, 0); // last click position      window.addEventListener("resize", resize);     window.addEventListener("keydown", keyDown);     window.addEventListener("keyup", keyRelease);          canvas.addEventListener("mousemove", mouseMove);     canvas.addEventListener("mousedown", mouseDown);     canvas.addEventListener("mouseup", mouseUp);     canvas.addEventListener("mouseenter", setPosition);     canvas.addEventListener("click", click);          canvas2.addEventListener("mousemove", mouseMoveGL);     canvas2.addEventListener("mousedown", mouseDownGL);     canvas2.addEventListener("mouseup", mouseUpGL);              canvas.style.backgroundColor = colorString(canvasBackgroundColor, false);      canvas.style.position = "absolute";      canvas.style.border = "1px solid black";              canvas2.style.position = "absolute";      canvas2.style.border = "1px solid black";         resize();          document.getElementById("checkboxoutlines").checked = false;     document.getElementById("checkboxsolid").checked = true;     document.getElementById("checkboxgrid").checked = false;     document.getElementById("toolslider").checked = true;     document.getElementById("checkboxtwosided").checked = true;     document.getElementById("checkboxwebgl").checked = false;     document.getElementById("checkbox2DWindow").checked = true;     document.getElementById("checkboxtransparent").checked = false;          if (gl != null)     {           gl.clearColor(canvasBackgroundColor.R / 255.0,        canvasBackgroundColor.G / 255.0, canvasBackgroundColor.B / 255.0, 1.0);       gl.enable(gl.DEPTH_TEST);       gl.depthFunc(gl.LEQUAL);       gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);     }          addShaderToList("Phong");     addShaderToList("Rainbow 1");     addShaderToList("Rainbow 2");     addShaderToList("Stripes");     addShaderToList("Chrome");     addShaderToList("Smear");     addShaderToList("Flat");     addShaderToList("T-Map");     addShaderToList("Comic");     addShaderToList("Comic 2");     addShaderToList("Topo");     addShaderToList("Paint By Numbers");          var rect = canvas.getBoundingClientRect();     origin = new Point(-(rect.width / 2), -(rect.height / 2));          setEditViewOptions();          hideInputForm(); }
  由于各种原因,程序中的所有编辑都是在 2D 上下文中完成的,因为我似乎更容易解决与 2D 上下文中的命中检测和用户交互相关的问题。在 2D 上下文中绘图也比在 WebGL 中绘图简单得多。
  为了在 2D 中创建 3D 投影,只需要发生一些事情。这是将 3D 点映射到二维的投影代码。为了实现这一点,只需要想象一个位于模型和观察者眼睛之间的沿 Z 轴的 X/Y 平面。然后计算从眼睛到每个 3D 模型顶点的光线与该平面相交的位置。 function To2D(p3d) // gives a 3D->2D perspective projection {     var point3d = new Point3D(p3d.x, p3d.y, p3d.z);          RotateXYZ(point3d, myCenter, radiansX, radiansY, radiansZ);      var xRise = point3d.x - myCenter.x;     var yRise = point3d.y - myCenter.y;      var zRunEye = zEyePlane - point3d.z;     var zRunView = zViewingPlane - point3d.z;      var factor = (zRunEye - zRunView) / zRunEye;      var x = (myCenter.x + (factor * xRise));     var y = (myCenter.y + (factor * yRise));      x *= ctx.canvas.width;     x /= docSize;     y *= ctx.canvas.width;     y /= docSize;          var p = new Point(Math.floor(x), -Math.floor(y));      // have to flip sign of Y coordinate, this makes it match the GL side          p.x -= origin.x;     p.y -= origin.y;          return p; }
  请注意,上述函数所做的第一件事是将点从其实际位置旋转到当前查看位置。这是为用户提供一种旋转作品并从各个角度查看的方式。这也是一件小事,如下所示。每当用户输入鼠标输入来旋转视图时,变量radiansX、 radiansY和radiansZ都会更新并重新绘制投影。 function RotateXYZ(p, rotation_point, radiansX, radiansY, radiansZ) {     if (radiansZ != 0.0) // rotate about Z axis     {         radiansZ = normalize_radians(radiansZ);          if (radiansZ != 0)         {             var ydiff = (p.y) - (rotation_point.y);             var xdiff = (p.x) - (rotation_point.x);              var xd = (xdiff * Math.cos(radiansZ)) - (ydiff * Math.sin(radiansZ));             xd = Math.round(xd, 0);              var yd = (xdiff * Math.sin(radiansZ)) + (ydiff * Math.cos(radiansZ));             yd = Math.round(yd, 0);              p.x = rotation_point.x + (xd);             p.y = rotation_point.y + (yd);         }     }          if (radiansY != 0.0) // rotate about the Y axis     {         radiansY = normalize_radians(radiansY);          if (radiansY != 0)         {             var zdiff = (p.z) - (rotation_point.z);             var xdiff = (p.x) - (rotation_point.x);              var xd = (xdiff * Math.cos(radiansY)) - (zdiff * Math.sin(radiansY));             xd = Math.round(xd, 0);              var zd = (xdiff * Math.sin(radiansY)) + (zdiff * Math.cos(radiansY));             zd = Math.round(zd, 0);              p.x = rotation_point.x + (xd);             p.z = rotation_point.z + (zd);         }     }          if (radiansX != 0.0) // rotate about the X axis     {         radiansX = normalize_radians(radiansX);          if (radiansX != 0)         {             var ydiff = (p.y) - (rotation_point.y);             var zdiff = (p.z) - (rotation_point.z);              var zd = (zdiff * Math.cos(radiansX)) - (ydiff * Math.sin(radiansX));             zd = Math.round(zd, 0);          var yd = (zdiff * Math.sin(radiansX)) + (ydiff * Math.cos(radiansX));             yd = Math.round(yd, 0);              p.z = rotation_point.z + (zd);             p.y = rotation_point.y + (yd);         }     }     }
  模型由方面组成。面由边组成,边由点组成。以下是保存模型的基本数据结构。请注意,就本程序而言,立方体仍然是立方体,无论它有多少面。每个模型都以具有 6 个面的立方体开始,但随着挤压、分割和平滑算法的应用,将向立方体添加更多面。 function cube(left, right, top, bottom, front, back) {     if (left == undefined)     {         left = 0;     }     if (right == undefined)     {         right = 0;     }     if (top == undefined)     {         top = 0;     }     if (bottom == undefined)     {         bottom = 0;     }     if (front == undefined)     {         front = 0;     }     if (back == undefined)     {         back = 0;     }          this.color = new Color(190, 180, 190); // default solid color     this.outlineColor = new Color(0, 0, 0); // default solid outline color     this.textureName = "";          this.nSubpide = 0;          this.left = left;     this.right = right;     this.top = top;     this.bottom = bottom;     this.front = front;     this.back = back;      this.previousFacetLists = [];     this.facets = [];          var lefttopback = new Point3D(left, top, back);     var lefttopfront = new Point3D(left, top, front);     var righttopfront = new Point3D(right, top, front);     var righttopback = new Point3D(right, top, back);          var leftbottomback = new Point3D(left, bottom, back);     var leftbottomfront = new Point3D(left, bottom, front);     var rightbottomfront = new Point3D(right, bottom, front);     var rightbottomback = new Point3D(right, bottom, back);          var topPoints = [];          topPoints.push(clonePoint3D(lefttopback));     topPoints.push(clonePoint3D(righttopback));     topPoints.push(clonePoint3D(righttopfront));     topPoints.push(clonePoint3D(lefttopfront));     topPoints.reverse();          var bottomPoints = [];          bottomPoints.push(clonePoint3D(leftbottomfront));     bottomPoints.push(clonePoint3D(rightbottomfront));     bottomPoints.push(clonePoint3D(rightbottomback));     bottomPoints.push(clonePoint3D(leftbottomback));     bottomPoints.reverse();          var frontPoints = [];          frontPoints.push(clonePoint3D(lefttopfront));     frontPoints.push(clonePoint3D(righttopfront));     frontPoints.push(clonePoint3D(rightbottomfront));     frontPoints.push(clonePoint3D(leftbottomfront));     frontPoints.reverse();      var backPoints = [];          backPoints.push(clonePoint3D(righttopback));     backPoints.push(clonePoint3D(lefttopback));     backPoints.push(clonePoint3D(leftbottomback));     backPoints.push(clonePoint3D(rightbottomback));     backPoints.reverse();          var leftPoints = [];          leftPoints.push(clonePoint3D(lefttopback));     leftPoints.push(clonePoint3D(lefttopfront));     leftPoints.push(clonePoint3D(leftbottomfront));     leftPoints.push(clonePoint3D(leftbottomback));     leftPoints.reverse();      var rightPoints = [];          rightPoints.push(clonePoint3D(righttopfront));     rightPoints.push(clonePoint3D(righttopback));     rightPoints.push(clonePoint3D(rightbottomback));     rightPoints.push(clonePoint3D(rightbottomfront));     rightPoints.reverse();          var id = 1;          var s1 = new Facet();     s1.ID = id++;     s1.points = topPoints;         this.facets.push(s1);          var s2 = new Facet();     s2.ID = id++;     s2.points = bottomPoints;         this.facets.push(s2);          var s3 = new Facet();     s3.ID = id++;     s3.points = backPoints;         this.facets.push(s3);          var s4 = new Facet();     s4.ID = id++;     s4.points = frontPoints;         this.facets.push(s4);          var s5 = new Facet();     s5.ID = id++;     s5.points = leftPoints;         this.facets.push(s5);          var s6 = new Facet();     s6.ID = id++;     s6.points = rightPoints;         this.facets.push(s6);              for (var n = 0; n < this.facets.length; n++)     {         this.facets[n].cube = this;     } }  function Facet() {     this.cube = -1;     this.ID = -1;     this.points = [];     this.point1 = new Point(0, 0);     this.point2 = new Point(0, 0);     this.closed = false;     this.fill = false;     this.averagePoint3D = new Point3D(0, 0, 0);     this.normal = -1;              this.edges = [];         this.neighbors = [];     this.greatestRotatedZ = 0;     this.greatestLeastRotatedZ = 0;     this.averageRotatedZ = 0;          this.boundsMin = new Point3D(0, 0, 0);     this.boundsMax = new Point3D(0, 0, 0); }  function Point3D(x, y, z) {     this.x = x;     this.y = y;     this.z = z; }
  要在 2D 中绘制模型,只需将每个 facet 描述的多边形从 3D 映射到 2D,然后填充生成的 2D 多边形。只有两个并发症。第一个是每个面必须根据其相对于表示光源的矢量的角度进行着色。第二个是在给定当前视图旋转的情况下,必须根据它们沿 Z 轴的位置从后到前对刻面进行排序。这样,首先绘制背面的刻面,而前面的刻面将它们遮住,这就是你想要的。
  需要注意的是,这种通过沿 Z 轴对多边形进行排序来描绘实体对象的方法是一种近似。它不考虑构面之间的交叉点。此外,当对象包含凹面时,Z 排序会给出看起来不正确的结果。然而,当对象没有凹面并且表面之间没有相交时,该方法会产生足够好的结果。当小平面相对于模型的大小较小时,异常的发生会大大减少,就像应用了平滑时一样。如果存在不规则性,你始终可以在编辑期间通过旋转模型和/或使用"清除"和"轮廓"查看选项并将模型视为具有透明表面的线框来解决它们。任何此类异常都不会出现在"查看"窗口中,
  要对多边形着色,必须获取其法线。这是一个垂直于刻面表面的向量(使用叉积计算)。计算此法线和光源矢量之间的角度(使用点积),这用于使刻面颜色变亮或变暗。如果角度更接近 0,则刻面颜色变亮。如果角度接近 180,则刻面颜色变暗。这是计算刻面法线并对刻面进行着色的代码。 function CalculateNormal(facet) {     var normal = -1;          if (facet.points.length > 2)     {         var p0 = facet.points[0];         var p1 = facet.points[1];         var p2 = facet.points[2];                  var a = timesPoint(minusPoints(p1, p0), 8);         var b = timesPoint(minusPoints(p2, p0), 8);          normal = new line(clonePoint3D(p0),                                  new Point3D((a.y * b.z) - (a.z * b.y), // cross product                                           -((a.x * b.z) - (a.z * b.x)),                                             (a.x * b.y) - (a.y * b.x))                 );          normal.end = LengthPoint(normal, cubeSize * 2);                  var avg =  averageFacetPoint(facet.points);                  normal.end.x += avg.x - normal.start.x;         normal.end.y += avg.y - normal.start.y;         normal.end.z += avg.z - normal.start.z;         normal.start = avg;             }          return normal; }  function getLightSourceAngle(normal) {     var angle = 0;          if (normal != -1)     {         angle = normalize_radians(vectorAngle                 (lightSource, minusPoints(ToRotated(normal.end), ToRotated(normal.start))));     }          return angle; }        function vectorAngle(vector1, vector2) {     var angle = 0.0;      var length1 = Math.sqrt((vector1.x * vector1.x) + (vector1.y * vector1.y) +                              (vector1.z * vector1.z));      var length2 = Math.sqrt((vector2.x * vector2.x) + (vector2.y * vector2.y) +                             (vector2.z * vector2.z));      var dot_product = (vector1.x * vector2.x + vector1.y * vector2.y + vector1.z * vector2.z);      var cosine_of_angle = dot_product / (length1 * length2);      angle = Math.acos(cosine_of_angle);      return angle; }                 function ShadeFacet(color, angle) {     var darken_range = 0.75;     var lighten_range = 0.75;          var result = new Color(color.R, color.G, color.B);      if (angle > 180)     {         angle = 360 - angle;     }      if (angle > 90) // darken     {         var darken_amount = (angle - 90) / 90;         darken_amount *= darken_range;          var r = color.R - (color.R * darken_amount);         var g = color.G - (color.G * darken_amount);         var b = color.B - (color.B * darken_amount);          r = Math.min(255, Math.max(0, r));         g = Math.min(255, Math.max(0, g));         b = Math.min(255, Math.max(0, b));                  result = new Color(r, g, b);     }     else // lighten     {         var lighten_amount = (90 - angle) / 90;         lighten_amount *= lighten_range;          var r = color.R + ((255 - color.R) * lighten_amount);         var g = color.G + ((255 - color.G) * lighten_amount);         var b = color.B + ((255 - color.B) * lighten_amount);          r = Math.max(0, Math.min(255, r));         g = Math.max(0, Math.min(255, g));         b = Math.max(0, Math.min(255, b));                  result = new Color(r, g, b);     }      return result; }
  一旦刻面被着色,就必须将它们从后到前排序,这样当你按顺序绘制它们时,最近的刻面将覆盖它们后面的刻面。 function sortFacets() {     allFacets = [];      for (var w = 0; w < cubes.length; w++)     {         var cube = cubes[w];                  for (var i = 0; i < cube.facets.length; i++)         {             allFacets.push(cube.facets[i]);         }             }      sortFacetsOnZ(allFacets); }  function sortFacetsOnZ(facets) {     for (var i = 0; i < facets.length; i++)     {         setAverageAndGreatestRotatedZ(facets[i]);     }      facets.sort(                     function(a, b)                     {                         if (a.greatestRotatedZ == b.greatestRotatedZ)                         {                             if (a.leastRotatedZ == b.leastRotatedZ)                             {                                 return a.averageRotatedZ - b.averageRotatedZ;                             }                             else                             {                                 return a.leastRotatedZ - b.leastRotatedZ;                             }                         }                         else                         {                             return a.greatestRotatedZ - b.greatestRotatedZ                         }                     }     ); }
  下面是一些在 2D 上下文中使用 3D 投影绘制编辑显示的代码。这里发生的基本事情是sortFacets()和drawCubes()。这就是产生立体形状错觉的 3D 投影的原因。此处的其他代码与更新 WebGL 视图和编辑 UI 的绘图元素有关。编辑 UI 元素包括矩形方向网格和上下文菜单,以及模型元素(面、边、顶点),这些元素会受到翻转行为和高亮行为的影响,必须根据当前工具和鼠标位置重新绘制不同的颜色。 function updateModel() {     for (var c = 0; c < cubes.length; c++)     {         updateCube(cubes[c]);     }      sortFacets();      reloadSceneGL();      draw(); }  function draw() {     if (isGL && gl != null)     {         drawSceneGL();     }      if (is2dWindow || !isGL)     {         ctx.clearRect(0, 0, canvas.width, canvas.height);          findGridOrientation();          if (gridChosen())         {                    drawGridXY();         }          lineColor = lineColorShape;          drawCubes();          if (mouseIsDown && draggingShape)         {             draw3DRectangleFrom2DPoints(mouseDownPos, pos, false, "white");                }          if (hitLine != -1)         {             var pts = [];             pts.push(To2D(hitLine.start));             pts.push(To2D(hitLine.end));             drawPolygonHighlighted(pts);         }          if (hitFacet != -1 && toolChosen() == "facet")         {             drawPolygon3d(hitFacet.points, true, true, "yellow", true);         }          for (var g = 0; g < selectedLines.length; g++)         {             var pts = [];             pts.push(To2D(selectedLines[g].start));             pts.push(To2D(selectedLines[g].end));             drawPolygonSelected(pts);         }          if (hitVertex != -1)         {             drawVertex(hitVertex, false);         }          for (var qq = 0; qq < selectedVertexes.length; qq++)         {             drawVertex(selectedVertexes[qq], true);         }          if (lineDiv != -1 &&             lineDiv2 != -1)         {             drawLine2D(lineDiv, "blue");             drawLine2D(lineDiv2, "blue");         }          if (draggingRect)         {             draw2DRectangleFrom2DPoints(mouseDownPos, pos, "black");                }          if (colorPickMode.length > 0)         {             drawColors(0, 0, colorPickHeight);         }          drawMenu();     } }  function drawCubes() {     var drawlines = isOutline || !isShade;      var drawNormals = isNormals;      var shadeSolids = isShade;      var dual = isDualSided;      for (var i = 0; i < allFacets.length; i++)     {         var facet = allFacets[i];          if (facet.normal == -1)         {             facet.normal = CalculateNormal(facet);         }          var c = facet.cube.color;          if (colorPickMode.length == 0)         {             if (facet.cube == hitSolid)             {                 c = new Color(23, 100, 123);             }                  if (listHas(selectedSolids, facet.cube))             {                 c = new Color(200, 30, 144);             }              if (listHas(selectedFacets, facet))             {                 c = new Color(0, 255, 255);             }         }          c = ShadeFacet(c, degrees_from_radians(getLightSourceAngle(facet.normal)));         var show = true;          if (!dual)         {             show = ShowFacet(degrees_from_radians(getFrontSourceAngle(facet.normal)));         }          var colorFillStyle = colorString(c, isTransparent);          var colorOutlineStyle = colorString(facet.cube.outlineColor, isTransparent);              if (listHas(selectedSolids, facet.cube))         {             drawlines = true;             colorOutlineStyle = "red";         }          if (show)         {             drawPolygon3d(facet.points, true, shadeSolids || listHas(selectedFacets, facet),                 colorFillStyle, drawlines, colorOutlineStyle);              if (drawNormals)             {                 drawLine3D(facet.normal, "magenta");             }         }     } }  function drawPolygon3d(points, isClosed, isFill, fillColor, isOutline, outlineColor) {     var result = [];          if (points.length > 0)     {         for (var i = 0; i < points.length; i++)         {             result.push(To2D(points[i]));         }              drawPolygon(result, isClosed, isFill, fillColor, isOutline, outlineColor);     } }  function drawPolygon (points, isClosed, isFill, fillColor, isOutline, outlineColor, lineThickness) {     if (points.length > 0)     {         isClosed = isClosed ? isClosed : false;         isFill = isFill ? isFill : false;         if (isOutline === undefined)         {             isOutline = true;         }         if (lineThickness === undefined)         {             lineThickness = 1;         }         if (outlineColor === undefined)         {             outlineColor = lineColor;         }                  ctx.beginPath();          ctx.lineWidth = lineThickness;         ctx.lineCap = "round";         ctx.strokeStyle = outlineColor;         if (isFill)         {             ctx.fillStyle = fillColor;         }          ctx.moveTo(points[0].x, points[0].y);         for (var i = 1; i < points.length; i++)         {                     ctx.lineTo(points[i].x, points[i].y);         }                  if (isClosed)         {             ctx.lineTo(points[0].x, points[0].y);         }          if (isFill)         {             ctx.fill();             }                  if (isOutline)         {             ctx.stroke();         }     } }4、构建 WebGL 模型
  因此 2D 编辑视图的制作相当简单。WebGL 视图的制作有点困难,将在以后的文章中更深入地讨论。我只会展示一些将我们的 JavaScript 数据结构绑定到我们模型的 WebGL 表示的代码。有五个基本元素必须被缓冲并绑定到 WebGL。这是完成这项工作的主要功能。 function bindModelGL() {     bindVerticesGL();      bindColorsGL();      bindVertexIndicesGL();      bindTextureCoordinatesGL();      bindNormalsGL(); }
  将颜色绑定到我们的模型。每个立方体只能是一种颜色。每个方面都有一个指向其父多维数据集的指针。请注意,就我们的目的而言,多维数据集只是一个分面列表,它可能是也可能不是实际的多维数据集。所有面的列表将为我们提供每个顶点的正确颜色。每个顶点需要 4 个元素:R、G、B 和 A(表示透明度的 alpha 通道)。我们为 A 使用 1.0,因此我们的 WebGL 模型将始终是不透明的。 function bindColorsGL() {     if (isGL && gl != null)     {         var generatedColors = [];          for (var i = 0; i < allFacets.length; i++)         {                var f = allFacets[i];              var c = color2FromColor(f.cube.color);                          var b = [];                          b.push(c.R);             b.push(c.G);             b.push(c.B);             b.push(1.0);                              // repeat each color 4 times for the 4 vertices of each facet             for (var s = 0; s < 4; s++)              {                             generatedColors.push(b[0]);                 generatedColors.push(b[1]);                 generatedColors.push(b[2]);                 generatedColors.push(b[3]);                             }         }                cubeVerticesColorBuffer = gl.createBuffer();         gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesColorBuffer);         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(generatedColors), gl.STATIC_DRAW);     } }
  我们必须绑定刻面法线,以便 WebGL 可以对模型进行着色。请注意,对于每个面法线,我们只需要 3 个数字。这是因为 WebGL 只关心法线的方向,而不关心它在空间中的位置。
  这里的一个具体问题是 Subsurfer 支持 Phong 着色,这需要顶点法线。如果你认为每个小平面法线垂直于小平面表面,那么顶点法线是包含该顶点的所有小平面的法线的平均值。因此,当 Phong 着色生效时,必须计算顶点法线。我们不在 2D 投影中使用这些,因为我们只做平面着色,所以我们只需要小平面法线。但是 WebGL 中的 Phong 着色需要顶点法线。如果我们在 WebGL 中进行平面着色,则不必计算顶点法线。在平面着色的情况下,我们只是使用面法线作为每个顶点的法线。 function bindNormalsGL() {                   if (isGL && gl != null)     {             cubeVerticesNormalBuffer = gl.createBuffer();         gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);          var vertexNormals = [];          for (q = 0; q < allFacets.length; q++)          {             var f = allFacets[q];              if (f.normal == -1)             {                 f.normal = CalculateNormal(f);             }         }          if (fastVertexNormalMethod)         {             if (isSmoothShading())             {                 allSortedPoints = getFacetPointsAndSetUpBackPointers(allFacets);                 sortPointsByXYZ(allSortedPoints);                 stageVertexNeighborFacets(allSortedPoints);             }         }          if (isSmoothShading())         {                     for (q = 0; q < allFacets.length; q++)              {                 var f = allFacets[q];                  for (var j = 0; j < f.points.length; j++)                 {                     var p = f.points[j];                     var vn = p.vertexNormal;                     if (vn == undefined)                     {                         vn = calculateVertexNormal(p, allFacets);                         p.vertexNormal = vn;                     }                     vertexNormals.push((vn.end.x / reductionFactor) -                                         (vn.start.x / reductionFactor));                     vertexNormals.push((vn.end.y / reductionFactor) -                                         (vn.start.y / reductionFactor));                     vertexNormals.push((vn.end.z / reductionFactor) -                                         (vn.start.z / reductionFactor));                 }             }                 }         else         {             for (q = 0; q < allFacets.length; q++)              {                 var f = allFacets[q];                  for (var i = 0; i < 4; i++)                  {                     vertexNormals.push((f.normal.end.x / reductionFactor) -                                         (f.normal.start.x / reductionFactor));                     vertexNormals.push((f.normal.end.y / reductionFactor) -                                         (f.normal.start.y / reductionFactor));                     vertexNormals.push((f.normal.end.z / reductionFactor) -                                         (f.normal.start.z / reductionFactor));                 }             }                 }                  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexNormals), gl.STATIC_DRAW);     } }
  我们必须绑定模型中的每个顶点。尽管 WebGL 需要三角形而不是四边形才能正常工作,但不必复制顶点,因为我们将在顶点缓冲区中提供索引列表。一些索引将被重复,这给了我们三角形。 function bindVerticesGL() {     cubeVerticesBuffer = gl.createBuffer();      gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesBuffer);      var vertices = [];        for (var i = 0; i < allFacets.length; i++)     {             var f = allFacets[i];         for (var j = 0; j < f.points.length; j++)         {             var point3d = f.points[j];                      vertices.push(point3d.x / reductionFactor);             vertices.push(point3d.y / reductionFactor);             vertices.push((point3d.z / reductionFactor));         }     }        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); }
  在这里,我们构建顶点索引缓冲区并将其绑定到 WebGL。索引模式 0、1、2 后跟 0、2、3 将我们的四个面顶点划分为两个三角形。 function bindVertexIndicesGL() {     cubeVerticesIndexBuffer = gl.createBuffer();     gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer);      var cubeVertexIndices = [];       var t = 0;          for (var i = 0; i < allFacets.length; i++)     {         cubeVertexIndices.push(t + 0);         cubeVertexIndices.push(t + 1);         cubeVertexIndices.push(t + 2);         cubeVertexIndices.push(t + 0);         cubeVertexIndices.push(t + 2);         cubeVertexIndices.push(t + 3);                  t += 4;     }      gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW); }
  我们模型中的每个顶点都有 X、Y、Z 表示空间位置,加上另外两个坐标 U 和 V,它们是纹理图像的偏移量。U 和 V 值介于 0 和 1 之间。对于复杂的形状,我们会自动分配 U 和 V 坐标,就好像纹理被包裹在图像周围一样。这是由assignPolarUV_2()函数完成的。 function bindTextureCoordinatesGL() {     for (var i = 0; i < cubes.length; i++)     {         assignPolarUV_2(cubes[i], i);     }      cubeVerticesTextureCoordBuffer = gl.createBuffer();     gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesTextureCoordBuffer);        var textureCoordinates = [];     for (var i = 0; i < allFacets.length; i++)     {         if (isPolarUV)         {             var f = allFacets[i];                          textureCoordinates.push(f.points[0].u);               textureCoordinates.push(f.points[0].v);                            textureCoordinates.push(f.points[1].u);               textureCoordinates.push(f.points[1].v);                textureCoordinates.push(f.points[2].u);               textureCoordinates.push(f.points[2].v);                textureCoordinates.push(f.points[3].u);               textureCoordinates.push(f.points[3].v);           }         else         {             textureCoordinates.push(0.0);               textureCoordinates.push(0.0);             textureCoordinates.push(1.0);               textureCoordinates.push(0.0);             textureCoordinates.push(1.0);               textureCoordinates.push(1.0);             textureCoordinates.push(0.0);               textureCoordinates.push(1.0);         }     }        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates),               gl.STATIC_DRAW); }5、平面着色器
  当直接处理 WebGL 时,有必要编写自己的着色器。这些是用称为 GLSL 的语言编写的。每个着色器都必须有一个main()过程。着色器是一个在计算机图形芯片上编译和加载的小程序。
  着色器包含在scriptHTML 文件的标签中,可以按名称寻址。如果您为模型使用纹理而不是纯色,则需要不同的着色器。Subsurfer 具有用于颜色和纹理的平面着色器,以及用于颜色和纹理的 Phong(平滑)着色器。还包括几个古怪的自定义着色器。在这里,我将只提到纯色情况下的平面着色器和 Phong 着色器。
  这是纯色的平面着色器。您必须提供顶点着色器和片段着色器。这适用于您实现的每种类型的着色。顶点着色器为您提供每个顶点的颜色。片段着色器可以在顶点之间进行插值以创建更平滑的外观。它基本上为每个单独的像素着色。
  下面的平面着色器的外观与我们在 JavaScript 中构建的 3D 投影非常相似,它显示在编辑窗口中。如果你看一下这个顶点着色器在做什么,它实际上和我们之前在 JavaScript 函数中看到的计算是一样的shadeFacet()。它采用顶点法线(在这种情况下,与小平面法线相同)和光源方向矢量之间的角度(点积),并使用它来使小平面颜色变亮或变暗。但是着色器可以更快地完成它,因为它在大规模并行设备上运行。此外,它还考虑了光的颜色,以及定向光和环境光。请注意,在此着色器中,灯光颜色和方向是硬编码的。
  这里的片段着色器并没有做太多,它只是一个传递。这是因为平面着色器没有插值或平滑处理,因此平面上的所有像素都可以被着色为相同的颜色。   6、Phong 着色器
  Phong 着色提供更平滑的外观,因为它在顶点之间进行插值以单独着色每个像素。颜色 Phong 着色器如下所示。
  请注意,顶点着色器并没有发生太多事情。大多数动作都发生在片段着色器中,因为我们要计算每个单独的像素。关于顶点着色器最有趣的一点是,转换后的顶点法线被声明为"可变的"。这将导致它为片段着色器中的每个像素进行平滑插值。
  所以这个片段着色器实际上对每个像素使用不同的法线。您看不到任何明确的代码来执行此操作,因为它内置于 GLSL 语言和"可变"类型中。与平面着色器一样,环境光和定向光的颜色是硬编码的,光的方向也是如此。此外,使用光方向向量与顶点法线之间的角度计算颜色与平面着色器非常相似。这里的不同之处在于,计算发生在片段着色器中,对每个像素使用不同的插值法线值。这就是赋予光滑外观的原因。Phong 着色器比平面着色器更慢,因为它必须进行更多的计算。
  关于 Phong 着色器的最后一件事是我已经实现了镜面反射。如果选中 UI 上的" 镜面反射 "复选框,则统一值specularUniform将设置为1. 如果发生这种情况,只要光源和顶点法线之间的角度足够小,该像素的颜色就会自动设置为白色。这会产生使模型看起来有光泽的镜面高光。   6、细分曲面算法
  我本来打算多说一些关于 Catmull-Clark 细分曲面算法以及我在 JavaScript 中的实现,但是这篇文章已经太长了,所以我将把它留到以后的文章中。但是,如果查看代码,你可以看到发生了什么。我只想说大部分动作发生在函数subpisionSurfaceProcessFacet() 内,它通过计算称为重心的加权平均值来细分单个面。使用计时器在三个函数中实现该算法的原因是,我可以在屏幕底部绘制一个进度指示计。我不得不这样做,因为 JavaScript 中没有真正的线程。该算法采用一个分面列表并将其替换为一个列表,其中每个分面都已被四个新分面替换。请注意,当模型中有孔时必须小心。位于此类孔边界上的刻面被视为特殊情况。 function startSubpision(solid) {     informUser("Subpiding, please wait...");     subpSurfaceLoopCounter = 0;     var facets = solid.facets;     solidToSubpide = solid;     isSubpiding = true;      if (solid.nSubpide == 0)     {         solid.previousFacetLists.push(solid.facets);     }          for (var i = 0; i < facets.length; i++)     {         facets[i].edges = getFacetLines(facets[i]);         facets[i].averagePoint3D = averageFacetPoint(facets[i].points);     }          findFacetNeighborsAndAdjacents(facets);          for (var i = 0; i < facets.length; i++)     {         var facet = facets[i];                  for (var j = 0; j < facet.edges.length; j++)         {             var edge = facet.edges[j];                          var list = [];              list.push(edge.start);             list.push(edge.end);              if (edge.parentFacet != -1 && edge.adjacentFacet != -1)             {                 list.push(edge.parentFacet.averagePoint3D);                 list.push(edge.adjacentFacet.averagePoint3D);             }              edge.edgePoint = averageFacetPoint(list);         }     }          subpTimerId = setTimeout(subpisionSurfaceProcessFacet, 0);          newSubpFacets = []; }      function subpisionSurfaceProcessFacet() {     var facet = solidToSubpide.facets[subpSurfaceLoopCounter];     var nEdge = 0;      var neighborsAndCorners = facetNeighborsPlusFacet(facet);      for (var j = 0; j < facet.points.length; j++)     {         var p = facet.points[j];              var facepoints = [];         var edgepoints = [];          var facetsTouchingPoint = findFacetsTouchingPoint(p, neighborsAndCorners);          for (var n = 0; n < facetsTouchingPoint.length; n++)         {             var f = facetsTouchingPoint[n];                          facepoints.push(averageFacetPoint(f.points));         }          var edgesTouchingPoint = findEdgesTouchingPoint(p, facetsTouchingPoint);          for (var m = 0; m < edgesTouchingPoint.length; m++)         {             var l = edgesTouchingPoint[m];                          edgepoints.push(midPoint3D(l.start, l.end));         }          var onBorder = false;         if (facepoints.length != edgepoints.length)         {                             onBorder = true; // vertex is on a border         }          var F = averageFacetPoint(facepoints);         var R = averageFacetPoint(edgepoints);          var n = facepoints.length;                  var barycenter = roundPoint(pPoint(plusPoints                          (plusPoints(F, timesPoint(R, 2)), timesPoint(p, n - 3)), n));          var n1 = nEdge;          if (n1 > facet.edges.length - 1)         {             n1 = 0;         }          var n2 = n1 - 1;          if (n2 < 0)         {             n2 = facet.edges.length - 1;         }          if (onBorder)         {             var borderAverage = [];                          var etp = edgesTouchingPoint;                          for (var q = 0; q < etp.length; q++)             {                 var l = etp[q];                                  if (lineIsOnBorder(l))                 {                     borderAverage.push(midPoint3D(l.start, l.end));                 }             }                          borderAverage.push(clonePoint3D(p));                          barycenter = averageFacetPoint(borderAverage);         }          var newFacet = new Facet();         newFacet.points.push(clonePoint3D(facet.edges[n2].edgePoint));         newFacet.points.push(clonePoint3D(barycenter));         newFacet.points.push(clonePoint3D(facet.edges[n1].edgePoint));         newFacet.points.push(clonePoint3D(facet.averagePoint3D));                                               newSubpFacets.push(newFacet);         newFacet.cube = solidToSubpide;          nEdge++;                             }              drawThermometer(solidToSubpide.facets.length, subpSurfaceLoopCounter);     subpSurfaceLoopCounter++;     if (subpSurfaceLoopCounter >= solidToSubpide.facets.length)     {         clearInterval(subpTimerId);         finishSubpision(solidToSubpide);     }     else     {         subpTimerId = setTimeout(subpisionSurfaceProcessFacet, 0);     } }  function finishSubpision(parentShape) {     parentShape.nSubpide++;      parentShape.facets = newSubpFacets;      fuseFaster(parentShape);          selectedFacets = [];     selectedLines = [];     selectedVertexes = [];      sortFacets();          setFacetCount(parentShape);      isSubpiding = false;          alertUser("");          reloadSceneGL();          draw(); }
  原文链接:http://www.bimant.com/blog/js-3d-modeler-dev-tutorial/

国际金价持续下跌,该去抄底买黄金吗?中国大妈何时再出手?金价又跌了,中国大妈准备下手么?据中国新闻网报道,国际金价在2022年3月创下2070。42美元盎司的高点后,一路下跌,近期更是连续下跌五个星期,创下近十个月来的新低。金价为何会持23点没睡,整个人都会不好!不要让熬夜成为习惯一23点没睡内分泌紊乱会导致机体内分泌失调偶尔熬夜可造成精神疲惫注意力不集中等症状,内分泌失调症状不明显。人体的内分泌系统分泌各种激素和神经系统一起调节人体的代谢和生理功能,正常情男足逼平原因曝光,连小说都不敢这么写,李霄鹏被又一次打脸虽然只是逼平没有取胜,但中国男足这次确实表现不错,也配得上赛后球迷给予他们的鼓励。实话说在实力对比上中国男足确实落于下风,而且日本队的求生欲望也要远远高于男足,但无奈只开花不结果,孙颖莎输球3大原因!2点训练可弥补,1致命缺点不改正难有突破WTT布达佩斯冠军赛已经落下了帷幕,国乒在五个单项中取得了四个冠军,在时隔近四个月后再度参加国际大赛,能够取得这样的成绩已经是实属不易。其中在女单项目中,国乒的优势依然非常明显,提许久不联系的人,突然联系你了,逃不过3个原因图源自网络侵权请联系删除在我们生命中,来来往往会认识很多人,有些人能陪着我们一起经历人生的悲欢,有的人却只陪我们一程,然后就在中途下了车,任你是不甘还是不舍,也无法让他们的步伐停下为什么宁愿买高配千元机,也不考虑低配旗舰机?3点原因太真实为什么宁愿买高配千元机,也不考虑低配旗舰机?3点原因太真实大家在买手机的时候应该会有比较纠结的时候,一个是到底是选择高配手机多用几年呢?还是低配手机少用几年?这个问题应该也困扰着很荣耀手机撤出印度,因为众所周知的原因印度发生了什么?7月21日晚,在荣耀的新品发布会会后,荣耀CEO赵明接受了媒体群访。对于印度市场,赵明表示,荣耀几年前就在印度有团队并经营了很长时间,后来由于众所周知的原因荣耀印度团队撤出,目前荣2022年最值得买的MiniLED电视TCLQ10G,画质价格双王炸MiniLED作为新一代的显示技术,自被苹果采用之后,其曝光率,便如加了Buff一般节节攀升,无疑是今年来最火热的显示技术。被市场选中,自然有市场的理由,相较于传统LED背光的LC下半年最值得入手的手机推荐这三款手机好用不贵价格最低至1299元下半年已经开始一段时间了,对于很多小白来说,上半年都没有选到合适的手机,下半年的手机又该如何去选择呢?现在手机的更新换代非常的快,手机的价格也降得非常的快,买的手机不好可能会很快被23点没睡,整个人都会不好!不要让熬夜成为习惯一23点没睡内分泌紊乱会导致机体内分泌失调偶尔熬夜可造成精神疲惫注意力不集中等症状,内分泌失调症状不明显。人体的内分泌系统分泌各种激素和神经系统一起调节人体的代谢和生理功能,正常情男足逼平原因曝光,连小说都不敢这么写,李霄鹏被又一次打脸虽然只是逼平没有取胜,但中国男足这次确实表现不错,也配得上赛后球迷给予他们的鼓励。实话说在实力对比上中国男足确实落于下风,而且日本队的求生欲望也要远远高于男足,但无奈只开花不结果,
神秘部队藏字419什么来头?印度蚕食领土,为何10年后动手?1962年对印自卫反击战前夕,为了打击印度侵略者,伟人毛主席决定成立西藏军区前指的作战指挥体系,同时也成立了一支崭新的部队,它的代号就是藏字419部队。这支部队是如何诞生的?它都是揭晓石红杏下线原因林满江无耻让其背锅,信仰崩塌无力承受后果宇哥带你读原著,一起来看由靳东,黄志忠,闫妮主演的热播电视剧突围解析第六回。看原著,更深入。楔子在原著的最后,齐本安望着窗外,想起了当年第一次去见师傅程端阳的场景。那时,他与林满江亚马逊贝佐斯为51岁女友拼了7800万美元夏威夷购庄园!当地人抱怨房价被推高,平均100万美元亚马逊创始人贝佐斯最近斥资7800万美元,在夏威夷州毛伊岛为自己和女友劳伦桑切斯购置了一处私人庄园,占地14英亩,周围是休眠的火山熔岩田。这笔交易进一步扩大了贝佐斯超过5亿美元的房社保基金又增持第一重仓股全国社保基金,主要来源是企事业员工交的养老保险,不向个人开放的。社保基金的管理机构是国务院钦点的每年博时嘉实华夏等公募基金,都会被考察,选中了的基金经理,才能管理社保基金。所以,社展望未来人类需要多少时间才能冲出太阳系和银河系?1969年7月21日凌晨2点56分,阿姆斯特朗的左脚踏上了月球,并说这是一个人的一小步,却是人类的一大步,目前人类已经冲出地球,各位小伙伴,你们有想过人类需要多少时间才能冲出太阳系张继科当年为何会被国家队开除?触碰国乒底线,孔令辉也难辞其咎世乒赛在即,在国乒的主力选手在全力备战而少有近态爆出的同时,不少老牌球员成为了球迷们热议的话题中心,张继科就是其中一位。我们知道,有着藏獒之称的张继科的打法向来以凶狠著称,由于优秀散文,寒夜无寐意,西窗晓月明晓月下,西窗里,淡淡的月光透过纱幔,落在枕边。城市就快要苏醒,可西窗的人影却还未走进梦境。远眺缠绵的云,静听敲窗的风,倦眸空洞,耳畔乱声,老街寂静,星海阑珊,仿佛一切都在凋零。拆开把生命开成一朵荷朋友在塘边筑了一间小屋,邀我去玩。我走进小屋时,朋友正躬身剥着莲子。我打了一声招呼,他缓缓地直起身子,对我说来了?以前风风火火的节奏不见了踪影,一个行走四方的旅人慢成了一个潇洒闲人散文诗飞雪纷纷岁月匆匆,红尘滚滚。斗转星移,时光飞逝。风从远方吹来,款款舞动季节的更替,闪耀着梦幻的云雨。枫叶又红枝头,绽放秋天的色彩,色彩里没有忧郁的云朵。时光静然,往事如梦,流逝的日子,不再生命历程中的恩赐生命历程中的恩赐,所以感动向日葵。这种感应,日积月累,在一次次成长中度过。可是,世界上的万事千言万物都是由许多多多为之动容的。在喧嚣的生命中,只要还需要自己,自己就可以成为自己的精月亮是地球分离出去的吗?最新推理来了关于月亮的由来,科学界至今也是众说纷纭,其中最普及的自然是地球俘获月球一说,但是至今都没有找到强而有力的科学证明。然而追溯到各种远古文明及传说,都没有月亮的记载。其中最著名的玛雅文