如何在 3d 中挤出路径? [英] how to extrude a path in 3d?

查看:37
本文介绍了如何在 3d 中挤出路径?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 3d 中挤出路径.还没有什么特别的,只是遵循一些点并使用正多边形作为管道".我现在使用 Processing 来快速制作原型,但稍后会将代码转换为 OpenGL.

I'm trying to extrude a path in 3d. Nothing fancy yet, just following some points and using a regular polygon for 'tubing'. I'm using Processing for now to quickly prototype, but will later turn the code into OpenGL.

我的问题是以直角旋转关节".我想我对如何获得角度有一个粗略的想法,不确定.

My problem is rotating the 'joints' at the right angles. I think I have a rough idea how to get the angles, not sure.

我从 Simon Greenwold 的示例开始(处理 > 文件 > 示例 > 3D > 表单 > 顶点).这是我迄今为止的尝试:

I've started from a sample by Simon Greenwold(Processing > File > Examples > 3D > Form > Vertices).Here's my attempt so far:

更新 > 重构/简化代码

Here is the main sketch code:
int pointsNum = 10;
Extrusion star;

int zoom = 0;

void setup() {
  size(500, 500, P3D);

  PVector[] points = new PVector[pointsNum+1];
  for(int i = 0 ; i <= pointsNum ; i++){
    float angle = TWO_PI/pointsNum * i;
    if(i % 2 == 0)
      points[i] = new PVector(cos(angle) * 100,sin(angle) * 100,0);
    else
      points[i] = new PVector(cos(angle) * 50,sin(angle) * 50,0);
  }

  star = new Extrusion(10,10,points,3);
}

void draw() {
  background(0);
  lights();
  translate(width / 2, height / 2,zoom);
  rotateY(map(mouseX, 0, width, 0, PI));
  rotateX(map(mouseY, 0, height, 0, PI));
  rotateZ(-HALF_PI);
  noStroke();
  fill(255, 255, 255);
  translate(0, -40, 0);
  star.draw();
}

void keyPressed(){
  if(key == 'a') zoom += 5;
  if(key == 's') zoom -= 5;
}

这是挤出类:

导入 processing.core.PMatrix3D;

import processing.core.PMatrix3D;

class Extrusion{

  float topRadius,bottomRadius,tall,sides;
  int pointsNum;
  PVector[] points;

  Extrusion(){}

  Extrusion(float topRadius, float bottomRadius, PVector[] points, int sides) {
    this.topRadius = topRadius;
    this.bottomRadius = bottomRadius;
    this.points = points;
    this.pointsNum = points.length;
    this.sides = sides;
  }

  void draw() {
    if(pointsNum >= 2){  
      float angle = 0;
      float angleIncrement = TWO_PI / sides;

      //begin draw segments between caps
      angle = 0;
      for(int i = 1; i < pointsNum ; ++i){
        beginShape(QUAD_STRIP);
        for(int j = 0; j < sides + 1; j++){
          vertex(points[i-1].x + cos(angle) * topRadius, points[i-1].y, points[i-1].z + sin(angle) * topRadius);
          vertex(points[i].x + cos(angle) * bottomRadius, points[i].y, points[i].z + sin(angle) * bottomRadius);

          angle += angleIncrement;
          }
        endShape();
      }
      //begin draw segments between caps
    }else println("Not enough points: " + pointsNum);
  }
}

更新

这是我的草图的样子:

处理拉伸 http://doc.gold.ac.uk/~ma802gp/extrude.gif

问题是关节的角度不正确,所以挤出看起来不对.这不是一个很好的例子,因为这可以用车床实现.如果我能让车床处理任意一组点和一个轴,那就太好了.我正在使用挤压因为我正在尝试根据 Liviu Stoicoviciu 的艺术创作几何体.

The problem is the joints aren't at the right angle, so the extrude looks wrong. This isn't a very good example, as this could be achieved with a lathe. If I can get a lathe to work with an arbitrary set of points and an axis that will be great. I am using extrusion because I am trying to create geometric bodies based on the art of Liviu Stoicoviciu.

以下是一些示例:

星画 http://doc.gold.ac.uk/~ma802gp/star_painting.jpg

星星纸雕 http://doc.gold.ac.uk/~ma802gp/star_paper_sculpture.jpg

三角形 http://doc.gold.ac.uk/~ma802gp/triangles_pencil.jpg

很抱歉质量很差.

正如您在三角形图像中看到的,这可以通过挤压来实现.

As you can see in the triangles image, that would be achieved with extrusions.

更新

这是我尝试在 draw 方法中使用 drhirsch 的帮助:

Here's my attempt to use drhirsch's help in the draw method:

