You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改WebGPU代码实现类似ShaderToy的SDF效果?

实现WebGPU中的SDF绘制(ShaderToy风格)

问题背景

要开发WebGPU版本的ShaderToy,已将画布裁剪空间设置为[-1,1]范围,现有基础渲染代码但无法实现SDF(有符号距离场)绘制,需要修改代码并获取相关参考内容。

核心修改步骤

1. 传递UV坐标到片元着色器

顶点着色器需要把当前顶点的位置作为UV坐标传递给片元着色器,因为裁剪空间已经是[-1,1],可以直接用顶点位置作为初始UV,后续再适配画布宽高比避免拉伸。

2. 实现SDF函数与颜色计算

片元着色器中接收UV,先修正宽高比,然后实现SDF函数(比如圆形),根据距离值生成颜色:

  • 修正UV:将UV按画布宽高比缩放,避免图形拉伸
  • SDF函数:计算像素到图形的有符号距离
  • 颜色输出:根据距离值绘制轮廓、填充或渐变

3. 适配画布尺寸(可选)

如果需要动态适配画布大小,可以添加uniform传递画布分辨率,以下示例先基于固定画布尺寸做静态适配。

完整修改后的代码

<!DOCTYPE html>
<title>SDF-WebGPU</title>
<canvas></canvas>
<script>'use strict';

const canvas = document.body.firstChild;

canvas.style = `
  display: block;
  image-rendering: pixelated;
  background-color: #ccc;
  user-select: none;
  touch-action: none;
  width:  ${ canvas.width  = 480 * devicePixelRatio, 480 }px;
  height: ${ canvas.height = 360 * devicePixelRatio, 360 }px;
`;

const init = async function(){
  const
  context = canvas.getContext(`webgpu`),
  format = navigator.gpu.getPreferredCanvasFormat(),
  adapter = await navigator.gpu.requestAdapter(),
  device = await adapter.requestDevice(),
  Q = device.queue,

  {VERTEX, COPY_DST} = GPUBufferUsage,
  
  SPACE_B = new Float32Array([-1,1, 1,1, -1,-1, 1,-1]),

  B = device.createBuffer({
    label: `SPACE`,
     size: SPACE_B.byteLength,
    usage: VERTEX | COPY_DST
  }),
    
  P = device.createRenderPipeline({
    layout: `auto`,
    vertex: {
      module: device.createShaderModule({
        code: `@vertex
        fn vSh(@location(0) p: vec2<f32>) -> struct {
          @builtin(position) pos: vec4<f32>,
          @location(0) uv: vec2<f32>
        } {
          return { pos: vec4<f32>(p, 0, 1), uv: p };
        }`
      }),
      entryPoint: `vSh`,
      buffers: [{
        arrayStride: 8, // 2*4 = 2 floats x 4 bytes
        attributes: [{
          shaderLocation: 0,
          offset: 0,
          format: `float32x2`
        }]
      }], // buffers
    },
    fragment: {
      module: device.createShaderModule({
        code: `@fragment
        fn fSh(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
          // 修正UV宽高比,适配480x360画布
          vec2 st = uv;
          st.x *= 480.0 / 360.0;

          // 圆形SDF:计算当前点到中心的距离,半径0.5
          float d = length(st) - 0.5;

          // 基于距离生成颜色,smoothstep实现边缘抗锯齿
          float col = smoothstep(0.0, 0.01, d);
          return vec4<f32>(1.0 - col, 0.2, 0.4, 1.0);
        }`
      }),
      entryPoint: `fSh`,
      targets: [ {format} ]
    },
    primitive:{
      topology: `triangle-strip`
    }
  }), // Pipeline

  frame=()=>{
    const
    C = device.createCommandEncoder(),
    R = C.beginRenderPass({
      colorAttachments:[{
        view: context.getCurrentTexture().createView(),
        loadOp: `clear`,
        storeOp: `store`
      }]
    });
  
    R.setPipeline(P);
    R.setVertexBuffer(0,B);
    R.draw(4);
    R.end();

    Q.submit([ C.finish() ])
  }; // frame

  context.configure({ device, format, alphaMode: `opaque` });

  Q.writeBuffer(B,0, SPACE_B);

  frame()

}() // init
</script>

关键代码说明

  1. 顶点着色器修改:新增uv输出变量,将裁剪空间坐标直接传递给片元着色器,作为后续SDF计算的基础坐标。
  2. UV修正:通过st.x *= 480.0 / 360.0修正宽高比,避免圆形被拉伸成椭圆。
  3. SDF实现:length(st) - 0.5是中心在原点、半径0.5的圆形SDF,返回值为负表示在圆内,正表示在圆外。
  4. 颜色计算:smoothstep函数实现边缘抗锯齿,生成平滑的颜色过渡,让图形边缘更柔和。

参考资料

  • WGSL基础语法:WebGPU官方着色器语言规范,包含向量操作、函数定义等核心内容,是编写WebGPU着色器的基础。
  • SDF核心公式:
    • 圆形:length(p) - r(p为点坐标,r为半径)
    • 矩形:length(max(abs(p)-b, 0.0)) + min(max(abs(p.x)-b.x, abs(p.y)-b.y), 0.0)(b为矩形半宽高)
    • 组合操作:并集min(d1, d2)、交集max(d1, d2)、差集max(-d1, d2)
  • WebGPU渲染流程:熟悉顶点输入、管线创建、命令提交等流程,确保SDF绘制的渲染链路正确。

内容的提问来源于stack exchange,提问作者Teamur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 21:40:46