新世界的大门

因此幻想是世界,谁若懂得,谁就会歌唱

新世界的大门
新世界的大門(Logo)

几何光学:

[...]的概念为基础,用[...]的方法研究光在介质中的传播规律和光学系统成像特征.

光波与光线

光从本质上来就是[...];光的传播实质上是[...]

波长

红外线的波长比可见光[...]

紫外线的波长比可见光[...]

光速、频率与波长的关系:

\[\nu=\dfrac c \lambda.\]

(不能写成其他形式)

光源和光线

光源(发光体)是[...]的物体.

光线是[...]的几何线.

[...]代表光的传播方向.

[...]称为光路.

波面

光波向周围传播,在某一时刻,其振动位相相同的各点所构成的[...]称为波阵面,简称波面.可分为[...]

在各向同性介质中,光沿着波面法线方向传播,所以可以认为[...]就是几何光学中的光线.

波前(wavefront): The surface reached by wave propagation at any time.

基本定律

光的直线传播定律

[...]中,光线按直线传播.

光线的独立传播定律

光线的独立传播定律不符合[...]

光的折射和反射定律

折射定律

折射率越大,介质对光线的折射能力越强。如,空气的折射率约为 \(1.00\),水的折射率约为 \(1.33\),则光线从空气射入水中时,入射角 \(\geq\) 折射角。

折射定律的公式:

\[n’\sin I’=n\sin I.\]

若在此式中令 \(n’=-n\), 则有 \(I’=-I\),即反射定律.

角的正负

[...][...]时针方向旋转形成的角度为正,反之为负.

在反射定律中,反射光线和入射光线位于法线的两侧,且反射角与入射角的绝对值相等,符号相反.

全反射

全反射现象发生的条件:

  1. 光线从[...]介质射向[...]介质;
  2. 入射角大于临界角.

临界角 \(\sin I_m = \dfrac{n’\sin I’}{n} = \dfrac{n’}{n}\sin 90° = \dfrac{n’}{n}\),其中 \(n \gt n’\)

马吕斯定律

马吕斯定律(Theorem of Malus)是指光线束在各向同性的均匀介质中传播时,始终[...],并且入射波面与出射波面对应点之间的[...]为定值.

费马原理

费马原理(Fermat’s principle)是指光是沿着光程为[...]的路径传播的.

[...]三者中的任意一个,均可以视为几何光学的一个基本定律,而把另外两个作为该基本定律的推论.

在 GitHub 仓库 Settings 页面中设置的 Actions secrets and variables 是只能重新填写的。同时,在 Action 执行的 log 里所有 secrets 都会被星号打码。不过,可以通过创建一个 Action 来方便地查看我们设置的 token。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
name: View GitHub Actions secrets
on:
push:
branches: [master]
workflow_dispatch:
# 可以在仓库的 Actions 页面中手动触发

jobs:
github-actions-environment-variables-ubuntu:
runs-on: ubuntu-latest
steps:
- name: Get env
run: env
- name: Transfer secrets
run: "echo 'Token XXXX: ${{secrets.XXXX}}' | curl -T - https://ppng.io/自己设定的一段口令"

在你的设备上运行 curl https://ppng.io/自己设定的一段口令 后再运行上述 action 就可以看到啦。

在新 VPS 上运行 Python 程序的时候出现了问题:pdm 无法正常 install 或者设置 config,poetry 可以但是无法正常运行。两者都在获取运行环境的时候发生了同样的错误。以下是一个最小复现例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[neko@host temp]$ ls
select.py
[neko@host temp]$ cat select.py
print("hello, world!")
[neko@host temp]$ python
Python 3.10.9 (main, Dec 19 2022, 17:35:49) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import platform
hello, world!
Traceback (most recent call last):
File "/usr/lib/python3.10/subprocess.py", line 69, in <module>
import msvcrt
ModuleNotFoundError: No module named 'msvcrt'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.10/platform.py", line 119, in <module>
import subprocess
File "/usr/lib/python3.10/subprocess.py", line 76, in <module>
import selectors
File "/usr/lib/python3.10/selectors.py", line 291, in <module>
class SelectSelector(_BaseSelectorImpl):
File "/usr/lib/python3.10/selectors.py", line 318, in SelectSelector
_select = select.select
AttributeError: module 'select' has no attribute 'select'
>>>

我一 Linux 怎么会需要 msvcrt?一开始以为是 Arch 仓库里的 Python 有问题,或者是 pip 安装的时候在 root 用户操作而破坏了系统,但是重装了几次全部依赖也没有解决。排查许久后,发现只有在我的代码文件夹里进入 REPL import platform 会报错。然后发现项目里用的一个模块的名称叫 select.py。是的,导入 Python 内置的 标准库 的时候,标准库内部的 import 也是在当前工作目录下进行的(!)。

最末的结论是,你不应该使用以下名称作为 Python 代码的文件/模块名:

