所以我正在玩OpenGL并试图弄清楚如何绘制一些有趣的形状。
现在,我正在做一个管子。我可以直接用直管绘制:
void tube(GLfloat radius, GLfloat segment_length) {
glPolygonMode(GL_BACK, GL_NONE);
glPolygonMode(GL_FRONT, GL_FILL);
glPushMatrix(); {
GLfloat z1 = 0.0;
GLfloat z2 = segment_length;
GLfloat y_offset = 0.0;
GLfloat y_change = 0.00;
int i = 0;
int j = 0;
for (j = 0; j < 20; j++) {
glPushMatrix(); {
glBegin(GL_TRIANGLE_STRIP); {
for (i = 360; i >= 0; i--) {
GLfloat theta = i * pi/180;
GLfloat x = radius * cos(theta);
GLfloat y = radius * sin(theta) + y_offset;
glVertex3f(x, y, z1);
glVertex3f(x, y, z2);
}
} glEnd();
} glPopMatrix();
// attach the front of the next segment to the back of the previous
z1 = z2;
z2 += segment_length;
// make some other adjustments
y_offset += y_change;
}
} glPopMatrix();
}
但是,我还没弄清楚如何使管子遵循任何预定义的路径,如螺旋线,甚至是简单的线。如果将y_change更改为0.01,则会在y方向上将每个管段偏移另外0.01。那太棒了,但我怎样才能让每个细分都指向那个方向呢?换句话说,现在,绘制每个线段使它们都面向相同的方向,方向不是管的方向(因为y_change = 0.01,方向略微“向上”)。
我不知道该怎么办。我通过获取前一段和当前段之间的向量来玩向量,但我不确定过去该怎么做。
答案 0 :(得分:19)
这个想法被称为路径控制挤压,即你有一个基本的n维形状,并沿着n + 1维曲线挤出它。我可以看到你的脸:“嗯,他说的是什么?”
所以这是一个粗略的轮廓。首先,您需要一个将值(通常称为t)映射到空间中连续,平滑曲线的函数。例如螺丝:
path(t): R → R³, t ↦ ( a·sin(k·t), b·cos(k·t), c·t )
想法是找到一个局部坐标基础来定义与该路径相关的顶点位置 - 有意义的是,其中一个坐标与路径平行对齐,因此您希望找到它的切线。这是通过找到它的梯度来完成的:
tangent(t): R → R³, t ↦ ( k·a·cos(k·t), -k·b·sin(k·t), c ) = d/dt path(t)
因此,这是指向曲线的局部基矢量,局部坐标系的原点位于曲线的t
点。
但我们需要另外两个向量,以形成一个完整的3d基础。将第二个基点垂直于曲率通常是一个不错的选择,通过找到切线的卷曲可以得到:
normal(t): R → R³, t ↦ ( -k²·a·sin(k·t), -k²·b·cos(k·t), 0 ) = d/dt tangent(t) = d²/dt² path(t)
这称为正常。
可以获得第三个基本向量,取正态和正切的交叉乘积,得到二次正规。我会让你把这个想象成一个练习。
现在要沿着曲线挤出一个形状,你只需要在选择的范围内迭代t
,就可以将路径分割成段,从而为你提供本地原点。挤压形状的点相对于此原点路径(t)。假设你的形状由x-y平面中的点P_n组成:
for t in [k..l]:
for p in P_n:
yield_vertex( path(t).x + binormal(t).x * p.x,
path(t).y + normal(t).y * p.y,
path(t).z )
我会把它留给你,弄清楚如何使它适应OpenGL,毕竟你应该通过思考来学习一些东西。如果明天之前你无法解决问题,我很乐意为你提供解决方案,但通常你自己解决这些问题会更有趣。
答案 1 :(得分:1)
答案 2 :(得分:1)
答案 3 :(得分:0)
物理 解释对您的挤压问题很好(从代数矢量的角度来看):
https://physics.stackexchange.com/questions/368634/direction-of-velocity-vector-in-3d-space
https://web.mit.edu/hyperbook/Patrikalakis-Maekawa-Cho/node24.html
其实可以看出:
1)path(t) 是 r(alpha(t)) 其中 alpha(t)=kt with k=2PI/w r(t) - 具有 3d(欧几里德空间)坐标的位置向量:x(t),y(t),z(t) alpha - 中心角度 w - 标量角速度 = 2PI/k = (r x v)/||r^2||这里 x 是向量积
tangent(t) 是 v(t) 切向速度 = dr / dt(梯度)
normal(t) 是 a(t) 法向加速度 = dv/dt
binormal(b) 是向量乘积 b(t) = a(t) x v (t) 的结果
副法线(b)、法线(a) 和切线(v) 向量定义了沿空间曲线(r) 的坐标正交局部系统——参见图2.6来自 node24.html 文章。
所以,如果已经记住了 b(= axv)、n(= a = dv/dt) 和 v(= dr/dt) 的计算局部值,我可以使用从 BNV 系统到 XYZ 的一种转换(从一个坐标系转换为其他;两者都是正交系统)以获得 x,y,z 坐标; 综上所述,XYZ 中的任意点(x,y,z) 都将被写成其在 BNV 系统中的投影点的线性组合。
在物理学中参见运动方程:https://en.wikipedia.org/wiki/Equations_of_motion
您可以在此数学文档(幻灯片 3 和 5)中看到这一点: https://www.robots.ox.ac.uk/~sjrob/Teaching/Vectors/
备注:
-不要忘记一些 n 可导函数可以分解为函数的线性组合,如 sin 和 cos(参见傅立叶:https://en.wikipedia.org/wiki/Fourier_series)
-In plus 不要忘记这个组合函数(需要区分时有用的规则):http://mathsfirst.massey.ac.nz/Calculus/ChainRule/decompose.html#vma
结论:
-如果找到一个 3D - 参数函数(=算法)用于一个 3d 体(= 3d 模型 = 3d 对象)然后可以在 3d 空间中绘制它;
-参数化不是绘制 3D 物体(3D 对象)的单一方法... 例如可以使用二维函数的旋转来获得它!
或者,查看另一种方法而不是参数化来获得 3D 物体:管(沿路径挤压)图像作为 http://www.songho.ca/opengl/gl_cylinder.html 中两个棱镜的交集结果 并将 P'(投影点 P 到两个棱镜的相交平面)计算为从 P(从初始面/轮廓的点)到 https://www.cs.princeton.edu/courses/archive/fall00/cs426/lectures/raycast/sld017.htm
(这种挤压方法的输入是一组profie=初始面和路径的点,可以改进以获得平滑的路径或轮廓:https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline,
https://jiharu.github.io/intm3d/week03.html,
https://www.ee.ucl.ac.uk/~mflanaga/java/Interpolation.html
或者,参见另一个更平滑的 Kockanek-Bartel(KB),名为 Tension-Continuity-Bias-spline(别名 TCB): https://download.java.net/media/java3d/javadoc/1.5.0/com/sun/j3d/utils/behaviors/interpolators/package-summary.html)
-但不要忘记,使用这些方法可以获得的只是一团近似 3D 曲线的点(不是整个 3d 模型)。
如果知道面的点云、边云和法线,则一个模型是完全定义的(参见基于 https://doc.cgal.org/latest/Poisson_surface_reconstruction_3/index.html 点云的泊松重建)。
请注意algebra vector3D(或3D-vectorial algebra)的力量!!!