将 3d 面绘制为 2d

发布于 2024-09-09 18:16:17 字数 5375 浏览 7 评论 0原文

我有 3d 网格,我想为每个面绘制 2d 形状。

我的想法是这样的: 对于每张脸 1.访问人脸法线 2.从法向量得到旋转矩阵 3. 将每个顶点乘以旋转矩阵以获得“2d like”平面中的顶点 4.从变换后的顶点获取2个坐标

我不知道这是否是最好的方法,所以欢迎任何建议。

目前我正在尝试从法线向量中获取旋转矩阵, 我该怎么做?

更新:

这是我需要的直观解释:

3d 到 2d

目前我有四边形,但没有问题 将它们转换成三角形。

我想旋转一个面的顶点,这样 其中一个维度被压平。

我还需要存储面部的原始 3d 旋转。 我想这将是面部的反向旋转 普通的。

我想我有点迷失在空间中:)

这是我使用 处理 制作的基本原型:

void setup(){
  size(400,400,P3D);
  background(255);
  stroke(0,0,120);
  smooth();
  fill(0,120,0);

  PVector x = new PVector(1,0,0);
  PVector y = new PVector(0,1,0);
  PVector z = new PVector(0,0,1);

  PVector n  = new PVector(0.378521084785,0.925412774086,0.0180059205741);//normal
  PVector p0 = new PVector(0.372828125954,-0.178844243288,1.35241031647);
  PVector p1 = new PVector(-1.25476706028,0.505195975304,0.412718296051);
  PVector p2 = new PVector(-0.372828245163,0.178844287992,-1.35241031647);
  PVector p3 = new PVector(1.2547672987,-0.505196034908,-0.412717700005);

  PVector[] face = {p0,p1,p2,p3};
  PVector[] face2d = new PVector[4];
  PVector   nr = PVector.add(n,new PVector());//clone normal

  float rx = degrees(acos(n.dot(x)));//angle between normal and x axis
  float ry = degrees(acos(n.dot(y)));//angle between normal and y axis
  float rz = degrees(acos(n.dot(z)));//angle between normal and z axis

  PMatrix3D r = new PMatrix3D();
  //is this ok, or should I drop the builtin function, and add 
  //the rotations manually
  r.rotateX(rx);
  r.rotateY(ry);
  r.rotateZ(rz);

  print("original: ");println(face);
  for(int i = 0 ; i < 4; i++){
    PVector rv = new PVector();
    PVector rn = new PVector();
    r.mult(face[i],rv);
    r.mult(nr,rn);
    face2d[i] = PVector.add(face[i],rv);
  }
  print("rotated: ");println(face2d);
  //draw
  float scale = 100.0;
  translate(width * .5,height * .5);//move to centre, Processing has 0,0 = Top,Lef
  beginShape(QUADS);
  for(int i = 0 ; i < 4; i++){
   vertex(face2d[i].x * scale,face2d[i].y * scale,face2d[i].z * scale);
  }
  endShape();
  line(0,0,0,nr.x*scale,nr.y*scale,nr.z*scale);

  //what do I do with this ?
  float c = cos(0), s = sin(0);
  float x2 = n.x*n.x,y2 = n.y*n.y,z2 = n.z*n.z; 
  PMatrix3D m = new PMatrix3D(x2+(1-x2)*c,  n.x*n.y*(1-c)-n.z*s,  n.x*n.z*(1-c)+n.y*s,  0,
                              n.x*n.y*(1-c)+n.z*s,y2+(1-y2)*c,n.y*n.z*(1-c)-n.x*s,0,
                              n.x*n.y*(1-c)-n.y*s,n.x*n.z*(1-c)+n.x*s,z2-(1-z2)*c,0,
                              0,0,0,1);
}

更新< /strong>

抱歉,如果我变得烦人,但我似乎不明白。

这里有一些使用 Blender 的 API 的 python:

import Blender
from Blender import *
import math
from math import sin,cos,radians,degrees

