8. WebGPU 平移变换

news2024/11/19 17:45:59

我们将开始编写与顶点缓冲区文章中的示例类似的代码,但这次将绘制单个 F 而不是一堆圆,并使用索引缓冲区来保持数据更小。

让我们在像素空间而不是裁剪空间中工作,就像 Canvas 2D API 我们将制作一个 F,将从 6 个三角形构建它
在这里插入图片描述

这是 F 的数据

function createFVertices() {
  const vertexData = new Float32Array([
    // left column
    0 ,   0, //0
    30,   0, //1
    0 , 150, //2
    30, 150, //3
 
    // top rung
    30 ,  0, //4
    100,  0, //5
    30 , 30, //6
    100, 30, //7
 
    // middle rung
    30, 60,  //8
    70, 60,  //9
    30, 90,  //10
    70, 90,  //11
  ]);
 
  const indexData = new Uint32Array([
    0,  1,  2,    2,  1,  3,  // left column
    4,  5,  6,    6,  5,  7,  // top run
    8,  9, 10,   10,  9, 11,  // middle run
  ]);
 
  return {
    vertexData,
    indexData,
    numVertices: indexData.length,
  };
}

上面的顶点数据在像素空间中,因此需要将其转换为裁剪空间。可以通过将分辨率传递给着色器并进行一些数学运算来做到这一点。下边是操作步骤。

struct Uniforms {
  color: vec4f,
  resolution: vec2f,
};
 
struct Vertex {
  @location(0) position: vec2f,
};
 
struct VSOutput {
  @builtin(position) position: vec4f,
};
 
@group(0) @binding(0) var<uniform> uni: Uniforms;
 
@vertex fn vs(vert: Vertex) -> VSOutput {
  var vsOut: VSOutput;
  
  let position = vert.position;
 
  // convert the position from pixels to a 0.0 to 1.0 value
  let zeroToOne = position / uni.resolution;
 
  // convert from 0 <-> 1 to 0 <-> 2
  let zeroToTwo = zeroToOne * 2.0;
 
  // covert from 0 <-> 2 to -1 <-> +1 (clip space)
  let flippedClipSpace = zeroToTwo - 1.0;
 
  // flip Y
  let clipSpace = flippedClipSpace * vec2f(1, -1);
 
  vsOut.position = vec4f(clipSpace, 0.0, 1.0);
  return vsOut;
}
 
@fragment fn fs(vsOut: VSOutput) -> @location(0) vec4f {
  return uni.color;
}

使用顶点位置并将其除以分辨率。这给了一个在画布上从 0 到 1 的值。然后乘以 2 以获得画布上从 0 到 2 的值。然后减去 1。现在我们的值在裁剪空间中,但它被翻转了,因为裁剪空间向上 Y 正向,而画布 2d 向下 Y 正向。所以将 Y 乘以 -1 来翻转它。现在有了所需的裁剪空间值,可以从着色器中输出它。

现在只有一个属性,所以渲染管线看起来像这样

  const pipeline = device.createRenderPipeline({
    label: 'just 2d position',
    layout: 'auto',
    vertex: {
      module,
      entryPoint: 'vs',
      buffers: [
        {
          arrayStride: (2) * 4, // (2) floats, 4 bytes each
          attributes: [
            {shaderLocation: 0, offset: 0, format: 'float32x2'},  // position
          ],
        },
      ],
    },
    fragment: {
      module,
      entryPoint: 'fs',
      targets: [{ format: presentationFormat }],
    },
  });

需要为uniforms设置一个缓冲区

  // color, resolution, padding
  const uniformBufferSize = (4 + 2) * 4 + 8;
  const uniformBuffer = device.createBuffer({
    label: 'uniforms',
    size: uniformBufferSize,
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
  });
 
  const uniformValues = new Float32Array(uniformBufferSize / 4);
 
  // offsets to the various uniform values in float32 indices
  const kColorOffset = 0;
  const kResolutionOffset = 4;
 
  const colorValue = uniformValues.subarray(kColorOffset, kColorOffset + 4);
  const resolutionValue = uniformValues.subarray(kResolutionOffset, kResolutionOffset + 2);
 
  // The color will not change so let's set it once at init time
  colorValue.set([Math.random(), Math.random(), Math.random(), 1]);

