在画布中为草设置动画的有效方法

Posted

技术标签:

【中文标题】在画布中为草设置动画的有效方法【英文标题】:Efficient way to animate grass in canvas 【发布时间】:2015-01-17 18:11:25 【问题描述】:

我一直在尝试使用 canvas 元素的 2d 上下文的功能来尝试创建相对逼真的 3d 场景,目前我正在尝试按照小提琴创建一个普通的 3d 'ish' 草:

http://jsfiddle.net/PlacidCow/j6h7sajz/

我的问题是,让草动起来以吸收风/物理等影响的最有效方法是什么?

目前我刚刚绘制到屏幕上 - 我尝试将每个刀片的尺寸存储在一个大数组中,然后需要很长时间才能循环播放动画(因此是滞后且毫无意义的)。有没有一种有效的方法来绘制每个刀片?或者人们会建议为每个块设置动画(但是如何解决,因为清除补丁会弄乱前面的刀片)?重复刀片以便他们批量制作动画?

在网上看过(也许我缺乏谷歌搜索技能...),但我什么也没找到,笼统的 webgl 演示或 C 等示例,但这使用了不适用于 2d 画布的着色器。

c = document.getElementsByTagName('canvas')[0];
ctx = c.getContext('2d');

var v = 
    w: c.clientWidth,
    h: c.clientHeight
;
c.width = v.w;
c.height = v.h;

var base = 
    c: [
        [-1, -1, 0],
        [-1, -1, -1],
        [1, -1, -1],
        [1, -1, 0]
    ],
    f: 'brown',
    d: -3
;

draw(base.c, base.f, base.d);

for (var j=1; j<120; j++) 
    for (var k=1; k<200; k++) 
        var z = -1 + 2*j/199;
        var x = -1 + (2*Math.random())/1.01;
        var y = 0.06+Math.random()*0.04;
        var fl = Math.pow(-1, (Math.random()*1000)|0) * Math.random()/20;
        var blade = 
c: [
    [x, -1, z-Math.random()/1000],
    [x+0.002+fl, -1+y, z-Math.random()/1000],
    [x+0.007+fl, -1+y, z-Math.random()/1000],
    [x+0.02, -1, z-Math.random()/1000]
],
f: 'green',
d: 2
        ;
        draw(blade.c, blade.f, blade.d);
    


function draw(cds, fill, d) 
    var v = 
        w: c.clientWidth,
        h: c.clientHeight
    ;

    ctx.beginPath();

    ctx.moveTo(((cds[0][0] * (1 / (1 - cds[0][2])) + 1) * v.w / 2)|0, ((v.h - (cds[0][1] + 1) * v.h / 2) * (1 / (1 - cds[0][2])))|0);

    for (var i = 1; i < cds.length; i++) 
        ctx.lineTo(((cds[i][0] * (1 / (1 - cds[i][2])) + 1) * v.w / 2)|0, ((v.h - (cds[i][1] + 1) * v.h / 2) * (1 / (1 - cds[i][2])))|0);

        ctx.lineTo(((cds[0][0] * (1 / (1 - cds[0][2])) + 1) * v.w / 2)|0, ((v.h - (cds[0][1]+1) * v.h / 2) * (1 / (1 - cds[0][2])))|0);

    if (Math.abs(d) == 1) 
        grd = ctx.createLinearGradient((200-200*d/Math.abs(d))/2, 0, (200+200*d/Math.abs(d))/2,0);
     else if (Math.abs(d) == 2) 
        grd = ctx.createLinearGradient(0,((v.h - (cds[1][1] + 1) * v.h / 2) * (1 / (1 - cds[1][2])))|0,0,((v.h - (cds[0][1] + 1) * v.h / 2) * (1 / (1 - cds[0][2])))|0+v.h/100);
     else if (Math.abs(d) == 3) 
        grd = ctx.createLinearGradient(0,(200-200*d/Math.abs(d))/2, 0, (200+200*d/Math.abs(d))/2);
    
    grd.addColorStop(0, fill);
    grd.addColorStop(1, '#000');

    ctx.fillStyle = grd;
    ctx.fill();

【问题讨论】:

这些是您想要的效果之一吗? ***.com/questions/17171010/… 我已经阅读了那篇文章,并且有一些巧妙的方法可以更换刀片(如果我错过了答案,我会再次阅读所有内容) - 我目前想知道的是一种可能影响的有效方法由于风,所有(在小提琴 200x200=40000 上)刀片都说 - 或者至少给人一种愚蠢的印象 在 2d 画布上最有效的方法可能是为每片草叶生成图像帧并通过它们进行播放。您可以生成多个集合并使用不同的起点来创造它们都是独一无二的错觉。 实际上,由于您需要 40,000 个刀片,每个刀片一张图像不够快(canvas 可以在 30FPS 下绘制大约 10-15k 图像),因此您需要创建代表多个刀片的图像。 为了提高效率,如果你的草是 200px 宽,那么创建第二个 300x5 画布(仅在内存画布中)。然后通过将第二个画布重复绘制到可见画布上来创建许多垂直的草行,每行随机 X 偏移。 【参考方案1】:

我要做的是预先生成每个草叶可能的所有形状。我不熟悉弯曲草背后的数学,但让我们假设每种形状的草都可以用 2 个变量来描述:bentX 和 bentZ。所以我将绘制过程分解为2个:生成草和渲染的图像帧。

帧生成伪代码:

var frames = [];
for (var i = min possible of bentX, l = max possible of bentX; i<=l; i += precision for bentX)
    for (var j = ... bentZ .... )
         frames[i][j] = image of grass at (i,j);

然后进行渲染:

for each grassObj:
    bentX, bentZ = mathCalcs(grassObj);
    bentX, bentZ = clampToValidValue(bendX, bentZ); 
    frames[bendX][bentZ].draw();

【讨论】:

以上是关于在画布中为草设置动画的有效方法的主要内容,如果未能解决你的问题,请参考以下文章

简答题在3ds Max中为动画设置关键帧有几种总方法

画布,使用正弦波沿方形路径为形状设置动画

在 UICollectionView 中为特定单元格设置动画

如何在 draw(_:) 中为 UIBezierPath 设置动画?

在 UIView 中为多个形状设置动画

如何在jQuery中为背景属性设置动画?