我需要创建一个立方体的3D模型,在一个面的中心打出一个圆形孔,完全穿过立方体到达另一侧.我能够为面孔和孔生成顶点.
四个面(不受孔影响)可以建模为单个三角形条.孔的内部也可以建模为单个三角形条.
如何为有孔的面生成索引缓冲区?是否有标准算法来执行此操作?
我正在使用Direct3D,但欢迎来自其他地方的想法.
要生成所需的索引缓冲区,您可以这样做.在2D中将所讨论的面思考为具有顶点(±1,±1)的正方形,并将孔作为圆形在中间.
您沿着圆的边缘行走,将其划分为若干段.
对于每个顶点,将其投影到周围的正方形上(x/M,y/M)
,其中M
是max(abs(x),abs(y))
.M
是最大坐标的绝对值,因此这将缩放(x,y)
,使最大坐标为±1.
这一行你也分成了一些段.
成对连接的两个后续行的段作为面.
这是一个示例,将圆圈细分为64个段,每个射线划分为8个段.您可以选择符合要求的数字.
alt text http://pici.se/pictures/AVhcssRRz.gif
这是一些Python代码,用于演示:
from math import sin, cos, pi from itertools import izip def pairs(iterable): """Yields the previous and the current item on each iteration. """ last = None for item in iterable: if last is not None: yield last, item last = item def circle(radius, subdiv): """Yields coordinates of a circle. """ for angle in xrange(0,subdiv+1): x = radius * cos(angle * 2 * pi / subdiv) y = radius * sin(angle * 2 * pi / subdiv) yield x, y def line(x0,y0,x1,y1,subdiv): """Yields coordinates of a line. """ for t in xrange(subdiv+1): x = (subdiv - t)*x0 + t*x1 y = (subdiv - t)*y0 + t*y1 yield x/subdiv, y/subdiv def tesselate_square_with_hole((x,y),(w,h), radius=0.5, subdiv_circle=64, subdiv_ray=8): """Yields quads of a tesselated square with a circluar hole. """ for (x0,y0),(x1,y1) in pairs(circle(radius,subdiv_circle)): M0 = max(abs(x0),abs(y0)) xM0, yM0 = x0/M0, y0/M0 M1 = max(abs(x1),abs(y1)) xM1, yM1 = x1/M1, y1/M1 L1 = line(x0,y0,xM0,yM0,subdiv_ray) L2 = line(x1,y1,xM1,yM1,subdiv_ray) for ((xa,ya),(xb,yb)),((xc,yc),(xd,yd)) in pairs(izip(L1,L2)): yield ((x+xa*w/2,y+ya*h/2), (x+xb*w/2,y+yb*h/2), (x+xc*w/2,y+yc*h/2), (x+xd*w/2,y+yd*h/2)) import pygame def main(): """Simple pygame program that displays the tesselated figure. """ print('Calculating faces...') faces = list(tesselate_square_with_hole((150,150),(200,200),0.5,64,8)) print('done') pygame.init() pygame.display.set_mode((300,300)) surf = pygame.display.get_surface() running = True while running: need_repaint = False for event in pygame.event.get() or [pygame.event.wait()]: if event.type == pygame.QUIT: running = False elif event.type in (pygame.VIDEOEXPOSE, pygame.VIDEORESIZE): need_repaint = True if need_repaint: print('Repaint') surf.fill((255,255,255)) for pa,pb,pc,pd in faces: # draw a single quad with corners (pa,pb,pd,pc) pygame.draw.aalines(surf,(0,0,0),True,(pa,pb,pd,pc),1) pygame.display.flip() try: main() finally: pygame.quit()