{"id":261,"date":"2022-02-28T22:05:57","date_gmt":"2022-02-28T14:05:57","guid":{"rendered":"https:\/\/blog.indeex.club\/?p=261"},"modified":"2022-03-30T21:05:39","modified_gmt":"2022-03-30T13:05:39","slug":"webgpu%e5%92%8cwgsl-%e5%9f%ba%e7%a1%80","status":"publish","type":"post","link":"https:\/\/blog.indeex.club\/index.php\/2022\/02\/28\/webgpu%e5%92%8cwgsl-%e5%9f%ba%e7%a1%80\/","title":{"rendered":"WebGPU\u57fa\u7840\uff08\u4e8c\uff09-\u65cb\u8f6c\u7684\u7acb\u65b9\u4f53"},"content":{"rendered":"<p>\u6839\u636e\u514b\u7f57\u8bfa\u65af\uff08Khronos\uff09\u4f1a\u8bae\uff0cWebGPU\u5c06\u4e8e2022\u5e74\u7b2c\u4e8c\u5b63\u5ea6\u53d1\u5e031.0\u7248\u672c\uff0cWGSL\u4e5f\u5728\u5feb\u901f\u5b8c\u5584\u3002<\/p>\n<p>\u6309\u7167\u4ee5\u5f80\u7684\u6280\u672f\u63a8\u5e7f\u8fdb\u5ea6\uff0c\u7406\u8bba\u4e0a\u6807\u51c6\u4f1a\u5728\u53d1\u5e03<em>5-7\u5e74\u540e<\/em>\u624d\u80fd\u88ab\u5927\u90e8\u5206\u4fe1\u4ef0<em>React\/Vue<\/em>\u7684\u5f00\u53d1\u8005\u63a5\u53d7\uff0c\u4f46\u968f\u7740\u786c\u4ef6\u8bbe\u5907\u7684\u4e0d\u65ad\u53d1\u5c55\u3001Web\u884c\u4e1a\u7684\u4e0d\u65ad\u58ee\u5927\u3001\u4f7f\u7528Typescript\u7684\u4eba\u8d8a\u6765\u8d8a\u591a\u4ee5\u53ca\u5143\u5b87\u5b99\u6982\u5ff5\u7684\u666e\u53ca\uff0c\u5c24\u5176\u5f88\u591a\u4ee5\u524d\u4f7f\u7528WebGL\u5f00\u53d1Flash\u6e38\u620f\u7684\u5f00\u53d1\u8005\u5f88\u591a\u5df2\u7ecf\u65e9\u65e9\u5f00\u59cb\u4e86WebGPU\u5f15\u64ce\u7684\u5f00\u53d1\u5de5\u4f5c\uff0c\u8fd9\u4e2a\u65f6\u95f4\u4f1a\u88ab\u6781\u5927\u7684\u7f29\u77ed\uff0c\u90a3\u4e48\u4eca\u5e74\u5c06\u6210\u4e3aWebGPU\u5143\u5e74\u3002<\/p>\n<p>\u90a3\u4e48WebGPU\u80fd\u505a\u4ec0\u4e48\u5462\uff1f\u7406\u8bba\u4e0a<em>WebGPU is All<\/em>\uff0c\u6bd4\u5982\u5728\u670d\u52a1\u5668\u4e0a\u53ef\u4ee5\u6316\u77ff\u3001\u5728\u6e38\u620f\u9886\u57df\u505a3A\u5927\u4f5c\u3001\u5728\u7535\u5546\u9886\u57df\u505a\u5546\u54c1\u5c55\u793a\u3001\u5728\u6559\u80b2\u9886\u57df\u505a\u7269\u7406\u5316\u5b66\u6a21\u62df\u3001\u5728\u79d1\u5b66\u9886\u57df\u505a\u5404\u79cd\u63a8\u6d4b\u548c\u6f14\u793a\u3001\u6253\u9020\u5143\u5b87\u5b99\u7b49\u7b49\uff0c\u53ea\u6709\u60f3\u4e0d\u5230\uff0c\u6ca1\u6709\u505a\u4e0d\u5230\u3002<\/p>\n<p>\u9762\u5bf9\u73b0\u5728\u5f88\u591a\u4eba\u60c6\u6005\u7684996\u3001007\u9762\u524d\uff0c\u8fd9\u9879\u6280\u672f\u4e0d\u4e00\u5b9a\u662f\u6700\u597d\u7684\uff0c\u4f46\u4e00\u5b9a\u662f\u6700\u5feb\u7684\uff0c\u80fd\u6781\u5927\u7684\u7f29\u77ed\u65f6\u95f4\u548c\u4eba\u5de5\u6210\u672c\u3002<\/p>\n<p>\u8fd9\u91cc\u662f\u4e00\u4e9b\u4f7f\u7528\u8fc7\u7a0b\u4e2d\u7684\u8bb0\u5f55\uff0c\u968f\u7740\u6807\u51c6\u7684\u4e0d\u65ad\u6539\u8fdb\uff0c\u4ee3\u7801\u4e5f\u4f1a\u4e0d\u65ad\u4fee\u6539\uff0c\u53ef\u80fd\u4f1a\u968f\u65f6\u65e0\u6cd5\u76f4\u63a5\u4f7f\u7528\uff0c\u4f46\u7406\u8bba\u548c\u601d\u60f3\u90fd\u662f\u4e00\u81f4\u7684\u3002<\/p>\n<p>W3C WebGPU\u6587\u6863\uff1a <a href=\"https:\/\/www.w3.org\/TR\/webgpu\/\">WebGPU \u5b98\u7f51<\/a><\/p>\n<p>W3C WGSL\u6587\u6863\uff1a <a href=\"https:\/\/www.w3.org\/TR\/WGSL\/\">WGSL \u5b98\u7f51<\/a><\/p>\n<p>\u4f7f\u7528WebGPU\u7684\u5e93\uff1a <a href=\"https:\/\/www.babylonjs.com\/\">Babylon \u5b98\u7f51<\/a><\/p>\n<p>\u4ee5\u524d\u4e5f\u6709\u76f8\u5173\u4f7f\u7528\u7684\u8bb0\u5f55\uff0c\u8bb0\u5f55\u662f\u67e5\u770b\u5f53\u65f6\u5b98\u7f51\u6587\u6863\u3001apple\u5b98\u65b9\u5f00\u53d1\u535a\u5ba2\u548c\u4e00\u4e9b\u5176\u4ed6\u4f8b\u5b50\u540e\u7684\u4e00\u4e2a\u6c47\u603b\uff0c\u56e0\u4e3a\u5f53\u65f6\u7684\u5b98\u65b9API\u53d8\u52a8\u8f83\u9891\u7e41\u4e14\u6539\u52a8\u8fc7\u5927\uff0c\u4e00\u4e2a\u7b80\u5355\u7684\u4f8b\u5b50\u4e2d\u95f4\u505a\u8fc7\u5f88\u591a\u6b21\u4fee\u6539\uff0c\u56fe\u7247\u53c2\u8003\u5f53\u65f6\u4e00\u4e2a\u56fd\u5916\u7684\u535a\u5ba2\uff0c\u8fd9\u91cc\u662f\u6700\u540e\u4fee\u6539\u7684\u4f8b\u5b50\u5730\u5740\uff1a<a href=\"https:\/\/blog.indeex.club\/index.php\/2021\/09\/19\/webgpu%e5%9f%ba%e7%a1%80\/\">Webgpu\u57fa\u7840<\/a><\/p>\n<h2>\u8bbe\u5907\u68c0\u6d4b<\/h2>\n<pre><code class=\"language-typescript\">if (!this.canvas) {\n    message.error(&quot;\u6ca1\u6709Canvas Element&quot;, 10);\n    throw new Error(&quot;\u6ca1\u6709Canvas Element&quot;);\n}\nif (!navigator.gpu) {\n    message.error(&quot;\u4e0d\u652f\u6301\u4f7f\u7528\u663e\u5361\u8bbe\u5907&quot;, 10);\n    throw new Error(&quot;\u4e0d\u652f\u6301\u663e\u5361\u8bbe\u5907&quot;);\n}\nthis.adapter = await navigator.gpu.requestAdapter({\n    powerPreference: &quot;high-performance&quot;,\/\/\u9ad8\u6027\u80fd\u6a21\u5f0f\uff0c\u76ee\u524d\u6709\u4e24\u79cd\uff1a\u9ad8\u6027\u80fd\u548c\u4f4e\u529f\u8017\u6a21\u5f0f\uff0c\u5efa\u8bae\u7b14\u8bb0\u672c\u3001\u53ea\u6709\u6838\u663e\u4ec0\u4e48\u7684\u4f7f\u7528\u4f4e\u529f\u8017\u6a21\u5f0f\uff0c\u9632\u6b62\u7cfb\u7edf\u5f3a\u5236\u4e22\u5931\u8bbe\u5907\n}) || undefined;\nif (!this.adapter) {\n    message.error(&quot;\u83b7\u53d6\u663e\u5361\u8bbe\u5907\u5931\u8d25&quot;,10);\n    throw new Error(&quot;\u83b7\u53d6\u663e\u5361\u8bbe\u5907\u5931\u8d25&quot;);\n}\nthis.device = await this.adapter.requestDevice();\nif (!this.device) {\n    message.error(&quot;\u663e\u5361\u8bbe\u5907\u672a\u627e\u5230&quot;,10);\n    throw new Error(&quot;\u663e\u5361\u8bbe\u5907\u672a\u627e\u5230&quot;);\n}<\/code><\/pre>\n<h2>Canvas\u9996\u9009\u9879<\/h2>\n<pre><code class=\"language-typescript\">this.context = this.canvas.getContext(&quot;webgpu&quot;);\nthis.format = this.context?.getPreferredFormat(this.adapter);\nthis.context?.configure({\n    device: this.device, format: this.format, size: this.size\n});<\/code><\/pre>\n<blockquote>\n<p>getPreferredFormat \u83b7\u53d6\u9002\u914d\u5668\u7eb9\u7406\u7684\u9996\u9009\u9879\u914d\u7f6e<br \/>\nconfigure \u7eb9\u7406\u914d\u7f6e<\/p>\n<\/blockquote>\n<h2>Vertex shader\u9876\u70b9\u7740\u8272\u5668<\/h2>\n<p>\u7740\u8272\u5668\u8be6\u89c1<a href=\"https:\/\/www.w3.org\/TR\/WGSL\/\">\u6587\u6863<\/a>\uff1a<\/p>\n<pre><code class=\"language-typescript\">struct Uniforms {\n  modelViewProjectionMatrix : mat4x4&lt;f32&gt;;\n};\n@binding(0) @group(0) var&lt;uniform&gt; uniforms : Uniforms;\n\nstruct VertexOutput {\n  @builtin(position) Position : vec4&lt;f32&gt;;\n  @location(0) fragUV : vec2&lt;f32&gt;;\n  @location(1) fragPosition: vec4&lt;f32&gt;;\n};\n\n@stage(vertex)\nfn main(@location(0) position : vec4&lt;f32&gt;,\n        @location(1) uv : vec2&lt;f32&gt;) -&gt; VertexOutput {\n  var output : VertexOutput;\n  output.Position = uniforms.modelViewProjectionMatrix * position;\n  output.fragUV = uv;\n  output.fragPosition = 0.5 * (position + vec4&lt;f32&gt;(1.0, 1.0, 1.0, 1.0));\n  return output;\n}<\/code><\/pre>\n<h2>Fragment shader\u7247\u6bb5\u7740\u8272\u5668<\/h2>\n<pre><code class=\"language-typescript\">@stage(fragment)\nfn main(@location(0) fragUV: vec2&lt;f32&gt;,\n        @location(1) fragPosition: vec4&lt;f32&gt;) -&gt; @location(0) vec4&lt;f32&gt; {\n  return fragPosition;\n}<\/code><\/pre>\n<h2>VerticesBuffer<\/h2>\n<p>Buffer\u64cd\u4f5c\uff0c\u8be6\u89c1<a href=\"https:\/\/www.w3.org\/TR\/webgpu\/#buffers\">\u5b98\u65b9\u6587\u6863<\/a>\uff1a<\/p>\n<pre><code class=\"language-typescript\">const verticesBuffer = this.device?.createBuffer({\n    size: cubeVertexArray.byteLength,\n    usage: GPUBufferUsage.VERTEX,\n    mappedAtCreation: true,\n});\nnew Float32Array(verticesBuffer!.getMappedRange()).set(cubeVertexArray);\nverticesBuffer?.unmap();\nthis.verticesBuffer = verticesBuffer;<\/code><\/pre>\n<h2>Pipeline\u7ba1\u7ebf<\/h2>\n<p>\u53ef\u4ee5\u4f7f\u7528\u9ed8\u8ba4\u914d\u7f6e\uff0c\u4e5f\u53ef\u4ee5\u6839\u636e\u9700\u6c42\u81ea\u884c\u914d\u7f6e\uff0c\u914d\u7f6e\u89c1<a href=\"https:\/\/www.w3.org\/TR\/webgpu\/#pipelines\">\u5b98\u65b9\u6587\u6863<\/a>\uff1a<\/p>\n<pre><code class=\"language-typescript\">const pipeline = this.device?.createRenderPipeline({\n    vertex: {\n        module: this.device.createShaderModule({ code: vertShaderCode }),\n        entryPoint: &#039;main&#039;,\n        buffers: [\n            {\n                arrayStride: cubeVertexSize,\n                attributes: [\n                    {\n                        shaderLocation: 0,\n                        offset: cubePositionOffset,\n                        format: &quot;float32x4&quot;,\n                    },\n                    {\n                        shaderLocation: 1,\n                        offset: cubeUVOffset,\n                        format: &quot;float32x2&quot;,\n                    }\n                ]\n            }\n        ]\n    },\n    fragment: {\n        module: this.device.createShaderModule({ code: fragShaderCode }),\n        entryPoint: &#039;main&#039;,\n        targets: [\n            {\n                format: this.format,\n            }\n        ]\n    },\n    primitive: {\n        topology: &quot;triangle-list&quot;,\n        cullMode: &quot;back&quot;,\n    },\n    depthStencil: {\n        depthWriteEnabled: true,\n        depthCompare: &quot;less&quot;,\n        format: &quot;depth24plus&quot;,\n    }\n});\nthis.pipeline = pipeline;<\/code><\/pre>\n<h2>Texture\u7eb9\u7406<\/h2>\n<p>\u7eb9\u7406\u914d\u7f6e\uff0c\u76f8\u89c1<a href=\"https:\/\/www.w3.org\/TR\/webgpu\/#textures\">\u6587\u6863<\/a>\uff1a<\/p>\n<pre><code class=\"language-typescript\">const depthTexture = this.device?.createTexture({\n    size: this.size,\n    format: &quot;depth24plus&quot;,\n    usage: GPUTextureUsage.RENDER_ATTACHMENT,\n});<\/code><\/pre>\n<blockquote>\n<p>format\u6709\u5f88\u591a\uff0c\u5177\u4f53\u89c1 <a href=\"https:\/\/www.w3.org\/TR\/webgpu\/#enumdef-gputextureformat\">GPUTextureFormat<\/a><\/p>\n<\/blockquote>\n<h2>BindGroup\u7ed1\u5b9a\u8d44\u6e90<\/h2>\n<p>\u8d44\u6e90\u4e00\u822c\u901a\u8fc7\u7ec4\u7ed1\u5b9a\uff0c<a href=\"https:\/\/www.w3.org\/TR\/webgpu\/#bindings\">\u6587\u6863<\/a>\uff1a<\/p>\n<pre><code class=\"language-typescript\">const matrixSize =  4 * 16;\/\/matrix 4*4\nconst offset = 256;\/\/256\u5bf9\u9f50\nconst uniformBuferSize = offset + matrixSize;\nconst uniformBuffer = this.device!.createBuffer({\n    size: uniformBuferSize,\n    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\n});\nthis.uniformBuffer = uniformBuffer;\n\nconst uniformBindGroup = this.device?.createBindGroup({\n    layout: pipeline!.getBindGroupLayout(0),\n    entries: [\n        {\n            binding: 0,\n            resource: {\n                buffer: uniformBuffer,\n                offset: 0,\n                size: matrixSize,\n            }\n        }\n    ]\n});\nthis.uniformBindGroup = uniformBindGroup;\n\nconst uniformBindGroup2 = this.device?.createBindGroup({\n    layout: pipeline!.getBindGroupLayout(0),\n    entries: [\n        {\n            binding: 0,\n            resource: {\n                buffer: uniformBuffer,\n                offset: offset,\n                size: matrixSize,\n            }\n        }\n    ]\n});\nthis.uniformBindGroup2 = uniformBindGroup2;\n\nconst renderPassDescriptor: any = {\n    colorAttachments: [\n        {\n            view: undefined,\n            clearValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0 },\n            loadOp: &quot;clear&quot;,\n            storeOp: &quot;store&quot;,\n        }\n    ],\n    depthStencilAttachment: {\n        view: depthTexture!.createView(),\n        depthClearValue: 1.0,\n        depthStoreOp: &quot;store&quot;,\n        depthLoadOp: &quot;clear&quot;,\n    }\n};\nthis.renderPassDescriptor = renderPassDescriptor;\n\nconst projectionMatrix = mat4.create();\nthis.projectionMatrix = projectionMatrix;\nconst aspect = this.canvas.clientWidth \/ this.canvas.clientHeight;\nmat4.perspective(projectionMatrix, (2 * Math.PI) \/ 5, aspect, 1, 100.0);<\/code><\/pre>\n<h2>Matrix\u77e9\u9635\u8f6c\u6362<\/h2>\n<p>\u53ef\u4ee5\u53c2\u8003 <a href=\"https:\/\/blog.indeex.club\/index.php\/2017\/11\/02\/%e7%9f%a9%e9%98%b5\/\">TransformationMatrix\u77e9\u9635<\/a><\/p>\n<pre><code class=\"language-typescript\">getTransformationMatrix() {\n    const modelMatrix = mat4.create();\n    mat4.translate(modelMatrix, modelMatrix, vec3.fromValues(-2, 0, 0));\n    this.modelMatrix = modelMatrix;\n    const modelMatrix2 = mat4.create();\n    mat4.translate(modelMatrix2, modelMatrix2, vec3.fromValues(2, 0, 0));\n    this.modelMatrix2 = modelMatrix2;\n\n    const modelViewProjectionMatrix = mat4.create();\n    this.modelViewProjectionMatrix = modelViewProjectionMatrix;\n    const modelViewProjectionMatrix2 = mat4.create();\n    this.modelViewProjectionMatrix2 = modelViewProjectionMatrix2;\n    const viewMatrix = mat4.create();\n    mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, 0, -7));\n    this.viewMatrix = viewMatrix;\n\n    const tmpMat4 = mat4.create();\n    const tmpMat42 = mat4.create();\n    this.tmpMat4 = tmpMat4;\n    this.tmpMat42 = tmpMat42;\n}<\/code><\/pre>\n<h2>\u66f4\u65b0\u77e9\u9635<\/h2>\n<pre><code class=\"language-typescript\">updateTransfromationMatrix(){\n    const now = Date.now() \/ 1000;\n\n    mat4.rotate(this.tmpMat4, this.modelMatrix, 1, vec3.fromValues(Math.sin(now), Math.cos(now), 0));\n    mat4.rotate(this.tmpMat42, this.modelMatrix2, 1, vec3.fromValues(Math.cos(now), Math.sin(now), 0));\n\n    mat4.multiply(this.modelViewProjectionMatrix, this.viewMatrix, this.tmpMat4);\n    mat4.multiply(this.modelViewProjectionMatrix, this.projectionMatrix, this.modelViewProjectionMatrix);\n    mat4.multiply(this.modelViewProjectionMatrix2, this.viewMatrix, this.tmpMat42);\n    mat4.multiply(this.modelViewProjectionMatrix2, this.projectionMatrix, this.modelViewProjectionMatrix2);\n}<\/code><\/pre>\n<h2>renderer\u6e32\u67d3<\/h2>\n<p>\u901a\u8fc7requestAnimationFrame\u66f4\u65b0\u6e32\u67d3\uff1a<\/p>\n<pre><code class=\"language-typescript\">render = () =&gt; {\n    this.updateTransfromationMatrix();\n    this.device?.queue.writeBuffer(this.uniformBuffer, 0, this.modelViewProjectionMatrix.buffer, this.modelViewProjectionMatrix.byteOffset, this.modelViewProjectionMatrix.byteLength);\n    this.device?.queue.writeBuffer(this.uniformBuffer, 256, this.modelViewProjectionMatrix2.buffer, this.modelViewProjectionMatrix2.byteOffset, this.modelViewProjectionMatrix2.byteLength);\n    this.renderPassDescriptor.colorAttachments[0].view = this.context?.getCurrentTexture().createView();\n\n    const commandEncoder: any = this.device?.createCommandEncoder();\n    const passEncoder = commandEncoder?.beginRenderPass(this.renderPassDescriptor);\n    passEncoder?.setPipeline(this.pipeline);\n    passEncoder?.setVertexBuffer(0, this.verticesBuffer);\n\n    passEncoder?.setBindGroup(0, this.uniformBindGroup);\n    passEncoder?.draw(cubeVertexCount, 1, 0, 0);\n\n    passEncoder?.setBindGroup(0, this.uniformBindGroup2);\n    passEncoder?.draw(cubeVertexCount, 1, 0, 0);\n    passEncoder?.end();\n    this.device?.queue.submit([commandEncoder.finish()]);\n    requestAnimationFrame(this.render);\n}<\/code><\/pre>\n<p>\u6548\u679c\u9884\u89c8\u5730\u5740\uff1a<\/p>\n<p><a href=\"https:\/\/hungking.org\/webgpu-wgsl-example\/#\/part5\">\u65cb\u8f6c\u7684\u7acb\u65b9\u4f53<\/a><\/p>\n<p><img src=\"https:\/\/hungking.cc\/assets\/imgs\/indeex.cc\/WebGPUWGSLbase1.gif\" alt=\"WebGPU\u548cWGSL \u57fa\u7840\" \/><\/p>\n<p>code enjoy! \ud83d\udc19\ud83d\udc19\ud83d\udc19\ud83d\udd78<\/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>\u6839\u636e\u514b\u7f57\u8bfa\u65af\uff08Khronos\uff09\u4f1a\u8bae\uff0cWebGPU\u5c06\u4e8e2022\u5e74\u7b2c\u4e8c\u5b63\u5ea6\u53d1\u5e031.0\u7248\u672c\uff0cWGSL\u4e5f\u5728\u5feb<a href=\"https:\/\/blog.indeex.club\/index.php\/2022\/02\/28\/webgpu%e5%92%8cwgsl-%e5%9f%ba%e7%a1%80\/\" 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\/261"}],"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=261"}],"version-history":[{"count":5,"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/posts\/261\/revisions"}],"predecessor-version":[{"id":273,"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/posts\/261\/revisions\/273"}],"wp:attachment":[{"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/media?parent=261"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/categories?post=261"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/tags?post=261"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}