You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 19:40:17