# 干扰示例

Colin Eberhardt 和 Ben Smith 的 WebAssembly 干扰效果 (在新窗口中打开),如果用 AssemblyScript 写的话。

# 内容

  • 从 WebAssembly 模块导出函数和变量。
  • 调用从 WebAssembly 导出的函数和读取变量。
  • 利用 32 位浮点数学通过使用 Mathf 来加速计算。
  • 将图像缓冲区保存在模块的内存中,并将其复制到画布。
  • 根据浏览器端视窗的大小手动增长内存。
  • 最后:持续更新和渲染图像缓冲区。

# 示例

癫痫警告

一小部分人可能会在接触某些光线模式或闪烁的灯光时经历癫痫发作。暴露于计算机屏幕上的某些模式或背景可能会在这些人中诱发癫痫发作。某些情况可能会诱发以前未被发现的癫痫症状,即使是那些没有癫痫发作史或癫痫病史的人。

如果您在观看时遇到以下任何症状——头晕、视力改变、眼或肌肉抽搐、意识丧失、迷失方向、任何不自主运动或抽搐——请立即停止使用并咨询您的医生。

#!optimize=speed&runtime=stub
var width  = 320;
var height = 200;

// Let's utilize the entire heap as our image buffer
export const offset = __heap_base;

/** Sets a single pixel's color. */
function set(x: i32, y: i32, v: f32): void {
  var vi = <i32>v;
  store<i32>(offset + ((width * y + x) << 2), ~vi << 24 | vi << 8);
}

/** Computes the distance between two pixels. */
function distance(x1: i32, y1: i32, x2: f32, y2: f32): f32 {
  var dx = <f32>x1 - x2;
  var dy = <f32>y1 - y2;
  return Mathf.sqrt(dx * dx + dy * dy);
}

/** Performs one tick. */
export function update(tick: f32): void {
  var w = <f32>width;
  var h = <f32>height;
  var hw = w * 0.5,
      hh = h * 0.5;
  var cx1 = (Mathf.sin(tick * 2) + Mathf.sin(tick      )) * hw * 0.3 + hw,
      cy1 = (Mathf.cos(tick)                            ) * hh * 0.3 + hh,
      cx2 = (Mathf.sin(tick * 4) + Mathf.sin(tick + 1.2)) * hw * 0.3 + hw,
      cy2 = (Mathf.sin(tick * 3) + Mathf.cos(tick + 0.1)) * hh * 0.3 + hh;
  var res = <f32>48 / Mathf.max(w, h);
  var y = 0;
  do {
    let x = 0;
    do {
      set(x, y, Mathf.abs(
        Mathf.sin(distance(x, y, cx1, cy1) * res) +
        Mathf.sin(distance(x, y, cx2, cy2) * res)
      ) * 120);
    } while (++x != width)
  } while (++y != height)
}

/** Recomputes and potentially grows memory on resize of the viewport. */
export function resize(w: i32, h: i32): void {
  width = w; height = h;
  // Pages are 64kb. Rounds up using mask 0xffff before shifting to pages.
  var needed = <i32>((offset + (w * h * sizeof<i32>() + 0xffff)) & ~0xffff) >>> 16;
  var actual = memory.size();
  if (needed > actual) memory.grow(needed - actual);
}

#!html
<canvas id="canvas" style="width: 100%; height: 100%; background: #aff"></canvas>
<script type="module">
const exports = await instantiate(await compile());
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
const step = 0.012;

// Upscale the image to speed up calculations
const upscaleFactor = 4;

var width, height, image;

// Inform the module about the viewport's size, incl. on resize
function onresize() {
  width = (canvas.offsetWidth / upscaleFactor) | 0;
  height = (canvas.offsetHeight / upscaleFactor) | 0;
  canvas.width = width;
  canvas.height = height;
  image = context.createImageData(width, height);
  exports.resize(width, height);
}
onresize();
new ResizeObserver(onresize).observe(canvas);

// Keep updating the image
var tick = 0.0;
(function update() {
  requestAnimationFrame(update);
  exports.update(tick += step);
  new Uint32Array(image.data.buffer).set(new Uint32Array(exports.memory.buffer, exports.offset.value, width * height));
  context.putImageData(image, 0, 0);
})();
</script>

注意

该示例假设了一个重要的事实:由于我们没有使用复杂的运行时,我们可以改为将整个堆(从 __heap_base 开始)作为我们的图像缓冲区。

只要不再满足此条件,就可以通过指定合适的 --memoryBase 来保留一些空间,或者导出一个动态实例化的内存块,例如 Uint32Array,并在 WebAssembly 和 JavaScript 中将其用作图像缓冲区。

# 本地运行

按照 入门 中的说明设置一个新的 AssemblyScript 项目,并将 module.ts 复制到 assembly/index.ts,并将 index.html 复制到项目的顶层目录。编辑 package.json 中的构建命令以包含

--runtime stub

现在可以用以下命令编译该示例:

npm run asbuild

要查看该示例,可以在 index.html 中将实例化从

loader.instantiate(module_wasm).then(({ exports }) => {

更改为

WebAssembly.instantiateStreaming(fetch('./build/optimized.wasm'), {
  JSMath: Math
}).then(({ exports }) => {

因为在这里最终不需要使用 加载器(没有交换管理对象)。如果改为使用加载器,它会自动提供 JSMath

一些浏览器可能在只打开 index.html 时会限制对本地资源的 fetch 操作,但您可以设置本地服务器作为解决方法

npm install --save-dev http-server
http-server . -o -c-1