crispigt.

DH2323 Project Specification

2026-04-18
buoyancyphysicsC++Unitysimulation

DH2323 Project Specification

Felix Stenberg | felixste@kth.se
Target Grade: A-B
crispigt.com/dh2323 | github.com/Crispigt/DH2323-WaveBuoyancy


The Idea

I want to build a real-time buoyancy simulation in Unity where meshes representing rigid hulls (boats, bunnies, etc.) float realistically on dynamic, wavy water. The simulation targets watertight meshes with consistently outward-oriented normals, i.e. the kind you'd use as a collision hull, not an art asset with open edges or double-sided faces (though it might inadvertently work for other meshes as well). The goal is not just "push the center of mass up when it's below the surface", but to actually compute the correct forces and torques per triangle so that objects tilt, rock, and settle naturally. I got interested in this because in the Game Design course our game is very much depending on buoyancy simulation and we felt the built-in Unity solution was not so good.

The main inspiration comes from three papers,

  • Fábián (2025) wrote a Eurographics short paper on buoyancy for arbitrary meshes. His approach uses volumes (tetrahedra) which works great for flat water, but gets quite messy when you try to extend it to wavy surfaces since you would need to compute curved "caps" where the water cuts through the mesh.
  • Hori (2021) proved that you can actually skip volumes entirely, just integrate hydrostatic pressure over the submerged surface of the mesh and you get the exact same result. No caps needed.
  • Hirae et al. (2025) from SIGGRAPH Asia took this further. The standard way to do surface pressure integration is to sample depth at each triangle's centroid, but this introduces so called "ghost torques" on coarse meshes (the torque integral is not polynomial, so centroid sampling cannot capture it exactly). Hirae derived a closed-form solution that computes the force and torque per triangle exactly using just the vertex coordinates, without any sampling error.

My plan is to implement Hirae's closed-form integrator in a C++ DLL that Unity calls every physics frame. The heavy math runs in C++, and Unity just passes in the mesh transform and wave parameters and gets back a force + torque vector.

Hirae's paper handles dynamic waves by clipping triangles against the water surface using lerp between the wave heights at the vertices. This works well when the triangles are small relative to the wavelength, but for larger triangles it assumes the wave is locally flat, which it is not.

My main contribution would be something I call Adaptive Curved Clipping. Instead of making a single straight cut across a partially submerged triangle, the idea is to sample NN evenly-spaced points along the line between the two edge intersections. By evaluating the actual wave height at each sample and checking for sign changes, we find a refined set of crossing positions directly on the triangle plane.

This creates a piecewise-linear waterline that closely follows the wave's actual curvature. We then fan-triangulate from the submerged vertex to each segment of this new polyline, yielding much more accurate submerged sub-triangles.

The method assumes at most one waterline crossing per edge, which is valid as long as the triangle edges are smaller than the shortest wavelength. Thus, it primarily targets the coarse-mesh / moderate-wave regime. As a stretch goal, the fixed-NN sampling could be upgraded to recursive bisection. This would automatically adapt the number of samples to the local curvature, though it is more computationally costly and harder to implement.

Adaptive curved clipping illustration

Adaptive curved clipping illustration.

So concretely, I'll be implementing Hirae's closed-form integrator then I'll implement and compare three clipping approaches:

  1. Flat water baseline, everything clips against y=0y = 0, the simplest case
  2. Linear wave clipping, Hirae's approach, lerp between vertex wave heights
  3. Adaptive curved clipping, my extension, fixed-NN waterline sampling (with recursive bisection as a stretch goal)

