一张丑图胜千言:与Cursor的一次失败合作,让我看到了多模态的真正威力
当我与AI沟通了10轮左右无果后,竟然仅凭一张丑陋的草图就让AI精准理解了我的需求。文+图,这种多模态的组合,直接是对语言理解的降维打击。在做UI效果交互过程中,这种使用技巧尤为重要。
目录
最近我在使用 Cursor开发一个 3D 分块翻转着色器效果,这个过程让我深刻体会到:在某些场景下:千言万语,比不过 一张丑陋的草图。
任务背景
我想实现一个类似 PPT 或 WPF 中的 3D 翻转效果,比如下面是从PPT录制的效果(WPS中的PPT翻页效果之一):

需要把一张图片分成 M×N 的网格,每个小方块像卡片一样绕 Y 轴旋转,有透视效果,呈现真实的 3D 空间感。看起来很简单对吧?我也这么想,因为WPF里面实现一个3D翻转就非常简单的几句代码就能搞定,区别就是我们要实现更多小方块的3D翻转,比如:
<Grid>
<!-- 带 3D 投影的元素 -->
<Border x:Name="card" Background="DodgerBlue" Width="200" Height="150"
CornerRadius="10" BorderBrush="White" BorderThickness="2">
<Border.Projection>
<PlaneProjection x:Name="projection" RotationY="0"/>
</Border.Projection>
<TextBlock Text="点击翻转" FontSize="24" Foreground="White"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</Grid>
<Window.Resources>
<!-- 翻转动画 -->
<Storyboard x:Key="FlipAnimation">
<DoubleAnimation Storyboard.TargetName="projection"
Storyboard.TargetProperty="RotationY"
From="0" To="180" Duration="0:0:0.6">
<DoubleAnimation.EasingFunction>
<CubicEase EasingMode="EaseInOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</Window.Resources>
反复无效的沟通
但是使用着色器实现这个效果,却需要从头起步,这可把我难坏了,我与AI进行了反复且艰难的对话:
我:把图片分为指定的行列数量,比如 5行七列,然后对每个小块 沿着这个小块的 x中心的y轴旋转,相当于flip翻转,从看不见到看得见,角度变化
我:你处理的不正确,完全看不到3d翻转的空间感,完全像是对宽度的改变的动画,应该像是我发的图片这样有空间感
我:也不正确,它不是以正面视角,而是有一定的仰望角度导致画面倾斜,而且我希望所有的小区块的翻转实际都有些时间差异,不要太整齐,
我:这个翻转效果还是不太对,我希望它像 wpf里面的 storyboard 对 Y 的翻转效果,有空间感
我:我看到了改进,但是需要按照以下进行修复...balabala...
反复的调整,也出现了五八门的错误,而且好像越调整越乱套的感觉:

