如何在Unity中实现平滑的Photoshop Liquify/Forward Warp Tool功能?
优化Unity中Liquify/Forward Warp Tool的平滑扭曲效果
原代码的核心问题在于扭曲过渡生硬、无像素插值,且未合理利用衰减因子,以下是针对性的优化方案:
核心问题分析
- 偏移量无渐变:所有半径内像素使用固定
delta偏移,未根据距离光标远近调整强度,导致扭曲边缘生硬。 - 无像素插值:直接取整数坐标像素,引发明显像素块,缺乏平滑过渡。
- 衰减因子未生效:定义了
factor但未用于偏移计算,完全浪费参数作用。
优化思路
- 用平滑衰减函数控制偏移强度:光标中心偏移最大,边缘逐渐衰减至0,实现自然过渡。
- 加入双线性插值:对非整数像素位置做颜色插值,消除像素化。
- 缩小计算范围:仅遍历光标半径内的像素,提升运行效率。
优化后代码
public Texture2D backgroundTexture; private Color[] originalPixels; // 保存原始纹理副本 private Color[] currentPixels; // 保存当前修改后的纹理,支持累积变形 public float radius = 50f; public float warpStrength = 2f; // 控制衰减速率,值越大衰减越慢 public void OnMouseDownEvent(MouseDownEvent evt) { originalPixels = backgroundTexture.GetPixels(); currentPixels = backgroundTexture.GetPixels(); } public void OnMouseMoveEvent(MouseMoveEvent evt) { WarpTexture(currentposition, delta); } private void WarpTexture(Vector2 cursorPos, Vector2 delta) { // 计算需要处理的像素范围,仅遍历光标半径内区域 int startX = Mathf.Max(0, Mathf.FloorToInt(cursorPos.x - radius)); int endX = Mathf.Min(backgroundTexture.width - 1, Mathf.CeilToInt(cursorPos.x + radius)); int startY = Mathf.Max(0, Mathf.FloorToInt(cursorPos.y - radius)); int endY = Mathf.Min(backgroundTexture.height - 1, Mathf.CeilToInt(cursorPos.y + radius)); for (int y = startY; y <= endY; y++) { for (int x = startX; x <= endX; x++) { Vector2 pixelPos = new Vector2(x, y); float distance = Vector2.Distance(cursorPos, pixelPos); if (distance < radius) { // 计算平滑衰减因子:距离越近,偏移越接近delta float falloff = 1f - Mathf.Pow(distance / radius, warpStrength); Vector2 offset = delta * falloff; // 计算原始像素位置(当前像素位置减去偏移,实现"拉拽"效果) Vector2 originalPixelPos = pixelPos - offset; // 确保原始位置在纹理范围内 if (originalPixelPos.x >= 0 && originalPixelPos.x < backgroundTexture.width && originalPixelPos.y >= 0 && originalPixelPos.y < backgroundTexture.height) { // 双线性插值获取平滑颜色 Color interpolatedColor = BilinearInterpolate(currentPixels, originalPixelPos, backgroundTexture.width, backgroundTexture.height); int currentIndex = y * backgroundTexture.width + x; currentPixels[currentIndex] = interpolatedColor; } } } } backgroundTexture.SetPixels(currentPixels); backgroundTexture.Apply(); } // 双线性插值函数,生成平滑的非整数位置颜色 private Color BilinearInterpolate(Color[] pixels, Vector2 pos, int width, int height) { int x0 = Mathf.FloorToInt(pos.x); int x1 = Mathf.Min(x0 + 1, width - 1); int y0 = Mathf.FloorToInt(pos.y); int y1 = Mathf.Min(y0 + 1, height - 1); float fx = pos.x - x0; float fy = pos.y - y0; Color c00 = pixels[y0 * width + x0]; Color c10 = pixels[y0 * width + x1]; Color c01 = pixels[y1 * width + x0]; Color c11 = pixels[y1 * width + x1]; // 先横向插值,再纵向插值 Color c0 = Color.Lerp(c00, c10, fx); Color c1 = Color.Lerp(c01, c11, fx); return Color.Lerp(c0, c1, fy); }
关键优化说明
- 累积变形支持:用
currentPixels保存每次修改后的纹理,实现类似Photoshop的连续拖动变形效果,而非每次从原始图重置。 - 平滑衰减:通过
falloff因子让偏移量从光标中心到边缘平滑衰减,解决生硬过渡问题。 - 双线性插值:消除像素块,让扭曲区域颜色过渡自然。
- 性能优化:仅处理光标半径内的像素,大幅减少不必要的计算。
内容的提问来源于stack exchange,提问作者Jeremy Khan
相关产品推荐
相关产品推荐

