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

opUnion: Boolean Union Operation

Geometric Composition for Distance Field Combination

The opUnion operations perform boolean union by combining multiple SDF shapes into unified geometry. This creates merged effects where shapes blend together, enabling complex geometric compositions through additive modeling.

Mathematical Foundation

Basic union uses the minimum distance between shapes:

dunion=min(d1,d2)d_{\text{union}} = \min(d_1, d_2)

Smooth union creates seamless transitions using interpolation:

h=clamp(12+d2d12k,0,1)h = \text{clamp}\left(\frac{1}{2} + \frac{d_2 - d_1}{2k}, 0, 1\right) dsmooth=mix(d2,d1,h)kh(1h)d_{\text{smooth}} = \text{mix}(d_2, d_1, h) - kh(1-h)

Function Variants

FunctionParametersDescription
opUniond1, d2Sharp boolean union
opUnionSmoothd1, d2, kSmoothed union with blending factor
opUnionSmoothVec4d1, d2, kSmooth vec4 union with material info

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.add(vec3(-0.5, 0, 0)), 0.7)
              const cube = cubeSDF(p.add(vec3(0.5, 0, 0)), 0.6)
              return opUnion(sphere, cube)
      })
      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)
}