def getRotMatrix(n):
    c = cos(0)
    s = sin(0)
    x2 = n.x*n.x
    y2 = n.y*n.y
    z2 = n.z*n.z
    l1 = x2+(1-x2)*c, n.x*n.y*(1-c)+n.z*s, n.x*n.y*(1-c)-n.y*s
    l2 = n.x*n.y*(1-c)-n.z*s,y2+(1-y2)*c,n.x*n.z*(1-c)+n.x*s
    l3 = n.x*n.z*(1-c)+n.y*s,n.y*n.z*(1-c)-n.x*s,z2-(1-z2)*c
    m = Mathutils.Matrix(l1,l2,l3)
    return m

scn = Scene.GetCurrent()
ob = scn.objects.active.getData(mesh=True)#access mesh

out = ob.name+'\n'
#face0
f = ob.faces[0]
n = f.v[0].no
out += 'face: ' + str(f)+'\n'
out += 'normal: ' + str(n)+'\n'

m = getRotMatrix(n)
m.invert()

rvs = []
for v in range(0,len(f.v)):
    out += 'original vertex'+str(v)+': ' + str(f.v[v].co) + '\n'
    rvs.append(m*f.v[v].co)

out += '\n'
for v in range(0,len(rvs)):
    out += 'original vertex'+str(v)+': ' + str(rvs[v]) + '\n'

f = open('out.txt','w')
f.write(out)
f.close

我所做的一切是获取当前对象,访问第一个面,获取法线,获取顶点,计算旋转矩阵,反转它,然后乘以每个顶点。 最后我写了一个简单的输出。

以下是默认平面的输出,我手动将所有顶点旋转 30 度:

Plane.008
face: [MFace (0 3 2 1) 0]
normal: [0.000000, -0.499985, 0.866024](vector)
original vertex0: [1.000000, 0.866025, 0.500000](vector)
original vertex1: [-1.000000, 0.866026, 0.500000](vector)
original vertex2: [-1.000000, -0.866025, -0.500000](vector)
original vertex3: [1.000000, -0.866025, -0.500000](vector)

rotated vertex0: [1.000000, 0.866025, 1.000011](vector)
rotated vertex1: [-1.000000, 0.866026, 1.000012](vector)
rotated vertex2: [-1.000000, -0.866025, -1.000012](vector)
rotated vertex3: [1.000000, -0.866025, -1.000012](vector)

这是著名的 Suzanne 网格的第一个面:

Suzanne.001
face: [MFace (46 0 2 44) 0]
normal: [0.987976, -0.010102, 0.154088](vector)
original vertex0: [0.468750, 0.242188, 0.757813](vector)
original vertex1: [0.437500, 0.164063, 0.765625](vector)
original vertex2: [0.500000, 0.093750, 0.687500](vector)
original vertex3: [0.562500, 0.242188, 0.671875](vector)

rotated vertex0: [0.468750, 0.242188, -0.795592](vector)
rotated vertex1: [0.437500, 0.164063, -0.803794](vector)
rotated vertex2: [0.500000, 0.093750, -0.721774](vector)
rotated vertex3: [0.562500, 0.242188, -0.705370](vector)

Plane.008 网格中的顶点已更改,Suzanne.001 网格中的顶点已更改 不是。他们不应该吗?我应该期望在一个轴上得到零吗? 一旦我从法向量得到旋转矩阵,x,y,z 上的旋转是多少?

注意: 1. Blender 的 Matrix 支持 * 运算符 2. 在 Blender 的坐标系中 Z 点向上。它看起来像一个右手系统,在 X 上旋转 90 度。

谢谢

I have 3d mesh and I would like to draw each face a 2d shape.

What I have in mind is this:
for each face
1. access the face normal
2. get a rotation matrix from the normal vector
3. multiply each vertex to the rotation matrix to get the vertices in a '2d like ' plane
4. get 2 coordinates from the transformed vertices

I don't know if this is the best way to do this, so any suggestion is welcome.

At the moment I'm trying to get a rotation matrix from the normal vector,
how would I do this ?

UPDATE:

Here is a visual explanation of what I need:

3d to 2d

At the moment I have quads, but there's no problem
converting them into triangles.

I want to rotate the vertices of a face, so that
one of the dimensions gets flattened.

I also need to store the original 3d rotation of the face.
I imagine that would be inverse rotation of the face
normal.