abc, aifc, argparse, array, ast, asyncio, atexit, audioop, base64, bdb, binascii, bisect, builtins, bz2, calendar, cgi, cgitb, chunk, cmath, cmd, code, codecs, codeop, collections, colorsys, compileall, concurrent, configparser, contextlib, contextvars, copy, copyreg, csv, ctypes, dataclasses, datetime, dbm, decimal, difflib, dis, doctest, email, ensurepip, enum, errno, faulthandler, filecmp, fileinput, fnmatch, fractions, ftplib, functools, gc, getopt, getpass, gettext, glob, graphlib, gzip, hashlib, heapq, hmac, html, http, imaplib, imghdr, imp, importlib, inspect, io, ipaddress, itertools, json, keyword, linecache, locale, logging, lzma, mailbox, mailcap, marshal, math, mimetypes, mmap, modulefinder, msilib, msvcrt, multiprocessing, netrc, nntplib, numbers, operator, optparse, os, pathlib, pdb, pickle, pickletools, pipes, pkgutil, platform, plistlib, poplib, pprint, profile, py_compile, pyclbr, pydoc, pyexpat, queue, quopri, random, re, reprlib, rlcompleter, runpy, sched, secrets, select, selectors, shelve, shlex, shutil, signal, site, smtplib, sndhdr, socket, socketserver, sqlite3, ssl, stat, statistics, string, stringprep, struct, subprocess, sunau, symtable, sys, sysconfig, tabnanny, tarfile, telnetlib, tempfile, test, textwrap, threading, time, timeit, tkinter, token, tokenize, tomllib, trace, traceback, tracemalloc, turtle, types, typing, unicodedata, unittest, urllib, uu, uuid, venv, warnings, wave, weakref, webbrowser, winreg, winsound, wsgiref, xdrlib, xml, xmlrpc, zipapp, zipfile, zipimport, zlib, zoneinfo 🐍

本文为《基于物理的渲染:从理论到实现》第三版第一章的笔记.此书通过结合具体的软件(pbrt)代码,讲解光线追踪的实现.

相关资源

本书的 官方网站.你可以在 这里 免费阅读第三版(英文),或者在 这里 免费下载第三版(中文)的 PDF(目前翻译进度到第八章).你也可以购买清华大学出版社翻译的《物理渲染:从理论到实现(第二版)》纸质书(ISBN 9787302449812),不过价格不菲.

pbrt-v3 是原书作者的 C++ 实现,代码在 GitHub

rs_pbrt 是一个 Rust 的 pbrt-v3 实现,不过目前还有多处待完成,缺少一些功能.不过 Rust 有统一的构建环境和更好的开发体验.你可以看看其 GitHub 仓库网站

本文末尾附有简明的编译和使用教程.

第一章:绪论

渲染(rendering)是由 3D 场景(scene)描述生成图像的过程.基于物理的(physically based)渲染运用物理学规律对光与物质的相互作用建模.

长期以来,实时(real-time)渲染主要采用栅格化(rasterization)或栅格化+光线追踪(ray-tracing),而本书中完全采用光线追踪.

1.1 文学编程

讲解书中伪代码的表示方法文学编程(literate programming).

不认可这种表示方法,即便想法很好——作为教学用的代码不能过长,但是这样代码支离破碎.为了便于理解,应该提供更小更快、能即时查看到效果的教科书.官方的电子版很好,可以展开折叠的代码,而不是在页内跳来跳去.

Jupyter Notebook 应是一个更好的 literate programming.

1.2 逼真渲染和光线追踪算法

1.2.1 相机

针孔相机也可以看作是把胶片平面放置在针孔的前方但距离不变——出于仿真目的,可以将胶片放在视见体(viewing volume)位置.视见体、小孔、胶片构成一个双锥体.

incident light 是「入射光」的意思.

1.2.2 光线-物体相交

交点信息

将射线 \(\boldsymbol{r}\) 写成参数形式:

\[{\boldsymbol r}(t)={\boldsymbol o}+t{\boldsymbol d},\]

其中 \({\boldsymbol o}\) 是射线端点, \({\boldsymbol d}\) 是其方向向量, \(t\) 是定义在 \((0,\infty)\) 的参数.

由隐函数 \(F(x,y,z)=0\) 定义曲面.将曲面的隐式方程代入射线方程即可求得交点.如果没有正根,则射线与球面错开了;如果有,则最小正根给出了交点.

该点的特定属性

除交点外,光线追踪器还需知道如曲面法线 \(\boldsymbol n\) 等额外信息以着色(shade).

加速结构

加速结构(acceleration structure)可使时间复杂度降为 \(O(I\log N)\) ,其中 \(I\) 是图像像素数目, \(N\) 是场景中物体的数量.

1.2.3 光的分布

围绕光源的单位球面在单位面积上的功率为 \(\displaystyle\frac{\varPhi}{4\mathrm{\pi}}\)

到达半径为 \(r\) 的球面上一点的单位面积功率 \(\propto\dfrac{1}{r^2}\)

光源若与法线有夹角, \(\mathrm dA\) 上积累的功率 \(\propto\cos\theta\)

综上所述,单位面积上的辐射照度(differential irradiance) \(\mathrm{d}E\)

\[\mathrm{d}E=\frac{\varPhi\cos\theta}{4\mathrm{\pi}r^2}.\]

1.2.4 可见性

若有阴影遮挡着色点,光源路径不畅通时不会照亮该点.通过 阴影射线(shadow ray)可判断是否可见.方法是 简单构造一条新射线,其端点是表面上的点,方向指向光源,如下图中虚线所示.

递归式的光线追踪

图片来源为 闫令琪.GAMES101: 现代计算机图形学入门.Lecture 13 光线追踪(基本原理)[pdf]

1.2.5 表面散射

为着色我们还需确定入射光如何被散射(scattered).物体的材质由双向反射分布函数(bidirectional reflectance distribution function,BRDF)描述.该函数告诉我们从入射(incoming)方向 \({\boldsymbol \omega}_\mathrm{i}\)出射(outgoing)方向 \({\boldsymbol \omega}_\mathrm o\) 会反射多少能量.

