Python的灵活运用之——100 行代码教你实现光线追踪 !

“Ray Tracing is the Future and Ever Will Be”

英伟达的一位大佬曾经这么说过:「光线追踪是未来,而且将永远是未来。」

光线追踪的原理是非常直观而优雅的,其应用的核心难点在于性能优化。本文将基于光线追踪的基础原理,用 100 行 python 代码实现一个简单的光线追踪渲染器。

基础原理

我们看到的物体,是由来自各个方向发射光、反射光、折射光照亮的。而光线的起点,是各种发光源;光线的终点,则是我们的眼睛。一束光由光源出发,经过不同物体的反射、折射,最终射入观察者的眼睛。

由于在几何光学中,光路具有可逆性。我们从观察者的眼睛射出一道虚拟的光线,它经过的路径将与射入观察者眼睛的那道光路径完全一致,方向相反。此时,如果我们在观察者眼前放置一个像素化的「窗口」,由观察者向每个像素发射一道虚拟光线,最终,这些光线都会「返回」到光源中。结合这些光的路径,和相关的物理模型/经验模型,我们可以计算出这个窗口上每一个像素点的颜色值,从而形成我们在计算机屏幕上看到的图像。

场景布置

为了简化代码,我们尽可能将场景简单化,并使用参数方程描述场景中的物体。

scene = [sphere([.75, .1, 1.], .6, [.8, .3, 0.]),           # 球心位置,半径,颜色
         sphere([-.3, .01, .2], .3, [.0, .0, .9]),
         sphere([-2.75, .1, 3.5], .6, [.1, .572, .184]),
         plane([0., -.5, 0.], [0., 1., 0.])]                # 平面上一点的位置,法向量
light_point = np.array([5., 5., -10.])                      # 点光源位置
light_color = np.array([1., 1., 1.])                        # 点光源的颜色值
ambient = 0.05                                              # 环境光

我们在场景里放置三个球和一个平面,并放置了一个白色点光源。

在现实世界中,完全黑暗不可见的场景是很少的。即使是在一个暗室中,点亮一枚微弱的蜡烛,在家具等物品的阴影内,也并不是完全黑暗。这些地方是由光源的光经过多次反射后,近似均匀地投射到各个角落的。为了描述这个复杂的物理现象,我们将其简化为一个较小的常数光照——环境光。现阶段的计算机图形学远无法精确地模拟真实的物理世界,在许多时候(特别是对计算实时性要求较高的时候),我们通常会用一个可接受的简化经验模型来替代相对真实的模型。

生成物品

def get_color(obj, P):
    color = obj['color']
    if not hasattr(color, '__len__'):
        color = color(P)
    return color

def sphere(position, radius, color, reflection=.85, diffuse=1., specular_c=.6, specular_k=50):
    return dict(type='sphere', position=np.array(position), radius=np.array(radius), 
                color=np.array(color), reflection=reflection, diffuse=diffuse, specular_c=specular_c, specular_k=specular_k)

def plane(position, normal, color=np.array([1.,1.,1.]), reflection=0.15, diffuse=.75, specular_c=.3, specular_k=50):
    return dict(type='plane', position=np.array(position), normal=np.array(normal), 
                color=lambda P: (np.array([1.,1.,1.]) if (int(P[0]*2)%2) == (int(P[2]*2)%2) else (np.array([0.,0.,0.]))),
                reflection=reflection, diffuse=diffuse, specular_c=specular_c, specular_k=specular_k)

我们用 python 的字典对象来描述球和平面的各种参数:位置、半径、法向量、颜色、镜面反射率、漫反射率、高光参数,等。其中一些参数的意义在后面的小节中再进行解释。

其中,我们用了一个匿名函数为平面生成黑白相间的棋盘格纹理。

基础准备

import numpy as np

def normalize(x):
    return x / np.linalg.norm(x)

def get_normal(obj, point):         # 获得物体表面某点处的单位法向量
    if obj['type'] == 'sphere':
        return normalize(point - obj['position'])
    if obj['type'] == 'plane':
        return obj['normal']

我们定义两个简单的函数 normalize 和 get_normal,分别用于将向量归一化,获取物体表面特定点的单位法向量。球面和平面的法向量获取方式相当简单。此处我们引用了 numpy 这个科学计算的库,对此不熟悉的读者可以参考这里进行安装:NumPy 安装教程

简单的几何学

def intersect(origin, dir, obj):    # 射线与物体的相交测试
    if obj['type'] == 'plane':
        ret
  • 7
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值