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函数的逻辑:
- 修改像素坐标生成与光线追踪流程:
(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()
- 原有
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
相关产品推荐
相关产品推荐

