多彩灯光算法

#灯光 #颜色 #算法

你的键盘,鼠标显卡,有没有带 RGB 灯?你是否好奇过这些灯光的变化是如何实现的?

RGB 灯效实现无非两种,一种是设备芯片实时运算,一种是搭配软件,在电脑计算每个灯珠的 RGB 传输到设备显示。通常前者可以显示的灯效比较简单,比如无动态的灯效,或是明暗呼吸,统一颜色渐变。而后者则通常用于较为复杂的灯光显示,比如波浪,波纹等等不同位置的灯珠需要控制不同颜色的场景。雷蛇键盘就是采用此方案。

那么是否有方法可以即做到不依赖于电脑的软件,又能实现复杂的灯光效果呢?在 2015 年的夏日,我就被这个问题困扰了很久。彼时公司新开案一个键盘产品,并提出希望用户在通过软件完成设置后关闭软件,也可以继续播放所设置的复杂灯效。

后来还是在那个夏日的某个周末午后,我灵感乍现,想出了这个灯光算法,可以使用极小的内存代价和简单的计算,就可以在固件中实现复杂的灯光算法。

灯效

我们可以先来看看灯效是怎么一回事儿,灯效的变化其实主要体现在颜色和亮度两个方面。通过对灯光的颜色和亮度进行调节,我们可以实现各种各样的灯光效果。而亮度,其实也是颜色的一部分。也就是通过颜色的变化,变化的速率,持续的时间,就可以构成许多单一灯效,比如恒亮,呼吸,闪烁,多色循环等等。

比如呼吸,就是从某种颜色到黑色(无色)的渐变,闪烁就是持续一段时间的某个颜色,和一段更长时间的黑色(无色)交替。多色循环则是多个颜色之间的渐变循环。

渐变

除了闪烁是直接切换颜色,其他两种都涉及到颜色的渐变。那么要如何计算颜色的渐变呢?

假设我们有两种颜色 A 和 B,它们的 RGB 值分别为 C1(R1, G1, B1)C2(R2, G2, B2),C1 通过 n 次变化成为 C2,那么第 k 次变化的颜色 Ck 可以通过以下公式计算:

Ck = (R1 + (R2 - R1) * k / n, G1 + (G2 - G1) * k / n, B1 + (B2 - B1) * k / n)

也就是直接将两个颜色的 RGB 分量的差值按比例分配到每次变化中就可以了。这样就可以形成一个渐变的颜色列表。

渐变

图案

而如果要进一步,实现更复杂的灯效,每个灯珠单独控制,就需要图案了,这样就可以实现诸如波浪,波纹,流动,反应等等。比如波浪效果,可以通过对每个灯珠的颜色进行周期性的变化来实现。这也和设备灯珠的分布相关,假设是一个 7 x 20 的矩阵,那么要实现波浪效果,可以使同一行的灯珠设置相同的颜色,而下一行灯珠是渐变颜色的列表的下一个颜色,如此循环变化,就可以形成波浪的效果。

矩阵

理解这一点,就会发现:只要有两组数据就可以实现复杂的灯效效果了——颜色列表与组成图案的颜色索引。颜色列表决定了灯效播放的颜色,而索引则决定了每个灯珠在每一帧的颜色。播放灯效只要每次把所有灯珠的颜色索引 + 1,就可以实现动态的灯效了。通过设置不同的颜色索引,就可以实现各种各样的灯效。下面是一个用 Web 实现的灯效例子:

按下 Play 就会开始播放灯效,切换 Play Mode 可以切换不同的灯效模式。查看 main.jssetItems 函数可以看到,不同的灯效模式对应不同的颜色索引设置方法。

play 函数则通过一个定时器执行 updateItems 每个时间单位将所有的颜色索引 + 1,从而实现动态的灯效。通过控制定时器的间隔时间,可以实现不同的灯效播放速度。

如此,便可以使用极低的内存消耗和简单的计算,在固件中实现复杂的灯光效果了。