我们把 \(\boldsymbol p\) 处的 BRDF 写作 \(f_{\mathrm{r}}({\boldsymbol p},{\boldsymbol \omega}_\mathrm{o},{\boldsymbol \omega}_\mathrm{i})\)

1.2.6 间接光传输

从物体上一点到达相机的光量由物体的发光量(如果它自己就是光源)与反射光量之和决定.它被形式化为光传输方程(light transport equation),表示从点 \(\boldsymbol p\) 沿方向 \({\boldsymbol \omega}_\mathrm{o}\) 的出射辐亮度 \(L_{\mathrm{o}}({\boldsymbol p},{\boldsymbol \omega}_\mathrm{o})\) 等于该点沿该方向的发光亮度加上点 \(\boldsymbol p\) 的邻域球面 \(S^2\) 所有方向上经 BSDF \(f({\boldsymbol p},{\boldsymbol \omega}_\mathrm{o},{\boldsymbol \omega}_\mathrm{i})\) 和余弦项调制的入射亮度:

\[L_{\mathrm o}({\boldsymbol p},{\boldsymbol \omega}_\mathrm{o})=L_{\mathrm e}({\boldsymbol p},{\boldsymbol \omega}_\mathrm{o})+\int_{S^2}f({\boldsymbol p},{\boldsymbol \omega}_\mathrm{o},{\boldsymbol \omega}_\mathrm{i})L_{\mathrm i}({\boldsymbol p},{\boldsymbol \omega}_\mathrm{i})|\cos{\theta_{\mathrm i} }| \,\mathrm{d}{\boldsymbol \omega}_\mathrm{i}\]

Whitted 算法把积分变为少量方向上的求和,故可以扩展到实现镜面和玻璃外的更多效果.

1.2.7 光线传播

在非真空中存在如烟、雾、尘等介质(participating media).

熄灭(衰减)

介质可以通过吸收或沿不同方向散射来熄灭(extinguish)aka. 衰减(attenuate)光.

需要计算射线与交点之间的透射率(transmittance) \(T\)

增强

介质也可以沿路线增强光.在介质发光(例如火焰)或从其他方向把光散射回该射线时可发生该现象.

可以通过数值计算体积光传输方程(volume light transport equation)来寻求该量,该方法还能计算光传输方程求得从表面反射回的光量.

1.3 pbrt:系统概述

1.3.1 执行阶段

pbrt 在概念上可分为两个执行阶段.

首先解析场景描述文件, 最终结果 是 SceneIntegrator 的实例,后者实现了渲染前者的算法,被称为积分器主要是计算 1.2.6 节中式的积分.

然后执行渲染主循环,由 Integrator::Render() 执行.

1.3.2 场景表示

程序首先解析命令行参数并 parse 场景描述文件,rs_pbrt 的这部分代码在 rs_pbrtparse_file 中.然后 就创建表示场景中光源和几何图元的对象.这两者都储存在 Scene 对象中.

1
2
3
4
5
6
pub struct Scene {
pub lights: Vec<Arc<Light>>,
pub infinite_lights: Vec<Arc<Light>>,
pub aggregate: Arc<Primitive>,
pub world_bound: Bounds3f,
}

C++ 的 shared_ptr 对应的就是 Arc

A thread-safe reference-counting pointer. 'Arc' stands for 'Atomically Reference Counted'.

光源

场景中每个光源都由Light对象表示,指定灯光的形状和发射能量的分布.

几何对象

场景中每个几何对象都由Primitive表示,由几何结构 Shape 和外观描述 Material 组成.他们都储存在 Primitive 中:

1
2
3
4
5
6
pub enum Primitive {
Geometric(Box<GeometricPrimitive>),
Transformed(Box<TransformedPrimitive>),
BVH(Box<BVHAccel>),
KdTree(Box<KdTreeAccel>),
}

这个聚合体是一种特殊的图元,它自己持有许多对其他图元的引用.聚合体的实现用加速的数据结构存储了所有场景图元,减少对远离给定光线的图元做不必要的光线相交测试量.

1.3.3 积分器接口与采样积分器

积分器(integrator)用于计算场景照明的一组测量值.Integrator 提供 render() 方法,接收一个 &Scene.其中一种实现是SamplerIntegrator,它是由来自 Sampler样本(sample)流驱动的,每个样本标识了图像上的一点,用于计算到达该点以构成图像的光量.

采样器的实现会极大影响系统生成图像的质量.它责选取光线要追踪的图像平面上的点,并且提供 1.2.6 节公式中所需的采样位置.

1.3.4 主渲染循环

流程示意图

为了并行化,图像会被分成图块,每个图块可并行独立处理.pbrt 固定使用 \(16 \times 16\) 的图块,这个粒度(granularity)对绝大多数图像都适用.

(C++) Li() 需要为每次辐亮度计算临时分配少量内存,所以我们会用一个 MemoryArena 实例管理内存池以启用比标准库例程更高性能的分配.

1.3.5 Whitted 光线追踪积分器

Whitted 积分器工作时递归地计算沿反射和折射光线方向的辐亮度.还是刚才这张图:

递归式的光线追踪

对于每个光源,积分器调用方法 Light::sample_li()计算从该光源落到表面上待着色点的辐亮度.

1.4 pbrt 的并行化

当执行多线程访问共享可改数据时它们必须以某种方式 同步(synchronize)其访问,即为互斥(mutual exclusion)和原子操作(atomic operation).

互斥

