メインコンテンツまでスキップ

opExtrude: Two-Dimensional to Three-Dimensional SDF Extrusion

Geometric Dimensional Extension Operation

The opExtrude function transforms a 2D signed distance field into a 3D volume by extruding along the Z-axis. This operation creates cylindrical or prismatic shapes from planar geometry by combining the 2D distance with Z-axis boundary constraints.

Mathematical Foundation

The extrusion operation combines 2D distance with axial bounds:

dextrude=min(max(d2D,pzh),0)+max(w,0)d_{\text{extrude}} = \min(\max(d_{2D}, |p_z| - h), 0) + |\max(\vec{w}, 0)|

where w=(d2D,pzh)\vec{w} = (d_{2D}, |p_z| - h) represents the combined distance vector and hh defines the extrusion height.

This formulation creates smooth transitions at edges while maintaining correct interior and exterior classifications.

Function Signature

ParameterTypeDescription
pvec33D sample point position
sdffloat2D SDF result at point (p.x, p.y)
hfloatHalf-height of extrusion

Implementation Demonstration

ライブエディター
const fragment = () => {
      const up = vec3(0, 1, 0)
      const eps = vec3(0.01, 0, 0)
      const eye = rotate3dY(iTime).mul(vec3(4))
      const sdf = Fn(([p]: [Vec3]) => {
              const sphere = sphereSDFRadius(p, 1)
              const box = cubeSDF(p, 0.25)
              return opExtrude(p, sphere, box)
      })
      const march = Fn(([eye, dir]: [Vec3, Vec3]) => {
              const p = eye.toVar()
              const d = sdf(p).toVar()
              Loop(16, ({ i }) => {
                      If(d.lessThanEqual(eps.x), () => {
                              const dx = sdf(p.add(eps.xyy)).sub(d)
                              const dy = sdf(p.add(eps.yxy)).sub(d)
                              const dz = sdf(p.add(eps.yyx)).sub(d)
                              const normal = vec3(dx, dy, dz).normalize()
                              const light = vec3(2, 4, 3).sub(p).normalize()
                              const diffuse = normal.dot(light).max(0.1)
                              return vec4(vec3(diffuse.mul(0.8).add(0.2)), 1)
                      })
                      p.addAssign(d.mul(dir))
                      d.assign(sdf(p))
              })
              return vec4(0)
      })
      const z = eye.negate().normalize()
      const x = z.cross(up)
      const y = x.cross(z)
      const scr = vec3(uv.sub(0.5), 2)
      const dir = mat3(x, y, z).mul(scr).normalize()
      return march(eye, dir)
}