3Dでパスを押し出す方法は?
-
08-07-2019 - |
質問
3Dでパスを押し出そうとしています。まだ特別なことは何もせず、いくつかのポイントに従い、「チューブ」に正多角形を使用するだけです。今のところ、プロトタイプを迅速に作成するために Processing を使用していますが、後でコードを OpenGL に変換する予定です。
私の問題は、「ジョイント」を直角に回転させることです。角度を取得する方法については大まかなアイデアはあると思いますが、わかりません。
Simon Greenwold によるサンプルから開始しました ([処理] > [ファイル] > [サンプル] > [3D] > [フォーム] > [頂点])。これまでの私の試みは次のとおりです。
更新 > リファクタリング/簡略化されたコード
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;
}
Extrusion クラスは次のとおりです。
インポート処理.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の芸術に基づいて幾何学的な体を作成しようとしているので、私は押出を使用しています。
以下にいくつかのサンプルを示します。
星の絵 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
品質が悪くてごめんなさい。
三角形の画像からわかるように、これは押し出しで実現されます。
アップデート
以下は、draw メソッドで drhirsch のヘルプを使用する私の試みです。
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 オブジェクトの Vector の代わりに PVector オブジェクトの配列を使用しています。
これは、エッシャー風の結果を伴う私のさらに別の試みです。
更新されたドロー
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で編集これ は機能するはずです:
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);
}
}
アップデート
これが私の問題の簡単な図です:
説明 http://doc.gold.ac.uk/~ma802gp/description.gif
青いパスは、pointsNum = 6 の場合、コード内の Points[] PVector 配列と同等です。赤い道は私が解決しようと苦労しているもの、緑の道は私が達成したいことです。
アップデート
頂点の順序にいくつかの小さな問題があると思います。以下に、6 ポイントとスター条件なし (if/else % 2) を使用したいくつかの印刷画面を示します。
解決
形状に法線ベクトル S があると仮定します。あなたの例では、形状が xy で平坦であるため、S は (0,0,1) になります。現在のパス ベクトル V (正規化) と S の外積を使用して、回転軸ベクトル R を取得できます。R を中心にシェイプを回転する必要があります。回転角は S と V の内積から求めることができます。それで:
R = S x V
a = arc cos(S . V)
これで、 回転行列 Rとaで形状を回転させます。
glRotate(...) を使用してスタック上の現在の行列を回転できますが、これは glBegin() と glEnd() の間では実行できません。したがって、行列の乗算を自分で行うか、ライブラリを使用して行う必要があります。
編集:使用しているライブラリを少し確認すると、次のように回転行列を設定できるはずです。
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 を乗算し、現在のパス ベクトルで変換します。
PVector rotVec;
rot.mult((PVector)shape[i], rotVec);
rotVec.add(current);