pbrt 采用 std::mutex 对象实现互斥.至于 C++ 中使用 mutex 的方法,需要声明一个值和一个 mutex,修改值时创建一个 std::lock_guard<std::mutex>,该锁会在 drop 时自动释放.cppreference.com 给出了 简明的例子,我将其贴在下方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <thread>
#include <mutex>
#include <iostream>

int g_i = 0;
std::mutex g_i_mutex; // protects g_i

void safe_increment()
{
const std::lock_guard<std::mutex> lock(g_i_mutex);
++g_i;
std::cout << "g_i: " << g_i << "; in thread #"
<< std::this_thread::get_id() << '\n';
// g_i_mutex is automatically released when lock
// goes out of scope
}

int main()
{
std::cout << "g_i: " << g_i << "; in main()\n";

std::thread t1(safe_increment);
std::thread t2(safe_increment);

t1.join();
t2.join();

std::cout << "g_i: " << g_i << "; in main()\n";
}

原子操作

C++ 中使用 std::atomic 完成原子操作.简单的例子如下:

1
2
std::atomic<int> x(0);
++x;

1.5 如何继续阅读本书

前面的铺垫也太长了,还是过于详细了……

1.6 使用和理解代码

传递 mullptr 来表示参数不可用或不该用,此时总是使用指针.

在当下 CPU 架构上最慢的数运算是除法、平方根和三角函数.

1.7 基于物理的渲染简史

基于物理的蒙特卡罗渲染方法成功用于制作的一大原因是它们最终提高了艺术家们的生产力.通过调整采样次数来快速获取缩略图;采用能量未必守恒的反射模型时反射参数可能需要在每个光照环境下都要调整; 光线追踪计算的阴影质量比栅格化方法好得多.

1.8 扩展阅读

无重要内容.

编译和运行

书中为 pbrt-v3 提供的场景文件在此 3.7 GB tar.gz 文件 中.images 里有渲染好的图像.

在 Windows 上安装和使用 C++ pbrt-v3

1
2
3
4
5
6
scoop install cmake
git clone --recursive "https://github.com/mmp/pbrt-v3/" --depth=1
cd pbrt-v3
mkdir build
cd build
cmake ..

若成功,则会有一 .sln 文件。用 Visual Studio 打开,构建 BUILD_ALL。若编译成功,则会提示

1
2
3
4
5
6
7
30>spectrum.cpp
30>正在生成代码...
30>pbrt_test.vcxproj -> …\pbrt-v3\build\Release\pbrt_test.exe
30>已完成生成项目“pbrt_test.vcxproj”的操作.
34>------ 已启动生成: 项目: ALL_BUILD, 配置: Release x64 ------
34>Building Custom Rule …/pbrt-v3/CMakeLists.txt
========== “生成”: 34 成功,0 失败,0 更新,0 已跳过 ==========

可以在 build 目录下的 Release(或 Debug)文件夹中找到 pbrt.exe

使用 rs_pbrt 渲染文件

我先 cargo build -r 进行 release build,然后通过

1
E:\code\rs_pbrt\target\release\rs_pbrt.exe "E:\downloads\pbrt-v3-scenes\killeroos\killeroo-simple.pbrt"

绝对路径使用会报错.其原因是 .pbrt 文件中有 Include 语句,在 Include "geometry/killeroo.pbrt" 时遇到相对路径,拼接的 parent 目录为 env::current_dir()cdpbrt-v3-scenes/killeroos/,参数填写绝对路径依然会有路径拼接错误.解决方法是填写相对路径.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
❯ E:\downloads\pbrt-v3-scenes\killeroos> E:\code\rs_pbrt\target\release\rs_pbrt.exe "killeroo-simple.pbrt"
pbrt version 0.9.8 (unknown) [Detected 24 cores]
Copyright (c) 2016-2022 Jan Douglas Bert Walter.
Rust code based on C++ code by Matt Pharr, Greg Humphreys, and Wenzel Jakob.
opening file FILE = killeroo-simple.pbrt
Film "image"
"string filename" ["killeroo-simple.exr"]
"integer xresolution" [700]
"integer yresolution" [700]
Sampler "sobol"
"integer pixelsamples" [64]
Integrator "directlighting"
Include "E:\\downloads\\pbrt-v3-scenes\\killeroos\\geometry/killeroo.pbrt"
opening file FILE = E:\downloads\pbrt-v3-scenes\killeroos\geometry/killeroo.pbrt
Include "E:\\downloads\\pbrt-v3-scenes\\killeroos\\geometry/killeroo.pbrt"
opening file FILE = E:\downloads\pbrt-v3-scenes\killeroos\geometry/killeroo.pbrt
Integrator "directlighting"
Rendering with 24 thread(s) ...
1936 / 1936 [======================================================================================================================================] 100.00 % 14.81/s
Writing image "pbrt.png" with bounds Bounds2i { p_min: Point2i { x: 0, y: 0 }, p_max: Point2i { x: 700, y: 700 } } 26.76 % 36.11/s 39s

渲染的图片在当前工作目录下.

渲染图 pbrt.png

数据截止到 2022 年 12 月 3 日,原记载于 Logseq。写这个是由 此文章 启发。

  • Archive.ph
  • Baidu.com
  • Calendar.google.com
  • Deepl.com
  • E[内网地址,数据删除]
  • Flypy.com
  • G[内网地址,数据删除]
  • Hkuri.cneas.tohoku.ac.jp/project1/manchu/list?groupId=11
  • Icourse163.org
  • J[内网地址,数据删除]
  • Kns.cnki.net/kns8/defaultresult/index
  • Leetcode.com
  • Mastodon.social
  • Notion.so
  • O3o.ca
  • Portal.office.com
  • Q[数据删除].github.io
  • Reverso.net/文字翻译
  • S[内网地址,数据删除]
  • Twitter.com/home
  • U.unipus.cn
  • V2ex.com
  • Wolframalpha.com
  • blog.Xinshijiededa.men
  • Youtube.com/watch?v=[数据删除]
  • Zhihu.com

