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

Python光线追踪代码中向量减法的TypeError问题求助

Python光线追踪项目中的TypeError问题(向量减法函数报错)

问题概述

开发Python光线追踪项目时,vec3_sub向量减法函数触发TypeError,错误提示列表与整数无法执行减法操作,报错位置指向:

color = raytrace(E, vec3_norm(vec3_sub(Q, E)), scene)

错误细节

错误信息

TypeError: unsupported operand type(s) for -: 'list' and 'int'

相关代码片段

向量减法函数:

def vec3_sub(v1, v2):
    return (v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2])

E和Q的定义:

E = (0, 0.35, -1)
Q = (x, y, [0] * (w * h))

原因分析

  • E是包含数值的元组,每个分量都是单个整数/浮点数;
  • 非numpy版本中,x、y是长度为w*h的坐标列表,Q的第三个分量也是列表,导致vec3_sub(Q, E)执行时,v1[0]为列表类型,v2[0]为整数类型,两者无法直接相减;
  • numpy版本中Q的第三个分量是数值0,x、y是numpy数组,numpy支持数组与数值的广播运算,因此不会触发错误。

解决方案

针对非numpy版本,有两种修复思路:

思路1:逐像素处理(推荐,逻辑更简单)

将批量处理改为逐像素循环,每个向量均为单个数值的元组,适配原有vec3函数的逻辑:

  1. 修改像素坐标生成与光线追踪流程:
(w, h) = (400, 300)
L = (5, 5, -10)
E = (0, 0.35, -1)
r = w / h
S = (-1, 1 / r + .25, 1, -1 / r + .25)

pixels = []
# 逐像素生成坐标并追踪光线
for j in range(h):
    y = S[1] + (S[3] - S[1]) * j / (h - 1)
    for i in range(w):
        x = S[0] + (S[2] - S[0]) * i / (w - 1)
        Q = (x, y, 0.0)
        D = vec3_norm(vec3_sub(Q, E))
        color = raytrace(E, D, scene)
        pixels.append(color)

# 生成图像
r_array = np.array([255 * max(min(c[0], 1), 0) for c in pixels], dtype=np.uint8).reshape((h, w))
g_array = np.array([255 * max(min(c[1], 1), 0) for c in pixels], dtype=np.uint8).reshape((h, w))
b_array = np.array([255 * max(min(c[2], 1), 0) for c in pixels], dtype=np.uint8).reshape((h, w))

rgb_image = Image.merge("RGB", [Image.fromarray(r_array, "L"), Image.fromarray(g_array, "L"), Image.fromarray(b_array, "L")])
rgb_image.show()
  1. 原有vec3函数无需修改,因为现在处理的都是单个数值的向量运算。

思路2:修改vec3函数支持列表与数值运算

如果要保留批量处理逻辑,需要修改所有vec3相关函数,支持列表与数值的逐元素运算:

例如修改vec3_sub:

def vec3_sub(v1, v2):
    def sub(a, b):
        if isinstance(a, list) and isinstance(b, (int, float)):
            return [x - b for x in a]
        elif isinstance(b, list) and isinstance(a, (int, float)):
            return [a - x for x in b]
        elif isinstance(a, list) and isinstance(b, list):
            return [x - y for x, y in zip(a, b)]
        else:
            return a - b
    return (sub(v1[0], v2[0]), sub(v1[1], v2[1]), sub(v1[2], v2[2]))

同理修改vec3_add、vec3_mul、vec3_dot等函数,确保所有向量运算都能处理列表与数值的组合。

完整修正后的非numpy版本代码

from PIL import Image
import math
import numpy as np

def vec3_mul(v, other):
    return (v[0] * other, v[1] * other, v[2] * other)

def vec3_add(v1, v2):
    return (v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2])

def vec3_sub(v1, v2):
    return (v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2])

def vec3_dot(v1, v2):
    return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]

def vec3_abs(v):
    return vec3_dot(v, v)

def vec3_norm(v):
    mag = math.sqrt(vec3_abs(v))
    return vec3_mul(v, 1.0 / (1 if mag == 0 else mag))

