如何在fragment shader中实现像素间隔拉伸而非像素变形的效果
像素艺术像素间隔拉伸Shader实现需求
我目前正在开发一款Shader以实现像素艺术图像的像素“拉开”效果,核心需求为拉伸像素之间的空隙,而非让像素本身发生形变,类似桌面上拂过沙子的效果:沙粒本身尺寸不变,仅沙粒之间的间距变大。
现有实现情况
- 目前已实现平滑扭曲效果:距离图像中心越远拉伸程度越高,但该方案为连续的平滑变形,所有像素始终保持相连,会出现挤压、smear等不符合需求的问题
- 使用modulo运算实现了近似效果,但该方案会丢弃一半的图像内容,不符合预期
效果参考
- 目标效果示意图:

- 现有modulo方案效果示意图:

现有核心代码
var fragmentShader = ` precision mediump float; varying vec2 vTextureCoord; uniform sampler2D uSampler; uniform highp vec4 inputSize; uniform float time; vec2 mapCoord( vec2 coord ) { coord *= inputSize.xy; coord += inputSize.zw; return coord; } vec2 unmapCoord( vec2 coord ) { coord -= inputSize.zw; coord /= inputSize.xy; return coord; } void main(void) { vec2 coord = mapCoord(vTextureCoord); float dist = distance(coord.x, inputSize.x/2.); coord.x += dist/4.; coord = unmapCoord(coord); gl_FragColor = texture2D(uSampler, coord); }`
实现方案
你要实现的效果核心是保持每个原始像素的采样范围不变,仅偏移每个像素块的整体位置,不要对UV做连续的平滑偏移,按以下步骤修改即可:
- 首先把UV按原始像素尺寸做离散化分块,拆分出「像素块索引」和「块内相对偏移」两部分,块内偏移始终保持不变就能保证像素本身不会发生形变
- 仅对每个像素块的中心坐标做拉伸偏移计算,偏移规则沿用你现有逻辑即可
- 最后把偏移后的像素块位置和原始块内偏移合并,换算为最终的采样UV
- 额外需要把纹理的过滤模式设置为
NEAREST邻近过滤,避免采样时出现插值模糊,保证像素边缘清晰
修改后的核心Shader代码如下:
void main(void) { // 拆分像素块索引和块内偏移 vec2 pixelGrid = vTextureCoord * inputSize.xy; vec2 originalPixelIdx = floor(pixelGrid); vec2 offsetInPixel = fract(pixelGrid); // 仅对像素块的中心做拉伸偏移 vec2 pixelCenter = originalPixelIdx + 0.5; float dist = distance(pixelCenter.x, inputSize.x / 2.0); pixelCenter.x += dist / 4.0; // 合并得到最终采样UV vec2 finalCoord = (pixelCenter + offsetInPixel - 0.5) / inputSize.xy; gl_FragColor = texture2D(uSampler, finalCoord); }
你之前用modulo出现内容丢失的问题,是因为直接对连续偏移的坐标取模,没有先做离散分块处理,按上述方案分块后所有原始像素都会保留,空隙位置会自动显示为透明或者你设置的背景色,不会出现内容丢失的问题。
内容的提问来源于stack exchange,提问作者Jacob Davis
相关产品推荐
相关产品推荐

