草庐IT

javascript - Canvas 上的多个光源

coder 2024-12-15 原文

我想在我正在制作的游戏的背景上放置多个光源,使用一个光源效果很好,如下所示:

这是通过将 .png 图像放在其他所有东西之上来实现的,它变得更加透明,像这样:

适用于一个光源,但我需要另一种方法,我可以添加更多光源并四处移动光源。

我考虑过为每一帧逐个像素绘制一个类似的“阴影层”,并根据到每个光源的距离计算透明度。但是,这可能会很慢,我相信有更好的方法来解决这个问题。

图像只是示例,每一帧将有相当多的内容可以使用 requestAnimationFrame 移动和更新。

是否有一种轻量级且简单的方法来实现这一目标?提前致谢!

编辑

在 ViliusL 的帮助下,我想出了这个掩蔽解决方案:

http://jsfiddle.net/CuC5w/1/

// Create canvas
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = 300;
canvas.height = 300;
document.body.appendChild(canvas);

// Draw background
var img=document.getElementById("cat");
ctx.drawImage(img,0,0);

// Create shadow canvas
var shadowCanvas = document.createElement('canvas');
var shadowCtx = shadowCanvas.getContext('2d');
shadowCanvas.width = canvas.width;
shadowCanvas.height = canvas.height;
document.body.appendChild(shadowCanvas);

// Make it black
shadowCtx.fillStyle= '#000';
shadowCtx.fillRect(0,0,canvas.width,canvas.height);

// Turn canvas into mask
shadowCtx.globalCompositeOperation = "destination-out";

// RadialGradient as light source #1
gradient = shadowCtx.createRadialGradient(80, 150, 0, 80, 150, 50);
gradient.addColorStop(0, "rgba(255, 255, 255, 1.0)");
gradient.addColorStop(1, "rgba(255, 255, 255, .1)");
shadowCtx.fillStyle = gradient;
shadowCtx.fillRect(0, 0, canvas.width, canvas.height);

// RadialGradient as light source #2
gradient = shadowCtx.createRadialGradient(220, 150, 0, 220, 150, 50);
gradient.addColorStop(0, "rgba(255, 255, 255, 1.0)");
gradient.addColorStop(1, "rgba(255, 255, 255, .1)");
shadowCtx.fillStyle = gradient;
shadowCtx.fillRect(0, 0, canvas.width, canvas.height);

最佳答案

另一种使用光的方法是使用 globalCompositeOperation 模式“ligther”来照亮事物,而只使用 globalAlpha 来使事物变暗。

首先这是一张图片,左边是卡通闪电,右边是更逼真的闪电,但你更愿意看 fiddle ,因为它是动画的:
http://jsfiddle.net/gamealchemist/ABfVj/

那么我是怎么做的:

变暗:
- 选择一种变暗的颜色(最有可能是黑色,但您可以选择红色或其他颜色来调整结果)。
- 选择一个不透明度(0.3 似乎是一个不错的起始值)。
- fillRect 您想变暗的区域。

function darken(x, y, w, h, darkenColor, amount) {
    ctx.fillStyle = darkenColor;
    ctx.globalAlpha = amount;
    ctx.fillRect(x, y, w, h);
    ctx.globalAlpha = 1;
}

减轻:
- 选择一种亮色。请注意,此颜色的 r,g,b 将添加到前一个点的 r,g,b 中:如果您使用高值,您的颜色会被烧焦。
- 将 globalCompositeOperation 更改为“更轻”
- 您也可以更改不透明度,以更好地控制闪电。
- fillRect 或 arc 您想要变亮的区域。

如果在打火机模式下画几个圆,结果会相加,所以你可以选择一个很低的值画几个圆。

function ligthen(x, y, radius, color) {
    ctx.save();
    var rnd = 0.03 * Math.sin(1.1 * Date.now() / 1000);
    radius = radius * (1 + rnd);
    ctx.globalCompositeOperation = 'lighter';
    ctx.fillStyle = '#0B0B00';
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, 2 * π);
    ctx.fill();
    ctx.fillStyle = color;
    ctx.beginPath();
    ctx.arc(x, y, radius * 0.90+rnd, 0, 2 * π);
    ctx.fill();
    ctx.beginPath();
    ctx.arc(x, y, radius * 0.4+rnd, 0, 2 * π);
    ctx.fill();
    ctx.restore();
}

请注意,我添加了正弦变化以使光线更加生动。