Architecture

  • C++ DLL handles all the buoyancy computation (vertex transforms, triangle classification, clipping, force/torque integration). Built with CMake, loaded by Unity via P/Invoke.
  • Unity (C#) manages the scene, sends mesh data to the DLL once at startup, then each frame sends the current transform matrix + wave parameters and applies the returned forces to the Rigidbody.
  • The wave function (sum of sines) runs in both the C++ DLL (for physics) and a Unity vertex shader (for visuals), using the same parameters so they stay in sync.

How the simulation will work

Every physics frame (50 Hz), the C++ DLL will run a pipeline like this for each floating object:

  1. Unity sends the object's 4×4 transform matrix. The DLL applies it to every vertex of the mesh (same as model transforms in a raytracer).

  2. For each triangle, check if its three vertices are above or below the water surface.

    • All below -> fully submerged
    • All above -> skip it
    • Mixed -> partially submerged, needs clipping
  3. Find where the triangle's edges cross the water surface and cut the triangle along that line. This produces 1-2 smaller sub-triangles that are fully submerged. This is where the three clipping methods differ, see above.

  4. Compute force and torque per submerged triangle. This is where Hirae's closed-form integrator comes in. For a triangle with vertices at positions (x1,y1,z1)(x_1,y_1,z_1), (x2,y2,z2)(x_2,y_2,z_2), (x3,y3,z3)(x_3,y_3,z_3) and wave surface height hih_i at the centroid:

Force per submerged triangle (Eq. 5):

Fi=ρgSi3(3hi+y1+y2+y3)ni\vec{F}_i = \frac{\rho g S_i}{3} (-3h_i + y_1 + y_2 + y_3) \, \vec{n}_i

Torque per triangle (about the center of mass):

τi=ρgSi12[{12hi4(y1+y2+y3)}xCoM+A]×ni\vec{\tau}_i = \frac{\rho g S_i}{12} \left[ \{12 h_i - 4(y_1 + y_2 + y_3)\} \vec{x}_{\text{CoM}} + \vec{A} \right] \times \vec{n}_i

Where:

  • ρ\rho = fluid density, gg = gravity
  • SiS_i = area of the triangle
  • ni\vec{n}_i = outward unit normal of the triangle
  • xCoM\vec{x}_{\text{CoM}} = center of mass of the rigid body
  • A\vec{A} = auxiliary vector built from the vertex coordinates (three components, each just additions and multiplications, no sine, sqrt, or anything expensive). The full expansion is in Hirae's paper (Equations 5-9).
  1. Sum all forces and torques. Return 6 floats to Unity (3 for force, 3 for torque).

  2. Apply angular momentum damping. Hirae showed that damping angular velocity directly (which is what Unity's built-in angularDrag does) produces artifacts. Instead, we should damp angular momentum L\vec{L} by scaling it each frame: Ldamped=αL\vec{L}_{\text{damped}} = \alpha \cdot \vec{L}, then derive angular velocity from that.

Here are some pictures for more clarity on the calculations:

Per-triangle force and torque from hydrostatic pressure

Figure 2: Per-triangle force Fi\vec{F}_i and torque τi\vec{\tau}_i from hydrostatic pressure.

Force per submerged triangle (Eq. 5):

Fi=ρgSi3(3hi+y1+y2+y3)  ni\vec{F}_i = \frac{\rho\, g\, S_i}{3}\,(-3h_i + y_1 + y_2 + y_3)\;\vec{n}_i

Torque per triangle about CoM (Eq. 6):

τi=ρgSi12[{12hi4(y1+y2+y3)}xCoM+A]×ni\vec{\tau}_i = \frac{\rho\, g\, S_i}{12} \left[\{12 h_i - 4(y_1 + y_2 + y_3)\}\,\vec{x}_{\text{CoM}} + \vec{A}\right] \times \vec{n}_i

Auxiliary vector A=(A1,A2,A3)\vec{A} = (A_1, A_2, A_3) (Eqs. 7-9), letting δ=4hi+2(y1+y2+y3)\delta = -4h_i + 2(y_1 + y_2 + y_3):

A1=(x1+x2+x3)δ+x1y1+x2y2+x3y3A_1 = (x_1 + x_2 + x_3)\,\delta + x_1 y_1 + x_2 y_2 + x_3 y_3 A2=(y1+y2+y3)δ2(y1y2+y2y3+y3y1)A_2 = (y_1 + y_2 + y_3)\,\delta - 2(y_1 y_2 + y_2 y_3 + y_3 y_1) A3=(z1+z2+z3)δ+z1y1+z2y2+z3y3A_3 = (z_1 + z_2 + z_3)\,\delta + z_1 y_1 + z_2 y_2 + z_3 y_3

While a naive simulation computes torque by incorrectly applying the force at the geometric centroid, true hydrostatic pressure increases with depth. This creates an offset, a Pressure Gradient Correction, which shifts the true point of force downward to the Center of Pressure (xCoP\vec{x}_{\text{CoP}}). The bracketed expression in Equation 6 is a computationally efficient expansion of the true moment arm from xCoP\vec{x}_{\text{CoP}} to xCoM\vec{x}_{\text{CoM}}. The auxiliary vector A\vec{A} computes the exact depth-weighted torque around the global origin, mathematically capturing the Pressure Gradient Correction organically. By combining this with the CoM term, Hirae's equation computes the true moment arm without ever calculating explicit CoP coordinates, which I think is very neat.

How it could be evaluated

  • Compare computed buoyancy forces against analytical solutions for simple shapes (e.g. a cube) at various submersion depths.
  • Benchmark the C++ DLL across meshes of different sizes, roughly ~1k to ~100k triangles, and maybe also look at multithreading if I have time for that.
  • Side-by-side visual comparisons of objects floating in wavy water with linear vs. adaptive clipping. Also comparing centroid-sampling (the naive approach) against Hirae's closed-form integrator to show the "ghost torque" problem on coarse meshes.
  • Write up a proposed perceptual study where participants compare linear vs. adaptive clipping in stormy water conditions to see if people can actually tell the difference visually (I think this is required for the grade?).

Questions for you guys

  • Is the math explained well enough in this proposal for me to just put in the final report? Is there something you did not understand that I should explain more clearly?
  • Is three sources enough?
  • Is the proposed user study needed? How indepth does that have to be?
  • Is comparing three clipping algorithms (flat baseline, linear, adaptive) enough scope for an A-B grade or should I aim for more?
  • Any other tips on the user study proposal section?
  • Would it also be useful to compare against Fábián's volume-based approach as a baseline, or is that overkill? It only works for flat water surfaces but his code is available on GitHub so it would not be too much extra work.
  • Is the overall scope appropriate for an A-B grade, or would you recommend changing anything?

References

  • Fábián, G. (2025). Approximate and exact buoyancy calculation for real-time floating simulation of meshes. Eurographics 2025 Short Paper.
  • Hirae, H., Morishima, S., & Ando, R. (2025). An Analytical Integrator for Solid-Fluid Coupled Buoyancy Forces. SIGGRAPH Asia 2025 Technical Communications.
  • Hori, T. (2021). Proof that the Center of Buoyancy is Equal to the Center of Pressure by means of the Surface Integral of Hydrostatic Pressure Acting on the Inclined Ship. Nagasaki Institute of Applied Science.