I think I'm a bit lost in space :)

Here's a basic prototype I did using Processing:

void setup(){
  size(400,400,P3D);
  background(255);
  stroke(0,0,120);
  smooth();
  fill(0,120,0);

  PVector x = new PVector(1,0,0);
  PVector y = new PVector(0,1,0);
  PVector z = new PVector(0,0,1);

  PVector n  = new PVector(0.378521084785,0.925412774086,0.0180059205741);//normal
  PVector p0 = new PVector(0.372828125954,-0.178844243288,1.35241031647);
  PVector p1 = new PVector(-1.25476706028,0.505195975304,0.412718296051);
  PVector p2 = new PVector(-0.372828245163,0.178844287992,-1.35241031647);
  PVector p3 = new PVector(1.2547672987,-0.505196034908,-0.412717700005);

  PVector[] face = {p0,p1,p2,p3};
  PVector[] face2d = new PVector[4];
  PVector   nr = PVector.add(n,new PVector());//clone normal

  float rx = degrees(acos(n.dot(x)));//angle between normal and x axis
  float ry = degrees(acos(n.dot(y)));//angle between normal and y axis
  float rz = degrees(acos(n.dot(z)));//angle between normal and z axis

  PMatrix3D r = new PMatrix3D();
  //is this ok, or should I drop the builtin function, and add 
  //the rotations manually
  r.rotateX(rx);
  r.rotateY(ry);
  r.rotateZ(rz);

  print("original: ");println(face);
  for(int i = 0 ; i < 4; i++){
    PVector rv = new PVector();
    PVector rn = new PVector();
    r.mult(face[i],rv);
    r.mult(nr,rn);
    face2d[i] = PVector.add(face[i],rv);
  }
  print("rotated: ");println(face2d);
  //draw
  float scale = 100.0;
  translate(width * .5,height * .5);//move to centre, Processing has 0,0 = Top,Lef
  beginShape(QUADS);
  for(int i = 0 ; i < 4; i++){
   vertex(face2d[i].x * scale,face2d[i].y * scale,face2d[i].z * scale);
  }
  endShape();
  line(0,0,0,nr.x*scale,nr.y*scale,nr.z*scale);

  //what do I do with this ?
  float c = cos(0), s = sin(0);
  float x2 = n.x*n.x,y2 = n.y*n.y,z2 = n.z*n.z; 
  PMatrix3D m = new PMatrix3D(x2+(1-x2)*c,  n.x*n.y*(1-c)-n.z*s,  n.x*n.z*(1-c)+n.y*s,  0,
                              n.x*n.y*(1-c)+n.z*s,y2+(1-y2)*c,n.y*n.z*(1-c)-n.x*s,0,
                              n.x*n.y*(1-c)-n.y*s,n.x*n.z*(1-c)+n.x*s,z2-(1-z2)*c,0,
                              0,0,0,1);
}

Update

Sorry if I'm getting annoying, but I don't seem to get it.

Here's a bit of python using Blender's API:

import Blender
from Blender import *
import math
from math import sin,cos,radians,degrees

def getRotMatrix(n):
    c = cos(0)
    s = sin(0)
    x2 = n.x*n.x
    y2 = n.y*n.y
    z2 = n.z*n.z
    l1 = x2+(1-x2)*c, n.x*n.y*(1-c)+n.z*s, n.x*n.y*(1-c)-n.y*s
    l2 = n.x*n.y*(1-c)-n.z*s,y2+(1-y2)*c,n.x*n.z*(1-c)+n.x*s
    l3 = n.x*n.z*(1-c)+n.y*s,n.y*n.z*(1-c)-n.x*s,z2-(1-z2)*c
    m = Mathutils.Matrix(l1,l2,l3)
    return m

scn = Scene.GetCurrent()
ob = scn.objects.active.getData(mesh=True)#access mesh

out = ob.name+'\n'
#face0
f = ob.faces[0]
n = f.v[0].no
out += 'face: ' + str(f)+'\n'
out += 'normal: ' + str(n)+'\n'

m = getRotMatrix(n)
m.invert()