每到年底,各种网站或应用的年度总结接踵而来,今年尤其地多,似乎一年的生活完全可以用统计数据来概括。但是也没有心情,抑或是勇气,像我看到的其他许多博主一样,直面过去的一年,写一些类似 馬特市年度問卷 的回想。或许是没到合适的时间,或许是没有合适的内容……无妨。让巨细靡遗的数据来说话吧。

多邻国

在 2022 年,我学习了🇰🇷韩语、🇩🇪德语等 13 种语言,共计学习了 1572 分钟,习得了 988 个单词,练习了 2585 个句子,坚持了 128 天的 🔥 streak;获得了 5936 经验,全球排名前 8%。

详细信息,请看 我的年度报告

Steam

在 2022 年,我玩了 14 款游戏,解锁了 64 项成就。玩的时间最长的是《八方旅人》,占全部游戏时间的 61%,启动了 88 次会话。我还在 夜之城流连了许久,也意识到 猫比人好。

详细信息,请看 我的 2022 年 STEAM 回顾

力扣(LCUS)

在 2022 年,我连续 124 天完成了🔥力扣每日一题LeetCode Daily Challenge,获得了 9 至 12 月的 Daily Challenge Badge 和 100 Days Badge 2022。在所有比赛中,最好的 ranking 为 1,635。

更多信息,请看 首页 | 首页(深色模式) 存档。

WakaTime

在 2022 年,我总计使用了 783 小时的编辑器,平均每日 2 小时 14 分钟,在 WakaTime 所有用户中排行前 4%。我最活跃的一天是 4 月 29 日,平均每周最活跃的一天是周日;使用最多的编辑器是 VS Code,使用最多的系统是 Windows,使用最多的语言是 Rust 和 Markdown。

详细信息,请查看我的 Code Stats for 2022

GitHub

Contributions

#GitHubUnwrapped

我的 2022 观影记录

本列表统计的观看情况至 2023 年 1 月 1 日 0 时 0 分 0 秒(1672502400)止。

效果

本文测试通过的 Tauri 版本为 1.01.2

鼠标穿透

依赖

cargo.toml 中添加

1
2
3
4
windows = { version = "0.43.0", features = [
"Win32_Foundation",
"Win32_UI_WindowsAndMessaging",
] }

代码

初始化窗口时通过 setup 传闭包拿到 window 进行操作。只支持 Windows。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
tauri::Builder::default()
.setup(|app| {
let window = app.get_window("main").unwrap();
#[cfg(windows)]
{
use windows::Win32::Foundation::HWND;
let hwnd = window.hwnd().unwrap().0;
let hwnd = HWND(hwnd);
unsafe {
let mut style_ex = WINDOW_EX_STYLE(GetWindowLongW(hwnd, GWL_EXSTYLE) as u32);
style_ex |= WS_EX_APPWINDOW // for taskbar
| WS_EX_COMPOSITED
| WS_EX_LAYERED
| WS_EX_TRANSPARENT
| WS_EX_TOPMOST;
use windows::Win32::UI::WindowsAndMessaging::*;
let nindex = GWL_EXSTYLE;
let _pre_val = SetWindowLongA(hwnd, nindex, style_ex.0 as i32);
}
}
Ok(())
})
.invoke_handler(tauri::generate_handler![...])
.run(tauri::generate_context!())
.expect("error while running tauri application");

官方的实现目前还没有进入 tauri,只能先凑合用用。

Hi, since tauri-apps/tao#421 was merged into tao, can that function be exposed in tauri's api?

透明窗口

窗口设置

为了实现透明窗口,还需要修改 tauri.config.json。当然,直接修改 window 或者通过 tauri::Builder 应该也是可以的。

1
2
3
4
5
6
7
8
9
10
11
"tauri": {
,
"windows": [
{
"fullscreen": true,
"resizable": false,
"transparent": true,
"alwaysOnTop": true
}
]
}

CSS

另外,不要忘记修改需要透明的地方的 CSS。如果你使用了 SvelteKit 模板,可以在 +layout.svelte 中添加:

1
2
3
4
5
<style>
main {
background: transparent;
}
</style>

以上。🪟

ᠮᠠᠨᠵᡠᡳ ᠮᠠᡶᠠᡵᡳᡳ ᠠᡩᠠᠯᡳ
満洲の 祖先の ように
像满洲的先民们一样

ᠮᡠᠰᡝ ᠰᠠᠰᠠ ᡤᠠᠪᡨᠠᠮᠪᡳ
僕達 一緒に 弓を射よう
咱们一起射箭吧

ᠣᡳᠯᠣᡥᠣᠨ ᡩᠠᠪᡩᡠᡵᡳ ᠵᠠᠯᠠᠨ ᡩᡝ
軽薄な 世界の中 で
在浮躁的人世间

ᠰᡳᠨᡳ ᠪᡠᠶᠠ ᠪᡝᠶᡝ ᠪᡳ
あなたの 小さな 姿が ある
有你渺小的身影

ᠮᠠᠨᠵᡠᡳ ᠮᠠᡶᠠᡵᡳᡳ ᠠᡩᠠᠯᡳ
満洲の 祖先の ように
像满洲的先民们一样

