16 色夜景调色板:为什么不是把颜色调暗
一张 16 色的夜色调色板,以及背后的三条规则:色相偏移、明度分层、局部光源。附配色表与在 Godot 里做调色板替换的写法。
新手做夜晚场景,第一反应是把白天画面整体调暗。结果通常是:灰、脏、分不清层次。夜晚不是「亮度更低」,而是光源变了——只有一盏灯、一个月亮,其他东西靠反射和轮廓活下来。
16 色,够用
这套色板用在像素猫小屋里,从地板到灯笼全靠它。不是标准答案,但它有一个好处:任意两色放在一起都不会脏。
| # | 色值 | 名字 | 主要用途 |
|---|---|---|---|
| 1 | #0B0F1C | 深夜 0 | 最暗的背景、场景外框 |
| 2 | #161B2E | 深夜 1 | 墙面阴影、家具暗面 |
| 3 | #232A44 | 深夜 2 | 地板、夜间描边 |
| 4 | #3B4368 | 蓝灰 | 远处的物体、冷反射 |
| 5 | #5A648F | 雾蓝 | 月光照到的冷面 |
| 6 | #8C9BC4 | 月白 | 高光边缘、玻璃反光 |
| 7 | #0E2A28 | 暗绿 | 植物暗部 |
| 8 | #1E5C4A | 松绿 | 叶子、猫眼底色 |
| 9 | #37A57E | 薄荷 | 主强调色,健康与成功 |
| 10 | #7CE0B8 | 荧光薄荷 | 灯光核心、选中态 |
| 11 | #4A2A2E | 砖红 | 木头暗部 |
| 12 | #8F4A3C | 陶红 | 箱子、地毯 |
| 13 | #FF9FBE | 樱花粉 | 猫耳内、腮红、第二个强调色 |
| 14 | #FFC85C | 灯黄 | 唯一的暖光源,灯笼与月光反射 |
| 15 | #FFF3D6 | 奶油 | 猫的毛、纸、最高光 |
| 16 | #120D1A | 紫黑 | 描边最深一档,替代纯黑 |
规则一:色相跟着光走
同一个物体,亮面和暗面的色相要朝光源方向和环境方向各自偏移,只改明度就会变成灰阶。
- 暖光(灯黄
#FFC85C)照射:向黄橙偏移 - 冷环境(月白
#8C9BC4):向蓝紫偏移 - 交界处:降低饱和度,而不是加黑
举例,木箱的三个面:暗面 #4A2A2E、中间 #8F4A3C、受光面往 #FFC85C 的方向走。注意受光面并不是「更亮的红」,而是偏黄的橙。
规则二:明度分三层,不超过三层
像素画在 48×48 以内,一个物体最多三层明度就够。第四层会让轮廓消失。大物体(比如地板)可以破例到四层,但要保证最亮层只占很小面积。
规则三:光要有来源,并且只给一个
整张图只允许一个主光源。我的场景里是右下角那盏灯笼,addLight() 只调用两次:一次给灯笼核心,一次给地面的大范围衰减。
// 简化版:距离平方衰减 + 暖色混合
function addLight(buf, lx, ly, radius, tint, strength) {
for (let y = 0; y < buf.h; y++) {
for (let x = 0; x < buf.w; x++) {
const d = Math.hypot(x - lx, y - ly) / radius;
if (d >= 1) continue;
const k = (1 - d) * (1 - d) * strength;
blend(buf, x, y, tint, k);
}
}
}用平方衰减而不是线性衰减,边缘会更自然。半径不要超过画面短边的 60%,否则整张图会发灰。
抖动:只在渐变处用
两种颜色之间需要过渡时,用 2×2 棋盘抖动,不要用第三色去「和」。抖动密度最好只有两档(25%、50%),三档以上在低分辨率下会变成噪点。
在 Godot 里换色板
如果你用索引色画图,可以在 shader 里做运行时换色板:
shader_type canvas_item;
uniform sampler2D palette : filter_nearest;
uniform float palette_size = 16.0;
void fragment() {
vec4 c = texture(TEXTURE, UV);
float idx = c.r * 255.0 / 16.0; // 把 R 通道当作索引
COLOR = texture(palette, vec2((idx + 0.5) / palette_size, 0.5));
}这样白天、夜晚、中毒、回忆等状态只需要换一张 16×1 的色板贴图,不用重画所有素材。
最后一条经验
把画面转成灰度看一眼。如果灰度下依然能分清「猫、地板、灯笼、门」,这套配色就成立;如果灰度下一团糊,说明你只是在靠颜色区分物体,而颜色在夜里是最先丢失的东西。