rvs = []
for v in range(0,len(f.v)):
    out += 'original vertex'+str(v)+': ' + str(f.v[v].co) + '\n'
    rvs.append(m*f.v[v].co)

out += '\n'
for v in range(0,len(rvs)):
    out += 'original vertex'+str(v)+': ' + str(rvs[v]) + '\n'

f = open('out.txt','w')
f.write(out)
f.close

All I do is get the current object, access the first face, get the normal, get the vertices, calculate the rotation matrix, invert it, then multiply it by each vertex.
Finally I write a simple output.

Here's the output for a default plane for which I rotated all the vertices manually by 30 degrees:

Plane.008
face: [MFace (0 3 2 1) 0]
normal: [0.000000, -0.499985, 0.866024](vector)
original vertex0: [1.000000, 0.866025, 0.500000](vector)
original vertex1: [-1.000000, 0.866026, 0.500000](vector)
original vertex2: [-1.000000, -0.866025, -0.500000](vector)
original vertex3: [1.000000, -0.866025, -0.500000](vector)

rotated vertex0: [1.000000, 0.866025, 1.000011](vector)
rotated vertex1: [-1.000000, 0.866026, 1.000012](vector)
rotated vertex2: [-1.000000, -0.866025, -1.000012](vector)
rotated vertex3: [1.000000, -0.866025, -1.000012](vector)

Here's the first face of the famous Suzanne mesh:

Suzanne.001
face: [MFace (46 0 2 44) 0]
normal: [0.987976, -0.010102, 0.154088](vector)
original vertex0: [0.468750, 0.242188, 0.757813](vector)
original vertex1: [0.437500, 0.164063, 0.765625](vector)
original vertex2: [0.500000, 0.093750, 0.687500](vector)
original vertex3: [0.562500, 0.242188, 0.671875](vector)

rotated vertex0: [0.468750, 0.242188, -0.795592](vector)
rotated vertex1: [0.437500, 0.164063, -0.803794](vector)
rotated vertex2: [0.500000, 0.093750, -0.721774](vector)
rotated vertex3: [0.562500, 0.242188, -0.705370](vector)

The vertices from the Plane.008 mesh are altered, the ones from Suzanne.001's mesh
aren't. Shouldn't they ? Should I expect to get zeroes on one axis ?
Once I got the rotation matrix from the normal vector, what is the rotation on x,y,z ?

Note: 1. Blender's Matrix supports the * operator 2.In Blender's coordinate system Z point's up. It looks like a right handed system, rotated 90 degrees on X.

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

不羁少年 2024-09-16 18:16:17

这对我来说看起来很合理。以下是如何从法线向量获取旋转矩阵。法线是向量。角度为 0。您可能想要反向旋转。

你的网格是三角形的吗?我假设是这样。如果是这样,您可以做到这一点,无需旋转矩阵。设面的点为A,B,C。取面的任意两个顶点,例如 AB。定义沿向量 AB 的 x 轴。 A 位于 0,0B 位于 0,|AB|C 可以通过三角函数使用 ACAB 之间的角度(通过使用点积得到)和长度 来确定|AC|

That looks reasonable to me. Here's how to get a rotation matrix from normal vector. The normal is the vector. The angle is 0. You probably want the inverse rotation.

Is your mesh triangulated? I'm assuming it is. If so, you can do this, without rotation matrices. Let the points of the face be A,B,C. Take any two vertices of the face, say A and B. Define the x axis along vector AB. A is at 0,0. B is at 0,|AB|. C can be determined from trigonometry using the angle between AC and AB (which you get by using the dot product) and the length |AC|.

新人笑 2024-09-16 18:16:17

您正确创建了 m 矩阵。这是对应于法线向量的旋转。您可以使用该矩阵的逆来“取消旋转”您的点。 face2d 的法线将为 x,即沿 x 轴的点。因此,相应地提取你的二维坐标。 (这假设您的四边形大约是平面的。)

我不知道您正在使用的库(处理),所以我只是假设有 m.invert() 的方法和用于将旋转矩阵应用于点的运算符。当然,它们也可以被称为其他名称。幸运的是,纯旋转矩阵的逆矩阵是其转置,如果需要,可以直接手动将矩阵和向量相乘。