ᠮᡠᠰᡝ ᠰᠠᠰᠠ ᡤᠠᠪᡨᠠᠮᠪᡳ
僕達 一緒に 弓を射よう
咱们一起射箭吧

ᠣᡳᠯᠣᡥᠣᠨ ᡩᠠᠪᡩᡠᡵᡳ ᠵᠠᠯᠠᠨ ᡩᡝ
軽薄な 世界の中 で
在浮躁的人世间

ᠮᡳᠨᡳ ᠪᡠᠶᠠ ᠪᡝᠶᡝ ᠪᡳ
僕の 小さな 姿が ある
有我渺小的身影

ᠰᡳᠨᡳ ᠪᡝᡵᡳ ᡩᡝ ᡠᠯᡳ ᠪᡝ ᡨᡝᠪᡠᡥᡝᠣ?
あなたの 弓は 張っているか?
你的弓上弦了吗?

ᠰᡳᠮᡥᡠᠨ ᡩᡝ ᡶᡝᡵᡤᡝᡨᡠᠨ ᠪᡝ ᡝᡨᡠᡥᡝᠣ?
親指 に 搬指 を 掛けているか?
扳指戴在拇指上了吗?

ᠰᡳᠨᡳ ᠰᡳᡵᡩᠠᠨ ᠪᡝ ᡤᠠᠪᡨᠠᡥᠠᠣ?
あなたの 矢 を 放ったか?
你的箭射出了吗?

ᡥᠣᡳᡥᠠᠨ ᠶᠠ ᠪᠠ ᡩᡝ ᠪᡳᠨᡳ?
囲場は どこ?
围场在哪里呢?

ᠮᡳᠨᡳ ᠪᡝᡵᡳ ᡩᡝ ᡠᠯᡳ ᠪᡝ ᡨᡝᠪᡠᡥᡝᠪᡳ
私の 弓は 張られている
我的弓已上弦

ᠰᡳᠮᡥᡠᠨ ᡩᡝ ᡶᡝᡵᡤᡝᡨᡠᠨ ᠪᡝ ᡝᡨᡠᡥᡝᠪᡳ
親指 に 搬指 を 掛けている
扳指已戴在拇指

ᠮᡳᠨᡳ ᠰᡳᡵᡩᠠᠨ ᠪᡝ ᡤᠠᠪᡨᠠᡥᠠᠪᡳ
僕の 矢 を 放った
我的箭已射出

ᡥᠣᡳᡥᠠᠨ ᠶᠠ ᠪᠠ ᡩᡝ ᠪᡳᠨᡳ?
囲場は どこ?
围场在哪里呢?

ᠮᡠᠰᡝ ᠰᠠᠰᠠ ᡤᠠᠪᡨᠠᠴᡳᠨᠠ ᡤᠠᠪᡨᠠᠴᡳᠨᠠ
僕達 一緒に 弓を射ろう 弓を射ろう
咱们一起射箭吧、射箭吧

ᠮᡠᠰᡝ ᠰᠠᠰᠠ ᡤᠠᠪᡨᠠᠴᡳᠨᠠ ᡤᠠᠪᡨᠠᠴᡳᠨᠠ
僕達 一緒に 弓を射ろう 弓を射ろう
咱们一起射箭吧、射箭吧

ᠮᠠᠨᠵᡠᡳ ᠮᠠᡶᠠᡵᡳᡳ ᠠᡩᠠᠯᡳ
満洲の 祖先の ように
像满洲的先民们一样

ᠮᡠᠰᡝ ᠰᠠᠰᠠ ᡤᠠᠪᡨᠠᠮᠪᡳ
僕達 一緒に 弓を射よう
咱们一起射箭吧

ᠣᡳᠯᠣᡥᠣᠨ ᡩᠠᠪᡩᡠᡵᡳ ᠵᠠᠯᠠᠨ ᡩᡝ
軽薄な 世界の中 で
在浮躁的人世间

ᠰᡳᠨᡳ ᠪᡠᠶᠠ ᠪᡝᠶᡝ ᠪᡳ
あなたの 小さな 姿が ある
有你渺小的身影

ᠮᠠᠨᠵᡠᡳ ᠮᠠᡶᠠᡵᡳᡳ ᠠᡩᠠᠯᡳ
満洲の 祖先の ように
像满洲的先民们一样

ᠮᡠᠰᡝ ᠰᠠᠰᠠ ᡤᠠᠪᡨᠠᠮᠪᡳ
僕達 一緒に 弓を射よう
咱们一起射箭吧

ᠣᡳᠯᠣᡥᠣᠨ ᡩᠠᠪᡩᡠᡵᡳ ᠵᠠᠯᠠᠨ ᡩᡝ
軽薄な 世界の中 で
在浮躁的人世间

ᠮᡳᠨᡳ ᠪᡠᠶᠠ ᠪᡝᠶᡝ ᠪᡳ
僕の 小さな 姿が ある
有我渺小的身影

ᠰᡳᠨᡳ ᠪᡝᡵᡳ ᡩᡝ ᡠᠯᡳ ᠪᡝ ᡨᡝᠪᡠᡥᡝᠣ?
あなたの 弓は 張っているか?
你的弓上弦了吗?

ᠰᡳᠮᡥᡠᠨ ᡩᡝ ᡶᡝᡵᡤᡝᡨᡠᠨ ᠪᡝ ᡝᡨᡠᡥᡝᠣ?
親指 に 搬指 を 掛けているか?
扳指戴在拇指上了吗?