在渲染时需要设置分辨率

  function render() {
    ...
 
    // Set the uniform values in our JavaScript side Float32Array
    resolutionValue.set([canvas.width, canvas.height]);
 
    // upload the uniform values to the uniform buffer
    device.queue.writeBuffer(uniformBuffer, 0, uniformValues);

Before we run it lets make the background of the canvas look like graph paper. We’ll set it’s scale so each grid cell of the graph paper is 10x10 pixels and every 100x100 pixels we’ll draw a bolder line.

在运行它之前,需要让画布的背景看起来像方格纸。将设置它的比例,使方格纸的每个网格单元为 10x10 像素,且每 100x100 像素我们将绘制一条粗线。效果图如下
在这里插入图片描述

:root {
  --bg-color: #fff;
  --line-color-1: #AAA;
  --line-color-2: #DDD;
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg-color: #000;
    --line-color-1: #666;
    --line-color-2: #333;
  }
}
canvas {
  display: block;  /* make the canvas act like a block   */
  width: 100%;     /* make the canvas fill its container */
  height: 100%;
  background-color: var(--bg-color);
  background-image: linear-gradient(var(--line-color-1) 1.5px, transparent 1.5px),
      linear-gradient(90deg, var(--line-color-1) 1.5px, transparent 1.5px),
      linear-gradient(var(--line-color-2) 1px, transparent 1px),
      linear-gradient(90deg, var(--line-color-2) 1px, transparent 1px);
  background-position: -1.5px -1.5px, -1.5px -1.5px, -1px -1px, -1px -1px;
  background-size: 100px 100px, 100px 100px, 10px 10px, 10px 10px;  
}

上面的 CSS 应该处理浅色和深色情况。

到目前为止,所有的示例都使用了不透明的画布。为了使其透明,以便可以看到刚刚设置的背景,需要进行一些更改。

首先需要在配置画布为 ‘premultiplied’ 时设置 alphaMode 。它默认为 ‘opaque’ 。

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

然后需要在 GPURenderPassDescriptor 中将画布清除为 0, 0, 0, 0。因为默认的 clearValue 是 0, 0, 0, 0 可以删除将它设置为其他内容。

  const renderPassDescriptor = {
    label: 'our basic canvas renderPass',
    colorAttachments: [
      {
        // view: <- to be filled out when we render
        //clearValue: [0.3, 0.3, 0.3, 1],
        loadOp: 'clear',
        storeOp: 'store',
      },
    ],
  };

有了这个,这是F的显示结果

在这里插入图片描述

注意 F 相对于它后面的网格的大小。 F 数据的顶点位置使 F 宽 100 像素,高 150 像素,与我们显示的相匹配。 F 从 0,0 开始,向右延伸到 100,0,向下延伸到 0,150

现在已经有了基础,让我们添加平移变换。

Translation is just the process of moving things so all we need to do is add translation to our uniforms and add that to our position
平移变换只是移动事物的过程,所以需要做的就是将平移添加到我们的uniforms 并将其与位置相加

struct Uniforms {
  color: vec4f,
  resolution: vec2f,
  translation: vec2f, //here
};
 
struct Vertex {
  @location(0) position: vec2f,
};
 
struct VSOutput {
  @builtin(position) position: vec4f,
};
 
@group(0) @binding(0) var<uniform> uni: Uniforms;
 
@vertex fn vs(vert: Vertex) -> VSOutput {
  var vsOut: VSOutput;
  
  // Add in the translation
  //let position = vert.position;
  let position = vert.position + uni.translation;
 
  // convert the position from pixels to a 0.0 to 1.0 value
  let zeroToOne = position / uni.resolution;
 
  // convert from 0 <-> 1 to 0 <-> 2
  let zeroToTwo = zeroToOne * 2.0;
 
  // covert from 0 <-> 2 to -1 <-> +1 (clip space)
  let flippedClipSpace = zeroToTwo - 1.0;
 
  // flip Y
  let clipSpace = flippedClipSpace * vec2f(1, -1);
 
  vsOut.position = vec4f(clipSpace, 0.0, 1.0);
  return vsOut;
}
 
@fragment fn fs(vsOut: VSOutput) -> @location(0) vec4f {
  return uni.color;
}

需要为uniforms 缓冲区增加空间

  // color, resolution, padding
  //const uniformBufferSize = (4 + 2) * 4 + 8;
  // color, resolution, translation
  const uniformBufferSize = (4 + 2 + 2) * 4; //here
  const uniformBuffer = device.createBuffer({
    label: 'uniforms',
    size: uniformBufferSize,
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
  });
 
  const uniformValues = new Float32Array(uniformBufferSize / 4);
 
  // offsets to the various uniform values in float32 indices
  const kColorOffset = 0;
  const kResolutionOffset = 4;
  const kTranslationOffset = 6; //here
 
  const colorValue = uniformValues.subarray(kColorOffset, kColorOffset + 4);
  const resolutionValue = uniformValues.subarray(kResolutionOffset, kResolutionOffset + 2);
  const translationValue = uniformValues.subarray(kTranslationOffset, kTranslationOffset + 2); //here