豆包也无济于事
我感觉自己的表达能力可能有问题,于是又去跟豆包沟通,让它重新整理了需求,看起来非常地精准和专业,并且尽量地消除歧义:
我:你得处理仍然不正确,请按照下面的任务重新处理:3D透视翻转图片分块动画 - 任务描述
核心目标
实现一张图片的3D透视分块翻转动画,每个小块从垂直于屏幕翻转至平行于屏幕,同时伴随透明度变化。
具体需求分解
1. 图片分块
· 输入:一张图片
· 将图片划分为 M行 × N列 的均匀网格
· 每个网格块作为一个独立的3D平面进行动画2. 动画属性(每个块同时变化)
· 透明度动画:从 0(完全透明)→ 1(完全不透明)
· 3D旋转动画:从 90°(垂直于屏幕)→ 0°(平行于屏幕)
· 旋转轴:Y轴(水平轴)
· 起始:块平面垂直于屏幕(像扑克牌侧面)
· 结束:块平面平行于屏幕(正常显示图片)3. 关键:3D透视效果(重要!)
· 不是简单的2D宽度缩放,必须有空间透视感
· 每个块在旋转过程中要有梯形变形(梯形失真)
· 上边缘和下边缘在旋转时倾斜角度不同:
· 远离摄像头的边缘看起来更短
· 靠近摄像头的边缘看起来更长
· 不同行有不同的透视强度:
· 靠近画面顶部的行(假设摄像头在上方)透视变形更明显
· 远离摄像头的行透视变形较小4. 视觉参考
· 类似WPF的3D翻转动画,或CSS 3D变换的透视效果
· 像多张卡片从侧面翻转到正面的过程,但由于视角原因,卡片边缘呈现梯形而非矩形技术实现要点
每个像素需要计算:
1. 块归属判断:像素属于哪个网格块
2. 动画进度:该块的当前旋转角度和透明度
3. 透视投影计算:
· 根据像素在块内的位置
· 根据块的3D旋转角度
· 根据摄像头视角位置(假设在屏幕正前方偏上方)
· 计算像素应有的梯形变形
4. 最终颜色:原图像素颜色 × 透明度透视计算的关键公式(概念):
```
投影坐标 = 3D世界坐标 × 旋转矩阵 × 透视投影矩阵
```· 每个块有自己的局部坐标系
· 旋转改变块的3D朝向
· 透视投影矩阵根据摄像头位置产生梯形变形预期视觉效果
· 图片像由多个小卡片组成
· 卡片从侧面(看不见)翻转到正面(完全可见)
· 翻转过程中,卡片有明显的3D空间感,不是扁平的
· 不同行的卡片翻转时透视角度不同,形成立体感避免的误解
· 不是简单的2D宽度动画
· 不是所有行透视效果相同
· 不是理想的正面视角旋转(那样只有宽度变化)
· 不是平面翻转,必须有深度感
这总能搞定了吧?结果还是不行。
我:你现在这个处理效果是不对的,因为这个效果我从单个小方块的感觉来描述,我当前看到的效果。好像是当前的这个小方块。仅仅是宽度,从很小变成了铺满的宽度,高度没有发生任何的变化...balabala...
我:我现在看到发生了一些变化,有一些变化是正确的,但是有一些。仍然是错误的,正确的变化是,我现在看到的右半侧的上边缘的效果是正确的和左半侧的下边缘的...balabala...
我:你现在又改成了严重错误的状态了,因为我看到整体的顶部始终是一条横平的直线,整体的底部始终是一条横平的直线,现在又变成了我眼睛看到的,好像只有宽度发生了。的变化,完全又没有了空间感
不经意尝试
效果仍然让人失望,来回在前面的几种状态下反复,当我快要放弃的时候,打算换一个思路,给它个效果帧的采样,它是否可能会理解呢?试试看。
我:仍然不正确,它们旋转过程中的形态应该像是我附件中发的图片中这样的:
然后,惊喜地一幕出现了:成功了!而且没有多余的任何解释,就直接正确了!!

我调整了效果的变换实际,让整体呈现从左到右的渐变感,因此越向右列时间延迟越大;并且让同列的行也呈现有序感,不同行递增时间延迟,整体呈现出来一种波浪推进的感觉,很酷。
然后基于正确的效果,我进行了方块数量的调整:

然后又进行了方块随机延迟的调整:

然后又进行了多图切换的调整:

以上的一系列的效果都非常棒。部分算法的核心代码(DirectX 12 + ComputeSharp + Win32 ):
public float4 Execute()
{
int2 xy = ThreadIds.XY;
float2 resolution = (float2)DispatchSize.XY;
float2 uv = ((float2)xy + 0.5f) / resolution;
// 图像适配屏幕
float imageAspect = imageWidth / imageHeight;
float screenAspect = screenWidth / screenHeight;
float2 sampleUv = uv;
if (imageAspect > screenAspect)
{
sampleUv.Y = (uv.Y - 0.5f) * (screenAspect / imageAspect) + 0.5f;
}
else
{
sampleUv.X = (uv.X - 0.5f) * (imageAspect / screenAspect) + 0.5f;
}
sampleUv = Hlsl.Clamp(sampleUv, 0f, 1f);
// 网格划分
float frows = (float)rows;
float fcols = (float)cols;
float cellY = sampleUv.Y * frows;
float cellX = sampleUv.X * fcols;
int row = (int)Hlsl.Floor(cellY);
int col = (int)Hlsl.Floor(cellX);
row = Hlsl.Clamp(row, 0, rows - 1);
col = Hlsl.Clamp(col, 0, cols - 1);
float localX = Hlsl.Frac(cellX);
float localY = Hlsl.Frac(cellY);
// 从左到右按列延迟 + 行随机延迟
float colFactor = (float)col / Hlsl.Max(fcols - 1f, 1f);
float colDelay = colFactor * ColumnDelayRatio;
float rowRandomDelay = Hash((float)row, (float)col) * RowRandomDelayRange;
float delay = colDelay + rowRandomDelay;
float span = Hlsl.Max(1f - ColumnDelayRatio - RowRandomDelayRange, 0.2f);
float progressCell = Hlsl.Saturate((progress - delay) / span);
// === 关键:两图切换的角度计算(无透明度变化) ===
//
// progressCell: 0 → 0.5 → 1
//
// 前半部分(progressCell < 0.5):显示 A 图,从 0° 转到 90°(正面→侧面)
// 后半部分(progressCell >= 0.5):显示 B 图,从 90° 转到 0°(侧面→正面)
bool showImageA = progressCell < 0.5f;
float angle;
if (showImageA)
{
// A 图:0° → 90°(正面翻转到侧面)
float t = progressCell * 2f; // 0 → 1
angle = t * PiOver2; // 0° → 90°
}
else
{
// B 图:90° → 0°(侧面翻转到正面)
float t = (progressCell - 0.5f) * 2f; // 0 → 1
angle = PiOver2 - t * PiOver2; // 90° → 0°
}
float cosA = Hlsl.Cos(angle);
float sinA = Hlsl.Sin(angle);
// 当角度接近 90° 时,cos 接近 0,方块几乎不可见
if (cosA < 0.01f)
return default;
// 透视参数(根据行号变化)
float rowFactor = frows > 1f ? (float)row / (frows - 1f) : 0.5f;
float d = CameraDistMin + rowFactor * (CameraDistMax - CameraDistMin);
// 块中心坐标
float sx = localX - 0.5f;
float sy = localY - 0.5f;
// === X 方向:完整的透视投影 ===
float denomLeft = Hlsl.Max(d - 0.5f * sinA, 0.01f);
float denomRight = Hlsl.Max(d + 0.5f * sinA, 0.01f);
float projLeftX = -0.5f * cosA * d / denomLeft;
float projRightX = 0.5f * cosA * d / denomRight;
if (sx < projLeftX - 0.001f || sx > projRightX + 0.001f)
return default;
// 逆透视 x
float denom = cosA * d - sx * sinA;
if (Hlsl.Abs(denom) < 0.001f)
return default;
float x3d = sx * d / denom;
if (x3d < -0.5f - 0.001f || x3d > 0.5f + 0.001f)
return default;
// === Y 方向:线性梯形效果(向右收缩) ===
float zRight = 0.5f * sinA;
float rightEdgeScale = d / (d + zRight);
float xNorm = (x3d + 0.5f);
float yScale = 1f - xNorm * (1f - rightEdgeScale);
float y3d = sy / yScale;
if (y3d < -0.5f - 0.001f || y3d > 0.5f + 0.001f)
return default;
// 纹理坐标
float texU_cell = x3d + 0.5f;
float texV_cell = y3d + 0.5f;
float texU = ((float)col + texU_cell) / fcols;
float texV = ((float)row + texV_cell) / frows;
texU = Hlsl.Clamp(texU, 0f, 1f);
texV = Hlsl.Clamp(texV, 0f, 1f);
// 根据当前阶段选择图片
Float4 texColor = showImageA
? imageA.Sample(new float2(texU, texV))
: imageB.Sample(new float2(texU, texV));
// V6: 透明度始终为 1,不进行淡入淡出
float finalAlpha = texColor.W;
return new float4(texColor.X * finalAlpha, texColor.Y * finalAlpha, texColor.Z * finalAlpha, finalAlpha);
}
一张丑图的反思
一张简单丑陋的图片,解决了10 轮左右文字沟通都无法解决的问题。
不禁让人反思:为什么图片如此有效?
1. 文字的歧义性
回顾我的描述:
"上下边缘平行" —— 是互相平行?还是都保持水平?
"有空间感" —— 是透视收缩?还是倾斜?还是深度感?
"像 3D 翻转" —— 3D 翻转有无数种可能的实现
每一个词都有多种理解方式,AI 选择了其中一种,但不一定是我想要的那种。
2. 图像的精确性
而一张图片:
明确展示了变形的**方向**(向右收缩)
明确展示了变形的**形状**(梯形,不是沙漏,不是平行四边形)
明确展示了**边缘的关系**(左高右低,渐变收缩)
图像直接传递了"结果",而文字只能传递"描述"。
3. 3D 效果的复杂性
3D 透视变换涉及:
旋转角度
摄像机位置
透视强度
边缘变形方式
上下/左右的对称性
用文字精确描述这些参数的组合效果,几乎是不可能的任务。空间理解力是ai的一大弱项。
多模态AI
基于这次经历,我总结了几条使用多模态 AI 的建议:
1. 视觉效果优先用图片
涉及到:
UI 设计
动画效果
图形变换
布局调整
先找一张参考图,或者画一个草图,比写 500 字的描述更有效。
2. 图片 + 文字的组合最强
发送图片时,附上简短说明:
"像这张图一样,但颜色要蓝色"
"效果像这个,但速度要慢一点"
3. 用对比来消除歧义
与其说"我要 A 效果",不如说:
"我要像 X 一样,但不要 Y"
"参考这张图,但把 Z 改成 W"
4. 及时止损,换个方式
如果文字沟通 2-3 轮后仍然没有进展,立刻停下来,考虑:
能否找一张参考图?
能否画一个简单的草图?
能否用对比的方式描述?
退一步,升一维。
让高维信息无损传递
文字作为一种低维的离散符号系统,天生就有其表达边界。当我们试图用语言描述视觉、空间、动态这类高维信息时,就像用摩尔斯电码描绘一幅画——即便词汇再精准,也难免丢失拓扑结构、空间关系这些核心信息。我以为的“详细描述”,在AI看来只是一堆孤立的关键词,它无法从文字中还原我脑海里的“空间直觉”,就像我们无法仅凭文字想象出从未见过的三维物体。
而那三张丑陋的示意图,虽然粗糙,却承载了文字无法传递的高维信息:旋转的方向、透视的强度、物体各部分的相对位置,这些信息以并行的方式直接呈现,AI无需猜测,只需捕捉其中的视觉规律。这让我深刻体会到,多模态的强大,不是“会看图说话”,而是它打破了模态壁垒,让高维信息得以无损传递,实现了“视觉意图”到“代码实现”的直接映射。
多模态的本质:让高维信息无损传递
信息传达的本质

