代码之家  ›  专栏  ›  技术社区  ›  George Profenza

将三维面绘制为二维

  •  3
  • George Profenza  · 技术社区  · 14 年前

    我有三维网格,我想为每个面绘制一个二维形状。

    我想到的是: 每一张脸 1。进入面部正常 2。从法向量得到旋转矩阵 三。将每个顶点乘以旋转矩阵,得到“二维相似”平面中的顶点。 4。从变换的顶点获取2个坐标

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

    现在我想从法向量得到一个旋转矩阵, 我该怎么做?

    更新:

    以下是我需要的直观解释:

    现在我有股四头肌,但没问题 将它们转换为三角形。

    我要旋转面的顶点,以便 其中一个维度变平了。

    我还需要存储人脸的原始三维旋转。 我想那是人脸的倒转。 正常。

    我想我有点迷路了:)

    以下是我使用 processing的基本原型:

    void setup()。{
    尺寸(400400,p3d);
    背景(255);
    行程(0,0120);
    平滑();
    填充(0120,0);
    
    pvector x=新pvector(1,0,0);
    pvector y=新pvector(0,1,0);
    pvector z=新pvector(0,0,1);
    
    pvector n=新pvector(0.378521084785,0.9254127748086,0.0180059205741);//正常
    pvector p0=新pvector(0.372828125954,-0.178844243288,1.35241031647);
    pvector p1=新pvector(-1.254767062028,0.505195975304,0.412718296051);
    pvector p2=新pvector(-0.372828245163,0.178844287992,-1.35241031647);
    pvector p3=新pvector(1.2547672987,-0.505196034908,-0.4127177000005);
    
    pvector[]面=p0,p1,p2,p3
    pvector[]face2d=新pvector[4];
    pvector nr=pvector.add(n,new pvector());//克隆正常
    
    float rx=度数(acos(n.dot(x));//法向和x轴之间的角度
    float ry=度数(acos(n.dot(y));//法轴和y轴之间的角度
    float rz=度数(acos(n.dot(z));//法轴和Z轴之间的角度
    
    pmatrix3d r=新pmatrix3d();
    //这样可以吗,还是应该删除内置函数,然后添加
    //手动旋转
    R.RATATEX(RX);
    R.RATATY(RY);
    RZ(RZ);
    
    print(“原件:”);println(面);
    对于(int i=0;i<4;i++){
    pvector rv=新pvector();
    pvector rn=new pvector();
    R.mult(面[I],RV);
    R.Mult(NR,RN);
    face2d[i]=pvector.add(face[i],rv);
    }
    print(“旋转”);println(face2d);
    绘制
    浮秤=100.0;
    translate(宽度*.5,高度*.5);//移动到中心,处理具有0,0=顶部,lef
    开始形状(四边形);
    对于(int i=0;i<4;i++){
    顶点(face2d[i].x*比例,face2d[i].y*比例,face2d[i].z*比例);
    }
    EnthHeult();
    线(0,0,0,nr.x*比例尺,nr.y*比例尺,nr.z*比例尺);
    
    我该怎么办?
    浮点c=cos(0),s=sin(0);
    浮动x2=n.x*n.x,y2=n.y*n.y,z2=n.z*n.z;
    pmatrix3d m=新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,
    0001);
    }
    < /代码> 
    
    

    更新

    抱歉,如果我有点烦人,但我似乎不明白。

    这里有一点python的用法blender的api:。

    导入搅拌机 从搅拌机进口* 导入数学 从数学导入sin、cos、radians、degrees 定义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.矩阵(l1、l2、l3) 返回M scn=scene.getcurrent()。 ob=scn.objects.active.getdata(mesh=true)访问网格 out=ob.name+'\n' 面部表情0 F=OB面〔0〕 n=f.v(0)。 out+='面:'+str(f)+'\n' out+='正常:'+str(n)+'\n' m=GetRotmatrix(n) m() RVS= 对于范围(0,长度(f.v))内的V: out+='原始顶点'+str(v)+':'+str(f.v[v].co)+'\n' 附加值(m*f.v[v].co) 输出+=’\n’ 对于范围(0,len(rvs))内的V: out+='原始顶点'+str(v)+':'+str(rvs[v])+'\n' F=open('out.txt','w') 写(出) 关闭 < /代码>

    我要做的就是得到当前对象,访问第一个面,得到法向,得到顶点,计算旋转矩阵,反转它,然后乘以每个顶点。 最后,我编写了一个简单的输出。

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

    plane.008
    面:【mFace(0 3 2 1)0】
    正常:[0.000000,-0.499985,0.866024](矢量)
    原始Vertex0:[1.000000,0.866025,0.500000](矢量)
    原始Vertex1:[-1.000000,0.866026,0.500000](矢量)
    原始Vertex2:[-1.000000,-0.866025,-0.500000](矢量)
    原始Vertex3:[1.000000,-0.866025,-0.500000](矢量)
    
    旋转顶点0:[1.000000,0.866025,1.000011](矢量)
    旋转顶点1:[-1.000000,0.866026,1.000012](矢量)
    旋转顶点2:[1.000000,-0.866025,-1.000012](矢量)
    旋转顶点3:[1.000000,-0.866025,-1.000012](矢量)
    < /代码> 
    
    

    这是著名的苏珊娜网布的第一张脸:

    suzanne.001
    面:【mFace(46 0 2 44)0】
    正常:[0.987976,-0.010102,0.154088](矢量)
    原始顶点0:[0.468750,0.242188,0.757813](矢量)
    原始顶点1:[0.437500,0.164063,0.765625](矢量)
    原始Vertex2:[0.500000,0.093750,0.687500](矢量)
    原始顶点3:[0.562500,0.242188,0.671875](矢量)
    
    旋转顶点0:[0.468750,0.242188,-0.795592](矢量)
    旋转顶点1:[0.437500,0.164063,-0.803794](矢量)
    旋转顶点2:[0.500000,0.093750,-0.721774](矢量)
    旋转顶点3:[0.562500,0.242188,-0.705370](矢量)
    < /代码> 
    
    

    平面.008网格的顶点被更改,Suzanne.001网格的顶点被更改。 不是吗?不是吗?我应该期望在一个轴上得到零吗? 从法向量得到旋转矩阵后,x,y,z上的旋转是什么?

    注:1.搅拌机矩阵支持*运算符2。在搅拌机坐标系中,Z点向上。它看起来像一个右手系统,在X轴上旋转90度。

    谢谢请记住: 每一张脸 1。进入面部正常 2。从法向量得到旋转矩阵 三。将每个顶点乘以旋转矩阵,得到“二维相似”平面中的顶点。 4。从变换的顶点获取2个坐标

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

    现在我想从法向量得到一个旋转矩阵, 我该怎么做?

    更新:

    以下是我需要的直观解释:

    3d to 2d

    现在我有股四头肌,但没问题 把它们转换成三角形。

    我要旋转面的顶点,以便 其中一个维度变平了。

    我还需要存储人脸的原始三维旋转。 我想那是人脸的倒转。 正常的。

    我想我有点迷路了:)

    这是我使用的一个基本原型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);
    }
    

    更新

    对不起,如果我有点烦人,但我好像不明白。

    这里有一点python的用法BlenderAPI:

    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.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)
    

    平面.008网格的顶点被更改,Suzanne.001网格的顶点被更改。 不是吗?不是吗?我应该期望在一个轴上得到零吗? 从法向量得到旋转矩阵后,x,y,z上的旋转是什么?

    :1。搅拌机矩阵支持*运算符2。在搅拌机坐标系中,Z点向上。它看起来像一个右手系统,在X轴上旋转90度。

    谢谢

    4 回复  |  直到 14 年前
        1
  •  2
  •   cape1232    14 年前

    我觉得这很合理。以下是如何获得 rotation matrix from normal vector . 法向是向量。角度为0。你可能想要反转。

    你的网格是三角形的吗?我想是的。如果是这样,你可以这样做,不需要旋转矩阵。让脸上的点 A,B,C . 取人脸的任意两个顶点,比如 A B . 沿矢量定义X轴 AB . 0,0 . 0,|AB| . C 可以用三角法确定 AC 抗体 (使用点积得到)和长度 |AC| .

        2
  •  1
  •   cape1232    14 年前

    您正确地创建了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...
    
    }
    
        3
  •  0
  •   SigTerm    14 年前

    对于面v0-v1-v3-v2向量v3-v0、v3-v2和面法向已经形成旋转矩阵,将二维面转换为三维面。

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

    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行)。从中构建矩阵。然后反转矩阵。你将得到一个矩阵,它将把一个三维面旋转成二维面。

    我有三维网格,我想画每个面二维形状。

    我怀疑 UV unwrapping algorithms 更接近你想要实现的,而不是试图从三维面得到旋转矩阵。

        4
  •  0
  •   Dave O.    14 年前

    这是很容易做到的:(注意:“面”是指“三角形”)。

    1. 创建一个视图矩阵,该矩阵表示正在查看面的相机。
      1. 用双线性插值法确定面中心。
      2. 确定面部的法线。
      3. 将相机放置在与正常方向相反的位置。
      4. 让相机看看脸的中心。
      5. 将相机沿面上任意顶点的中间方向设置向量点。
      6. 将纵横比设置为1。
      7. 使用此数据计算视图矩阵。
    2. 创建正交投影矩阵。
      1. 设置视图截锥的宽度和高度,其大小应足以容纳整个面(例如面最长位置的长度)。
      2. 计算投影矩阵。
    3. 对于面的每个顶点v,将其乘以两个矩阵:v*视图*投影。

    其结果是将三维面投影到二维空间中,就好像在没有任何透视干扰的情况下正对着它们看一样。最终坐标将采用标准化屏幕坐标,其中(-1,-1)是左下角,(0,0)是中心,(1,1)是右上角。