Instanced Cube

代碼分析說明:

  import { mat4, vec3 } from 'wgpu-matrix';
import { makeSample, SampleInit } from '../../components/SampleLayout';

import {
  cubeVertexArray,
  cubeVertexSize,
  cubeUVOffset,
  cubePositionOffset,
  cubeVertexCount,
} from '../../meshes/cube';

import instancedVertWGSL from './instanced.vert.wgsl';
import vertexPositionColorWGSL from '../../shaders/vertexPositionColor.frag.wgsl';

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

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

  const devicePixelRatio = window.devicePixelRatio;
  canvas.width = canvas.clientWidth * devicePixelRatio;
  canvas.height = canvas.clientHeight * devicePixelRatio;
  const presentationFormat = navigator.gpu.getPreferredCanvasFormat();

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

  // Create a vertex buffer from the cube data.
  const verticesBuffer = device.createBuffer({
    size: cubeVertexArray.byteLength,
    usage: GPUBufferUsage.VERTEX,
    mappedAtCreation: true,
  });
  new Float32Array(verticesBuffer.getMappedRange()).set(cubeVertexArray);
  verticesBuffer.unmap();

  const pipeline = device.createRenderPipeline({
    layout: 'auto',
    vertex: {
      module: device.createShaderModule({
        code: instancedVertWGSL,
      }),
      entryPoint: 'main',
      buffers: [
        {
          arrayStride: cubeVertexSize,
          attributes: [
            {
              // position
              shaderLocation: 0,
              offset: cubePositionOffset,
              format: 'float32x4',
            },
            {
              // uv
              shaderLocation: 1,
              offset: cubeUVOffset,
              format: 'float32x2',
            },
          ],
        },
      ],
    },
    fragment: {
      module: device.createShaderModule({
        code: vertexPositionColorWGSL,
      }),
      entryPoint: 'main',
      targets: [
        {
          format: presentationFormat,
        },
      ],
    },
    primitive: {
      topology: 'triangle-list',

      // Backface culling since the cube is solid piece of geometry.
      // Faces pointing away from the camera will be occluded by faces
      // pointing toward the camera.
      cullMode: 'back',
    },

    // Enable depth testing so that the fragment closest to the camera
    // is rendered in front.
    depthStencil: {
      depthWriteEnabled: true,
      depthCompare: 'less',
      format: 'depth24plus',
    },
  });

  const depthTexture = device.createTexture({
    size: [canvas.width, canvas.height],
    format: 'depth24plus',
    usage: GPUTextureUsage.RENDER_ATTACHMENT,
  });

  const xCount = 4;
  const yCount = 4;
  // 實(shí)例化渲染的總數(shù)
  const numInstances = xCount * yCount;
  const matrixFloatCount = 16; // 4x4 matrix
  // 一個(gè)矩陣的大小
  const matrixSize = 4 * matrixFloatCount;
  // 實(shí)例化需要的矩陣buffer的大小
  const uniformBufferSize = numInstances * matrixSize;

  // Allocate a buffer large enough to hold transforms for every
  // instance. 實(shí)例化矩陣uniform buffer
  const uniformBuffer = device.createBuffer({
    size: uniformBufferSize,
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
  });

  const uniformBindGroup = device.createBindGroup({
    layout: pipeline.getBindGroupLayout(0),
    entries: [
      {
        binding: 0,
        resource: {
          buffer: uniformBuffer,
        },
      },
    ],
  });

  const aspect = canvas.width / canvas.height;
  const projectionMatrix = mat4.perspective(
    (2 * Math.PI) / 5,
    aspect,
    1,
    100.0
  );

  type Mat4 = mat4.default;
  // 模型矩陣的數(shù)組
  const modelMatrices = new Array<Mat4>(numInstances);
  // MVP 矩陣的數(shù)據(jù) 
  const mvpMatricesData = new Float32Array(matrixFloatCount * numInstances);

  const step = 4.0;

  // Initialize the matrix data for every instance.
  let m = 0;
  for (let x = 0; x < xCount; x++) {
    for (let y = 0; y < yCount; y++) {
      // 設(shè)置4行4列不同的位置扔涧,更改每一個(gè)模型矩陣
      modelMatrices[m] = mat4.translation(
        vec3.fromValues(
          step * (x - xCount / 2 + 0.5),
          step * (y - yCount / 2 + 0.5),
          0
        )
      );
      m++;
    }
  }

  // 創(chuàng)建并設(shè)置視圖矩陣
  const viewMatrix = mat4.translation(vec3.fromValues(0, 0, -12));

  const tmpMat4 = mat4.create();

  // Update the transformation matrix data for each instance.
  function updateTransformationMatrix() {
    const now = Date.now() / 1000;

    let m = 0,
      i = 0;
    for (let x = 0; x < xCount; x++) {
      for (let y = 0; y < yCount; y++) {
        // 旋轉(zhuǎn)每一個(gè)模型矩陣
        mat4.rotate(
          modelMatrices[i],
          vec3.fromValues(
            Math.sin((x + 0.5) * now),
            Math.cos((y + 0.5) * now),
            0
          ),
          1,
          tmpMat4
        );

        mat4.multiply(viewMatrix, tmpMat4, tmpMat4);
        mat4.multiply(projectionMatrix, tmpMat4, tmpMat4);
         // 計(jì)算得到每一個(gè)mvp矩陣尸饺,存在一個(gè)大的float32Array中
        mvpMatricesData.set(tmpMat4, m);

        i++;
        m += matrixFloatCount;
      }
    }
  }

  const renderPassDescriptor: GPURenderPassDescriptor = {
    colorAttachments: [
      {
        view: undefined, // Assigned later

        clearValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0 },
        loadOp: 'clear',
        storeOp: 'store',
      },
    ],
    depthStencilAttachment: {
      view: depthTexture.createView(),

      depthClearValue: 1.0,
      depthLoadOp: 'clear',
      depthStoreOp: 'store',
    },
  };

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

    // Update the matrix data.
    updateTransformationMatrix();
    // 提交所有的mvp矩陣
    device.queue.writeBuffer(
      uniformBuffer,
      0,
      mvpMatricesData.buffer,
      mvpMatricesData.byteOffset,
      mvpMatricesData.byteLength
    );

    renderPassDescriptor.colorAttachments[0].view = context
      .getCurrentTexture()
      .createView();

    const commandEncoder = device.createCommandEncoder();
    const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
    passEncoder.setPipeline(pipeline);
    passEncoder.setBindGroup(0, uniformBindGroup);
    passEncoder.setVertexBuffer(0, verticesBuffer);
    // 這里的參數(shù)不同,設(shè)置了實(shí)例化的個(gè)數(shù) numInstances
    passEncoder.draw(cubeVertexCount, numInstances, 0, 0);
    passEncoder.end();
    device.queue.submit([commandEncoder.finish()]);

    requestAnimationFrame(frame);
  }
  requestAnimationFrame(frame);
};

