iOS 16壁纸设置中DUOTONE与COLOR WASH滤镜算法实现问询
DUOTONE与COLOR WASH滤镜的实现方法
一、GLSL着色器实现
1. DUOTONE(双色调)滤镜
双色调滤镜通过将图像灰度值映射为两种指定颜色的渐变实现:暗部使用一种颜色,亮部使用另一种颜色,中间灰度按比例混合两种颜色。结合你提供的黑白滤镜示例,以下是具体实现:
#version 300 es precision mediump float; uniform sampler2D uTextureUnit; // 暗部颜色参数 uniform vec3 uShadowColor; // 亮部颜色参数 uniform vec3 uHighlightColor; in vec2 vTexCoord; out vec4 vFragColor; void main() { vec4 textureColor = texture(uTextureUnit, vTexCoord); // 计算像素灰度值(采用ITU-R BT.601标准权重) float gray = dot(textureColor.rgb, vec3(0.299, 0.587, 0.114)); // 按灰度值混合暗部与亮部颜色 vec3 duotoneColor = mix(uShadowColor, uHighlightColor, gray); vFragColor = vec4(duotoneColor, textureColor.a); }
你可以通过修改uShadowColor和uHighlightColor参数调整配色,比如暗部设为深蓝、亮部设为暖黄,就能得到经典复古双色调效果。
2. COLOR WASH(色彩冲洗)滤镜
色彩冲洗滤镜的核心是给图像叠加统一色调,同时保留原图像的明暗层次,常见于复古胶片风格。以下是两种常用实现方式:
方式一:灰度映射式色彩冲洗
直接用原图像灰度值缩放目标色调,保留明暗细节:
#version 300 es precision mediump float; uniform sampler2D uTextureUnit; // 目标冲洗色调 uniform vec3 uWashColor; in vec2 vTexCoord; out vec4 vFragColor; void main() { vec4 textureColor = texture(uTextureUnit, vTexCoord); float gray = dot(textureColor.rgb, vec3(0.299, 0.587, 0.114)); // 用灰度值控制色调亮度,保留明暗层次 vec3 washColor = uWashColor * gray; vFragColor = vec4(washColor, textureColor.a); }
方式二:色相偏移式色彩冲洗
通过调整色相统一图像色系,同时保留原色彩饱和度与亮度:
#version 300 es precision mediump float; uniform sampler2D uTextureUnit; // 色相偏移值(范围0~1,对应0~360度) uniform float uHueShift; in vec2 vTexCoord; out vec4 vFragColor; // RGB转HSV辅助函数 vec3 rgb2hsv(vec3 c) { vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); float d = q.x - min(q.w, q.y); float e = 1.0e-10; return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); } // HSV转RGB辅助函数 vec3 hsv2rgb(vec3 c) { vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } void main() { vec4 textureColor = texture(uTextureUnit, vTexCoord); vec3 hsv = rgb2hsv(textureColor.rgb); // 偏移色相,保留饱和度与明度 hsv.x = fract(hsv.x + uHueShift); vec3 washColor = hsv2rgb(hsv); vFragColor = vec4(washColor, textureColor.a); }
二、LUT文件的生成与使用
LUT(色彩查找表)是预计算色彩映射的方案,适合复杂滤镜效果的快速复用与调整。
1. 生成LUT文件
方法一:Photoshop可视化制作
- 创建标准LUT模板:可使用16x16x16的3D LUT模板(行业通用),或256x256的2D LUT模板。
- 应用滤镜效果:
- 双色调:给模板添加「双色调」调整层,设置暗部与亮部颜色。
- 色彩冲洗:添加「色彩平衡」「色相/饱和度」调整层,或用纯色叠加层(混合模式选「颜色」或「柔光」)。
- 导出LUT:将调整后的模板导出为
.cube格式(3D LUT标准格式),或.png格式的2D LUT。
方法二:代码批量生成(Python示例)
通过代码自定义算法生成LUT,适合批量或复杂效果:
import numpy as np # 生成16x16x16的3D LUT lut_size = 16 lut = np.zeros((lut_size, lut_size, lut_size, 3), dtype=np.float32) # 定义双色调配色 shadow_color = np.array([0.1, 0.2, 0.4]) # 深蓝 highlight_color = np.array([0.9, 0.7, 0.3]) # 暖黄 for b in range(lut_size): for g in range(lut_size): for r in range(lut_size): # 计算当前RGB值的灰度 gray = 0.299 * (r / lut_size) + 0.587 * (g / lut_size) + 0.114 * (b / lut_size) # 混合双色调颜色 lut[b, g, r] = shadow_color * (1 - gray) + highlight_color * gray # 保存为.cube格式 with open("duotone_lut.cube", "w") as f: f.write("TITLE \"Duotone Filter LUT\"\n") f.write(f"LUT_3D_SIZE {lut_size}\n") for b in range(lut_size): for g in range(lut_size): for r in range(lut_size): f.write(f"{lut[b,g,r,0]:.6f} {lut[b,g,r,1]:.6f} {lut[b,g,r,2]:.6f}\n")
2. GLSL中使用LUT
将生成的LUT文件加载为纹理后,即可在着色器中采样使用:
#version 300 es precision mediump float; uniform sampler2D uTextureUnit; uniform sampler2D uLUTTexture; in vec2 vTexCoord; out vec4 vFragColor; void main() { vec4 textureColor = texture(uTextureUnit, vTexCoord); float lut_size = 16.0; float half_step = 0.5 / lut_size; // 计算LUT采样坐标 vec2 uv_r = vec2(textureColor.r, textureColor.b) * (lut_size - 1.0)/lut_size + half_step; vec2 uv_g = vec2(textureColor.g, textureColor.b) * (lut_size - 1.0)/lut_size + half_step; // 采样LUT获取映射后的颜色分量 vec3 r_channel = texture(uLUTTexture, uv_r).rgb; vec3 g_channel = texture(uLUTTexture, uv_g).rgb; vec3 b_channel = texture(uLUTTexture, vec2(textureColor.b, 0.0)).rgb; vec3 finalColor = vec3(r_channel.r, g_channel.g, b_channel.b); vFragColor = vec4(finalColor, textureColor.a); }
加载LUT纹理时,需设置纹理过滤方式为GL_LINEAR,避免出现色块断层。
内容的提问来源于stack exchange,提问作者kevinzhang
相关产品推荐
相关产品推荐