“一图胜千言”,其实背后是信息维度的差异:视觉是二维空间加时间的高维载体,每一个像素都包含着位置、颜色、关系等多重信息,其带宽是文字的成千上万倍;而文字是一维的串行符号,需要大脑进行二次解码和重构,自然容易产生歧义。
这个经历也让我对“维度”有了更实际的理解。以前总觉得“升维”是抽象的哲学概念,现在才明白,所谓升维,有时只是切换信息的表征方式——从文字切换到图像,从抽象描述切换到具体示意。对于视觉类开发、设计、沟通等场景,这种切换不是“锦上添花”,而是“降维打击”般的颠覆性效率。
AI时代的视觉素养
现在很多人担心AI绘画会取代设计师、艺术家,但事实恰恰相反:AI解决了“画得像”的执行问题,却无法替代“想得对”的创意与判断。当图像成为更高效的信息载体,“用图像表达思想”不再是艺术家的专属技能,而是每个人都需要的基础能力。我们不需要成为专业画师,但需要具备把想法转化为关键帧、示意图的视觉抽象能力,需要具备判断“哪种视觉表达更有效”的审美素养——这些能力,在多模态时代只会越来越重要。
当再遇到视觉类需求,先画草图再写文字;复杂逻辑用流程图替代纯文本描述。这种“图像定形,文字定规”的模式,让协作效率大大提升。
总结
这次看似失败的AI合作,成了一次宝贵的认知升级。它让我明白,与AI协作的核心,不是提升我们的文字描述能力,而是找到最适合信息传递的模态。在这个多模态日益强大的时代,真正的高效协作,是让合适的信息在合适的维度里流动。看似“丑陋”的示意图,实际上是跨维度壁垒的极高效的捷径。
最后,再来看看这个漂亮的效果吧:

更多推荐



所有评论(0)