void setup(){
  size(400,400,P3D);
  background(255);
  stroke(0,0,120);
  smooth();
  fill(0,120,0);

  PVector x = new PVector(1,0,0);
  PVector y = new PVector(0,1,0);
  PVector z = new PVector(0,0,1);

  PVector n  = new PVector(0.378521084785,0.925412774086,0.0180059205741);//normal
  PVector p0 = new PVector(0.372828125954,-0.178844243288,1.35241031647);
  PVector p1 = new PVector(-1.25476706028,0.505195975304,0.412718296051);
  PVector p2 = new PVector(-0.372828245163,0.178844287992,-1.35241031647);
  PVector p3 = new PVector(1.2547672987,-0.505196034908,-0.412717700005);

  PVector[] face = {p0,p1,p2,p3};
  PVector[] face2d = new PVector[4];

  //what do I do with this ?
  float c = cos(0), s = sin(0);
  float x2 = n.x*n.x,y2 = n.y*n.y,z2 = n.z*n.z; 
  PMatrix3D m_inverse = 
      new PMatrix3D(x2+(1-x2)*c, n.x*n.y*(1-c)+n.z*s, n.x*n.y*(1-c)-n.y*s, 0,
                    n.x*n.y*(1-c)-n.z*s,y2+(1-y2)*c,n.x*n.z*(1-c)+n.x*s,   0,
                     n.x*n.z*(1-c)+n.y*s,n.y*n.z*(1-c)-n.x*s,z2-(1-z2)*c,  0,
                    0,0,0,1);

  face2d[0] = m_inverse * p0; // Assuming there's an appropriate operator*().
  face2d[1] = m_inverse * p1; 
  face2d[2] = m_inverse * p2;
  face2d[3] = m_inverse * p3;

  // print & draw as you did before...

}

You created the m matrix correctly. This is the rotation that corresponds to your normal vector. You can use the inverse of this matrix to "unrotate" your points. The normal of face2d will be x, i.e. point along the x-axis. So extract your 2d coordinates accordingly. (This assumes your quad is approximately planar.)

I don't know the library you are using (Processing), so I'm just assuming there are methods for m.invert() and an operator for applying a rotation matrix to a point. They may of course be called something else. Luckily the inverse of a pure rotation matrix is its transpose, and multiplying a matrix and a vector are straightforward to do manually if you need to.

void setup(){
  size(400,400,P3D);
  background(255);
  stroke(0,0,120);
  smooth();
  fill(0,120,0);

  PVector x = new PVector(1,0,0);
  PVector y = new PVector(0,1,0);
  PVector z = new PVector(0,0,1);

  PVector n  = new PVector(0.378521084785,0.925412774086,0.0180059205741);//normal
  PVector p0 = new PVector(0.372828125954,-0.178844243288,1.35241031647);
  PVector p1 = new PVector(-1.25476706028,0.505195975304,0.412718296051);
  PVector p2 = new PVector(-0.372828245163,0.178844287992,-1.35241031647);
  PVector p3 = new PVector(1.2547672987,-0.505196034908,-0.412717700005);

  PVector[] face = {p0,p1,p2,p3};
  PVector[] face2d = new PVector[4];

  //what do I do with this ?
  float c = cos(0), s = sin(0);
  float x2 = n.x*n.x,y2 = n.y*n.y,z2 = n.z*n.z; 
  PMatrix3D m_inverse = 
      new PMatrix3D(x2+(1-x2)*c, n.x*n.y*(1-c)+n.z*s, n.x*n.y*(1-c)-n.y*s, 0,
                    n.x*n.y*(1-c)-n.z*s,y2+(1-y2)*c,n.x*n.z*(1-c)+n.x*s,   0,
                     n.x*n.z*(1-c)+n.y*s,n.y*n.z*(1-c)-n.x*s,z2-(1-z2)*c,  0,
                    0,0,0,1);

  face2d[0] = m_inverse * p0; // Assuming there's an appropriate operator*().
  face2d[1] = m_inverse * p1; 
  face2d[2] = m_inverse * p2;
  face2d[3] = m_inverse * p3;

  // print & draw as you did before...

}
朱染 2024-09-16 18:16:17

