Resize Canvas

2024-01-14  本文已影响0人  不决书

代码分析说明:
部分说明请参考 Hello Triangle MSAA 以下只解释该功能的实现部分代码

import { assert, makeSample, SampleInit } from '../../components/SampleLayout';

import triangleVertWGSL from '../../shaders/triangle.vert.wgsl';
import redFragWGSL from '../../shaders/red.frag.wgsl';

// 通过样式来设置Canvas 的宽度缩放动画
import styles from './animatedCanvasSize.module.css';

const init: SampleInit = async ({ canvas, pageState }) => {
  const adapter = await navigator.gpu.requestAdapter();
  assert(adapter, 'requestAdapter returned null');
  const device = await adapter.requestDevice();

  if (!pageState.active) return;
  const context = canvas.getContext('webgpu') as GPUCanvasContext;

  const presentationFormat = navigator.gpu.getPreferredCanvasFormat();

  const devicePixelRatio = window.devicePixelRatio;
  canvas.width = canvas.clientWidth * devicePixelRatio;
  canvas.height = canvas.clientHeight * devicePixelRatio;

  context.configure({
    device,
    format: presentationFormat,
    alphaMode: 'premultiplied',
  });

  const sampleCount = 4;

  const pipeline = device.createRenderPipeline({
    layout: 'auto',
    vertex: {
      module: device.createShaderModule({
        code: triangleVertWGSL,
      }),
      entryPoint: 'main',
    },
    fragment: {
      module: device.createShaderModule({
        code: redFragWGSL,
      }),
      entryPoint: 'main',
      targets: [
        {
          format: presentationFormat,
        },
      ],
    },
    primitive: {
      topology: 'triangle-list',
    },
    multisample: {
      count: 4,
    },
  });

  // 渲染到目标纹理
  let renderTarget: GPUTexture | undefined = undefined;
  // 目标纹理的view
  let renderTargetView: GPUTextureView;
  
// canvas 添加动画样式
  canvas.classList.add(styles.animatedCanvasSize);

  function frame() {
    // Sample is no longer the active page.
    if (!pageState.active) return;

    const currentWidth = canvas.clientWidth * devicePixelRatio;
    const currentHeight = canvas.clientHeight * devicePixelRatio;

    // The canvas size is animating via CSS.
    // When the size changes, we need to reallocate the render target.
    // We also need to set the physical size of the canvas to match the computed CSS size.
    // 如果canvas大小发生了变化
    if (
      (currentWidth !== canvas.width || currentHeight !== canvas.height) &&
      currentWidth &&
      currentHeight
    ) {
      // 如果渲染目标存在
      if (renderTarget !== undefined) {
        // Destroy the previous render target  删除上一次的目标
        renderTarget.destroy();
      }

      // Setting the canvas width and height will automatically resize the textures returned
      // when calling getCurrentTexture() on the context.
      // 设置当前的canvas 大小
      canvas.width = currentWidth;
      canvas.height = currentHeight;
  
      // 创建渲染的目标纹理,包括多重采样
      // Resize the multisampled render target to match the new canvas size.
      renderTarget = device.createTexture({
        size: [canvas.width, canvas.height],
        sampleCount,
        format: presentationFormat,
        usage: GPUTextureUsage.RENDER_ATTACHMENT,
      });
      
      // 渲染目标的view
      renderTargetView = renderTarget.createView();
    }

    const commandEncoder = device.createCommandEncoder();

    const renderPassDescriptor: GPURenderPassDescriptor = {
      colorAttachments: [
        {
          // 传递渲染目标
          view: renderTargetView,
          // 最终渲染到canvas的view
          resolveTarget: context.getCurrentTexture().createView(),
          clearValue: { r: 0.2, g: 0.2, b: 0.2, a: 1.0 },
          loadOp: 'clear',
          storeOp: 'store',
        },
      ],
    };

    const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
    passEncoder.setPipeline(pipeline);
    passEncoder.draw(3);
    passEncoder.end();

    device.queue.submit([commandEncoder.finish()]);
    requestAnimationFrame(frame);
  }

  requestAnimationFrame(frame);
};

顶点着色器

@vertex
fn main(
  @builtin(vertex_index) VertexIndex : u32
) -> @builtin(position) vec4<f32> {
  var pos = array<vec2<f32>, 3>(
    vec2(0.0, 0.5),
    vec2(-0.5, -0.5),
    vec2(0.5, -0.5)
  );

  return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
}

片元着色器

@fragment
fn main() -> @location(0) vec4<f32> {
  return vec4(1.0, 0.0, 0.0, 1.0);
}

动画样式

@keyframes animated-size {
  0% {
    width: 10px;
    height: 600px;
  }
  50% {
    width: 100%;
    height: 600px;
  }
  100% {
    width: 10px;
    height: 600px;
  }
}

.animatedCanvasSize {
  animation-duration: 3s;
  animation-iteration-count: infinite;
  animation-name: animated-size;
  animation-timing-function: ease;
}

总结步骤:

  1. 整体步骤与上一个示例一致,不同点在于将目标纹理对象放在每一帧渲染中,目前渲染纹理的大小与Canvas的大小同步变化
上一篇下一篇

猜你喜欢

热点阅读