ᠰᡳᠨᡳ ᠰᡳᡵᡩᠠᠨ ᠪᡝ ᡤᠠᠪᡨᠠᡥᠠᠣ?
あなたの 矢 を 放ったか?
你的箭射出了吗?

ᡥᠣᡳᡥᠠᠨ ᡨᡠᠪᠠ ᡩᡝ ᠪᡳᠨᡳ
囲場は あそこ だ
围场在那里

ᠮᡳᠨᡳ ᠪᡝᡵᡳ ᡩᡝ ᡠᠯᡳ ᠪᡝ ᡨᡝᠪᡠᡥᡝᠪᡳ
私の 弓は 張られている
我的弓已上弦

ᠰᡳᠮᡥᡠᠨ ᡩᡝ ᡶᡝᡵᡤᡝᡨᡠᠨ ᠪᡝ ᡝᡨᡠᡥᡝᠪᡳ
親指 に 搬指 を 掛けている
扳指已戴在拇指

ᠮᡳᠨᡳ ᠰᡳᡵᡩᠠᠨ ᠪᡝ ᡤᠠᠪᡨᠠᡥᠠᠪᡳ
僕の 矢 を 放った
我的箭已射出

ᡥᠣᡳᡥᠠᠨ ᡨᡠᠪᠠ ᡩᡝ ᠪᡳᠨᡳ
囲場は あそこ だ
围场在那里

ᠮᡠᠰᡝ ᠰᠠᠰᠠ ᡤᠠᠪᡨᠠᠴᡳᠨᠠ ᡤᠠᠪᡨᠠᠴᡳᠨᠠ
僕達 一緒に 弓を射ろう 弓を射ろう
咱们一起射箭吧、射箭吧

ᠮᡠᠰᡝ ᠰᠠᠰᠠ ᡤᠠᠪᡨᠠᠴᡳᠨᠠ ᡤᠠᠪᡨᠠᠴᡳᠨᠠ
僕達 一緒に 弓を射ろう 弓を射ろう
咱们一起射箭吧、射箭吧

ᠰᡳᠨᡳ ᠪᡝᡵᡳ ᡩᡝ ᡠᠯᡳ ᠪᡝ ᡨᡝᠪᡠᡥᡝᠪᡳ
あなたの 弓は 張られている
你的弓已上弦

ᠰᡳᠮᡥᡠᠨ ᡩᡝ ᡶᡝᡵᡤᡝᡨᡠᠨ ᠪᡝ ᡝᡨᡠᡥᡝᠪᡳ
親指 に 搬指 を 掛けている
扳指已戴在拇指

ᠰᡳᠨᡳ ᠰᡳᡵᡩᠠᠨ ᠪᡝ ᡤᠠᠪᡨᠠᡥᠠᠪᡳ
あなたの 矢 を 放った
你的箭已射出

ᡥᠣᡳᡥᠠᠨ ᠶᠠ ᠪᠠ ᡩᡝ ᠪᡳᠨᡳ?
囲場は どこ?
围场在哪里呢?

ᠮᡳᠨᡳ ᠪᡝᡵᡳ ᡩᡝ ᡠᠯᡳ ᠪᡝ ᡨᡝᠪᡠᡥᡝᠪᡳ
私の 弓は 張られている
我的弓已上弦

ᠰᡳᠮᡥᡠᠨ ᡩᡝ ᡶᡝᡵᡤᡝᡨᡠᠨ ᠪᡝ ᡝᡨᡠᡥᡝᠪᡳ
親指 に 搬指 を 掛けている
扳指已戴在拇指

ᠮᡳᠨᡳ ᠰᡳᡵᡩᠠᠨ ᠪᡝ ᡤᠠᠪᡨᠠᡥᠠᠪᡳ
僕の 矢 を 放った
我的箭已射出

ᡥᠣᡳᡥᠠᠨ ᠶᠠ ᠪᠠ ᡩᡝ ᠪᡳᠨᡳ?
囲場は どこ?
围场在哪里呢?

ᠮᠠᠨᠵᡠᡳ ᠮᠠᡶᠠᡵᡳᡳ ᠠᡩᠠᠯᡳ
満洲の 祖先の ように
像满洲的先民们一样

ᠮᡠᠰᡝ ᠰᠠᠰᠠ ᡤᠠᠪᡨᠠᠮᠪᡳ
僕達 一緒に 弓を射よう
咱们一起射箭吧

ᠣᡳᠯᠣᡥᠣᠨ ᡩᠠᠪᡩᡠᡵᡳ ᠵᠠᠯᠠᠨ ᡩᡝ
軽薄な 世界の中 で
在浮躁的人世间

ᠰᡳᠨᡳ ᠪᡠᠶᠠ ᠪᡝᠶᡝ ᠪᡳ
あなたの 小さな 姿が ある
有你渺小的身影

ᠰᡳᠨᡳ ᠪᡠᠶᠠ ᠪᡝᠶᡝ ᠪᡳ
あなたの 小さな 姿が ある
有你渺小的身影


穆麟德转写

manju-i mafari-i adali
満洲の 祖先の ように
像满洲的先民们一样

muse sasa gabtambi
僕達 一緒に 弓を射よう
咱们一起射箭吧

oilohon dabduri jalan de
軽薄な 世界の中 で
在浮躁的人世间

sini buya beye bi
あなたの 小さな 姿が ある
有你渺小的身影

manju-i mafari-i adali
満洲の 祖先の ように
像满洲的先民们一样

muse sasa gabtambi
僕達 一緒に 弓を射よう
咱们一起射箭吧

