![](/img/trans.png)
[英]How to generate a numpy Manhattan distance array with torus geometry fast?
[英]How to generate a torus knot?
讓我通過提供一些我們將用來玩的樣板代碼來開始這個問題:
mcve_framework.py
import time
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import glm
from glm import unProject
from glm import vec2
from glm import vec3
from glm import vec4
# -------- Camera --------
class BaseCamera():
def __init__(
self,
eye=None, target=None, up=None,
fov=None, near=0.1, far=100000
):
self.eye = eye or glm.vec3(0, 0, 1)
self.target = target or glm.vec3(0, 0, 0)
self.up = up or glm.vec3(0, 1, 0)
self.original_up = glm.vec3(self.up)
self.fov = fov or glm.radians(45)
self.near = near
self.far = far
def update(self, aspect):
self.view = glm.lookAt(
self.eye, self.target, self.up
)
self.projection = glm.perspective(
self.fov, aspect, self.near, self.far
)
# def zoom(self, *args):
# delta = -args[1] * 0.1
# self.eye = self.target + (self.eye - self.target) * (delta + 1)
def zoom(self, *args):
x = args[2]
y = args[3]
v = glGetIntegerv(GL_VIEWPORT)
viewport = vec4(float(v[0]), float(v[1]), float(v[2]), float(v[3]))
height = viewport.w
pt_wnd = vec3(x, height - y, 1.0)
pt_world = unProject(pt_wnd, self.view, self.projection, viewport)
ray_cursor = glm.normalize(pt_world - self.eye)
delta = args[1] * 10
self.eye = self.eye + ray_cursor * delta
self.target = self.target + ray_cursor * delta
def load_projection(self):
width = glutGet(GLUT_WINDOW_WIDTH)
height = glutGet(GLUT_WINDOW_HEIGHT)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(glm.degrees(self.fov), width / height, self.near, self.far)
def load_modelview(self):
e = self.eye
t = self.target
u = self.up
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluLookAt(e.x, e.y, e.z, t.x, t.y, t.z, u.x, u.y, u.z)
class Camera(BaseCamera):
def rotate_target(self, delta):
right = glm.normalize(glm.cross(self.target - self.eye, self.up))
M = glm.mat4(1)
M = glm.translate(M, self.eye)
M = glm.rotate(M, delta.y, right)
M = glm.rotate(M, delta.x, self.up)
M = glm.translate(M, -self.eye)
self.target = glm.vec3(M * glm.vec4(self.target, 1.0))
def rotate_around_target(self, target, delta):
right = glm.normalize(glm.cross(self.target - self.eye, self.up))
amount = (right * delta.y + self.up * delta.x)
M = glm.mat4(1)
M = glm.rotate(M, amount.z, glm.vec3(0, 0, 1))
M = glm.rotate(M, amount.y, glm.vec3(0, 1, 0))
M = glm.rotate(M, amount.x, glm.vec3(1, 0, 0))
self.eye = glm.vec3(M * glm.vec4(self.eye, 1.0))
self.target = target
self.up = self.original_up
def rotate_around_origin(self, delta):
return self.rotate_around_target(glm.vec3(0), delta)
class GlutController():
FPS = 0
ORBIT = 1
def __init__(self, camera, velocity=100, velocity_wheel=100):
self.velocity = velocity
self.velocity_wheel = velocity_wheel
self.camera = camera
def glut_mouse(self, button, state, x, y):
self.mouse_last_pos = vec2(x, y)
self.mouse_down_pos = vec2(x, y)
if button == GLUT_LEFT_BUTTON:
self.mode = self.FPS
elif button == GLUT_RIGHT_BUTTON:
self.mode = self.ORBIT
def glut_motion(self, x, y):
pos = vec2(x, y)
move = self.mouse_last_pos - pos
self.mouse_last_pos = pos
if self.mode == self.FPS:
self.camera.rotate_target(move * 0.005)
elif self.mode == self.ORBIT:
self.camera.rotate_around_origin(move * 0.005)
def glut_mouse_wheel(self, *args):
self.camera.zoom(*args)
# -------- Miscelanea --------
def render_text(x, y, text):
glColor3f(1, 1, 1)
glRasterPos2f(x, y)
glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, text.encode("utf-8"))
def line(p0, p1, color=None):
c = color or glm.vec3(1, 1, 1)
glColor3f(c.x, c.y, c.z)
glVertex3f(p0.x, p0.y, p0.z)
glVertex3f(p1.x, p1.y, p1.z)
def grid(segment_count=10, spacing=1, yup=True):
size = segment_count * spacing
right = glm.vec3(1, 0, 0)
forward = glm.vec3(0, 0, 1) if yup else glm.vec3(0, 1, 0)
x_axis = right * size
z_axis = forward * size
i = -segment_count
glBegin(GL_LINES)
while i <= segment_count:
p0 = -x_axis + forward * i * spacing
p1 = x_axis + forward * i * spacing
line(p0, p1)
p0 = -z_axis + right * i * spacing
p1 = z_axis + right * i * spacing
line(p0, p1)
i += 1
glEnd()
def axis(size=1.0, yup=True):
right = glm.vec3(1, 0, 0)
forward = glm.vec3(0, 0, 1) if yup else glm.vec3(0, 1, 0)
x_axis = right * size
z_axis = forward * size
y_axis = glm.cross(forward, right) * size
glBegin(GL_LINES)
line(x_axis, glm.vec3(0, 0, 0), glm.vec3(1, 0, 0))
line(y_axis, glm.vec3(0, 0, 0), glm.vec3(0, 1, 0))
line(z_axis, glm.vec3(0, 0, 0), glm.vec3(0, 0, 1))
glEnd()
# -------- Mcve --------
class BaseWindow:
def __init__(self, w, h):
self.width = w
self.height = h
glutInit()
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH)
glutInitWindowSize(w, h)
glutCreateWindow('OpenGL Window')
self._startup()
glutReshapeFunc(self.reshape)
glutDisplayFunc(self._display)
glutMouseFunc(self.controller.glut_mouse)
glutMotionFunc(self.controller.glut_motion)
glutMouseWheelFunc(self.controller.glut_mouse_wheel)
glutKeyboardFunc(self.keyboard_func)
glutIdleFunc(self.idle_func)
def keyboard_func(self, *args):
try:
key = args[0].decode("utf8")
if key == "\x1b":
glutLeaveMainLoop()
if key in ['1']:
if key == '1':
self.index_camera = "BPL"
self.camera = self.cameras[self.index_camera]
self.controller.camera = self.camera
except Exception as e:
import traceback
traceback.print_exc()
def display(self):
pass
def startup(self):
pass
def _startup(self):
glEnable(GL_DEPTH_TEST)
params = {
"eye": glm.vec3(0, 150, 150),
"target": glm.vec3(0, 0, 0),
"up": glm.vec3(0, 1, 0)
}
self.start_time = time.time()
self.cameras = {
"BPL": Camera(**params)
}
self.index_camera = "BPL"
self.yup = True
self.camera = self.cameras[self.index_camera]
self.model = glm.mat4(1)
self.controller = GlutController(self.camera)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
self.startup()
def run(self):
glutMainLoop()
def idle_func(self):
glutPostRedisplay()
def reshape(self, w, h):
glViewport(0, 0, w, h)
self.width = w
self.height = h
def render_points(self, vertices):
glColor3f(0.0, 0.0, 0.0)
glBegin(GL_POINTS)
for v in vertices:
glVertex3f(v.x, v.y, v.z)
glEnd()
def render_triangles(self, vertices):
glBegin(GL_TRIANGLES)
for i in range(0, len(vertices), 3):
v0 = vertices[i]
v1 = vertices[i + 1]
v2 = vertices[i + 2]
glVertex3f(v0.x, v0.y, v0.z)
glVertex3f(v1.x, v1.y, v1.z)
glVertex3f(v2.x, v2.y, v2.z)
glEnd()
def render_quads(self, vertices):
glBegin(GL_QUADS)
for i in range(0, len(vertices), 4):
v0 = vertices[i]
v1 = vertices[i + 1]
v2 = vertices[i + 2]
v3 = vertices[i + 3]
glVertex3f(v0.x, v0.y, v0.z)
glVertex3f(v1.x, v1.y, v1.z)
glVertex3f(v2.x, v2.y, v2.z)
glVertex3f(v3.x, v3.y, v3.z)
glEnd()
def render_indexed_triangles(self, indices, vertices):
glBegin(GL_TRIANGLES)
for i in indices:
for j in range(3):
v = vertices[i[j]]
glVertex3f(v.x, v.y, v.z)
glEnd()
def render_indexed_quads(self, indices, vertices):
glBegin(GL_QUADS)
for f1, f2 in zip(indices[::2], indices[1::2]):
i = [f1[0], f1[1], f1[2], f2[2]]
for j in range(4):
v = vertices[i[j]]
glVertex3f(v.x, v.y, v.z)
glEnd()
def _display(self):
self.camera.update(self.width / self.height)
glClearColor(0.2, 0.3, 0.3, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
self.camera.load_projection()
self.camera.load_modelview()
self.display()
glLineWidth(5)
axis(size=70, yup=True)
glLineWidth(1)
grid(segment_count=7, spacing=10, yup=True)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(-1, 1, -1, 1, -1, 1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
info = "\n".join([
"{}: Camera - {}".format(i, k) for i, k in enumerate(self.cameras.keys())
])
render_text(-1.0, 1.0 - 0.1, info)
render_text(-1.0, -1.0, "{} camera is active".format(self.index_camera))
glutSwapBuffers()
如果你想使用上面的代碼,你只需要安裝 pyopengl 和 pygml。 之后,你可以創建你自己的BaseWindow
子類,覆蓋startup
和render
,你應該有一個非常基本的 glut 窗口,具有簡單的功能,如相機旋轉/縮放以及一些渲染點/三角形/四邊形和 indexed_triangles/indexed_quads 的方法.
題
現在真正的問題是,考慮這個小片段:
mcve_torusknot.py
from math import cos
from math import pi
from math import sin
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from glm import cross
from glm import normalize
from glm import vec3
from mcve_framework import BaseWindow
def sample(theta, p, q, out):
r = cos(q * theta) + 2.0
out.x = r * cos(p * theta)
out.y = r * sin(q * theta)
out.z = -sin(q * theta)
def gen_torusknot(tess_u, tess_v, p, q):
vertices = []
pp = vec3()
centerpoint = vec3()
nextpoint = vec3()
T = vec3()
B = vec3()
N = vec3()
r2 = 5.0
for u in range(tess_u):
theta = (u / tess_u) * 2 * pi
sample(theta, p, q, centerpoint)
theta = (u + 1) * 2 * pi / tess_u
sample(theta, p, q, nextpoint)
T = (nextpoint - centerpoint)
N = (nextpoint + centerpoint)
B = normalize(cross(T, N))
N = normalize(cross(B, T))
for v in range(tess_v):
theta = (v / tess_v) * 2 * pi
pointx = sin(theta) * r2
pointy = cos(theta) * r2
pp = N * pointx + B * pointy + centerpoint
vertices.append(pp * 10)
return vertices
class McveTorusKnot(BaseWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def startup(self):
self.torusknot = gen_torusknot(60, 60, 1.000000001, 0.000000001)
def display(self):
glPointSize(3)
glPushMatrix()
self.render_points(self.torusknot)
glPopMatrix()
if __name__ == '__main__':
window = McveTorusKnot(800, 600)
window.run()
這里的最終目標是弄清楚如何生成和渲染圓環結。 但在這樣一個雄心勃勃的目標之前,我想弄清楚為什么在使用p=1
和q=0
參數時,我沒有得到像這里顯示的簡單環面https://www.geeks3d.com/20140516/pq -torus-knot/ ,而不是我得到這樣的東西:
所以是的,這基本上是我的問題,首先,我想知道我上面的代碼有什么問題,所以我沒有從通用公式中得到一個簡單的環面,然后......我想知道什么是創建網格連接的方法(又名索引,無論三角形/四邊形/三角形條帶)?
注意:為了簡單起見,此時法線或紋理坐標無關緊要,只需知道如何正確生成網格的位置頂點/索引就足夠了:)
...首先,我想知道我上面的代碼有什么問題,所以我沒有得到一個簡單的環面......
函數 sample 只需要在半徑為r
的圓上生成一個點:
def sample(theta, p, q, out):
r = 5.0
out.x = r * cos(theta)
out.y = r * sin(theta)
out.z = 0
切線 ( T
) 是從圓上的當前點到下一點(近似值)的向量,但副切線 ( B
) 是從圓環的中心到圓上的當前點的向量:
def gen_torusknot(tess_u, tess_v, p, q):
vertices = []
pp = vec3()
centerpoint = vec3()
nextpoint = vec3()
T = vec3()
B = vec3()
N = vec3()
r2 = 2.0
for u in range(tess_u):
theta = (u / tess_u) * 2 * pi
sample(theta, p, q, centerpoint)
theta = (u + 1) * 2 * pi / tess_u
sample(theta, p, q, nextpoint)
T = (nextpoint - centerpoint)
B = normalize(centerpoint)
N = normalize(cross(T, B))
T = normalize(cross(B, N))
for v in range(tess_v):
theta = (v / tess_v) * 2 * pi
pointx = sin(theta) * r2
pointy = cos(theta) * r2
pp = N * pointx + B * pointy + centerpoint
vertices.append(pp * 10)
return vertices
環面結的第一步是在幾何表示上采樣點,並在環面中心的圓上采樣點。
圓上的點的計算方法與游覽上的點相同,只是管的半徑 ( r2
) 為 0。
def sample(phi, p, q, r1, r2, out):
out.x = (r1 + r2 * cos(p * phi)) * cos(q * phi)
out.y = (r1 + r2 * cos(p * phi)) * sin(q * phi)
out.z = r2 * -sin(p * phi)
def gen_torusknot(tess_u, tess_v, p, q):
vertices = []
pt_tk = vec3()
pt_c = vec3()
r1, r2 = 5, 2
p, q = 7, 3
for u in range(tess_u):
phi = (u / tess_u) * 2 * pi
sample(phi, p, q, r1, r2, pt_tk)
sample(phi, p, q, r1, 0, pt_c)
vertices.append(pt_tk * 10)
vertices.append(pt_c * 10)
return vertices
最后,切線 ( T
) 和副切線 ( B
) 的計算方法類似於圓環體的切線 ( T
) 和雙切線 ( B
)。
切線 (T) 是從圓環結上的當前點到圓環結上的下一個點(近似值)的向量。 副切線 (B) 是從圓上的樣本點到圓環結上的當前樣本點的向量:
def sample(phi, p, q, r1, r2, out):
out.x = (r1 + r2 * cos(p * phi)) * cos(q * phi)
out.y = (r1 + r2 * cos(p * phi)) * sin(q * phi)
out.z = r2 * -sin(p * phi)
def gen_torusknot(tess_u, tess_v, p, q):
vertices = []
pt_tk = vec3()
pt_tk_next = vec3()
pt_c = vec3()
r1, r2, r3 = 5, 2, 0.5
p, q = 7, 3
for u in range(tess_u):
phi = (u / tess_u) * 2 * pi
sample(phi, p, q, r1, r2, pt_tk)
phi = (u + 1) * 2 * pi / tess_u
sample(phi, p, q, r1, r2, pt_tk_next)
sample(phi, p, q, r1, 0, pt_c)
T = (pt_tk_next - pt_tk)
B = normalize(pt_tk - pt_c)
N = normalize(cross(T, B))
T = normalize(cross(B, N))
for v in range(tess_v):
theta = (v / tess_v) * 2 * pi
px = sin(theta) * r3
py = cos(theta) * r3
pp = N * px + B * py + pt_tk
vertices.append(pp * 10)
return vertices
設置p = 3
和q = 2
將產生一個三葉結,而p = 0
和q = 1
將產生一個環面。
我激活了多重采樣,以生成上面的圖像。
glutInit()
glutSetOption( GLUT_MULTISAMPLE, 8 )
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_MULTISAMPLE)
此外,我使用了一個函數將 [0, 1] 范圍內的色調值轉換為 RGB 顏色
def HUEtoRGB(self, H):
R = abs(H * 6.0 - 3.0) - 1.0
G = 2.0 - abs(H * 6.0 - 2.0)
B = 2.0 - abs(H * 6.0 - 4.0)
return (R, G, B)
以及以下繪制點的函數:
def render_points(self, vertices):
glPointSize(5)
glBegin(GL_POINTS)
for i in range(len(vertices)):
v = vertices[i]
H = i / len(vertices)
glColor4f(*self.HUEtoRGB(H), 0.0)
glVertex3f(v.x, v.y, v.z)
glEnd()
可以通過基元GL_TRIANLGL_STRIP
生成網格而不是點。 tess_u
* 條帶沿着結曲線渲染,每個條帶由tess_v
部分( tess_v
* 2 個三角形)組成:
def render_strips(self, vertices, tess_v):
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
no_strips = len(vertices) // tess_v
for i_strip in range(no_strips):
glBegin(GL_TRIANGLE_STRIP)
for i_v in range(tess_v+1):
for i in [i_strip, (i_strip+1) % no_strips]:
v, H = vertices[i*tess_v + i_v % tess_v], i / no_strips
glColor4f(*self.HUEtoRGB(H), 0.0)
glVertex3f(v.x, v.y, v.z)
glEnd()
class McveTorusKnot(BaseWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def startup(self):
self.tess_v = 60
self.torusknot = gen_torusknot(300, self.tess_v, 1.000000001, 0.000000001)
def display(self):
glPushMatrix()
#self.render_points(self.torusknot)
self.render_strips(self.torusknot, self.tess_v)
glPopMatrix()
以下三葉結是通過使用以下參數呈現的:
r1, r2, r3 = 5, 2, 1
p, q = 3, 2
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.