This Week In Moeif 25

这周开始学习画像素画了,还不是很熟练,不过每天会多少画几分钟,使用数位板。以前使用数位板不太习惯,慢慢的对于它的控制会越来越顺手。 另外,这周完成了 Ray Tracing in One Weekend 系列的博客Rust重实现。接下来就要开始第二篇章了。 今天上海下雨了,下个周的温度,可能会冷很多。 项目进度 游戏码农:那些打工的日子 这周主要完成了头像自定义模块,简历完善模块,招聘信息模块,以及添加了能用的提示消息界面。确定了核心玩法界面的基本结构,参考了其他同类型的游戏对于时间上的处理逻辑。基本上确定了由事件来驱动整个游戏的进程,如果没有事件填充,那么时间就会停止,这样避免了很多以前想不到解决方案的问题,只是这样唯一的问题是没法一直挂机,可能过一段时间就需要操作一下。另外确定的小玩法有赛车,地下搏击俱乐部这两个。整理了一下游戏的核心,就是围绕着金钱,健康,快乐的循环。 本周电影 花与罪 (2021) 你 第一季 You Season 1 (2018) 本周书籍 《无后为大》(阅读中) 《重构:改善即有代码的设计》(阅读中) 下周初步计划 游戏码农逻辑编写

December 12, 2021 · 1 min · fred

Rust 光线追踪 14: 最终汇总效果

这一节没有新的知识,只是将之前知识汇总起来,然后生成一张最终的图,最终生成的图如下 首先,将创建世界及添加物体的代码从 main 函数中删除,然后抽象成一个函数 random_scene,在这个函数中,会随机生成一些球体,并且根据规则,随机使用我们的已有的三个材质。 // src/main.rs fn random_scene() -> HittableList { let mut world = HittableList::new(); let ground_material = Lambertian::new(Color::new(0.5, 0.5, 0.5)); world.add(Box::new(Sphere::new( Vec3::new(0.0, -1000.0, 0.0), 1000.0, ground_material, ))); let mut rng = rand::thread_rng(); // 生成一些小球 for a in -11..11 { for b in -11..11 { let choose_mat: f64 = rng.gen(); let x_offset: f64 = rng.gen(); let z_offset: f64 = rng.gen(); let center = Vec3::new(a as f64 + 0.9 * x_offset, 0....

December 11, 2021 · 2 min · fred

Rust 光线追踪 13: 散焦模糊

散焦模糊,通俗来讲就是在聚焦区域外的东西,都是模糊的。通常这种效果被称为 Depth Of Field(DOF),也就是景深。最终效果如下 ...

December 10, 2021 · 2 min · fred

Rust 光线追踪 12: 正交相机

正交相机所看到的东西大小,与远近无关,只与正交相机的视野(FOV)有关。FOV越大,能看到的世界范围就大,也就是能看到更多的东西,而FOV越小,能看到的世界范围就越小,也就是只能看到较少的东西。 由于FOV越小,看到的范围就越小,从而,相机的上下界,所发出的射线,所能覆盖的范围,就小。也就是相当于所有的射线,都集中在世界中一个小范围,从这个小范围中取得颜色,填充画布(最后渲染的图片),所以看到的东西就大。而如果FOV很大,射线所能覆盖的世界范围就大,用这个大范围来填充画布,自然同一个物体就会看起来小。 可以想象一个两个盒子,一个大的,假设口径是50厘米,扣在一把键盘上,可以扣住整个键盘,相当于摄像机看到了整个键盘。而将一个1厘米口径的盒子,扣在键盘上,可能只能覆盖其中一个键,也就是相机只能看到这一个键范围的东西。但最后都会将扣到的东西填充到画布上,所以,就相当于FOV越小,看到的东西就越大。 添加相机FOV逻辑 在本文中,相机的FOV,我们使用角度来表示,上图中,$\theta$ 就是相机的开口大小,而 $h = \tan(\frac{\theta}{2})$,$2 * h$就是视口的高度,而视口的宽度,通过自定义的宽高比,来动态计算出来。 下面修改 camera.rs 的代码,添加 fov 和 宽高比。 // src/camera.rs impl Camera { pub fn new(vfov: f64, aspect_ratio: f64) -> Self { let theta = vfov * std::f64::consts::PI / 180.0; let h = (theta / 2.0).tan(); let viewport_height = 2.0 * h; let viewport_width = aspect_ratio * viewport_height; let focal_length = 1.0; let origin = Vec3::zero(); let horizontal = Vec3::new(viewport_width, 0.0, 0....

December 8, 2021 · 2 min · fred

Rust 光线追踪 11: 折射