oilohon dabduri jalan de
軽薄な 世界の中 で
在浮躁的人世间

mini buya beye bi
僕の 小さな 姿が ある
有我渺小的身影

sini beri de uli be tebuheo?
あなたの 弓は 張っているか?
你的弓上弦了吗?

simhun de fergetun be etuheo?
親指 に 搬指 を 掛けているか?
扳指戴在拇指上了吗?

sini sirdan be gabtahao?
あなたの 矢 を 放ったか?
你的箭射出了吗?

hoihan ya ba de bini?
囲場は どこ?
围场在哪里呢?

mini beri de uli be tebuhebi
私の 弓は 張られている
我的弓已上弦

simhun de fergetun be etuhebi
親指 に 搬指 を 掛けている
扳指已戴在拇指

mini sirdan be gabtahabi
僕の 矢 を 放った
我的箭已射出

hoihan ya ba de bini?
囲場は どこ?
围场在哪里呢?

muse sasa gabtacina gabtacina
僕達 一緒に 弓を射ろう 弓を射ろう
咱们一起射箭吧、射箭吧

muse sasa gabtacina gabtacina
僕達 一緒に 弓を射ろう 弓を射ろう
咱们一起射箭吧、射箭吧

manju-i mafari-i adali
満洲の 祖先の ように
像满洲的先民们一样

muse sasa gabtambi
僕達 一緒に 弓を射よう
咱们一起射箭吧

oilohon dabduri jalan de
軽薄な 世界の中 で
在浮躁的人世间

sini buya beye bi
あなたの 小さな 姿が ある
有你渺小的身影

manju-i mafari-i adali
満洲の 祖先の ように
像满洲的先民们一样

muse sasa gabtambi
僕達 一緒に 弓を射よう
咱们一起射箭吧

oilohon dabduri jalan de
軽薄な 世界の中 で
在浮躁的人世间

mini buya beye bi
僕の 小さな 姿が ある
有我渺小的身影

sini beri de uli be tebuheo?
あなたの 弓は 張っているか?
你的弓上弦了吗?

simhun de fergetun be etuheo?
親指 に 搬指 を 掛けているか?
扳指戴在拇指上了吗?

sini sirdan be gabtahao?
あなたの 矢 を 放ったか?
你的箭射出了吗?

hoihan tuba de bini
囲場は あそこ だ
围场在那里

mini beri de uli be tebuhebi
私の 弓は 張られている
我的弓已上弦

simhun de fergetun be etuhebi
親指 に 搬指 を 掛けている
扳指已戴在拇指

mini sirdan be gabtahabi
僕の 矢 を 放った
我的箭已射出

hoihan tuba de bini
囲場は あそこ だ
围场在那里

muse sasa gabtacina gabtacina
僕達 一緒に 弓を射ろう 弓を射ろう
咱们一起射箭吧、射箭吧

muse sasa gabtacina gabtacina
僕達 一緒に 弓を射ろう 弓を射ろう
咱们一起射箭吧、射箭吧

sini beri de uli be tebuhebi
あなたの 弓は 張られている
你的弓已上弦

simhun de fergetun be etuhebi
親指 に 搬指 を 掛けている
扳指已戴在拇指

sini sirdan be gabtahabi
あなたの 矢 を 放った
你的箭已射出

hoihan ya ba de bini?
囲場は どこ?
围场在哪里呢?

mini beri de uli be tebuhebi
私の 弓は 張られている
我的弓已上弦

simhun de fergetun be etuhebi
親指 に 搬指 を 掛けている
扳指已戴在拇指

mini sirdan be gabtahabi
僕の 矢 を 放った
我的箭已射出

hoihan ya ba de bini?
囲場は どこ?
围场在哪里呢?

manju-i mafari-i adali
満洲の 祖先の ように
像满洲的先民们一样

muse sasa gabtambi
僕達 一緒に 弓を射よう
咱们一起射箭吧

oilohon dabduri jalan de
軽薄な 世界の中 で
在浮躁的人世间

sini buya beye bi
あなたの 小さな 姿が ある
有你渺小的身影

sini buya beye bi
あなたの 小さな 姿が ある
有你渺小的身影🏹

Chrome 开发者工具的「网络」面板可以查看所有的网络请求。fetch 发送的请求不会出现在「源代码」面板中,只能通过「网络」里下载。讨厌的是,Chrome 没有提供在列表内直接下载对应的响应的功能,双击对应的请求仍然只会在新标签页重新发送请求,而不是下载已抓取的资源。若远程服务器上的资源已失效,则无法成功下载。虽然可以直接通过「响应」查看内容,但若请求是 binary,则可能包含非法的 Unicode 位点,无法通过复制粘贴将内容正确地保存到本地。

此时,可以通过右键任意一请求,点选菜单最下方「以 HAR 格式保存所有内容」或点击如下所示的下载按钮保存 HAR。

保存 HAR

HAR 实质是 JSON。通过文本编辑器打开下载的 HAR 文件,依据「标头」-「响应标头」中的 Content-Length 查找到对应的请求内容,即可在 content > text 中看到 Base64 编码的 response 内容。

此时我们可以安心地将内容复制出来,解码即可。推荐使用命令行工具。

1
2
3
4
5
6
7
8
9
import base64
from codecs import open

with open("b64.txt", "r") as f:
content = base64.b64decode(f.read())
print(content)

with open("out.txt", "wb") as f:
f.write(content)

注意若用 Python 处理,写入文件的内容不一定是合法字符串,故 open 的参数需要为 wb,否则会有 TypeError: write() argument must be str, not bytes

0%