void draw() {
    if(pointsNum >= 2){  
      float angle = 0;
      float angleIncrement = TWO_PI / sides;

      //begin draw segments between caps
      angle = 0;
      for(int i = 1; i < pointsNum ; ++i){
        beginShape(QUAD_STRIP);
        for(int j = 0; j < sides + 1; j++){

          PVector s = new PVector(0,0,1);
          PVector cn = new PVector();
          points[i].normalize(cn);
          PVector r = s.cross(cn);
          float a = acos(s.dot(cn));
          PMatrix3D rot = new PMatrix3D(1,0,0,0,
                                        0,1,0,0,
                                        0,0,1,0,
                                        0,0,0,1);
          rot.rotate(a,r.x,r.y,r.z);
          PVector rotVec = new PVector();
          rot.mult(points[i],rotVec);
          rotVec.add(new PVector(cos(angle) * topRadius,0,sin(angle) * topRadius));

          vertex(points[i-1].x + cos(angle) * topRadius, points[i-1].y, points[i-1].z + sin(angle) * topRadius);
          vertex(rotVec.x,rotVec.y,rotVec.y);

          //vertex(points[i-1].x + cos(angle) * topRadius, points[i-1].y, points[i-1].z + sin(angle) * topRadius);
          //vertex(points[i].x + cos(angle) * bottomRadius, points[i].y, points[i].z + sin(angle) * bottomRadius);

          angle += angleIncrement;
          }
        endShape();
      }
      //begin draw segments between caps
    }else println("Not enough points: " + pointsNum);
  }

我已经重构了代码,所以现在以前称为 CShape 的类称为 Extrude,代码更少并且希望很简单,我使用 PVector 对象数组而不是 PVector 对象的 Vector,这可能会令人困惑.

I've refactored the code so now the class that used to be called CShape is called Extrude, the code is less and hopefully simples, and I use an array of PVector objects instead of a Vector of PVector objects which might be confusing.

这是我的又一次尝试,取得了一些埃舍尔式的结果:

Here is my yet another attempt with some escher-esque results:

升级抽奖

void draw() {
    if(pointsNum >= 2){  
      float angle = 0;
      float angleIncrement = TWO_PI / sides;

      //begin draw segments between caps
      angle = 0;
      for(int i = 1; i < pointsNum ; ++i){
        beginShape(QUAD_STRIP);
        float angleBetweenNextAndPrevious = 0.0;
        if(i < pointsNum - 1) angleBetweenNextAndPrevious = PVector.angleBetween(points[i],points[i+1]);

        for(int j = 0; j < sides + 1; j++){

          PVector s = new PVector(0,0,1);
          PVector s2 = new PVector(0,0,1);
          PVector cn = new PVector();
          PVector cn2 = new PVector();
          points[i-1].normalize(cn);
          points[i].normalize(cn);
          PVector r = s.cross(cn);
          PVector r2 = s.cross(cn2);
          PMatrix3D rot = new PMatrix3D(1,0,0,0,
                                        0,1,0,0,
                                        0,0,1,0,
                                        0,0,0,1);
          PMatrix3D rot2 = new PMatrix3D(1,0,0,0,
                                        0,1,0,0,
                                        0,0,1,0,
                                        0,0,0,1);

          rot.rotate(angleBetweenNextAndPrevious,r.x,r.y,r.z);
          rot2.rotate(angleBetweenNextAndPrevious,r2.x,r2.y,r2.z);

          PVector rotVec = new PVector();
          rot.mult(points[i-1],rotVec);
          rotVec.add(new PVector(cos(angle) * topRadius,0,sin(angle) * topRadius));
          PVector rotVec2 = new PVector();
          rot2.mult(points[i],rotVec2);
          rotVec2.add(new PVector(cos(angle) * topRadius,0,sin(angle) * topRadius));

          vertex(rotVec.x,rotVec.y,rotVec.z);
          vertex(rotVec2.x,rotVec2.y,rotVec2.z);
          //vertex(points[i-1].x + cos(angle) * topRadius, points[i-1].y, points[i-1].z + sin(angle) * topRadius);
          //vertex(points[i].x + cos(angle) * bottomRadius, points[i].y, points[i].z + sin(angle) * bottomRadius);

          angle += angleIncrement;
          }
        endShape();
      }
      //begin draw segments between caps
    }else println("Not enough points: " + pointsNum);
  }
}

fix_test http://doc.gold.ac.uk/~ma802gp/extrude2.gif

由 drhirsch 编辑这应该有效:

Edit by drhirsch This should work:

