You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Unity中实现平滑的Photoshop Liquify/Forward Warp Tool功能?

优化Unity中Liquify/Forward Warp Tool的平滑扭曲效果

原代码的核心问题在于扭曲过渡生硬、无像素插值,且未合理利用衰减因子,以下是针对性的优化方案:

核心问题分析

  1. 偏移量无渐变:所有半径内像素使用固定delta偏移,未根据距离光标远近调整强度,导致扭曲边缘生硬。
  2. 无像素插值:直接取整数坐标像素,引发明显像素块,缺乏平滑过渡。
  3. 衰减因子未生效:定义了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);
}

关键优化说明

  1. 累积变形支持:用currentPixels保存每次修改后的纹理,实现类似Photoshop的连续拖动变形效果,而非每次从原始图重置。
  2. 平滑衰减:通过falloff因子让偏移量从光标中心到边缘平滑衰减,解决生硬过渡问题。
  3. 双线性插值:消除像素块,让扭曲区域颜色过渡自然。
  4. 性能优化:仅处理光标半径内的像素,大幅减少不必要的计算。

内容的提问来源于stack exchange,提问作者Jeremy Khan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 07:13:24