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

Python光线追踪实现异常:输出图像仅黑白无色彩

光线追踪实现问题:输出黑白图像,物体形状正确但无色彩

我正在用Python实现光线追踪,输出的图像只有黑白两色,物体形状是对的,但没有预期的色彩。我是编程新手,找不到问题根源。以下是我的实现代码:

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.pyplot


def vector_nomalization(vector): # 向量归一化函数
    return vector / np.linalg.norm(vector)


def intersection_with_sphere(center, radius, origin, direction_vector): # 检测光线与球体的交点
    b = 2 * np.dot(direction_vector, origin - center)
    c = np.linalg.norm(origin - center) ** 2 - radius ** 2 # (原点到球心距离)^2 - 半径^2
    delta = b **2 - 4 * c
    if delta > 0:
        t1 = (-b +np.sqrt(delta))/2
        t2 = (-b - np.sqrt(delta))/2
        if t1 > 0 and t2 > 0:
            return min(t1,t2) # 返回原点到最近交点的距离
    return None


def closest_intersection(spheres, origin, direction): # 找到与光线相交的最近球体
    distances = [intersection_with_sphere(sphere['center'], sphere['radius'], origin, direction) for sphere in spheres]
    closest_sphere = None
    min_distance = np.inf
    for index, distance in enumerate(distances):
        if distance and distance < min_distance:
            min_distance = distance
            closest_sphere = spheres[index]
    return closest_sphere, min_distance


def reflection_ray(vector, axis):
    return vector - 2 * np.dot(vector, axis) * axis


spheres =[
    { 'center': np.array([-0.2, 0, -1]), 'radius': 0.7, 'ambient': np.array([0.1, 0, 0]), 'Diffuse': np.array([0.7, 0, 0]), 'specular': np.array([1, 1, 1]), 'shininess': 100, 'reflection': 0.5 },
    { 'center': np.array([0.1, -0.3, 0]), 'radius': 0.1, 'ambient': np.array([0.1, 0, 0.1]), 'Diffuse': np.array([0.7, 0, 0.7]), 'specular': np.array([1, 1, 1]), 'shininess': 100, 'reflection': 0.5 },
    { 'center': np.array([-0.3, 0, 0]), 'radius': 0.15, 'ambient': np.array([0, 0.1, 0]), 'Diffuse': np.array([0, 0.6, 0]), 'specular': np.array([1, 1, 1]), 'shininess': 100, 'reflection': 0.5 },
    { 'center': np.array([0, -9000, 0]), 'radius': 9000 - 0.7, 'ambient': np.array([0.1, 0.1, 0.1]), 'Diffuse': np.array([0.6, 0.6, 0.6]), 'specular': np.array([1, 1, 1]), 'shininess': 100, 'reflection': 0.5}
]

light = {'position': np.array([5, 5, 5]), 'ambient': np.array([1, 1, 1]), 'Diffuse': np.array([1, 1, 1]), 'specular': np.array([1, 1, 1])}


width = 300
height = 200

max_depth = 3

camera = np.array([0, 0, 1])
ratio = float(width) / height  # 图像宽高比
screen = (
-1, 1 / ratio, 1, -1 / ratio)  # 屏幕边界:左、上、右、下

image = np.zeros((height, width, 3))

for i, y in enumerate(np.linspace(screen[1], screen[3], height)):  # 遍历屏幕y方向像素
    for j, x in enumerate(np.linspace(screen[0], screen[2], width)): # 遍历屏幕x方向像素
        pixel = np.array([x, y, 0])
        origin = camera
        direction_vector = vector_nomalization(pixel - origin) # 光线方向向量

        color = np.zeros((3))
        reflection = 1

        for k in range(max_depth):

            # 检测是否有交点
            closest_sphere, min_distance = closest_intersection(spheres, origin, direction_vector)
            if closest_sphere is None:
                break

            # 计算光线与最近球体的交点
            intersection = origin + min_distance * direction_vector

            normalized_to_surface = vector_nomalization(intersection - closest_sphere['center'])
            shifted_point = intersection + 1e-5 * normalized_to_surface # 偏移点避免自遮挡
            lights_intersection = vector_nomalization(light['position'] - shifted_point)

            # 检测是否在阴影中
            _, min_distance = closest_intersection(spheres, shifted_point, lights_intersection)
            lights_intersection_distance = np.linalg.norm(light['position']-intersection)
            shadowed = min_distance < lights_intersection_distance

            # 计算光照
            illumination = np.zeros((3))

            # 环境光
            illumination += closest_sphere['ambient'] * light['ambient']

            if not shadowed:
                # 漫反射
                dot_product = np.dot(lights_intersection, normalized_to_surface)
                illumination += closest_sphere['Diffuse'] * light['Diffuse'] * max(dot_product, 0)

                # 高光
                reflection_light = reflection_ray(-lights_intersection, normalized_to_surface)
                view_dir = vector_nomalization(camera - intersection)
                spec_dot = np.dot(reflection_light, view_dir)
                illumination += closest_sphere['specular'] * light['specular'] * (max(spec_dot, 0) ** closest_sphere['shininess'])

            # 累加色彩(反射系数乘以光照值)
            color += reflection * illumination
            reflection *= closest_sphere['reflection']

            # 更新光线参数用于反射计算
            origin = shifted_point
            direction_vector = reflection_ray(direction_vector, normalized_to_surface)

        image[i, j] = np.clip(color, 0, 1)
    print("progress: %d/%d" % (i + 1, height))

plt.imsave('image.png', image)

图像说明

  • 当前输出:黑白图像,可分辨球体和地面的形状,但所有物体均无色彩区分
  • 预期效果:彩色图像,红色大球、蓝色小球、绿色小球和灰色地面清晰区分,带有高光和阴影效果

核心修复点

  1. 色彩累加逻辑错误:原代码color += reflection + illumination改为color += reflection * illumination,用反射系数乘以光照值,避免标量与数组相加破坏色彩信息
  2. 添加环境光计算:加入环境光分量,确保物体在阴影中也有基础颜色
  3. 修正高光计算:使用标准Phong光照模型,计算反射光线与视线的点积并取shininess次方,还原高光效果
  4. 调整阴影逻辑:阴影时不再直接终止计算,而是仅关闭漫反射和高光,保留环境光,避免物体完全变黑
  5. 漫反射值取非负:对漫反射的点积结果取最大值,避免出现负色彩值

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 21:41:12