然后需要在渲染时设置平移

  const settings = {
    translation: [0, 0],
  };
 
  function render() {
    ...
 
    // Set the uniform values in our JavaScript side Float32Array
    resolutionValue.set([canvas.width, canvas.height]);
    translationValue.set(settings.translation);//here
 
    // upload the uniform values to the uniform buffer
    device.queue.writeBuffer(uniformBuffer, 0, uniformValues);

最后添加一个 UI,这样就可以调整平移距离

import GUI from '/3rdparty/muigui-0.x.module.js';
 
...
  const settings = {
    translation: [0, 0],
  };
 
  const gui = new GUI();
  gui.onChange(render);
  gui.add(settings.translation, '0', 0, 1000).name('translation.x');
  gui.add(settings.translation, '1', 0, 1000).name('translation.y');

现在添加了平移

在这里插入图片描述

请注意它与我们的像素网格相匹配。如果我们将平移设置为 200,300,则绘制 F 时其 0,0 左上角顶点位于 200,300。

这篇文章可能看起来非常简单。尽管我们将其命名为“offset”,但之前已经在几个示例中使用了平移。本文是系列文章的一部分。虽然它很简单,但希望它的要点在我们继续本系列的上下文中有意义。

接下来是旋转。

原文地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/641260.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

啥?PCB拼版对SMT组装有影响!

PCB为什么要拼版&#xff1f; 拼版主要是为了满足生产的需求&#xff0c;有些PCB板太小&#xff0c;不满足做夹具的要求&#xff0c;所以需要拼在一起进行生产。 拼版也可以提高SMT贴片的焊接效率&#xff0c;如只需要过一次SMT&#xff0c;即可完成多块PCB的焊接。 同时也可…

你知道ai绘画工具都有哪些吗?ai画图的软件分享给你

大家好&#xff01;你有没有想过&#xff0c;如果我们能有一种神奇的工具&#xff0c;可以帮助我们实现想象中的绘画作品&#xff0c;该有多好呢&#xff1f;现在&#xff0c;随着人工智能的发展&#xff0c;我们可以借助ai绘画工具来探索艺术的奇妙世界了&#xff01;不过你是…

又双叕搞事?我拿着这份“满级”的JVM笔记,拼进了阿里

JVM JVM&#xff0c;一个熟悉又陌生的名词&#xff0c;从认识Java的第一天起&#xff0c;我们就会听到这个名字&#xff0c;在参加工作的前一两年&#xff0c;面试的时候还会经常被问到JDK&#xff0c;JRE&#xff0c;JVM这三者的区别。 JVM&#xff08;Java Virtual Machine…

汽车IVI中控开发中视频相关的一些知识点

前言: 视频最早的渊源来源于电视。做汽车仪表/IVI中控,尤其是IVI信息娱乐部分,都要涉及到视频这个知识点,各种概念很多,首先需要明确一条主线,那就是SDTV标清电视->HDTV高清电视->UHDTV超高清电视的一个发展脉络,BT601/656是SDTV标清电视接口,BT1120则对应HDTV高…

ShardingSphere

一、基本概念 1、什么是ShardingSphere 1、一套开源的分布式数据库中间件解决方案 2、有三个产品&#xff1a;Sharding-JDBC和Sharding-Proxy、Sharding-Sidecar 3、定位为关系型数据库中间件&#xff0c;合理在分布式环境下使用关系型数据库操作 2、分库分表 1什么是分库…

文物和古建筑防雷综合解决方案

文物和古建筑作为珍贵的历史遗产&#xff0c;需要受到专业的防雷保护&#xff0c;以保持其完整性和安全性。本文将介绍详细的文物和古建筑防雷方案和措施&#xff0c;包括避雷针安装、接地系统建设、监测技术和定期维护等。 引言&#xff1a; 文物和古建筑承载着珍贵的历史记忆…

防雷抗浪涌插排插座推荐,同为科技(TOWE)防雷桌面PDU安全可靠

同为科技TOWE双排防雷抗浪涌桌面PDU插座 随着夏天天气越来越热&#xff0c;强对流天气增多&#xff0c;雷雨天气频发。在雷电季节&#xff0c;通常影响家用电器安全的主要原因是由于雷电感应的侵入&#xff0c;特别是对绝缘强度低、过电压耐受力差的微电子产品影响甚大。而所谓…

JVM笔记(二)

JVM内存管理 在之前&#xff0c;我们了解了JVM的大致运作原理以及相关特性&#xff0c;这一章&#xff0c;我们首先会从内存管理说起。 在传统的C/C开发中&#xff0c;我们经常通过使用申请内存的方式来创建对象或是存放某些数据&#xff0c;但是这样也带来了一些额外的问题&…

一次XxlJob调度任务重复执行的问题排查

目录 东老师的问题1. 为什么会重复执行2. 为什么时间间隔改为1min就不会重复执行** 开始排查先看下任务配置 任务第一次执行排查执行类 》JobThreadJobThread的核心逻辑1.循环消费 一个阻塞队列 不断的去消费队列中TriggerParam 这个参数2.看下TriggerParam&#xff0c;这正是我…

硬件学习 软件 Cadence day10 查看网表导入进度,钻孔保护等一些操作

1. 查看网表导入状态。 2. 放置元器件 之前 画板框 3.放置元器件 4.把导入的DXF 文件变成板框 1.首先导入DXF文件 2. 点击按钮 3. 鼠标点击需要 调整为板框的地方 3.1 其中包括边框 3.2 固定的钻孔 5.给钻孔打上保护&#xff08;防止布线的时候区域错误&#xff0c;在固定的时…

12. AbstractQueuedSynchronizer之AQS

12.1 前置知识 ● 公平锁和非公平锁 ○ 公平锁&#xff1a;锁被释放以后&#xff0c;先申请的线程先得到锁。性能较差一些&#xff0c;因为公平锁为了保证时间上的绝对顺序&#xff0c;上下文切换更频繁 ○ 非公平锁&#xff1a;锁被释放以后&#xff0c;后申请的线程可能会先获…

第58讲:Python编程中最难以理解的递归函数核心概念以及应用案例

文章目录 1.递归函数的概念2.递归函数的使用2.1.案例一2.1.1.需求描述2.1.2.使用常规的循环来实现2.1.3.使用递归函数实现 2.2.案例二2.2.1.需求描述2.2.2.使用常规的循环来实现2.2.3.使用递归函数实现 3.使用递归函数计算阶乘3.1.阶乘的概念3.2.使用递归函数实现阶乘的算法3.3…

分布式Profinet IO模块

PROFINET IO模块是一种用于工业自动化控制系统中的设备控制模块。它使用以太网技术&#xff0c;在现场设备和处理器/控制器之间提供快速、精确和可靠的数据交换。PROFINET IO模块通常是面向过程的&#xff0c;可以用于监测和控制工业过程中的各种设备和参数&#xff0c;如传感器…

Vue中使用editor.md(2):添加拖拽图片上传功能

0. 背景 在对editor.md简单使用后&#xff0c;希望添加图片拖拽或粘贴上传的功能。 简单使用参考&#xff1a;Vue中使用editor.md&#xff08;1&#xff09;&#xff1a;简单使用 1. 实现 1.1 添加粘贴监听 // 使用axios import axios from /api/indexfunction initPasteDra…

什么是Vue的插件?如编写自定义 Plugin?

什么是Vue的插件&#xff1f; 在Vue开发中&#xff0c;我们经常需要使用一些第三方库或功能性模块&#xff0c;Vue插件就是一种将这些库或模块集成到Vue应用中的方式。插件是Vue.js提供的一种机制&#xff0c;用于扩展Vue的功能。插件通常用于封装某些特定的功能&#xff0c;例…

【AI人工智能】 你如果要使用最强大的语言模型,你还要有最精美的浏览器标签页iTab (2)

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&#x1…

UnityA*导航算法,通俗易懂

首先A*寻路算法分为2D算法和3D算法&#xff0c;目前它甚至不如NAVMesh算法效率高。但NAVMesh不适用于2D&#xff0c;因此2D还是要靠A*算法来进行实现。 当前就来说说2D是如何实现的。 首先2DA*算法先要将地图划分成格子分块标记成二维数组 每个格子依据x&#xff0c;y&#xf…

又一新型技术全面铺开,高精度光刻机已突破壁垒,赶超只是时间

众所周知&#xff0c;光刻机是制造高精度芯片的关键步骤。 随着科技的不断进步&#xff0c;光刻机的精度和速度也在不断提高&#xff0c;使得我们可以制造出更小更精细的芯片&#xff0c;满足了人们对于高性能电子设备的需求。 我国的光刻机技术&#xff0c;国产大飞机&#x…

【华为OD机试真题2023B卷 JAVAJS】二维伞的雨滴效应

华为OD2023(B卷)机试题库全覆盖,刷题指南点这里 二维伞的雨滴效应 知识点递归树 时间限制:1s 空间限制:256MB 限定语言:不限 题目描述: 普通的伞在二维平面世界中,左右两侧均有一条边,而两侧伞边最下面各有一个伞坠子,雨滴落到伞面,逐步流到伞坠处,会将伞坠的信息携…

UML类图(二)

相信希望&#xff0c;相信自己 上一章简单介绍了 设计模式的原则(一), 如果没有看过,请观看上一章 本文参考文章: 常见UML符号详解 UML (Unified modeling language) 统一建模语言&#xff0c;是一种用于软件系统分析和设计的语言工具&#xff0c; 它用于帮助软件开发人员进行…