代碼分析說明:
import { makeSample, SampleInit } from '../../components/SampleLayout';
import fullscreenTexturedQuadWGSL from '../../shaders/fullscreenTexturedQuad.wgsl';
import sampleExternalTextureWGSL from '../../shaders/sampleExternalTexture.frag.wgsl';
const init: SampleInit = async ({ canvas, pageState, gui }) => {
// Set video element
// 創(chuàng)建一個(gè)視頻元素
const video = document.createElement('video');
video.loop = true;
video.autoplay = true;
video.muted = true;
video.src = '../assets/video/pano.webm';
// 加載完成后自動(dòng)播放視頻
await video.play();
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',
});
const pipeline = device.createRenderPipeline({
layout: 'auto',
vertex: {
module: device.createShaderModule({
code: fullscreenTexturedQuadWGSL,
}),
entryPoint: 'vert_main',
},
fragment: {
module: device.createShaderModule({
code: sampleExternalTextureWGSL,
}),
entryPoint: 'main',
targets: [
{
format: presentationFormat,
},
],
},
primitive: {
topology: 'triangle-list',
},
});
const sampler = device.createSampler({
magFilter: 'linear',
minFilter: 'linear',
});
const settings = {
requestFrame: 'requestAnimationFrame',
};
gui.add(settings, 'requestFrame', [
'requestAnimationFrame',
'requestVideoFrameCallback',
]);
function frame() {
// Sample is no longer the active page.
if (!pageState.active) return;
const uniformBindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{
binding: 1,
resource: sampler,
},
{
binding: 2,
// 使用 importExternalTexture 綁定視頻
resource: device.importExternalTexture({
source: video,
}),
},
],
});
const commandEncoder = device.createCommandEncoder();
const textureView = context.getCurrentTexture().createView();
const renderPassDescriptor: GPURenderPassDescriptor = {
colorAttachments: [
{
view: textureView,
clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },
loadOp: 'clear',
storeOp: 'store',
},
],
};
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.setPipeline(pipeline);
passEncoder.setBindGroup(0, uniformBindGroup);
passEncoder.draw(6);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
if (settings.requestFrame == 'requestVideoFrameCallback') {
// 根據(jù)視頻播放的回調(diào)渲染
video.requestVideoFrameCallback(frame);
} else {
// 瀏覽器的逐幀回調(diào)
requestAnimationFrame(frame);
}
}
};
頂點(diǎn)著色器
@group(0) @binding(0) var mySampler : sampler;
@group(0) @binding(1) var myTexture : texture_2d<f32>;
struct VertexOutput {
@builtin(position) Position : vec4<f32>,
@location(0) fragUV : vec2<f32>,
}
@vertex
fn vert_main(@builtin(vertex_index) VertexIndex : u32) -> VertexOutput {
// 頂點(diǎn)數(shù)據(jù)為 quad
const pos = array(
vec2( 1.0, 1.0), // 右上
vec2( 1.0, -1.0), // 右下
vec2(-1.0, -1.0), // 左下
vec2( 1.0, 1.0), // 右上
vec2(-1.0, -1.0), // 右下
vec2(-1.0, 1.0), // 左上
);
// 對(duì)于的UV 坐標(biāo), 左上是 0 0 點(diǎn)
const uv = array(
vec2(1.0, 0.0),
vec2(1.0, 1.0),
vec2(0.0, 1.0),
vec2(1.0, 0.0),
vec2(0.0, 1.0),
vec2(0.0, 0.0),
);
var output : VertexOutput;
output.Position = vec4(pos[VertexIndex], 0.0, 1.0);
output.fragUV = uv[VertexIndex];
return output;
}
@fragment
fn frag_main(@location(0) fragUV : vec2<f32>) -> @location(0) vec4<f32> {
return textureSample(myTexture, mySampler, fragUV);
}
片元著色器
@group(0) @binding(1) var mySampler: sampler;
@group(0) @binding(2) var myTexture: texture_external;
@fragment
fn main(@location(0) fragUV : vec2<f32>) -> @location(0) vec4<f32> {
// 從紋理中采樣顏色
return textureSampleBaseClampToEdge(myTexture, mySampler, fragUV);
}
總結(jié)步驟:
- 該示例主要是如何將視頻加入到WebGPU渲染中
- 加載視頻
- 在createBindGroup中,綁定紋理采用
device.importExternalTexture({ source: video, }
- 頂點(diǎn)數(shù)據(jù)定位為NDC坐標(biāo)即可,
- 逐幀渲染使用盏道,視頻回調(diào)
video.requestVideoFrameCallback
或者瀏覽器自帶的requestAnimationFrame
- 在片元著色器中萤晴,紋理的的類型為
texture_external
,需要注意一下