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

圆锥与球体相交立体角计算异常:Blender实现问题排查

圆锥与球体相交立体角计算错误排查

问题背景

需要计算圆锥与球体相交区域的立体角,通过Blender辅助实现。圆锥通过顶点apex、半张角halfOpeningDeg、方位角azimuth、仰角elevation参数化定义,球体通过球心centreSphere和半径radiusSphere定义,示例代码如下:

from Cone import Cone
from Sphere import Sphere

apex: tuple[float, float, float] = (0.0, 0.0, 0.0)
height: float = 1.0e1
halfOpeningDeg: float = 15.0
azimuth: float = 45.0
elevation: float = 15.0
cone: Cone = Cone(
                apex = apex,
                height = height,
                halfOpeningDeg = halfOpeningDeg,
                azDeg = azimuth,
                elDeg = elevation,
            )

centreSphere: tuple[float, float, float] = (5.0, 3.0, 0.0)
radiusSphere: float = 3.5
sphere: Sphere = Sphere(
                        radius = radiusSphere,
                        centre = centreSphere,
                    )

基于上述对象编写了相交检测、相交体积计算、立体角计算函数,但运行后Blender视图显示圆锥完全穿过球体,理论上相交区域立体角应等于圆锥本身的立体角,实际结果却不符,需要排查计算错误。

核心错误分析

1. calculate_solid_angle函数的逻辑错误

错误的球内圆锥判断条件

当前代码中(distToIntersec + apexBaseDist) <= sphereRadius的判断完全不符合几何意义:

  • distToIntersec是锥顶到球心的直线距离,apexBaseDist是锥顶到锥底的斜距,两者相加与球半径比较无法判断圆锥是否完全在球内。

错误的立体角缩放逻辑

代码中distToIntersec -= sphereRadius和(sphereRadius - distToIntersec) / coneHeight的计算没有几何依据:

  • 立体角是从锥顶出发的射线与球体相交区域对应的角度,当圆锥完全穿过球体时,所有圆锥射线都穿过球体,立体角应等于圆锥本身的立体角,而非按高度比例缩放。

2. 球体半径计算错误

代码中sphereRadius: float = (0.5 * sphereBlend.dimensions.x) * sphere.sphere.scale.x存在属性调用错误,正确写法应为sphereBlend.scale.x(sphere是自定义Sphere对象,其sphere属性才是Blender对象):

sphereRadius: float = (0.5 * sphereBlend.dimensions.x) * sphereBlend.scale.x

3. check_intersection函数的圆锥半径计算错误

coneRadiusHeight的计算中,(0.5 * coneScales[0]) * coneScales[0]应为(0.5 * coneDims[0] * coneScales[0]),需结合原始尺寸与缩放比例计算实际半径:

coneRadiusHeight = (projLength / (coneDims[2] * coneScales[2])) * (0.5 * coneDims[0] * coneScales[0])

修正后的立体角计算函数

def calculate_solid_angle(cone: Cone, sphere: Sphere) -> float:
    """
    Compute the solid angle underlying the intersection of a cone and a sphere

    Parameters
    ----------
    cone : Cone
        Cone
    sphere : Sphere
        Sphere

    Return
    ------
    solidAngIntersec : float
        Solid angle of the intersection, in steradians
    """
    coneSolidAng: float = cone.get_solidAngle()
    coneBlend: bpy.types.Object = cone.cone
    sphereBlend: bpy.types.Object = sphere.sphere
    
    # 修正球体半径计算
    sphereRadius: float = (0.5 * sphereBlend.dimensions.x) * sphereBlend.scale.x
    coneHeight: float = coneBlend.dimensions.z * coneBlend.scale.z
    halfOpeningRad: float = np.radians(cone.halfOpeningDeg)
    
    # 获取圆锥轴线方向
    cone_axis = mathutils.Vector((0.0, 0.0, 1.0))
    cone_axis.rotate(coneBlend.rotation_quaternion)
    
    # 锥顶到球心的向量及距离
    apex_to_centre = sphereBlend.location - coneBlend.location
    d = apex_to_centre.length
    # 球心到圆锥轴线的垂直距离
    h = apex_to_centre.cross(cone_axis).length
    
    # 圆锥底半径
    cone_base_radius = coneHeight * np.tan(halfOpeningRad)
    
    # 情况1:圆锥与球不相交
    if h > sphereRadius + cone_base_radius:
        return 0.0
    # 情况2:整个圆锥在球内
    if d + coneHeight <= sphereRadius:
        return coneSolidAng
    # 情况3:圆锥完全穿过球体(所有圆锥射线均穿过球)
    base_centre = coneBlend.location + coneHeight * cone_axis
    base_dist_to_centre = (base_centre - sphereBlend.location).length
    if (d - sphereRadius) * (base_dist_to_centre - sphereRadius) < 0:
        return coneSolidAng
    # 情况4:圆锥部分伸入球内,通过Blender计算相交区域在单位球上的表面积(即立体角)
    bpy.ops.object.select_all(action='DESELECT')
    
    # 复制圆锥和球体
    cone_copy = coneBlend.copy()
    cone_copy.data = coneBlend.data.copy()
    sphere_copy = sphereBlend.copy()
    sphere_copy.data = sphereBlend.data.copy()
    bpy.context.collection.objects.link(cone_copy)
    bpy.context.collection.objects.link(sphere_copy)
    
    # 计算圆锥与球体的交集
    cone_copy.select_set(True)
    bpy.context.view_layer.objects.active = cone_copy
    bpy.ops.object.modifier_add(type='BOOLEAN')
    bpy.context.object.modifiers["Boolean"].operation = 'INTERSECT'
    bpy.context.object.modifiers["Boolean"].object = sphere_copy
    bpy.ops.object.modifier_apply(modifier="Boolean")
    
    # 创建以锥顶为中心的单位球
    bpy.ops.mesh.primitive_uv_sphere_add(radius=1, location=coneBlend.location)
    unit_sphere = bpy.context.active_object
    
    # 计算交集与单位球的重叠区域(对应立体角的表面)
    cone_copy.select_set(True)
    bpy.context.view_layer.objects.active = cone_copy
    bpy.ops.object.modifier_add(type='BOOLEAN')
    bpy.context.object.modifiers["Boolean.001"].operation = 'INTERSECT'
    bpy.context.object.modifiers["Boolean.001"].object = unit_sphere
    bpy.ops.object.modifier_apply(modifier="Boolean.001")
    
    # 计算重叠区域的表面积,即立体角(单位球表面积对应立体角)
    bm = bmesh.new()
    bm.from_mesh(cone_copy.data)
    solid_angle = bm.calc_surface_area()
    bm.free()
    
    # 清理临时对象
    bpy.ops.object.select_all(action='DESELECT')
    cone_copy.select_set(True)
    sphere_copy.select_set(True)
    unit_sphere.select_set(True)
    bpy.ops.object.delete()
    
    return solid_angle

内容的提问来源于stack exchange,提问作者aheuchamps

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 12:30:54