返回

文章详情

我用Brainfuck写了一个光线追踪器

Hacker News2026年9月25日 10:08

当我为C++系统编程比赛做准备时,我开始重新学习CMake,因为Cargo让我过于依赖它。同时,我在教程中注意到了一个有趣的声明。通常,正确的答案是用一种通用编程语言编写一个工具来解决问题,并教CMake如何在构建过程中调用该工具。代码生成、加密签名工具,甚至光线追踪器都可以用CMake编写,但这并不是推荐的做法。CMake语言基础在之前编写过光线追踪器并为GPU重写过之后,这个说法引起了我的注意,并让我想知道还有什么更好的语言可以写光线追踪器。上次重写涉及编写代码,几乎没有基于第一原理的方法,而主要依赖相对复杂的API集合。因此,我选择了我所知的最简单的语言BF,因为简单的语言显然会导致非常简单的代码库。实际上,BF中的代码库通常只有几行长。此外,Muller在README中的评论让我想展示一个反例。代码可在mTvare6/rayfuck查看。基础BF是一种相当简单的语言,仅涉及8个操作和一个“数据结构”:一条单向无限细胞带,每个细胞能够存储一个u8。当看到字符>时,数据指针会向右移动,反之为<。输入输出通过,和.管理。第一个存储数据指针指向的输入字节,后者则将其打印出来。除了输入输出,唯一可以改变值的基本操作是通过+和-在数据指针处进行的增量和减量。限制应该显而易见:没有像其他机器那样的n > 1寄存器,没有操作多个单元的指令,也没有加法或乘法的指令。使BF图灵完备的最后一个要素是用[和]写成的循环。当遇到token [时,运行时检查数据指针指向的单元:如果它为零,则执行跳过匹配的],否则进入循环。在]处,如果单元不为零则返回匹配的[,否则退出循环。一个快速的练习是写一个cat程序,尝试用5个字符写一个,以获取对该环境的一些直觉。深思熟虑在开始之前看过这些内容之后,我决定避免查找任何结果或实现细节,并尽可能从第一原理出发写下所有内容。为了保持范围最小,并使程序显然是一个光线追踪器,我决定让它渲染通过RIW的Metal部分所渲染的确切图像。无论如何,C代码有点过于复杂,编写一个C解析器显然超出了范围。编写一个未维护的C解析器更适合由Anthropic处理。我决定将每个双精度[以及其他数据类型如布尔值]通过组合单元来表示,半个比特表示小数部分,另一个半部分表示整数部分,有效地在它们之间放置一个固定的二进制点。我后来了解到这被称为Q格式。选择更便宜的有符号Q8.8将给予1/256的分辨率和大约[-128, 128)的范围。但显然,这还不够,因为场景中用于地面的球体必须有r=1000才能看起来是平的,因此我选择了更贵的有符号Q16.16格式。它具有1/2^16的分辨率和[-2^15, 2^15)的范围,这是足够的。我决定将代码转换为类似SSA的格式[并决定这将是LLM唯一的工作],在其中递归代码被改写为迭代,并且在函数中定义的变量用匈牙利命名法前缀以避免地址查找时名称冲突。同样,分离解析和代码生成似乎是必要的,将复杂性分成两个代码区域,中间使用“DSL”作为IR。DSL包含简单操作,例如abs、add、and、call、copy、div、else、end、eq、func、ge、gt、if、int、le、lt、mul、neg、not、or、print2、print3、set、sqrt、sub、text、var和while。下一个棘手的部分是几个库调用。所使用的是sqrt、rand和abs。最初我计划使用一个双状态解决方案,如:A = ( A - B ) % 256 B = ( B + 1 ) % 256或B = ( B + A + p ) % 256 # 对于某个质数p,但大多数这些变体的周期很差。我决定使用更简单的A = ( 5 * A + 1 ) % 256,因为它保证仅在完整序列256值之后重复,这对于这个用例[即超采样抗锯齿]来说还不错。sqrt有一个明显的候选者,海伦公式[我在相同的CMake教程中刷新了我的记忆]。但很明显这很糟糕,因为它涉及除法。重复减法虽然生成更小的代码,但...

赞助内容

NordVPN Next-gen Antivirus

本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。

☕请我喝杯咖啡