对于面 v0-v1-v3-v2,向量 v3-v0、v3-v2 和面法线已经形成旋转矩阵,可将 2d 面转换为 3d 面。

矩阵表示坐标系。每行(或列,取决于符号)对应于新坐标系内的轴坐标系。 3d 旋转/平移矩阵可以表示为:

vx.x    vx.y    vx.z    0
vy.x    vy.y    vy.z    0
vz.x    vz.y    vz.z    0
vp.x    vp.y    vp.z    1

其中 vx 是坐标系的 x 轴,vy - y 轴,vz - z 轴,vp - 新系统的原点。

假设 v3-v0 是 y 轴(第 2 行),v3-v2 - x 轴(第 1 行),法线 - z 轴(第 3 行)。从它们构建一个矩阵。然后求逆矩阵。您将得到一个矩阵,它将 3d 面旋转为 2d 面。

我有 3d 网格,我想将每个面绘制为 2d 形状。

我怀疑 UV 展开 算法比尝试从 3d 面获取旋转矩阵更接近您想要实现的目标。

For face v0-v1-v3-v2 vectors v3-v0, v3-v2 and a face normal already form rotation matrix that would transform 2d face into 3d face.

Matrix represents coordinate system. Each row (or column, depending on notation) corresponds to axis coordinate system within new coordinate system. 3d rotation/translation matrix can be represented as:

vx.x    vx.y    vx.z    0
vy.x    vy.y    vy.z    0
vz.x    vz.y    vz.z    0
vp.x    vp.y    vp.z    1

where vx is an x axis of a coordinate system, vy - y axis, vz - z axis, and vp - origin of new system.

Assume that v3-v0 is an y axis (2nd row), v3-v2 - x axis (1st row), and normal - z axis (3rd row). Build a matrix from them. Then invert matrix. You'll get a matrix that will rotate a 3d face into 2d face.

I have 3d mesh and I would like to draw each face a 2d shape.

I suspect that UV unwrapping algorithms are closer to what you want to achieve than trying to get rotation matrix from 3d face.

风筝有风,海豚有海 2024-09-16 18:16:17

这很容易实现:(注意:“面部”我的意思是“三角形”)

  1. 创建一个视图矩阵来表示相机看着面部。
    1. 通过双线性插值确定面部的中心。
    2. 确定面部的法线。
    3. 将相机放置在与法线相反的方向上一些单位。
    4. 让摄像头对准脸部中央。
    5. 将相机向上矢量点设置在面部任意顶点的中间方向。
    6. 将宽高比设置为 1。
    7. 使用此数据计算视图矩阵。
  2. 创建正交投影矩阵。
    1. 将视锥体的宽度和高度设置得足够大以包含整个面(例如面最长部位的长度)。
    2. 计算投影矩阵。
  3. 对于面的每个顶点 v,将其乘以两个矩阵:v * 视图 * 投影。

结果是将您的 3D 面孔投影到 2D 空间,就好像您正在完全正交地观看它们一样,没有任何透视干扰。最终坐标将采用标准化屏幕坐标,其中 (-1, -1) 是左下角,(0, 0) 是中心,(1, 1) 是右上角。

That's very easy to achieve: (Note: By "face" I mean "triangle")

  1. Create a view matrix that represents a camera looking at a face.
    1. Determine the center of the face with bi-linear interpolation.
    2. Determine the normal of the face.
    3. Position the camera some units in opposite normal direction.
    4. Let the camera look at the center of the face.
    5. Set the cameras up vector point in the direction of the middle of any vertex of the face.
    6. Set the aspect ratio to 1.
    7. Compute the view matrix using this data.
  2. Create a orthogonal projection matrix.
    1. Set the width and height of the view frustum large enough to contain the whole face (e.g. the length of the longest site of a face).
    2. Compute the projection matrix.
  3. For every vertex v of the face, multiply it by both matrices: v * view * projection.

The result is a projection of Your 3d faces into 2d space as if You were looking at them exactly orthogonal without any perspective disturbances. The final coordinates will be in normalized screen coordinates where (-1, -1) is the bottom left corner, (0, 0) is the center and (1, 1) is the top right corner.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文