实现Photoshop风格点画效果的GLSL算法咨询
实现Photoshop风格点画效果的GLSL方案
我尝试用以下GLSL代码实现点画效果,但输出不符合预期,想要达到Photoshop中的点画效果,求对应的实现算法和GLSL代码。
我的代码:
float rand(vec2 co) { return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453); } void mainImage( out vec4 fragColor, in vec2 fragCoord ) { // 归一化像素坐标(0到1) vec2 uv = fragCoord/iResolution.xy; float iDotSize = 0.01; // 归一化点大小(0到1) float iMaxDotDistance = iDotSize * 2.0; // 计算带随机偏移的点位置 float dotOffset = (rand(uv) * 2.0 - 1.0) * iMaxDotDistance; vec2 dotPosition = uv + dotOffset; // 根据期望半径计算点大小 float dotSize = iDotSize * iResolution.x; // 在点位置采样输入图像颜色 vec3 dotColor = texture(iChannel0, dotPosition).rgb; // 绘制点 if (distance(uv, dotPosition) < dotSize) { fragColor = vec4(dotColor, 1.0); } else { // 绘制背景 fragColor = vec4(0.0); } }
效果对比:
- 预期效果(Photoshop点画):规则排列的网点,暗部区域网点大,亮部区域网点小,整体呈现均匀的半色调质感
- 当前输出效果:随机分布的点,大小固定,无明暗对应关系,不符合预期
正确算法:Photoshop点画本质是半色调(Halftone)网点效果,核心逻辑是:
- 将图像转换为灰度亮度值,亮度越高(越亮),对应网点越小;亮度越低(越暗),对应网点越大
- 采用规则网格排列网点(而非随机),每个网格单元对应一个网点中心
- 计算当前像素到所在网格单元中心的距离,对比基于亮度计算的网点半径,决定是否填充该像素
实现代码:
void mainImage( out vec4 fragColor, in vec2 fragCoord ) { // 归一化坐标 vec2 uv = fragCoord / iResolution.xy; // 采样原图颜色 vec4 texColor = texture(iChannel0, uv); // 计算灰度亮度(ITU-R BT.601标准) float luminance = dot(texColor.rgb, vec3(0.299, 0.587, 0.114)); // 网点网格大小(控制点密度,值越小点越密) float gridSize = 16.0; // 计算当前像素所在的网格单元中心 vec2 gridPos = floor(fragCoord / gridSize) * gridSize + gridSize * 0.5; vec2 gridUV = gridPos / iResolution.xy; // 计算网点半径:亮度越高,半径越小(暗部大网点,亮部小网点) // 最大半径取网格大小的一半,最小半径为0 float maxRadius = gridSize * 0.45; float dotRadius = maxRadius * (1.0 - luminance); // 计算当前像素到网格中心的距离 float dist = distance(fragCoord, gridPos); // 绘制网点:距离小于半径则取原图颜色,否则取背景色(这里用黑色) if (dist < dotRadius) { fragColor = texColor; } else { fragColor = vec4(0.0, 0.0, 0.0, 1.0); } }
代码说明:
gridSize:控制网点的密度,数值越小,网点越密集- 亮度计算采用标准的灰度转换公式,保证亮度判断准确
- 网点半径和亮度成反比,完美匹配Photoshop点画的明暗对应关系
- 规则网格排列保证网点分布均匀,符合半色调效果的视觉特征
内容的提问来源于stack exchange,提问作者ZeroOneZeroR
相关产品推荐
相关产品推荐