void draw() {
    if(pointsNum >= 2){  
      float angle = 0;
      float angleIncrement = TWO_PI / sides;

      //begin draw segments between caps
      angle = 0;
      for(int i = 1; i < pointsNum ; ++i){
        beginShape(QUAD_STRIP);
        float angleBetweenNextAndPrevious = 0.0;
        if(i < pointsNum - 1) angleBetweenNextAndPrevious = PVector.angleBetween(points[i],points[i+1]);
        PVector s = new PVector(0,0,1);
        PVector s2 = new PVector(0,0,1);
        PVector cn = new PVector();
        PVector cn2 = new PVector();
        points[i-1].normalize(cn);
        points[i].normalize(cn2);
        PVector r = s.cross(cn);
        PVector r2 = s.cross(cn2);
        PMatrix3D rot = new PMatrix3D(1,0,0,0,
                                      0,1,0,0,
                                      0,0,1,0,
                                      0,0,0,1);
        PMatrix3D rot2 = new PMatrix3D(1,0,0,0,
                                       0,1,0,0,
                                       0,0,1,0,
                                       0,0,0,1);

        rot.rotate(angleBetweenNextAndPrevious,r.x,r.y,r.z);
        rot2.rotate(angleBetweenNextAndPrevious,r2.x,r2.y,r2.z);
        PVector rotVec = new PVector();
        PVector rotVec2 = new PVector();

        for(int j = 0; j < sides + 1; j++){
          // I am still not sure about this. Should the shape be in the xy plane 
          // if the extrusion is mainly along the z axis? If the shape is now in
          // the xz plane, you need to use (0,1,0) as normal vector of the shape
          // (this would be s and s2 above, don't use the short names I have
          // used, sorry)
          PVector shape = new PVector(cos(angle) * topRadius,0,sin(angle) * topRadius);

          rot.mult(shape, rotVec);
          rot2.mult(shape,rotVec2);

          rotVec.add(points[i-1]);
          rotVec2.add(points[i]);

          vertex(rotVec.x,rotVec.y,rotVec.z);
          vertex(rotVec2.x,rotVec2.y,rotVec2.z);
          //vertex(points[i-1].x + cos(angle) * topRadius, points[i-1].y, points[i-1].z + sin(angle) * topRadius);
          //vertex(points[i].x + cos(angle) * bottomRadius, points[i].y, points[i].z + sin(angle) * bottomRadius);

          angle += angleIncrement;
          }
        endShape();
      }
      //begin draw segments between caps
    }else println("Not enough points: " + pointsNum);
  }
}

更新

这是我的问题的简单说明:

Here is a simple illustration of my problem:

说明 http://doc.gold.ac.uk/~ma802gp/description.gif

如果pointsNum = 6,蓝色路径相当于我代码中的points[] PVector数组.红色路径是我正在努力解决的问题,绿色路径是我想要实现的.

The blue path is equivalent to the points[] PVector array in my code, if pointsNum = 6. The red path is what I'm struggling to solve, the green path is what I want to achieve.

更新

我认为顶点顺序的一些小问题.以下是一些使用 6 分且无 (if/else % 2) 星级条件的打印屏幕.

Some minor issues with the order of vertices I think. Here are some print screens using 6 points and no (if/else % 2) star condition.

points1 http://doc.gold.ac.uk/~ma802gp/points1.gif

替代文字 http://doc.gold.ac.uk/~ma802gp/points2.gif

推荐答案

假设您的形状具有法向量 S.在您的示例中,S 将是 (0,0,1),因为您的形状在 xy 中是平坦的.您可以使用当前路径矢量 V(归一化)和 S 之间的叉积来获得旋转轴矢量 R.您需要围绕 R 旋转您的形状.可以从 S 和 V 之间的点积获得旋转角度.所以:

Assuming your shape has a normal vector S. In your example S would be (0,0,1), because your shape is flat in xy. You can use the cross product between the current path vector V (normalized) and S to obtain the rotation axis vector R. You need to rotate your shape around R. The angle of rotation can be obtained from the dot product between S and V. So:

R = S x V
a = arc cos(S . V)

现在您可以使用 R 和 a 设置旋转矩阵并通过它旋转形状.

Now you can setup a rotation matrix with R and a and rotate the shape by it.

您可以使用 glRotate(...) 来旋转堆栈上的当前矩阵,但这不能在 glBegin() 和 glEnd() 之间完成.所以你必须自己做矩阵乘法,或者用库来做.

You can use glRotate(...) to rotate the current matrix on the stack, but this can't be done between glBegin() and glEnd(). So you have to do the matrix multiplication by yourself or with a library.

在对您正在使用的库进行短暂浏览后,您应该能够使用

After a short look at the library you are using, you should be able to setup the rotation matrix with

PVector s = new PVector(0,0,1);  // is already normalized (meaning is has length 1)
PVector cn;
current.normalize(cn);
PVector r = s.cross(cn);
float a = acos(s.dot(cn));
PMatrix rot = new PMatrix(1, 0, 0, 0,
                          0, 1, 0, 0,
                          0, 0, 1, 0,
                          0, 0, 0, 1);
rot.rotate(a, r.x, r.y, r.z);

现在将形状的每个元素与 rot 相乘,并通过当前路径向量进行平移:

and now multiply each element of your shape with rot and translate it by your current path vector:

PVector rotVec;
rot.mult((PVector)shape[i], rotVec);
rotVec.add(current);

这篇关于如何在 3d 中挤出路径?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