Python光线追踪项目自定义复杂形状实现方法咨询
Python光线追踪中自定义复杂形状的实现方案
1. 自定义形状的通用方法
自定义形状的核心是实现光线-形状相交检测逻辑——只要能计算出光线与形状的交点(及表面法向量),就能无缝集成到现有光线追踪框架中。常用两种思路:
- 符号距离函数(SDF)+ 光线步进:用数学函数描述空间任意点到形状表面的最短距离(外部为正,内部为负),通过迭代步进光线找到交点。适合参数化的复杂形状,无需依赖外部模型。
- 三角网格导入:从Blender等建模软件导出.obj/.ply格式的网格文件,读取后对每个三角形做光线相交检测。适合人体这类精细、非参数化的模型。
2. 人体形状的实现与集成
方法一:用SDF构建简化人体
通过组合基础形状的SDF(球体、胶囊体)模拟人体结构,再用光线步进实现相交检测:
首先定义基础SDF工具函数:
import numpy as np def sdf_sphere(p, radius): return np.linalg.norm(p) - radius def sdf_capsule(p, a, b, radius): pa = p - a ba = b - a h = np.clip(np.dot(pa, ba) / np.dot(ba, ba), 0.0, 1.0) return np.linalg.norm(pa - ba * h) - radius def sdf_union(a, b): return min(a, b)
然后实现人体SDF类,继承你的光线追踪框架中的Shape基类:
class SDFHuman(Shape): def __init__(self, position=np.array([0, 1, 0]), scale=1.0): self.pos = position self.scale = scale def _sdf(self, p): # 转换到模型局部坐标 p_local = (p - self.pos) / self.scale # 组合人体各部位的SDF head = sdf_sphere(p_local - np.array([0, 1.5, 0]), 0.3) torso = sdf_capsule(p_local - np.array([0, 0.8, 0]), np.array([0, -0.5, 0]), np.array([0, 1.2, 0]), 0.3) left_arm = sdf_capsule(p_local - np.array([-0.6, 0.8, 0]), np.array([-0.3, 0.8, 0]), np.array([-1.0, 0.2, 0]), 0.15) right_arm = sdf_capsule(p_local - np.array([0.6, 0.8, 0]), np.array([0.3, 0.8, 0]), np.array([1.0, 0.2, 0]), 0.15) left_leg = sdf_capsule(p_local - np.array([-0.2, -0.5, 0]), np.array([-0.2, -0.5, 0]), np.array([-0.3, -1.2, 0]), 0.2) right_leg = sdf_capsule(p_local - np.array([0.2, -0.5, 0]), np.array([0.2, -0.5, 0]), np.array([0.3, -1.2, 0]), 0.2) body = sdf_union(head, torso) body = sdf_union(body, left_arm) body = sdf_union(body, right_arm) body = sdf_union(body, left_leg) body = sdf_union(body, right_leg) return body * self.scale def intersect(self, ray_origin, ray_dir): # 光线步进算法求交点 t = 0.0 max_t = 100.0 epsilon = 1e-6 for _ in range(200): p = ray_origin + t * ray_dir d = self._sdf(p) if d < epsilon: # 用有限差分计算法向量 delta = 1e-4 nx = self._sdf(p + np.array([delta, 0, 0])) - self._sdf(p - np.array([delta, 0, 0])) ny = self._sdf(p + np.array([0, delta, 0])) - self._sdf(p - np.array([0, delta, 0])) nz = self._sdf(p + np.array([0, 0, delta])) - self._sdf(p - np.array([0, 0, delta])) normal = np.array([nx, ny, nz]) normal = normal / np.linalg.norm(normal) return (t, normal) if t > max_t: return None t += d return None
方法二:导入人体三角网格
读取外部建模软件导出的.obj文件,实现网格的光线相交检测:
class MeshHuman(Shape): def __init__(self, obj_path, position=np.array([0, 0, 0]), scale=1.0): self.pos = position self.scale = scale self.triangles = self._load_obj(obj_path) def _load_obj(self, path): vertices = [] triangles = [] with open(path, 'r') as f: for line in f: if line.startswith('v '): vertices.append(np.array(list(map(float, line.strip().split()[1:])))) elif line.startswith('f '): idx = [int(p.split('/')[0])-1 for p in line.strip().split()[1:]] if len(idx) == 3: triangles.append([vertices[idx[0]], vertices[idx[1]], vertices[idx[2]]]) # 应用缩放和位移 return [[v * self.scale + self.pos for v in tri] for tri in triangles] def _ray_triangle_intersect(self, ray_origin, ray_dir, v0, v1, v2): # Möller-Trumbore 光线-三角相交算法 edge1 = v1 - v0 edge2 = v2 - v0 h = np.cross(ray_dir, edge2) a = np.dot(edge1, h) if abs(a) < 1e-6: return None # 光线与三角形平行 f = 1.0 / a s = ray_origin - v0 u = f * np.dot(s, h) if u < 0 or u > 1: return None q = np.cross(s, edge1) v = f * np.dot(ray_dir, q) if v < 0 or u + v > 1: return None t = f * np.dot(edge2, q) if t > 1e-6: normal = np.cross(edge1, edge2) normal = normal / np.linalg.norm(normal) return (t, normal) return None def intersect(self, ray_origin, ray_dir): min_t = float('inf') closest_normal = None for tri in self.triangles: hit = self._ray_triangle_intersect(ray_origin, ray_dir, tri[0], tri[1], tri[2]) if hit and hit[0] < min_t: min_t, closest_normal = hit return (min_t, closest_normal) if min_t != float('inf') else None
集成到现有框架
假设你的框架有渲染循环,只需将自定义形状加入场景列表即可:
def render_scene(shapes, width=640, height=480): fov = 90.0 aspect_ratio = width / height img = np.zeros((height, width, 3)) for y in range(height): for x in range(width): # 像素转光线方向 u = (2*(x+0.5)/width -1) * np.tan(np.radians(fov/2)) * aspect_ratio v = (1-2*(y+0.5)/height) * np.tan(np.radians(fov/2)) ray_dir = np.array([u, v, -1.0]) ray_dir /= np.linalg.norm(ray_dir) ray_origin = np.array([0, 0, 5]) min_t = float('inf') hit_normal = None for shape in shapes: hit = shape.intersect(ray_origin, ray_dir) if hit and hit[0] < min_t: min_t, hit_normal = hit if hit_normal is not None: # 简单漫反射着色 light_dir = np.array([1,1,-1]) / np.linalg.norm(np.array([1,1,-1])) diff = max(np.dot(hit_normal, light_dir), 0) img[y,x] = np.array([diff,diff,diff])*255 else: img[y,x] = np.array([0,0,0]) return img.astype(np.uint8) # 使用示例 if __name__ == "__main__": import matplotlib.pyplot as plt # 用SDF人体 scene = [SDFHuman()] # 或用导入的网格(替换为你的.obj路径) # scene = [MeshHuman("human_model.obj", position=np.array([0,1,0]), scale=0.5)] img = render_scene(scene) plt.imshow(img) plt.show()
注意事项
- SDF方法的精度和速度由迭代次数控制,可根据需求调整循环次数;
- 三角网格方法的渲染速度与面数成正比,复杂模型建议加入BVH(层次包围盒)加速相交检测;
- 两种方法都只需实现
Shape基类的intersect方法,即可兼容现有框架的着色、光照逻辑。
内容的提问来源于stack exchange,提问作者FrostDream
相关产品推荐
相关产品推荐