FARAWAY = 1.0e39

def raytrace(O, D, scene, bounce=0):
    distances = [s_intersect(O, D, s) for s in scene]
    nearest = min(distances)
    color = (0, 0, 0)
    for (s, d) in zip(scene, distances):
        hit = (nearest != FARAWAY) and (d == nearest)
        if hit:
            color = vec3_add(color, s_light(O, D, d, scene, bounce, s))
    return color

def s_intersect(O, D, s):
    b = 2 * vec3_dot(D, vec3_sub(O, s[0]))
    c = vec3_abs(s[0]) + vec3_abs(O) - 2 * vec3_dot(s[0], O) - (s[1] * s[1])
    disc = (b ** 2) - (4 * c)
    sq = math.sqrt(max(0, disc))
    h0 = (-b - sq) / 2
    h1 = (-b + sq) / 2
    h = h0 if (h0 > 0) and (h0 < h1) else h1
    pred = (disc > 0) and (h > 0)
    return h if pred else FARAWAY

def s_light(O, D, d, scene, bounce, s):
    M = vec3_add(O, vec3_mul(D, d))
    N = vec3_mul(vec3_sub(M, s[0]), 1.0 / s[1])
    toL = vec3_norm(vec3_sub(L, M))
    toO = vec3_norm(vec3_sub(E, M))
    nudged = vec3_add(M, vec3_mul(N, 0.0001))
    light_distances = [s_intersect(nudged, toL, scene_obj) for scene_obj in scene]
    light_nearest = min(light_distances)
    seelight = light_distances[scene.index(s)] == light_nearest
    color = (0.05, 0.05, 0.05)
    lv = max(vec3_dot(N, toL), 0)
    color = vec3_add(color, vec3_mul(s[2], lv * seelight))
    if bounce < 2:
        rayD = vec3_norm(vec3_sub(D, vec3_mul(N, 2 * vec3_dot(N, D))))
        color = vec3_add(color, vec3_mul(raytrace(nudged, rayD, scene, bounce + 1), s[3]))
    phong = vec3_dot(N, vec3_norm(vec3_add(toL, toO)))
    color = vec3_add(color, vec3_mul((1, 1, 1), math.pow(max(min(phong, 1), 0), 50) * seelight))
    return color

scene = [
    ((.75, .1, 1), .6, (0, 0, 1), 0.5),
    ((-.75, .1, 2.25), .6, (.5, .223, .5), 0.5),
    ((-2.75, .1, 3.5), .6, (1, .572, .184), 0.5),
    ((0, -99999.5, 0), 99999, (.75, .75, .75), 0.25),
]

(w, h) = (400, 300)
L = (5, 5, -10)
E = (0, 0.35, -1)
r = w / h
S = (-1, 1 / r + .25, 1, -1 / r + .25)

pixels = []
for j in range(h):
    y = S[1] + (S[3] - S[1]) * j / (h - 1)
    for i in range(w):
        x = S[0] + (S[2] - S[0]) * i / (w - 1)
        Q = (x, y, 0.0)
        D = vec3_norm(vec3_sub(Q, E))
        color = raytrace(E, D, scene)
        pixels.append(color)

r_array = np.array([255 * max(min(c[0], 1), 0) for c in pixels], dtype=np.uint8).reshape((h, w))
g_array = np.array([255 * max(min(c[1], 1), 0) for c in pixels], dtype=np.uint8).reshape((h, w))
b_array = np.array([255 * max(min(c[2], 1), 0) for c in pixels], dtype=np.uint8).reshape((h, w))

rgb_image = Image.merge("RGB", [Image.fromarray(r_array, "L"), Image.fromarray(g_array, "L"), Image.fromarray(b_array, "L")])
rgb_image.show()

说明

numpy版本代码本身可正常运行,无需修改;非numpy版本的核心问题是向量结构不匹配,通过逐像素处理可直接复用原有向量运算逻辑,避免复杂的列表数值兼容处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 21:09:52