目錄
- 1. 技術說明
- 2. 三角形例子
- HTML
- JavaScript
- 3. 著色器決議
- 幀緩沖坐標圖示
- 原始碼
1. 技術說明
- 使用最新 Edge/Chrome Canary 瀏覽器
- 使用 VSCode 插件
LiveServer的 HTTP 服務器對本機提供 5500 埠的頁面服務,即http://localhost:5500/index.html - 使用 es-module 風格的 JavaScript 實作
2. 三角形例子
先上效果,后面再決議片元著色器:

HTML
html 部分就簡單一些
<canvas id="c" height="600" style="border: 1px solid darkseagreen;"></canvas>
<script type="module" src="https://www.cnblogs.com/onsummer/archive/2022/04/27/main.js"></script>
不出意外的話,你可以看到一個帶暗綠色邊框的 canvas,長寬均為 600 像素,
JavaScript
JavaScript 代碼也比較簡單,省略大部分動態代碼和有無判斷代碼:
const canvas = document.getElementById('c')
const shaderText = `/* 著色器代碼,后面會給 */`
const init = async () => {
const adapter = await navigation.gpu.requestAdapter()
const device = await adapter.requestDevice()
const context = canvas.getContext('webgpu')
const presentationFormat = context.getPreferredFormat(adapter)
context.configure({
device,
format: presentationFormat,
size: [ 600, 600 ], // canvas 的畫圖尺寸
})
const pipeline = device.createRenderPipeline({
vertex: {
module: device.createShaderModule({
code: shaderText
}),
entryPoint: 'vertexMain'
},
fragment: {
module: device.createShaderModule({
code: shaderText
}),
entryPoint: 'fragmentMain',
targets: [{ format: presentationFormat }],
},
primitive: { topology: 'triangle-list' },
})
const render = () => {
/*
每幀創建編碼器并“錄制”編碼程序,最終提交給設備
*/
const commandEncoder = device.createCommandEncoder()
const textureView = context.getCurrentTexture().createView()
const renderPassDescriptor = {
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.draw(3, 1, 0, 0)
passEncoder.end()
device.queue.submit([commandEncoder.finish()])
requestAnimationFrame(render)
}
requestAnimationFrame(render)
} // async function init
init()
我保留了完整的 rAF 幀影片結構,
為了方便說明內置在片元著色器中的幀緩沖坐標變數,我將三角形頂點值寫死在頂點著色器中,見下文,
3. 著色器決議
著色器代碼:
const shaderText = /* wgsl */`
@stage(vertex)
fn vertexMain(
@builtin(vertex_index) VertexIndex: u32
) -> @builtin(position) vec4<f32> {
var pos = array<vec2<f32>, 3>(
vec2<f32>(0.0, 0.5),
vec2<f32>(-0.5, -0.5),
vec2<f32>(0.5, -0.5)
);
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
}
@stage(fragment)
fn fragmentMain(
@builtin(position) FrameBufferCoord: vec4<f32>
) -> @location(0) vec4<f32> {
var color = vec4<f32>(1.0, 0.5, 0.0, .5);
let x: f32 = (FrameBufferCoord.x - 300.0) / 300.0;
let y: f32 = (-FrameBufferCoord.y + 300.0) / 300.0;
let r: f32 = sqrt(x * x + y * y);
if (x > -0.1 && x < 0.1) {
return vec4<f32>(1.0, 0.0, 0.5, 1.0);
} else if (y > -0.1 && y < 0.1) {
return vec4<f32>(0.0, 0.5, 1.0, 1.0);
} else if (r < 0.4) {
return vec4<f32>(FrameBufferCoord.rgb / 600.0, 0.5);
} else {
discard;
}
}
`
WGSL 褒貶不一,就不說它的語法如何了,
主要是看片元著色器的輸入,@builtin(position) FrameBufferCoord: vec4<f32>,它向每一個片元著色器傳入了當前片元的幀緩沖坐標,型別是 vec4<f32>
幀緩沖坐標圖示
幀緩沖在 WebGPU 規范中有說明,它的坐標軸、原點和坐標值域是這樣的:

我對幀緩沖坐標進行了縮放、平移,也就是計算了 x、y,將原點移動到 canvas 中央,然后把坐標區間從 [0, 600] 映射到 [-1, 1],
然后就是最后的那個多步邏輯分支了,也很簡單:
- 第一個 if 對應圖中的 粉色
- 第二個 if 對應圖中的 藍色
- 第三個 if 對應圖中三角形區域內、藍色、粉色像素外的 圓區域,半徑是 0.4(映射后的半徑),它使用幀緩沖坐標作為顏色值(除以幀緩沖的長寬 600 映射到了
[0, 1]),加了 0.5 的透明度 - 最后其它的片元使用陳述句
discard丟棄,即不渲染
OK,今天就學到這里,
原始碼
微云
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/466005.html
標籤:其他
上一篇:Vulnhub-DC-4靶機實戰