点亮:另一种方式:
您还可以在仍然使用“更亮”模式的同时,使用渐变来获得更平滑的效果(第一个更像卡通,除非您画了很多圆圈。)。

function ligthenGradient(x, y, radius) {
    ctx.save();
    ctx.globalCompositeOperation = 'lighter';
    var rnd = 0.05 * Math.sin(1.1 * Date.now() / 1000);
    radius = radius * (1 + rnd);
    var radialGradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
    radialGradient.addColorStop(0.0, '#BB9');
    radialGradient.addColorStop(0.2 + rnd, '#AA8');
    radialGradient.addColorStop(0.7 + rnd, '#330');
    radialGradient.addColorStop(0.90, '#110');
    radialGradient.addColorStop(1, '#000');
    ctx.fillStyle = radialGradient;
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, 2 * π);
    ctx.fill();
    ctx.restore();
}

我还在这里添加了一个 sin 变体。
Rq:在每次绘制时创建渐变会产生垃圾:如果您使用单个渐变,请存储渐变,如果您想要为渐变设置动画,则将它们存储在数组中。
如果您在多个地方使用相同的光,请构建一个以 (0,0) 为中心的单一渐变,并在始终使用此单一渐变绘制之前平移 Canvas 。

Rq 2 :您可以使用裁剪来防止屏幕的某些部分变亮(如果有障碍物)。 我在我的例子中添加了蓝色圆圈来展示这一点。

因此,您可能希望使用这些效果直接照亮您的场景,或者单独创建一个光层,在将其绘制到屏幕上之前,您可以根据需要将其变暗/变亮。

这里讨论的场景太多了(灯光动画与否,裁剪与否,预计算灯光层与否,......)但就速度而言,对于 Safari 和 iOS safari,使用矩形/圆弧绘制的解决方案 - 无论是渐变还是实心填充 - 都比绘制图像/ Canvas 快得多。
在 Chrome 上,情况恰恰相反:当几何数增加时,绘制图像比绘制每个几何体更快。
Firefox 在这方面与 Chrome 非常相似。

关于javascript - Canvas 上的多个光源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19787235/

有关javascript - Canvas 上的多个光源的更多相关文章

  1. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  2. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

    我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

  3. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  4. ruby - 多个属性的 update_column 方法 - 2

    我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2

  5. ruby-on-rails - 在 ruby​​ .gemspec 文件中,如何指定依赖项的多个版本? - 2

    我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这

  6. ruby-on-rails - date_field_tag,如何设置默认日期? [ rails 上的 ruby ] - 2

    我想设置一个默认日期,例如实际日期,我该如何设置?还有如何在组合框中设置默认值顺便问一下,date_field_tag和date_field之间有什么区别? 最佳答案 试试这个:将默认日期作为第二个参数传递。youcorrectlysetthedefaultvalueofcomboboxasshowninyourquestion. 关于ruby-on-rails-date_field_tag,如何设置默认日期?[rails上的ruby],我们在StackOverflow上找到一个类似的问

  7. ruby-on-rails - openshift 上的 rails 控制台 - 2

    我将我的Rails应用程序部署到OpenShift,它运行良好,但我无法在生产服务器上运行“Rails控制台”。它给了我这个错误。我该如何解决这个问题?我尝试更新ruby​​gems,但它也给出了权限被拒绝的错误,我也无法做到。railsc错误:Warning:You'reusingRubygems1.8.24withSpring.UpgradetoatleastRubygems2.1.0andrun`gempristine--all`forbetterstartupperformance./opt/rh/ruby193/root/usr/share/rubygems/rubygems

  8. ruby-on-rails - 相关表上的范围为 "WHERE ... LIKE" - 2

    我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que

  9. ruby - 使用多个数组创建计数 - 2

    我正在尝试按0-9和a-z的顺序创建数字和字母列表。我有一组值value_array=['0','1','2','3','4','5','6','7','8','9','a','b','光盘','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','','u','v','w','x','y','z']和一个组合列表的数组,按顺序,这些数字可以产生x个字符,比方说三个list_array=[]和一个当前字母和数字组合的数组(在将它插入列表数组之前我会把它变成一个字符串,]current_combo['0','0','0']

  10. ruby-on-rails - before_filter 运行多个方法 - 2

    是否有可能:before_filter:authenticate_user!||:authenticate_admin! 最佳答案 before_filter:do_authenticationdefdo_authenticationauthenticate_user!||authenticate_admin!end 关于ruby-on-rails-before_filter运行多个方法,我们在StackOverflow上找到一个类似的问题: https://

随机推荐