頂點(diǎn)著色器

  struct Uniforms {
  modelViewProjectionMatrix : array<mat4x4<f32>, 16>,
}

@binding(0) @group(0) var<uniform> uniforms : Uniforms;

struct VertexOutput {
  @builtin(position) Position : vec4<f32>,
  @location(0) fragUV : vec2<f32>,
  @location(1) fragPosition: vec4<f32>,
}

@vertex
fn main(
  // 實(shí)例化渲染對象的索引
  @builtin(instance_index) instanceIdx : u32,
  @location(0) position : vec4<f32>,
  @location(1) uv : vec2<f32>
) -> VertexOutput {
  var output : VertexOutput;
   // 根據(jù)索引獲取不同的mvp矩陣
  output.Position = uniforms.modelViewProjectionMatrix[instanceIdx] * position;
  output.fragUV = uv;
  output.fragPosition = 0.5 * (position + vec4(1.0));
  return output;
}

片元著色器

@fragment
fn main(
  @location(0) fragUV: vec2<f32>,
  @location(1) fragPosition: vec4<f32>
) -> @location(0) vec4<f32> {
  return fragPosition;
}

總結(jié)步驟

實(shí)例化渲染的不同之處:

  1. 創(chuàng)建一個(gè)可以容納所有實(shí)例化矩陣大小的buffer
  2. 為每個(gè)實(shí)例創(chuàng)建不同的模型矩陣顷霹,并將所有的模型矩陣與視圖熊赖,投影矩陣相乘歧譬,放在一個(gè)大的Float32Array的數(shù)組中岸浑,保存所有的MVP矩陣
  3. 編碼器設(shè)置draw函數(shù)時(shí),傳遞實(shí)例化的數(shù)量passEncoder.draw(cubeVertexCount, numInstances, 0, 0)
  4. 在頂點(diǎn)著色器中瑰步,根據(jù)內(nèi)建的instance_index 獲取不同的mvp矩陣矢洲,具體如:
  // 根據(jù)索引獲取不同的mvp矩陣
  output.Position = uniforms.modelViewProjectionMatrix[instanceIdx] * position;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市缩焦,隨后出現(xiàn)的幾起案子读虏,更是在濱河造成了極大的恐慌,老刑警劉巖袁滥,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盖桥,死亡現(xiàn)場離奇詭異,居然都是意外死亡题翻,警方通過查閱死者的電腦和手機(jī)揩徊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嵌赠,“玉大人塑荒,你說我怎么就攤上這事〗Γ” “怎么了齿税?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長炊豪。 經(jīng)常有香客問我凌箕,道長,這世上最難降的妖魔是什么词渤? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任牵舱,我火速辦了婚禮,結(jié)果婚禮上缺虐,老公的妹妹穿的比我還像新娘芜壁。我一直安慰自己,他們只是感情好志笼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著把篓,像睡著了一般纫溃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上韧掩,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天紊浩,我揣著相機(jī)與錄音,去河邊找鬼。 笑死坊谁,一個(gè)胖子當(dāng)著我的面吹牛费彼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播口芍,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼箍铲,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鬓椭?” 一聲冷哼從身側(cè)響起颠猴,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎小染,沒想到半個(gè)月后翘瓮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡裤翩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年资盅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片踊赠。...
    茶點(diǎn)故事閱讀 40,117評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡呵扛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出臼疫,到底是詐尸還是另有隱情择份,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布烫堤,位于F島的核電站荣赶,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鸽斟。R本人自食惡果不足惜拔创,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望富蓄。 院中可真熱鬧剩燥,春花似錦、人聲如沸立倍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽口注。三九已至变擒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寝志,已是汗流浹背娇斑。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工策添, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人毫缆。 一個(gè)月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓唯竹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親苦丁。 傳聞我的和親對象是個(gè)殘疾皇子浸颓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容