{"id":280,"date":"2022-04-02T22:59:24","date_gmt":"2022-04-02T14:59:24","guid":{"rendered":"https:\/\/blog.indeex.club\/?p=280"},"modified":"2022-04-13T14:59:24","modified_gmt":"2022-04-13T06:59:24","slug":"webgpu%e5%9f%ba%e7%a1%80%ef%bc%88%e4%ba%94%ef%bc%89-%e5%8a%a0%e8%bd%bd%e6%a8%a1%e5%9e%8b","status":"publish","type":"post","link":"https:\/\/blog.indeex.club\/index.php\/2022\/04\/02\/webgpu%e5%9f%ba%e7%a1%80%ef%bc%88%e4%ba%94%ef%bc%89-%e5%8a%a0%e8%bd%bd%e6%a8%a1%e5%9e%8b\/","title":{"rendered":"WebGPU\u57fa\u7840\uff08\u4e94\uff09- \u52a0\u8f7d\u6a21\u578b"},"content":{"rendered":"<p>\u5728WebGPU\u5f00\u53d1\u4e2d\uff0c\u4f1a\u52a0\u8f7d\u5404\u79cd\u5404\u6837\u7684\u6a21\u578b\uff0c<\/p>\n<h2>\u4e0b\u8f7d\u6a21\u578b<\/h2>\n<p>\u8fd9\u91cc\u4ee5<strong>\u901a\u7528OBJ\u6a21\u578b<\/strong>\u4e3a\u4f8b\uff0c\u9996\u5148\u662f\u52a0\u8f7d\u6587\u4ef6\uff1a<\/p>\n<pre><code class=\"language-typescript\">\/\/...\n\nprivate modeObj?: any;\nprivate modeData?: any;\n\n\/\/...\n\n\/\/...\n\nfunction loadFile(url: string) {\n    new Promise((resolve, reject) =&gt; {\n\n    });\n    var xhr = new XMLHttpRequest();\n    xhr.open(&#039;GET&#039;, url, false);\n    xhr.onload = function () {\n        if (xhr.status != 200) {\n            console.log(&#039;\u52a0\u8f7d\u4e2d:&#039; + xhr.status + &#039;\/ &#039; + xhr.statusText);\n        } else {\n            result = xhr.responseText;\n        }\n    }\n    xhr.send();\n}\n\nmodeObj = await loadFile(CUBE, &#039;https:\/\/hungking.cc\/xxxx.obj&#039;);\n\n\/\/...<\/code><\/pre>\n<p>\u8fd9\u91cc\u4e3a\u4e86\u9632\u6b62\u65e0\u8111\u6284\u88ad\uff0c\u4f7f\u7528\u539f\u751f\u3002\u5b9e\u9645\u751f\u4ea7\u4f1a\u4f7f\u7528<em>fetch<\/em>\u6216\u8005\u4f7f\u7528<em>Promise<\/em>\uff0c\u7b2c\u4e09\u65b9\u5e93<em>axios<\/em>\u7b49\u3002<\/p>\n<h2>\u6a21\u578b\u5904\u7406<\/h2>\n<p>\u6a21\u578b\u52a0\u8f7d\u5b8c\u540e\u4e0d\u80fd\u76f4\u63a5\u4f7f\u7528\uff0c\u9700\u8981\u5904\u7406\u6210\u53ef\u88ab\u8bc6\u522b\u7684\u6570\u636e\u7ed3\u6784\uff0c \u6570\u636e\u5904\u7406\u6bd4\u8f83\u7b80\u5355\uff1a<\/p>\n<pre><code class=\"language-typescript\">\/\/...\n\nasync function loadObj(obj: any) {\n    if (!obj) throw new Error(&quot;\u6570\u636e\u9519\u8bef\uff1a&quot; + obj);\n    let objText = obj.trim() + &quot;\\n&quot;;\n    let startSTR = 0;\n    let finishSTR = objText.indexOf(&quot;\\n&quot;, 0);\n    let V = [1];\n    let T = [1];\n    let N = [1];\n    let F = [1];\n    let vertex = [];\n    let uv = [];\n    let normals = [];\n    let faces = [];\n    let face = [];\n    let faceindex = 0;\n    let objLineArray = objText.split(&quot;\\n&quot;);\n\n    for (let indexLine = 0; indexLine &lt; objLineArray.length; indexLine++) {\n        let line = objLineArray[indexLine];\n        line = line.trim().replace(\/\\s+\/g, &#039; &#039;);\n        let lineData: any = line.split(&quot; &quot;);\n        const typeVertexData = lineData[0];\n        if (typeVertexData === &quot;v&quot;) {\n            lineData.shift();\n            lineData = lineData.map(parseFloat);\n            V.push(lineData);\n        } else if (typeVertexData === &quot;vt&quot;) {\n            lineData.shift();\n            lineData = lineData.map(parseFloat);\n            lineData[2] = 0.0;\n            T.push(lineData.slice(0, 2));\n        } else if (typeVertexData === &quot;vn&quot;) {\n            lineData.shift();\n            lineData = lineData.map(parseFloat);\n            N.push(lineData);\n        } else if (typeVertexData === &quot;f&quot;) {\n            lineData.shift();\n            for (let index = 0; index &lt; lineData.length - 2; index++) {\n                for (let indexTriangel = 0; indexTriangel &lt; 3; indexTriangel++) {\n                    let vertexDataArray = lineData[indexTriangel + index];\n                    if (indexTriangel == 2) {\n                        indexTriangel = lineData.length - 1;\n                        vertexDataArray = lineData[indexTriangel];\n                    }\n\n                    let vertexData = vertexDataArray.split(&quot;\/&quot;);\n                    for (let i = 0; i &lt; 3; i++) {\n                        if (vertexData[0] &lt; 0) {\n                            vertexData[0] = V.length + 1 + parseInt(vertexData[0]);\n                        }\n                        if (vertexData[1] &lt; 0) {\n                            vertexData[1] = T.length + 1 + parseInt(vertexData[1]);\n                        }\n                        if (vertexData[2] &lt; 0) {\n                            vertexData[2] = N.length + 1 + parseInt(vertexData[2]);\n                        }\n                    }\n\n                    for (let index = 0; index &lt; vertexData.length; index++) {\n                        switch (index) {\n                            case 0:\n                                vertex.push(V[vertexData[0]]);\n                                break;\n                            case 1:\n                                uv.push(T[vertexData[1]]);\n                                break;\n                            case 2:\n                                normals.push(N[vertexData[2]]);\n                                break;\n                            default:\n                                break;\n                        }\n                    }\n                    faces.push(faceindex);\n                    faceindex++;\n                }\n            }\n        }\n    }\n\n    return {\n        vertex,\n        uv,\n        normals,\n        faces,\n    };\n}\n\n\/\/...\n\nmodelData = await loadModel(modeObj);<\/code><\/pre>\n<p>\u5904\u7406\u5b8c\u540e\uff0c\u6570\u7ec4\u4e2d\u542b\u6709\u591a\u7ef4\u6570\u7ec4\uff0c\u9700\u8981\u6241\u5e73\u5316\u5904\u7406\uff0c\u4e5f\u53ef\u4ee5\u5728\u5904\u7406\u6570\u636e\u65f6\u76f4\u63a5\u5904\u7406\u6389\uff1a<\/p>\n<pre><code class=\"language-typescript\">\/\/...\n\nmodel_vertex = new Float32Array(modelData.vertex.flat());\nmodel_uv = new Float32Array(modelData.uv.flat());\nmodel_index = new Uint32Array(modelData.faces.flat());\n\n\/\/...<\/code><\/pre>\n<h2>Uniform Data<\/h2>\n<p>\u7136\u540e\u5904\u7406<strong>uniform<\/strong>\u6570\u636e\uff0c\u9996\u5148\u521b\u5efa\uff1a<\/p>\n<pre><code class=\"language-typescript\">\/\/...\n\nconst uniformBuffer = device.createBuffer({\n    size: 64 + 64 + 64,\n    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST\n});\n\n\/\/...\n\nlet MODELMATRIX = mat4.create();\nlet VIEWMATRIX = mat4.create();\nlet PROJMATRIX = mat4.create();\n\nmat4.lookAt(VIEWMATRIX, [0.0, 0.0, 5.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0]);\nmat4.identity(PROJMATRIX);\nmat4.perspective(PROJMATRIX, fovy, canvas.width \/ canvas.height, 1, 25);\n\n\/\/...<\/code><\/pre>\n<p>\u8fd9\u91cc\u53ef\u4ee5\u5c01\u88c5\u5230\u4e4b\u524d\u7684<strong>camera<\/strong>\u4e2d\u53bb\u3002<\/p>\n<p>\u7136\u540e\u662f\u9876\u70b9\u3001UV\u3001\u7d22\u5f15\u7684<strong>buffer<\/strong>\uff1a<\/p>\n<pre><code class=\"language-typescript\">\/\/...\n\nconst vertexBuffer = device.createBuffer({\n    size: cube_vertex.byteLength,\n    usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n    mappedAtCreation: true\n});\n\nnew Float32Array(vertexBuffer.getMappedRange()).set(cube_vertex);\n\nvertexBuffer.unmap();\n\nconst uvBuffer = device.createBuffer({\n    size: cube_uv.byteLength,\n    usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n    mappedAtCreation: true\n});\n\nnew Float32Array(uvBuffer.getMappedRange()).set(cube_uv);\n\nuvBuffer.unmap();\n\nconst indexBuffer = device.createBuffer({\n    size: cube_index.byteLength,\n    usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,\n    mappedAtCreation: true\n});\n\nnew Uint32Array(indexBuffer.getMappedRange()).set(cube_index);\nindexBuffer.unmap();\n\n\/\/...<\/code><\/pre>\n<h2>\u7ba1\u7ebf<\/h2>\n<p>\u63a5\u7740\u5904\u7406\u7ba1\u7ebf\uff1a<\/p>\n<pre><code class=\"language-typescript\">const pipeline = device.createRenderPipeline({\n    vertex: {\n        module: device.createShaderModule({\n            code: shaderVertexCode,\n        }),\n        entryPoint: &quot;main&quot;,\n        buffers: [{\n            arrayStride: 4 * 3,\n            attributes: [{\n                shaderLocation: 0,\n                format: &quot;float32x3&quot;,\n                offset: 0\n            }]\n        },\n        {\n            arrayStride: 4 * 2,\n            attributes: [{\n                shaderLocation: 1,\n                format: &quot;float32x2&quot;,\n                offset: 0\n            }]\n        }\n        ]\n    },\n    fragment: {\n        module: device.createShaderModule({\n            code: shaderFragmentCode,\n        }),\n        entryPoint: &quot;main&quot;,\n        targets: [{\n            format: format,\n        },],\n    },\n    primitive: {\n        topology: &quot;triangle-list&quot;,\n        frontFace: &quot;ccw&quot;,\n        cullMode: &quot;back&quot;\n    },\n    depthStencil: {\n        format: &quot;depth24plus&quot;,\n        depthWriteEnabled: true,\n        depthCompare: &quot;less&quot;\n    }\n});<\/code><\/pre>\n<h2>\u7ed1\u5b9a\u7ec4<\/h2>\n<pre><code class=\"language-typescript\">\/\/...\n\nconst uniformBindGroup = device.createBindGroup({\n    layout: pipeline.getBindGroupLayout(0),\n    entries: [{\n        binding: 0,\n        resource: {\n            buffer: uniformBuffer,\n            offset: 0,\n            size: 64 + 64 + 64\n        }\n    },\n    ]\n});\n\n\/\/...<\/code><\/pre>\n<h2>\u7eb9\u7406<\/h2>\n<p>\u4f46\u662f\uff0c\u8fd9\u79cd\u52a0\u8f7d\u53ea\u6709\u6a21\u578b\uff0c\u6ca1\u6709\u7eb9\u7406\uff0c\u53ef\u4ee5\u52a0\u8f7d\u7eb9\u7406\uff1a<\/p>\n<pre><code class=\"language-typescript\">\/\/...\n\nlet img: any = new Image();\nimg.src = &#039;https:\/\/hungking.cc\/xxxxxx.png&#039;;\nimg.crossOrigin = true;\nawait img.decode();\n\nconst imageBitmap = await createImageBitmap(img);\n\nconst sampler = device.createSampler({\n    minFilter: &#039;linear&#039;,\n    magFilter: &#039;linear&#039;,\n    addressModeU: &#039;repeat&#039;,\n    addressModeV: &#039;repeat&#039;\n});\n\nconst texture = device.createTexture({\n    size: [imageBitmap.width, imageBitmap.height, 1],\n    format: &#039;rgba8unorm&#039;,\n    usage: GPUTextureUsage.TEXTURE_BINDING |\n        GPUTextureUsage.COPY_DST |\n        GPUTextureUsage.RENDER_ATTACHMENT\n});\n\ndevice.queue.copyExternalImageToTexture({ source: imageBitmap }, { texture: texture }, [imageBitmap.width, imageBitmap.height]);<\/code><\/pre>\n<p>\u7eb9\u7406\u53ef\u4ee5\u81ea\u884c\u8c03\u6574\uff0c\u6bd4\u5982\uff1a<\/p>\n<pre><code class=\"language-typescript\">const sampler = device.createSampler({\n    \/\/...\n    mipmapFilter : &quot;nearest&quot;,\n    \/\/...\n});<\/code><\/pre>\n<p>\u5c06\u7eb9\u7406\u7ed1\u5b9a\u5230\u7ec4\uff1a<\/p>\n<pre><code class=\"language-typescript\">const uniformBindGroup = device.createBindGroup({\n    \/\/...\n\n    {\n        binding: 1,\n        resource: sampler\n    },\n    {\n        binding: 2,\n        resource: texture.createView()\n    }\n\n    \/\/...\n});<\/code><\/pre>\n<h2>\u961f\u5217<\/h2>\n<p>\u4e4b\u540e\u6240\u6709\u7684\u64cd\u4f5c\u90fd\u8981\u63d0\u4ea4\u5230\u961f\u5217\u4e2d\uff0c\u6bd4\u5982\uff1a<\/p>\n<pre><code class=\"language-typescript\">\/\/...\n\ndevice.queue.writeBuffer(uniformBuffer, 0, PROJMATRIX);\ndevice.queue.writeBuffer(uniformBuffer, 64, VIEWMATRIX);\ndevice.queue.writeBuffer(uniformBuffer, 64 + 64, MODELMATRIX);\n\n\/\/...<\/code><\/pre>\n<h2>commandEncoder\u548cDraw<\/h2>\n<p>\u63a5\u7740\u662f\u501f\u9274Apple\u8bbe\u8ba1\u4e2d\u7684<strong>commandEncoder<\/strong>\uff0c\u7136\u540e\u662f\u6e32\u67d3\uff0c\u6700\u540e\u589e\u52a0\u4ea4\u4e92\uff0c\u4e4b\u524d\u5df2\u7ecf\u7b80\u5355\u63d0\u8fc7\uff0c\u8fd9\u91cc\u4e0d\u518d\u8d58\u8ff0\u3002<\/p>\n<pre><code class=\"language-typescript\">\/\/...\n\nmat4.rotateY(MODELMATRIX, MODELMATRIX, ly);\nmat4.rotateX(MODELMATRIX, MODELMATRIX, lx);\ndevice.queue.writeBuffer(uniformBuffer, 64 + 64, MODELMATRIX);\n\nconst commandEncoder = device.createCommandEncoder();\ntextureView = context.getCurrentTexture().createView();\nrenderPassDescription.colorAttachments[0].view = textureView;\n\nconst renderPass = commandEncoder.beginRenderPass(renderPassDescription);\n\nrenderPass.setPipeline(pipeline);\nrenderPass.setVertexBuffer(0, vertexBuffer);\nrenderPass.setVertexBuffer(1, uvBuffer);\nrenderPass.setIndexBuffer(indexBuffer, &quot;uint32&quot;);\nrenderPass.setBindGroup(0, uniformBindGroup);\nrenderPass.drawIndexed(cube_index.length);\nrenderPass.end();\n\ndevice.queue.submit([commandEncoder.finish()]);\n\n\/\/...<\/code><\/pre>\n<p>\u76ee\u524d\u6839\u636e\u65b0\u7684\u6807\u51c6\uff0c\u90e8\u5206\u53c2\u6570\u5df2\u7ecf\u5f03\u7528\u6216\u6539\u6210\u5176\u4ed6\u53c2\u6570\u4e86\uff0c\u6bd4\u5982\uff1a<\/p>\n<pre><code class=\"language-typescript\">\/\/..\n\nlet textureView = context.getCurrentTexture().createView();\n\nlet depthTexture = device.createTexture({\n    size: [canvas.clientWidth * devicePixelRatio, canvas.clientHeight * devicePixelRatio, 1],\n    format: &quot;depth24plus&quot;,\n    usage: GPUTextureUsage.RENDER_ATTACHMENT\n});\n\n\/\/...\n\nconst renderPassDescription = {\n    colorAttachments: [{\n        view: textureView,\n        loadValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0 },\n        clearValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0 },\n        loadValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0 },\n        loadOp: &quot;load&quot;,\n        storeOp: &quot;store&quot;,\n    },],\n    depthStencilAttachment: {\n        view: depthTexture.createView(),\n        depthLoadValue: 1.0,\n        depthLoadOp: &quot;clear&quot;,\n        depthClearValue: 1.0,\n        depthStoreOp: &quot;store&quot;,\n        stencilLoadValue: 0,\n        stencilStoreOp: &quot;store&quot;,\n        stencilLoadOp: &quot;clear&quot;\n    }\n};\n\n\/\/...<\/code><\/pre>\n<p>\u5728<em>renderPassDescription<\/em>\u4e2d\uff1a<em>stencilLoadValue<\/em>\u3001<em>stencilStoreOp<\/em>\u3001<em>stencilLoadOp<\/em>\uff0c\u5982\u679c\u4e3a\u4e86\u517c\u5bb9\u76ee\u524d\u6b63\u5f0f\u7248\u7684\u6d4f\u89c8\u5668\u53ef\u4ee5\u505a\u7ee7\u7eed\u4f7f\u7528\uff0c\u4f46\u5efa\u8bae\u5c3d\u91cf\u6839\u636e\u6d4f\u89c8\u5668\u517c\u5bb9\uff0c\u8fd9\u91cc\u4e0d\u518d\u6f14\u793a\u3002<\/p>\n<p><em>\u6ce8\u610f\uff1a\u4f8b\u5b50\u4e2d\u7684\u65b9\u6cd5\u548c\u5c5e\u6027\u53ef\u80fd\u968f\u65f6\u8c03\u6574\uff0c\u5177\u4f53\u4f7f\u7528\u8bf7\u53c2\u8003W3C\u7684<a href=\"https:\/\/www.w3.org\/TR\/webgpu\/\">\u5b98\u65b9\u6587\u6863<\/a><\/em><\/p>\n<p>\u6548\u679c\u9884\u89c8\u5730\u5740\uff0c\u968f\u4fbf\u8d34\u4e86\u5f20\ud83d\udc38\u7684\uff1a<\/p>\n<p><a href=\"https:\/\/hungking.org\/webgpu-wgsl-example\/#\/part11\">\u52a0\u8f7d\u6a21\u578b<\/a><\/p>\n<p><img src=\"https:\/\/hungking.cc\/assets\/imgs\/indeex.cc\/WebGPUWGSLbase4.gif\" alt=\"\u52a0\u8f7d\u6a21\u578b\" \/><\/p>\n<p>code enjoy! \ud83e\udd96\ud83e\udd96\ud83e\udd96<\/p>\n<p>\u4f5c\u8005\uff1aindeex  <\/p>\n<p>\u94fe\u63a5\uff1a<a href=\"https:\/\/indeex.club\/\">https:\/\/indeex.club<\/a>  <\/p>\n<p>\u8457\u4f5c\u6743\u5f52\u4f5c\u8005\u6240\u6709\u3002\u5546\u4e1a\u8f6c\u8f7d\u8bf7\u8054\u7cfb\u4f5c\u8005\u83b7\u5f97\u6388\u6743\uff0c\u975e\u5546\u4e1a\u8f6c\u8f7d\u8bf7\u6ce8\u660e\u51fa\u5904\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u5728WebGPU\u5f00\u53d1\u4e2d\uff0c\u4f1a\u52a0\u8f7d\u5404\u79cd\u5404\u6837\u7684\u6a21\u578b\uff0c \u4e0b\u8f7d\u6a21\u578b \u8fd9\u91cc\u4ee5\u901a\u7528OBJ\u6a21\u578b\u4e3a\u4f8b\uff0c\u9996\u5148\u662f\u52a0\u8f7d\u6587\u4ef6\uff1a <a href=\"https:\/\/blog.indeex.club\/index.php\/2022\/04\/02\/webgpu%e5%9f%ba%e7%a1%80%ef%bc%88%e4%ba%94%ef%bc%89-%e5%8a%a0%e8%bd%bd%e6%a8%a1%e5%9e%8b\/\" class=\"read-more\">Read More<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[10],"tags":[15],"_links":{"self":[{"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/posts\/280"}],"collection":[{"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/comments?post=280"}],"version-history":[{"count":2,"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/posts\/280\/revisions"}],"predecessor-version":[{"id":284,"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/posts\/280\/revisions\/284"}],"wp:attachment":[{"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/media?parent=280"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/categories?post=280"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/tags?post=280"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}