绘制3d面作为2d

时间:2010-07-17 01:17:19

标签: math opengl 3d vector matrix

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

我想到的是: 对于每张脸 1.进入脸部正常 2.从法向量得到一个旋转矩阵 3.将每个顶点乘以旋转矩阵,以获得'2d like'平面中的顶点 4.从变换顶点获得2个坐标

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

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

更新

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

3d to 2d

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

我想旋转一个面的顶点,这样 其中一个维度变得扁平化。

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

我想我在太空中有点迷失了:)

这是我使用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);
}

更新

很抱歉,如果我变得烦人,但我似乎没有得到它。

这是使用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度。

由于

4 个答案:

答案 0 :(得分:2)

这对我来说看起来很合理。以下是获取rotation matrix from normal vector的方法。法线是矢量。角度为0.您可能需要反向旋转。

你的网格是否是三角形的?我假设是。如果是这样,你可以这样做,没有旋转矩阵。让脸部的点数为A,B,C。取脸的任意两个顶点,例如AB。沿向量AB定义x轴。 A位于0,0B位于0,|AB|。可以使用CAC(使用点积得到的)和长度AB之间的角度从三角学确定|AC|

答案 1 :(得分:1)

您正确创建了m矩阵。这是与您的法向量对应的旋转。您可以使用此矩阵的反转来“解旋”您的点。 face2d的法线将是x,即沿x轴指向。因此,相应地提取您的2D坐标。 (这假设你的四边形大致是平面的。)

我不知道你正在使用的库(Processing),所以我只是假设有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...

}

答案 2 :(得分:0)

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

Matrix表示坐标系。每行(或列,取决于符号)对应于新坐标系内的轴坐标系。 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脸。

  

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

我怀疑UV unwrapping algorithms比尝试从3d脸上获取旋转矩阵更接近你想要达到的目的。

答案 3 :(得分:0)

这很容易实现:(注:“面部”是指“三角形”)

  1. 创建一个代表相机的视图矩阵。
    1. 使用双线性插值确定面部的中心。
    2. 确定脸部的法线。
    3. 将相机放置在正常方向相反的位置。
    4. 让相机看着脸的中心。
    5. 将摄像机设置为面部任意顶点中间方向的矢量点。
    6. 将宽高比设置为1.
    7. 使用此数据计算视图矩阵。
  2. 创建正交投影矩阵。
    1. 将视锥体的宽度和高度设置得足够大,以包含整个脸部(例如脸部最长部位的长度)。
    2. 计算投影矩阵。
  3. 对于面部的每个顶点v,将其乘以两个矩阵:v * view * projection。
  4. 结果是将您的3D脸部投影到2d空间,就好像您正在看它们完全正交而没有任何透视干扰。最终坐标将在标准化的屏幕坐标中,其中(-1,-1)是左下角,(0,0)是中心,(1,1)是右上角。