关于折射的实现,可以使用斯涅尔定律。当光波从一种介质传播到另一种介质时,如果两种介质拥有不同的折射率,那么光线就会发生折射现象。例如光线从空气中进入水中,或者从空气中进入玻璃中。 下面涉及到的公式,也可以不用理解推导过程,只要拿来用就行。 斯涅尔定律 斯涅尔定律表明,当光波从介质1传播到介质2时,假若两种介质的折射率不同,则会发生折射现象,其入射光和折射光都处于同一平面,称为“入射平面”,并且与界面法线的夹角满足如下关系: $n_{1}\sin\theta_{1} = n_{2}\sin\theta_{2}$ 其中,$n_{1}$、$n_{3}$ 分别是两种介质的折射率,$\theta_{1}$、$\theta_{2}$ 分别是入射光线、折射光线与界面法线的夹角,分别叫做入射角和折射角。 要求折射光线的方向,就需要解出 $\sin\theta_{2}$ 来。根据上面的公式可知 $\sin\theta_{2} = \frac{n_{1}}{n_{2}} \cdot \sin\theta_{1}$ 我们可以将折射光线的向量,分解为一个垂直向量和一个平行向量。计算出这两个向量,然后相加,即可得到最终的折射向量。 $ 垂直向量 = \frac{n_{1}}{n_{2}}(入射向量 + \cos\theta_{1}*法向量)$ $ 平行向量 = - \sqrt{1 - |垂直向量|^2 * 法向量}$ 由于,两个向量的点乘与其夹角的 cos 值有关,也就是 $ a \cdot b = |a||b|\cos\theta $ ,如果 a 和 b 都是单位向量,那么可得到 $a \cdot b = \cos\theta$ 根据上面的规则,可以使用新的方式来表示垂直向量 $ 垂直向量 = \frac{n_{1}}{n_{2}}(入射向量 + (-入射向量 \cdot 法向量) * 法向量) $ 综上,可以使用代码来实现折射逻辑,在 vec3.rs 中添加一个新的函数 refract...

December 7, 2021 · 3 min · fred

This Week In Moeif 24

这周跑步了四天,还可以,下周继续。我发现跑步的时候可以捋清一些事情,这周主要跑 2.5 公里,然后走 2.5 公里,整体感觉还是挺舒适的,下个周看看是否可以加大一点跑步里程。 这周输出了4篇博客和公众号,Rust光线追踪系列以及后续的博客,会同步到掘金这个平台上。 这周把产品页面也搭建好了,更新了 moeif.com 这个域名。 项目进度 游戏码农:那些打工的日子 这周开始写游戏逻辑了,已经确定了主要玩法界面的逻辑。这周把之前的Lua代码全删了,想了想,还是全用C#来写了,逻辑上不会有什么功能性的更新,如果加功能,加玩法模块,那就直接大版本更新,用C#的开发和调试效率也会更高一些。 本周电影 最后的决斗 The Last Duel (2021) 本周书籍 《KK三部曲》(阅读中) 《游戏设计艺术》(阅读中) 下周初步计划 游戏码农逻辑编写

December 5, 2021 · 1 min · fred

Rust 光线追踪 10: 材质

在前面的文章中,我们渲染出来的图片使用的是漫反射的材质。不同的材质,可以简单理解为对于光线的影响不同,这里的影响包括如何吸收,如何散射等。在这一节,我们将加入另一种材质,金属材质。 最终的渲染图如下 ...

December 4, 2021 · 5 min · fred

编译 Apple Silicon 版本 Aseprite

编译苹果 M1 芯片版本的 Aseprite,步骤如下 ...

December 1, 2021 · 1 min · fred

Rust 光线追踪 09: 理想散射

上一节生成的图看起来很暗,有一个问题是因为有些物体反射的光线,会在 t = 0 时再次击中自己,而由于浮点数精度的问题,这些值可能是 0.00000001 或 -0.0000001 之类的任意接近0的浮点数,所以我们 hit 函数的 t_min 参数,需要忽略掉 0 附近的一小部分范围,防止物体发出的光线再次与自己相交。这样也就避免了阴影痤疮(Shadow ance)的产生。 修改 main.rs 中 ray_color 函数中的 word.hit(),改为 if let Some(hit_record) = world.hit(r, 0.001, f64::INFINITY),也就是 t_min 参数传值 0.001。 cargo run --release > diffuse_random_in_sphere.ppm 生成图如下 可以看到,已经比上一节生成的图亮了很多。 我们上一节使用的漫反射光线散射的方法,是在球体内部生成一个随机的点。然后,这样生成的向量,有很大的概率会和法线方向相近,并且及小概率会沿着入射方向反射回去。然而,真正的理想散射(Lambertian 反射)后的光线距离法向量比较近的概率会更高,但是分布规律会更加均衡,而实现这个方法,就是在球面上选取一个点,而不是在球内。我们可以通过在球内选取一个点,然后将其标准化,来得到球面上的点,因为我们的球是单位球。 在 vec3.rs 中 Vec3 的实现里,添加一个新的方法 random_unit_vector,代码如下 // src/vec3.rs pub fn random_unit_vector() -> Vec3 { let point = Vec3::random_in_unit_sphere(); return Vec3::unit_vector(point); } 然后修改 main.rs 中的 ray_color 函数,将原来调用 random_in_unit_sphere 的代码改成调用 random_unit_vector。...

December 1, 2021 · 2 min · fred

Rust 光线追踪 08: 漫反射和伽马校正

漫反射的通俗理解是,当一个光线打到某一个物体的某一个点上,这条光线一部分会被吸收,一部分会被随机的反射出去,而反射出去的光线,又可能会打到另一个物体面上的一个点,然后又会被吸收,以及随机的反射出去。现实中的光线可能会无限递归下去,但是我们在程序中实现,不可能无限递归,会设置一个反射次数,达到了那个次数,就停止。 ...

November 30, 2021 · 3 min · fred