{"id":749,"date":"2026-08-13T18:13:37","date_gmt":"2026-08-13T18:13:37","guid":{"rendered":"https:\/\/summergeometry.org\/sgi2026\/?p=749"},"modified":"2026-08-25T16:12:01","modified_gmt":"2026-08-25T16:12:01","slug":"inverse-geodesic-design","status":"publish","type":"post","link":"https:\/\/summergeometry.org\/sgi2026\/inverse-geodesic-design\/","title":{"rendered":"Inverse Geodesic Design"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Mentor(s):<\/strong> <a href=\"https:\/\/ahdhn.github.io\/\">Dr. Ahmed Mahmoud<\/a> [MIT], <a href=\"https:\/\/anadodik.github.io\/\">Ana Dodik<\/a> [MIT]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Fellow(s): <\/strong><a href=\"https:\/\/adigokul.github.io\">Gokul Adithya Suresh<\/a> [RVCE], Yufan (Diana) Hu [Macalester College]<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Volunteer<\/strong>: <a href=\"https:\/\/yanruyu126.github.io\/\">Ruyu Yan<\/a> [Princeton University]<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">When we think about the shortest path between two points, we usually imagine a straight line (a Euclidean distance). But once those points lie on a curved surface, the notion of a straight line becomes more subtle. The shortest path must follow the geometry of the surface itself. Such paths are called <strong>geodesics<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Geodesics are fundamental to understanding how geometry shapes motion and distance. Given a surface, we can ask where its geodesics go, how far apart points are, and how the geometry influences the paths between them. This is the familiar <em>forward<\/em> perspective: <strong>given the geometry, determine the resulting geodesic structure<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But what if we reverse the question?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of starting with a surface and asking what paths it produces, we can start with a desired path or geodesic behavior and ask <strong>what geometry would produce it<\/strong>. This shift, from computing geodesics on a given surface to designing the surface around desired geodesics, is the central idea of <strong>inverse geodesic design<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">At its core, inverse geodesic design is therefore a problem of shaping geometry through the behavior it induces. By modifying the surface, we modify its intrinsic metric, which in turn changes its geodesic distances and paths. The challenge is to find a geometry whose geodesic structure matches a desired objective.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This simple reversal opens up an interesting computational problem: <strong>can we design a surface by optimizing it directly through its geodesics?<\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Geodesics with the Heat Method<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To design a surface around its geodesics, we first need to measure geodesic distance on a mesh, and measure it in a way we can differentiate later on. We use the <strong>Heat Method<\/strong> for this (Crane, Weischedel and Wardetzky, 2013).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The idea goes back to an observation by Varadhan. Put a spot of heat at a point, let it diffuse for a very short time, and the way it spreads already tells you how far away everything is. Nearby points warm up fast, far points stay cold. The Heat Method takes this and turns it into three steps, and each one is just a sparse linear solve. That last part is what we care about, since it means we can differentiate through the whole thing.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Step 1: Diffuse heat from the source<\/strong><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">We place a unit heat source on the source set <math><semantics><mi class=\"mathcal\">\ud835\udcae<\/mi><annotation encoding=\"application\/x-tex\">\\mathcal S<\/annotation><\/semantics><\/math> and let it spread for a short time <math><semantics><mi>t<\/mi><annotation encoding=\"application\/x-tex\">t<\/annotation><\/semantics><\/math>. In discrete form this is a single backward-Euler step of the heat equation:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mrow><mo fence=\"true\" form=\"prefix\">(<\/mo><mi>M<\/mi><mo>\u2212<\/mo><mi>t<\/mi><mspace width=\"0.1667em\"><\/mspace><msub><mi>L<\/mi><mi>C<\/mi><\/msub><mo fence=\"true\" form=\"postfix\">)<\/mo><\/mrow><mspace width=\"0.1667em\"><\/mspace><mi>u<\/mi><mo>=<\/mo><msub><mi>\u03b4<\/mi><mi class=\"mathcal\">\ud835\udcae<\/mi><\/msub><mo separator=\"true\">,<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\left(M &#8211; t\\,L_C\\right)\\,u = \\delta_{\\mathcal S},<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">where <math><semantics><msub><mi>L<\/mi><mi>C<\/mi><\/msub><annotation encoding=\"application\/x-tex\">L_C<\/annotation><\/semantics><\/math> is the cotangent Laplacian, <math><semantics><mi>M<\/mi><annotation encoding=\"application\/x-tex\">M<\/annotation><\/semantics><\/math> is the (lumped) mass matrix, and <math><semantics><msub><mi>\u03b4<\/mi><mi class=\"mathcal\">\ud835\udcae<\/mi><\/msub><annotation encoding=\"application\/x-tex\">\\delta_{\\mathcal S}<\/annotation><\/semantics><\/math> marks the source vertices. Solving this gives <math><semantics><mi>u<\/mi><annotation encoding=\"application\/x-tex\">u<\/annotation><\/semantics><\/math>, a smooth blob of heat sitting around the source.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Step 2: Normalize the heat gradient<\/strong><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">The gradient of the heat field points away from the source, which is the direction distance grows in. Its length is not useful to us, since the heat fades as you move away, but its direction is exactly what we want, so we normalize it to a unit vector field:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>X<\/mi><mo>=<\/mo><mo form=\"prefix\" stretchy=\"false\">\u2212<\/mo><mfrac><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2207<\/mo><mi>u<\/mi><\/mrow><mrow><mo form=\"prefix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">\u2016<\/mo><mo form=\"prefix\" stretchy=\"false\">\u2207<\/mo><mi>u<\/mi><mo form=\"postfix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">\u2016<\/mo><\/mrow><\/mfrac><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">X = -\\frac{\\nabla u}{\\lVert \\nabla u \\rVert}.<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">The minus sign makes <math><semantics><mi>X<\/mi><annotation encoding=\"application\/x-tex\">X<\/annotation><\/semantics><\/math> point away from the source. This gives us a good guess for which way the real distance function should be increasing at each point.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Step 3: Recover distance with a Poisson solve<\/strong><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">The last step is to find a scalar field <math><semantics><mi>\u03d5<\/mi><annotation encoding=\"application\/x-tex\">\\phi<\/annotation><\/semantics><\/math> whose gradient lines up with <math><semantics><mi>X<\/mi><annotation encoding=\"application\/x-tex\">X<\/annotation><\/semantics><\/math> as closely as it can. That is a Poisson equation:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>L<\/mi><mi>C<\/mi><\/msub><mspace width=\"0.1667em\"><\/mspace><mi>\u03d5<\/mi><mo>=<\/mo><mo form=\"prefix\" stretchy=\"false\">\u2207<\/mo><mo form=\"prefix\" stretchy=\"false\">\u22c5<\/mo><mi>X<\/mi><mo separator=\"true\">,<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">L_C\\,\\phi = \\nabla\\cdot X,<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">and then we shift <math><semantics><mi>\u03d5<\/mi><annotation encoding=\"application\/x-tex\">\\phi<\/annotation><\/semantics><\/math> so it reads zero at the source. What comes out is the geodesic distance, up to the usual approximation, from <math><semantics><mi class=\"mathcal\">\ud835\udcae<\/mi><annotation encoding=\"application\/x-tex\">\\mathcal S<\/annotation><\/semantics><\/math> to every vertex.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The time <math><semantics><mi>t<\/mi><annotation encoding=\"application\/x-tex\">t<\/annotation><\/semantics><\/math> sets how much the field gets smoothed. We use <math><semantics><mrow><mi>t<\/mi><mo>=<\/mo><mi>c<\/mi><mo>\u00b7<\/mo><msup><mi>h<\/mi><mn>2<\/mn><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">t = c\\cdot h^2<\/annotation><\/semantics><\/math> with <math><semantics><mi>h<\/mi><annotation encoding=\"application\/x-tex\">h<\/annotation><\/semantics><\/math> the mean edge length and <math><semantics><mrow><mi>c<\/mi><mo>=<\/mo><mn>10<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">c = 10<\/annotation><\/semantics><\/math> across all our grids. Since the three steps are all sparse solves, <math><semantics><mi>\u03d5<\/mi><annotation encoding=\"application\/x-tex\">\\phi<\/annotation><\/semantics><\/math> comes out as a differentiable function of the vertex positions. In iskra this is one call, <code>heat_method_distance(V, F, src)<\/code>, and we drop it straight into the optimization loop so the gradients can flow back through it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is the Heat Method on a real 3D mesh. We put one source on the bunny&#8217;s ear and colour each vertex by its geodesic distance from it, so every band is a ring of equal distance. The bands wrap around the ears and body instead of cutting straight across, which is the surface geometry bending the distances. Later we deform a surface on purpose to control exactly this.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/heat_method_mesh.png\" alt=\"\" \/><figcaption class=\"wp-element-caption\">Geodesic distance from a single source (pink dot) on the Stanford bunny. Colour runs from near (blue) to far (red), and each band is a level set of equal geodesic distance.<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Building Geometric Intuition<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Formulas are easier to believe once you can poke at them and watch what happens. So we built a small interactive demo in Polyscope for the forward problem, the one we have been describing so far.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Forward: distance follows the geometry<\/strong><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">In the forward demo you click a source vertex and the distance field and its isolines redraw live. On a flat sheet the contours come out as neat circles. Bend the surface into hills and valleys and they start to stretch and bunch up, since the shortest path now has to climb over or go around whatever is in the way. This is the idea the whole project sits on: the shape of the surface is what sets the distances.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You drop a source and the distance field shows up right away. We start with one source, then add a second, and the field redraws. Each point just takes the distance to the nearer source, so a line forms where the two regions meet.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/forward_demo.gif\" alt=\"\" \/><figcaption class=\"wp-element-caption\">Forward demo: geodesic distance from a source on the bunny (colour + isolines). A second source is added partway through and the field re-renders; each point takes the distance to whichever source is nearer.<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Inverse Geodesics<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">So far we have only read distances off a fixed surface. The inverse problem turns that around. We decide where the source and the target points sit and what distance we want between them, and then we let the surface itself change until that is true. Nothing about the layout moves, only the heights, so the terrain rises and dips until the geodesic distance to the target lands where we asked. The rest of this section makes that concrete: how we set up the terrain, what we minimize, and how we keep the surface from turning into noise.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Problem Formulation<\/strong><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">For our experiments, we keep the setup clean. We represent the terrain as a triangular heightfield over the <math><semantics><mrow><mi>x<\/mi><mo>\u2212<\/mo><mi>z<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">x-z<\/annotation><\/semantics><\/math> plane. The planar coordinates and mesh connectivity remain fixed, while the vertex heights (y &#8211; coordinates) are optimized.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Given a source set <math><semantics><mi class=\"mathcal\">\ud835\udcae<\/mi><annotation encoding=\"application\/x-tex\">\\mathcal S<\/annotation><\/semantics><\/math> and target set <math><semantics><mi class=\"mathcal\">\ud835\udcaf<\/mi><annotation encoding=\"application\/x-tex\">\\mathcal T<\/annotation><\/semantics><\/math>, our goal is to deform the surface until every vertex in <math><semantics><mi class=\"mathcal\">\ud835\udcaf<\/mi><annotation encoding=\"application\/x-tex\">\\mathcal T<\/annotation><\/semantics><\/math> has approximately the same geodesic distance from <math><semantics><mi class=\"mathcal\">\ud835\udcae<\/mi><annotation encoding=\"application\/x-tex\">\\mathcal S<\/annotation><\/semantics><\/math>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For the examples here, we pick a grid diagonal <math><semantics><mrow><mi class=\"mathcal\">\ud835\udcaf<\/mi><mo>=<\/mo><mo form=\"prefix\" stretchy=\"false\">{<\/mo><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>i<\/mi><mo separator=\"true\">,<\/mo><mi>j<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo lspace=\"0.22em\" rspace=\"0.22em\" stretchy=\"false\">|<\/mo><mi>i<\/mi><mo>+<\/mo><mi>j<\/mi><mo>=<\/mo><mi>k<\/mi><mo form=\"postfix\" stretchy=\"false\">}<\/mo><mo separator=\"true\">,<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\mathcal T=\\{(i,j)\\mid i+j=k\\},<\/annotation><\/semantics><\/math> as our target, though in practice <math><semantics><mi class=\"mathcal\">\ud835\udcaf<\/mi><annotation encoding=\"application\/x-tex\">\\mathcal{T}<\/annotation><\/semantics><\/math> can be any arbitrary subsets of vertices you want to align.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Distance Objective and Optimization Workflow<\/strong><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">We measure the geodesic distance <math><semantics><msub><mi>\u03d5<\/mi><mi>i<\/mi><\/msub><annotation encoding=\"application\/x-tex\">\\phi_i<\/annotation><\/semantics><\/math> at every vertex using the differentiable Heat Method. Let <math><semantics><mi class=\"mathcal\">\ud835\udcaf<\/mi><annotation encoding=\"application\/x-tex\">\\mathcal T<\/annotation><\/semantics><\/math> be the target vertex set and let <math><semantics><msub><mi>d<\/mi><mrow><mtext><\/mtext><mi>target<\/mi><\/mrow><\/msub><annotation encoding=\"application\/x-tex\">d_{\\mathrm{target}}<\/annotation><\/semantics><\/math> be the desired geodesic distance from the source set. We define the distance loss as<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>L<\/mi><mtext>distance<\/mtext><\/msub><mo>=<\/mo><mfrac><mn>1<\/mn><mrow><mi>|<\/mi><mi class=\"mathcal\">\ud835\udcaf<\/mi><mi>|<\/mi><\/mrow><\/mfrac><mrow><munder><mo>\u2211<\/mo><mrow><mi>i<\/mi><mo>\u2208<\/mo><mi class=\"mathcal\">\ud835\udcaf<\/mi><\/mrow><\/munder><\/mrow><msup><mrow><mo fence=\"true\" form=\"prefix\">(<\/mo><msub><mi>\u03d5<\/mi><mi>i<\/mi><\/msub><mo>\u2212<\/mo><msub><mi>d<\/mi><mrow><mtext><\/mtext><mi>target<\/mi><\/mrow><\/msub><mo fence=\"true\" form=\"postfix\">)<\/mo><\/mrow><mn>2<\/mn><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">L_\\text{distance} = \\frac{1}{|\\mathcal T|}\\sum_{i\\in\\mathcal T}\\left(\\phi_i-d_{\\mathrm{target}}\\right)^2<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">In plain terms, this asks every target vertex to sit at the same chosen distance from the source, and it grows whenever one of them drifts off that distance.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We optimize the mean squared distance error <math><semantics><msub><mi>L<\/mi><mrow><mtext><\/mtext><mi>distance<\/mi><\/mrow><\/msub><annotation encoding=\"application\/x-tex\">L_{\\mathrm{distance}}<\/annotation><\/semantics><\/math> and report its square root,<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mrow><mtext><\/mtext><mi>RMSE<\/mi><\/mrow><mo>=<\/mo><msqrt><msub><mi>L<\/mi><mrow><mtext><\/mtext><mi>distance<\/mi><\/mrow><\/msub><\/msqrt><mo separator=\"true\">,<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\mathrm{RMSE} = \\sqrt{L_{\\mathrm{distance}}},<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">which has the same units as geodesic distance. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We optimize the vertex heights using Adam over a fixed number of iterations. Each iteration follows this pipeline:<\/p>\n\n\n\n<p class=\"has-text-align-center wp-block-paragraph\">Vertex heights<br>\u2193<br>Construct the heightfield mesh<br>\u2193<br>Compute Heat Method distances<br>\u2193<br>Evaluate <math><semantics><msub><mi>L<\/mi><mrow><mtext><\/mtext><mi>distance<\/mi><\/mrow><\/msub><annotation encoding=\"application\/x-tex\">L_{\\mathrm{distance}}<\/annotation><\/semantics><\/math><br>\u2193<br>Compute height gradients with backpropagation<br>\u2193<br>Update vertex heights with Adam<br>\u2193<br>Repeat until the final iteration<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Since the Heat Method implementation is differentiable, the gradient of <math><semantics><msub><mi>L<\/mi><mrow><mtext><\/mtext><mi>distance<\/mi><\/mrow><\/msub><annotation encoding=\"application\/x-tex\">L_{\\mathrm{distance}}<\/annotation><\/semantics><\/math> can be computed with respect to the vertex heights. We initialize the surface with a small sinusoidal perturbation (amplitude <math><semantics><msup><mn>10<\/mn><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>3<\/mn><\/mrow><\/msup><annotation encoding=\"application\/x-tex\">10^{-3}<\/annotation><\/semantics><\/math>) to break initial planarity without introducing high-frequency noise.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Baseline: Distance Loss Only<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">We first optimized the heightfield using only the distance objective, that is, with the smoothness weight set to zero (we introduce that term in the next section):<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>L<\/mi><mo>=<\/mo><msub><mi>L<\/mi><mtext>distance<\/mtext><\/msub><\/mrow><annotation encoding=\"application\/x-tex\">L = L_\\text{distance}<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">The optimization reduces the <math><semantics><msub><mi>L<\/mi><mtext>distance<\/mtext><\/msub><annotation encoding=\"application\/x-tex\">L_\\text{distance}<\/annotation><\/semantics><\/math> on <math><semantics><mi class=\"mathcal\">\ud835\udcaf<\/mi><annotation encoding=\"application\/x-tex\">\\mathcal{T}<\/annotation><\/semantics><\/math>, making the target diagonal approximately follow a geodesic-distance contour.<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"1008\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.40.47-1024x1008.png\" alt=\"\" class=\"wp-image-888\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.40.47-1024x1008.png 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.40.47-300x295.png 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.40.47-768x756.png 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.40.47-1200x1182.png 1200w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.40.47.png 1308w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">(2D) Heat Method distance contours projected onto the original x -z domain. <br>The red points are the target vertices, and the yellow curve is the desired distance contour (<math><semantics><mrow><mi>\u03d5<\/mi><mo>=<\/mo><mn>0.4<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">\\phi = 0.4<\/annotation><\/semantics><\/math>). <\/figcaption><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"803\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.40.11-1024x803.png\" alt=\"\" class=\"wp-image-887\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.40.11-1024x803.png 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.40.11-300x235.png 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.40.11-768x602.png 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.40.11-1200x941.png 1200w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.40.11.png 1280w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">(Side view) Heightfield optimized using only <math><semantics><msub><mi>L<\/mi><mtext>distance<\/mtext><\/msub><annotation encoding=\"application\/x-tex\">L_\\text{distance}<\/annotation><\/semantics><\/math>. <\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">While minimizing <math><semantics><msub><mi>L<\/mi><mrow><mtext><\/mtext><mi>distance<\/mi><\/mrow><\/msub><annotation encoding=\"application\/x-tex\">L_{\\mathrm{distance}}<\/annotation><\/semantics><\/math> successfully aligns the target distance, it places no constraint on local surface geometry. Therefore, a small distance loss can still produce high-frequency surface noise and localized spiky oscillations.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Regularizing the Surface<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">To enforce smoothness on the optimized surface, we consider two regularization schemes: <strong>height smoothness<\/strong> and <strong>normal smoothness<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We first penalize height differences between neighboring vertices. Let <math><semantics><mi>E<\/mi><annotation encoding=\"application\/x-tex\">E<\/annotation><\/semantics><\/math> be the set of horizontal and vertical grid edges, and let <math><semantics><msub><mi>y<\/mi><mi>p<\/mi><\/msub><annotation encoding=\"application\/x-tex\">y_p<\/annotation><\/semantics><\/math> be the height of vertex <math><semantics><mi>p<\/mi><annotation encoding=\"application\/x-tex\">p<\/annotation><\/semantics><\/math>. We define the <strong>height smoothness<\/strong> as,<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>L<\/mi><mrow><mtext><\/mtext><mi>height<\/mi><\/mrow><\/msub><mo>=<\/mo><mfrac><mn>1<\/mn><mrow><mi>|<\/mi><mi>E<\/mi><mi>|<\/mi><\/mrow><\/mfrac><mrow><munder><mo>\u2211<\/mo><mrow><mo form=\"prefix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">(<\/mo><mi>p<\/mi><mo separator=\"true\">,<\/mo><mi>q<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>\u2208<\/mo><mi>E<\/mi><\/mrow><\/munder><\/mrow><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msub><mi>y<\/mi><mi>p<\/mi><\/msub><mo>\u2212<\/mo><msub><mi>y<\/mi><mi>q<\/mi><\/msub><msup><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mn>2<\/mn><\/msup><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">L_{\\mathrm{height}} =\\frac{1}{|E|}\\sum_{(p,q)\\in E}(y_p-y_q)^2.<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">The loss is small when neighboring vertices have similar heights and large when their height difference increases. Then, the complete objective is<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>L<\/mi><mo>=<\/mo><msub><mi>L<\/mi><mrow><mtext><\/mtext><mi>distance<\/mi><\/mrow><\/msub><mo>+<\/mo><msub><mi>w<\/mi><mtext>smoothness<\/mtext><\/msub><msub><mi>L<\/mi><mrow><mtext><\/mtext><mi>height<\/mi><\/mrow><\/msub><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">L =L_{\\mathrm{distance}} + w_\\text{smoothness} L_{\\mathrm{height}}.<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Height smoothness directly controls neighboring height differences. However, it does not directly measure changes in triangle orientation. A surface can therefore have relatively small height differences while still containing visible local bends.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To fix this, we switch to <strong>Normal Smoothness<\/strong>, which penalizes orientation changes between adjacent face normals rather than absolute height differences. For each adjacent face pair <math><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msub><mi>f<\/mi><mi>i<\/mi><\/msub><mo separator=\"true\">,<\/mo><msub><mi>f<\/mi><mi>j<\/mi><\/msub><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>\u2208<\/mo><mi class=\"mathcal\">\ud835\udc9c<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">(f_i,f_j)\\in\\mathcal A<\/annotation><\/semantics><\/math>, let <math><semantics><msub><mi>\ud835\udc27<\/mi><mi>i<\/mi><\/msub><annotation encoding=\"application\/x-tex\">\\mathbf n_i<\/annotation><\/semantics><\/math> and <math><semantics><msub><mi>\ud835\udc27<\/mi><mi>j<\/mi><\/msub><annotation encoding=\"application\/x-tex\">\\mathbf n_j<\/annotation><\/semantics><\/math> be their unit normals. <\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>L<\/mi><mrow><mtext><\/mtext><mi>normal<\/mi><\/mrow><\/msub><mo>=<\/mo><mfrac><mn>1<\/mn><mrow><mi>|<\/mi><mi class=\"mathcal\">\ud835\udc9c<\/mi><mi>|<\/mi><\/mrow><\/mfrac><mrow><munder><mo>\u2211<\/mo><mrow><mo form=\"prefix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">(<\/mo><msub><mi>f<\/mi><mi>i<\/mi><\/msub><mo separator=\"true\">,<\/mo><msub><mi>f<\/mi><mi>j<\/mi><\/msub><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>\u2208<\/mo><mi class=\"mathcal\">\ud835\udc9c<\/mi><\/mrow><\/munder><\/mrow><msubsup><mrow><mo fence=\"true\" form=\"prefix\">\u2016<\/mo><msub><mi>\ud835\udc27<\/mi><mi>i<\/mi><\/msub><mo>\u2212<\/mo><msub><mi>\ud835\udc27<\/mi><mi>j<\/mi><\/msub><mo fence=\"true\" form=\"postfix\">\u2016<\/mo><\/mrow><mn>2<\/mn><mn>2<\/mn><\/msubsup><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">L_{\\mathrm{normal}}\n=\n\\frac{1}{|\\mathcal A|}\n\\sum_{(f_i,f_j)\\in\\mathcal A}\n\\left\\|\n\\mathbf n_i-\\mathbf n_j\n\\right\\|_2^2.<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">The final objective is<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>L<\/mi><mo>=<\/mo><msub><mi>L<\/mi><mrow><mtext><\/mtext><mi>distance<\/mi><\/mrow><\/msub><mo>+<\/mo><msub><mi>w<\/mi><mrow><mi>s<\/mi><mi>m<\/mi><mi>o<\/mi><mi>o<\/mi><mi>t<\/mi><mi>h<\/mi><mi>n<\/mi><mi>e<\/mi><mi>s<\/mi><mi>s<\/mi><\/mrow><\/msub><msub><mi>L<\/mi><mrow><mtext><\/mtext><mi>normal<\/mi><\/mrow><\/msub><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">L = L_{\\mathrm{distance}} + w_{smoothness}  L_{\\mathrm{normal}}.<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Pareto Analysis  for Smoothness-Weight Selection<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">The regularization weight <math><semantics><msub><mi>w<\/mi><mrow><mtext><\/mtext><mi>smoothness<\/mi><\/mrow><\/msub><annotation encoding=\"application\/x-tex\">w_{\\mathrm{smoothness}}<\/annotation><\/semantics><\/math> dictates the trade-off between geodesic target fidelity and surface smoothness. To select more reasonable optimal hyperparameter values, we sweep <math><semantics><msub><mi>w<\/mi><mtext>smoothness<\/mtext><\/msub><annotation encoding=\"application\/x-tex\">w_\\text{smoothness}<\/annotation><\/semantics><\/math> and construct Pareto frontiers plotting RMSE against <math><semantics><msub><mi>L<\/mi><mrow><mtext><\/mtext><mi>height<\/mi><\/mrow><\/msub><annotation encoding=\"application\/x-tex\">L_{\\mathrm{height}}<\/annotation><\/semantics><\/math> and <math><semantics><msub><mi>L<\/mi><mrow><mtext><\/mtext><mi>normal<\/mi><\/mrow><\/msub><annotation encoding=\"application\/x-tex\">L_{\\mathrm{normal}}<\/annotation><\/semantics><\/math>, respectively. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For height regularization, each point is plotted as<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mrow><mo fence=\"true\" form=\"prefix\">(<\/mo><msub><mi>L<\/mi><mrow><mtext><\/mtext><mi>height<\/mi><\/mrow><\/msub><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mrow><mtext><\/mtext><mi>RMSE<\/mi><\/mrow><mo fence=\"true\" form=\"postfix\">)<\/mo><\/mrow><mo separator=\"true\">,<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\left(L_{\\mathrm{height}},\\ \\mathrm{RMSE}\\right),<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">and for normal regularization as<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mrow><mo fence=\"true\" form=\"prefix\">(<\/mo><msub><mi>L<\/mi><mrow><mtext><\/mtext><mi>normal<\/mi><\/mrow><\/msub><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mrow><mtext><\/mtext><mi>RMSE<\/mi><\/mrow><mo fence=\"true\" form=\"postfix\">)<\/mo><\/mrow><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">\\left(L_{\\mathrm{normal}},\\ \\mathrm{RMSE}\\right).<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Both coordinates are minimized. A point is Pareto-optimal if no other run achieves both lower RMSE and lower smoothness energy.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"645\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-16.54.14-1024x645.png\" alt=\"\" class=\"wp-image-877\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-16.54.14-1024x645.png 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-16.54.14-300x189.png 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-16.54.14-768x484.png 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-16.54.14-1536x967.png 1536w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-16.54.14-2048x1290.png 2048w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-16.54.14-1200x756.png 1200w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-16.54.14-1980x1247.png 1980w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><em>Accuracy\u2013smoothness trade-off for height regularization. <\/em><\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"664\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-16.58.52-1024x664.png\" alt=\"\" class=\"wp-image-878\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-16.58.52-1024x664.png 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-16.58.52-300x194.png 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-16.58.52-768x498.png 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-16.58.52-1536x996.png 1536w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-16.58.52-2048x1328.png 2048w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-16.58.52-1200x778.png 1200w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-16.58.52-1980x1283.png 1980w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><em>Accuracy\u2013smoothness trade-off for normal regularization.<\/em><\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">These plots are used independently because the two smoothness energies have different meanings and scales. For each regularizer, we choose a weight near the knee of its Pareto curve: the point beyond which further smoothing causes a much larger increase in distance error. <\/p>\n\n\n\n<h5 class=\"wp-block-heading\">Visual Comparison<\/h5>\n\n\n\n<p class=\"wp-block-paragraph\">Based on the Pareto curves, we select one representative weight for each regularizer and rerun the optimization to visualize the resulting terrains. Both experiments use the same source, target vertices, desired distance, and initial heightfield.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Set <math><semantics><mrow><msub><mi>w<\/mi><mtext>height<\/mtext><\/msub><mo>=<\/mo><mn>0.003<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">w_\\text{height} = 0.003<\/annotation><\/semantics><\/math> for height regularization<\/li>\n<\/ul>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"1007\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.16.32-1024x1007.png\" alt=\"\" class=\"wp-image-884\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.16.32-1024x1007.png 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.16.32-300x295.png 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.16.32-768x755.png 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.16.32-1200x1180.png 1200w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.16.32.png 1314w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">(2D) Heat Method distance contours projected onto the original x &#8211; z domain under height smoothness.<br><\/figcaption><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"789\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.24.32-1024x789.png\" alt=\"\" class=\"wp-image-885\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.24.32-1024x789.png 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.24.32-300x231.png 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.24.32-768x592.png 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.24.32-1200x925.png 1200w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.24.32.png 1212w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">(Side view) Heightfield optimized with height smoothness. <\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n<\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">It produces several sharp folds and zigzag-like oscillations near the target set. It controls neighboring height differences but does not directly constrain changes in face orientation.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Set <math><semantics><mrow><msub><mi>w<\/mi><mtext>normal<\/mtext><\/msub><mo>=<\/mo><mn>0.01<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">w_\\text{normal} = 0.01<\/annotation><\/semantics><\/math> for normal regularization<\/li>\n<\/ul>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1013\" height=\"1024\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.10.12-1013x1024.png\" alt=\"\" class=\"wp-image-881\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.10.12-1013x1024.png 1013w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.10.12-297x300.png 297w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.10.12-768x776.png 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.10.12-1200x1213.png 1200w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.10.12.png 1294w\" sizes=\"auto, (max-width: 1013px) 100vw, 1013px\" \/><figcaption class=\"wp-element-caption\">(2D) Heat Method distance contours projected onto the original x &#8211; z domain under normal smoothness. <\/figcaption><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"820\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.10.53-1024x820.png\" alt=\"\" class=\"wp-image-882\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.10.53-1024x820.png 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.10.53-300x240.png 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.10.53-768x615.png 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-17.10.53.png 1156w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">(Side view) Heightfield optimized with normal smoothness. <br><\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Normal regularization produces a smoother and more coherent terrain. The deformation is distributed gradually around the target set, forming a continuous ridge without the sharp folds and zigzag-like oscillations observed under height regularization. This reflects the direct penalty on orientation changes between adjacent faces.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The corresponding measurements are summarized below.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>Metric <\/td><td>Height regularization<\/td><td>Normal regularization<\/td><\/tr><tr><td>Target-distance RMSE<\/td><td><math><semantics><mrow><mn>6.52<\/mn><mo>\u00d7<\/mo><msup><mn>10<\/mn><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>4<\/mn><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">6.52 \\times10^{-4}<\/annotation><\/semantics><\/math><\/td><td><math><semantics><mrow><mn>3.51<\/mn><mo>\u00d7<\/mo><msup><mn>10<\/mn><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>3<\/mn><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">3.51\\times10^{-3}<\/annotation><\/semantics><\/math><\/td><\/tr><tr><td>Maximum target error<\/td><td><math><semantics><mrow><mn>1.06<\/mn><mo>\u00d7<\/mo><msup><mn>10<\/mn><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>3<\/mn><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">1.06 \\times 10^{-3}<\/annotation><\/semantics><\/math><\/td><td><math><semantics><mrow><mn>6.35<\/mn><mo>\u00d7<\/mo><msup><mn>10<\/mn><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>3<\/mn><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">6.35\\times10^{-3}<\/annotation><\/semantics><\/math><\/td><\/tr><tr><td>Target-distance range<\/td><td><math><semantics><mrow><mn>1.92<\/mn><mo>\u00d7<\/mo><mn>10<\/mn><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>3<\/mn><\/mrow><\/mrow><annotation encoding=\"application\/x-tex\">1.92 \\times 10{-3}<\/annotation><\/semantics><\/math><\/td><td><math><semantics><mrow><mn>8.84<\/mn><mo>\u00d7<\/mo><msup><mn>10<\/mn><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>3<\/mn><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">8.84\\times10^{-3}<\/annotation><\/semantics><\/math><\/td><\/tr><tr><td>Height smoothness<\/td><td><math><semantics><mrow><mn>1.70<\/mn><mo>\u00d7<\/mo><msup><mn>10<\/mn><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>3<\/mn><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">1.70\\times10^{-3}<\/annotation><\/semantics><\/math><\/td><td><math><semantics><mrow><mn>3.20<\/mn><mo>\u00d7<\/mo><msup><mn>10<\/mn><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>4<\/mn><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">3.20\\times10^{-4}<\/annotation><\/semantics><\/math><\/td><\/tr><tr><td>Normal smoothness<\/td><td><math><semantics><mrow><mn>3.58<\/mn><mo>\u00d7<\/mo><msup><mn>10<\/mn><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>1<\/mn><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">3.58 \\times 10^{-1}<\/annotation><\/semantics><\/math><\/td><td><math><semantics><mrow><mn>3.07<\/mn><mo>\u00d7<\/mo><msup><mn>10<\/mn><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>2<\/mn><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">3.07\\times10^{-2}<\/annotation><\/semantics><\/math><\/td><\/tr><tr><td>Height range<\/td><td><math><semantics><mrow><mn>1.78<\/mn><mo>\u00d7<\/mo><msup><mn>10<\/mn><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>1<\/mn><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">1.78 \\times 10^{-1}<\/annotation><\/semantics><\/math><\/td><td><math><semantics><mrow><mn>1.44<\/mn><mo>\u00d7<\/mo><msup><mn>10<\/mn><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>1<\/mn><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">1.44\\times10^{-1}<\/annotation><\/semantics><\/math><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Remark: Our main goal is to remove noisy folds and produce a smoother terrain. Normal regularization achieves this more effectively by directly penalizing orientation changes between adjacent faces. We therefore use normal smoothness in the following experiments.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Forward Solver Comparison: Convex RGD vs. Heat Method<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">iskra&#8217;s original code computes the forward distance field with a convex-optimization solver, following the regularized geodesic distance (RGD) formulation of Edelstein et al., solved with an ADMM scheme. We compare that convex solver against the Heat Method while keeping the inverse optimization setup fixed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Both methods use the same grid, source, target vertices, desired distance, stopping tolerance, and maximum number of iterations. Each experiment is repeated five times.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><th>Forward solver<\/th><th>Converged runs<\/th><th>Iterations<\/th><th>Median time<\/th><th>Final L2 error<\/th><th>Final RMSE<\/th><th>Max error<\/th><\/tr><tr><td>Convex RGD (Edelstein et al.)<\/td><td>0\/5<\/td><td>50<\/td><td>59.41 s<\/td><td>0.1307<\/td><td>0.0394<\/td><td>0.0694<\/td><\/tr><tr><td>Heat Method<\/td><td>5\/5<\/td><td>29<\/td><td>3.97 s<\/td><td>0.00948<\/td><td>0.00286<\/td><td>0.00575<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Across all test runs, the Heat Method converges in all five runs and is approximately 15 times faster than the convex RGD solver. It also reaches a substantially lower distance error. We therefore use the Heat Method as the forward solver in the following experiments.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Before moving on, here is the whole thing running. We fix a target outline and a distance, and the flat sheet bends frame by frame until the geodesic contour traces the shape we asked for. The source ends up on top and the contours run downhill into the target.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/backward_square.gif\" alt=\"\" \/><figcaption class=\"wp-element-caption\">Inverse design in action: the heightfield bends until the geodesic distance contour from the central source traces a square target. The source sits on the summit and the contours ripple downhill into a square.<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Going Neural: Terrain as an MLP<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Up to this point the unknowns were the vertex heights themselves, one number per vertex. In week 6 we swapped that out. Instead of keeping a separate height at every vertex, we let a small neural network hand us the height as a continuous function of position:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>y<\/mi><mi>i<\/mi><\/msub><mo>=<\/mo><msub><mi>f<\/mi><mi>\u03b8<\/mi><\/msub><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msub><mi>x<\/mi><mi>i<\/mi><\/msub><mo separator=\"true\">,<\/mo><msub><mi>z<\/mi><mi>i<\/mi><\/msub><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo separator=\"true\">,<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">y_i = f_\\theta(x_i, z_i),<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">and we optimize the network weights <math><semantics><mi>\u03b8<\/mi><annotation encoding=\"application\/x-tex\">\\theta<\/annotation><\/semantics><\/math> rather than the heights. The rest of the loop does not change at all. We build the mesh from these heights, run the Heat Method, measure the distance loss, and take an optimizer step. The one thing that is different is where the heights come from.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There are a few reasons to do this. The network is defined everywhere on the domain, not only at the grid points, so it does not really care about the mesh resolution and we can train on a coarse grid and then read it off on a finer one. It also comes out smooth on its own, and the parameter count stays fixed no matter how many vertices we throw at it.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Implementation<\/strong><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">The height field <math><semantics><msub><mi>f<\/mi><mi>\u03b8<\/mi><\/msub><annotation encoding=\"application\/x-tex\">f_\\theta<\/annotation><\/semantics><\/math> is a small multilayer perceptron. It takes the coordinate pair <math><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>x<\/mi><mo separator=\"true\">,<\/mo><mi>z<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">(x,z)<\/annotation><\/semantics><\/math>, normalized to <math><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">[<\/mo><mo>\u2212<\/mo><mn>1<\/mn><mo separator=\"true\">,<\/mo><mn>1<\/mn><mo form=\"postfix\" stretchy=\"false\">]<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">[-1,1]<\/annotation><\/semantics><\/math>, runs it through two hidden layers of width 64 with <math><semantics><mrow><mi>tanh<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">\\tanh<\/annotation><\/semantics><\/math> activations, and returns a single number. We squash that output so the heights cannot run off to anything extreme:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>y<\/mi><mo>=<\/mo><mtext>scale<\/mtext><mo>\u22c5<\/mo><mrow><mi>tanh<\/mi><mo>\u2061<\/mo><\/mrow><mo fence=\"false\" symmetric=\"true\" minsize=\"1.2em\" maxsize=\"1.2em\">(<\/mo><mtext>MLP<\/mtext><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>x<\/mi><mo separator=\"true\">,<\/mo><mi>z<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo fence=\"false\" symmetric=\"true\" minsize=\"1.2em\" maxsize=\"1.2em\">)<\/mo><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">y = \\text{scale}\\cdot\\tanh\\big(\\text{MLP}(x,z)\\big).<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Here <math><semantics><mtext>scale<\/mtext><annotation encoding=\"application\/x-tex\">\\text{scale}<\/annotation><\/semantics><\/math> is a height cap, setting how far up or down the terrain is allowed to go. The whole thing still sits on top of iskra&#8217;s differentiable Heat Method, so the gradient from the distance loss makes it all the way back to the weights <math><semantics><mi>\u03b8<\/mi><annotation encoding=\"application\/x-tex\">\\theta<\/annotation><\/semantics><\/math>. The loop now looks like this:<\/p>\n\n\n\n<p class=\"has-text-align-center wp-block-paragraph\"><strong>Network weights \u03b8<\/strong><br>\u2193<br>Heights <em>y<sub>i<\/sub><\/em> = <em>f<sub>\u03b8<\/sub><\/em>(<em>x<sub>i<\/sub><\/em>, <em>z<sub>i<\/sub><\/em>)<br>\u2193<br>Construct the heightfield mesh<br>\u2193<br>Compute Heat Method distances<br>\u2193<br>Evaluate <em>L<\/em><sub>distance<\/sub><br>\u2193<br>Update <em>\u03b8<\/em> with Adam<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Preliminary visuals based on MLP<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">To validate implicit neural representations for terrain design, we fit a coordinate-based MLP on a coarse <math><semantics><mrow><mn>32<\/mn><mo>\u00d7<\/mo><mn>32<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">32\\times32<\/annotation><\/semantics><\/math> grid.  The network converges after <math><semantics><mn>378<\/mn><annotation encoding=\"application\/x-tex\">378  <\/annotation><\/semantics><\/math>iterations in <math><semantics><mn>29.8<\/mn><annotation encoding=\"application\/x-tex\">29.8 <\/annotation><\/semantics><\/math> seconds. The final target-distance RMSE is <math><semantics><mrow><mn>1.00<\/mn><mo>\u00d7<\/mo><msup><mn>10<\/mn><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>3<\/mn><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">1.00\\times10^{-3}<\/annotation><\/semantics><\/math>, with a maximum target error of <math><semantics><mrow><mn>1.68<\/mn><mo>\u00d7<\/mo><msup><mn>10<\/mn><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>3<\/mn><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">1.68\\times10^{-3}<\/annotation><\/semantics><\/math>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"770\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-19.29.23-1024x770.png\" alt=\"\" class=\"wp-image-912\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-19.29.23-1024x770.png 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-19.29.23-300x226.png 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-19.29.23-768x578.png 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-19.29.23.png 1106w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><strong>Training resolution <\/strong><math><semantics><mrow><mn>32<\/mn><mo>\u00d7<\/mo><mn>32<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">32\\times32<\/annotation><\/semantics><\/math><strong>.<\/strong> <\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">After training, we evaluate the same MLP on a finer <math><semantics><mrow><mn>128<\/mn><mo>\u00d7<\/mo><mn>128<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">128\\times128<\/annotation><\/semantics><\/math> grid without further optimization. This coarse-to-fine evaluation produces a denser visualization of the learned continuous heightfield while preserving the height range <math><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">[<\/mo><mo form=\"prefix\" stretchy=\"false\">\u2212<\/mo><mn>0.2,0.2<\/mn><mo form=\"postfix\" stretchy=\"false\">]<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">[-0.2,0.2]<\/annotation><\/semantics><\/math>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1092\" height=\"886\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-14-at-14.20.40.png\" alt=\"\" class=\"wp-image-927\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-14-at-14.20.40.png 1092w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-14-at-14.20.40-300x243.png 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-14-at-14.20.40-1024x831.png 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-14-at-14.20.40-768x623.png 768w\" sizes=\"auto, (max-width: 1092px) 100vw, 1092px\" \/><figcaption class=\"wp-element-caption\"><strong>Fine evaluation <\/strong><math><semantics><mrow><mn>128<\/mn><mo>\u00d7<\/mo><mn>128<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">128\\times128<\/annotation><\/semantics><\/math>. <\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The finer sampling shows that the learned terrain remains continuous across resolutions and that the target set closely follows the desired distance contour.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Effect of Output Scale and Activation Function<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">The MLP output scale limits how much the terrain can deform. When the scale is too small, the network cannot produce enough height variation to match the target distance.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><th>Scale<\/th><th>Converged<\/th><th>Iterations<\/th><th>RMSE<\/th><th>Normal smoothness<\/th><th>Height range<\/th><th>Runtime<\/th><\/tr><tr><td>0.05<\/td><td>No<\/td><td>400<\/td><td>0.2935<\/td><td>0<\/td><td>0<\/td><td>30.54 s<\/td><\/tr><tr><td>0.10<\/td><td>No<\/td><td>400<\/td><td>0.1432<\/td><td>0.00689<\/td><td>0.200<\/td><td>30.17 s<\/td><\/tr><tr><td>0.15<\/td><td>No<\/td><td>400<\/td><td>0.0678<\/td><td>0.00866<\/td><td>0.300<\/td><td>30.91 s<\/td><\/tr><tr><td><strong>0.20<\/strong><\/td><td>Yes<\/td><td>27<\/td><td>0.0317<\/td><td>0.00241<\/td><td>0.400<\/td><td>2.13 s<\/td><\/tr><tr><td>0.25<\/td><td>Yes<\/td><td>21<\/td><td>0.0303<\/td><td>0.00092<\/td><td>0.443<\/td><td>1.66 s<\/td><\/tr><tr><td>0.30<\/td><td>Yes<\/td><td>18<\/td><td>0.0289<\/td><td>0.00079<\/td><td>0.505<\/td><td>1.40 s<\/td><\/tr><tr><td>0.40<\/td><td>Yes<\/td><td>14<\/td><td>0.0315<\/td><td>0.00063<\/td><td>0.543<\/td><td>1.10 s<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The optimization fails to converge for scales below <math><semantics><mn>0.2<\/mn><annotation encoding=\"application\/x-tex\">0.2<\/annotation><\/semantics><\/math>. At these scales, the learned height range reaches the available output range, indicating that the MLP does not have enough vertical freedom. For the following experiments, we use scale = <math><semantics><mn>0.2.<\/mn><annotation encoding=\"application\/x-tex\">0.2.<\/annotation><\/semantics><\/math><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We also compare several activation functions across five random seeds. The seed controls the initial values of the network parameters. Repeating each experiment with multiple seeds therefore measures the stability of each activation rather than relying on a single favorable run.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>Activation<\/td><td>Convergence rate<\/td><td>Median iterations<\/td><td>Median RMSE<\/td><td>Median normal smoothness<\/td><td>Median runtime<\/td><\/tr><tr><td>Tanh<\/td><td>0.80<\/td><td>30<\/td><td>0.0369<\/td><td>0.00255<\/td><td>2.39 s<\/td><\/tr><tr><td>ReLU<\/td><td>1.00<\/td><td>30<\/td><td>0.0307<\/td><td>0.00311<\/td><td>2.33 s<\/td><\/tr><tr><td>Softplus<\/td><td>1.00<\/td><td>56<\/td><td>0.0294<\/td><td>0.00350<\/td><td>4.37 s<\/td><\/tr><tr><td>SiLU<\/td><td>1.00<\/td><td>39<\/td><td>0.0336<\/td><td>0.00235<\/td><td>3.05 s<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">No single activation function performs best across all metrics. ReLU provides the lowest median runtime, Softplus achieves the lowest median RMSE, and SiLU produces the lowest median normal-smoothness energy. Tanh is less robust, with one run failing to converge.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Consequently, this is a full multi-objective comparison that could use Pareto analysis to balance accuracy, smoothness, runtime, and convergence stability. We leave this analysis for future work and use <strong>Tanh<\/strong> as a fixed activation function in the following experiments<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Fourier Analysis of the Plain MLP Heightfield<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Before introducing positional encoding, we first examine the frequencies represented by the plain coordinate-based MLP. Since the learned terrain is  a continuous function <math><semantics><mrow><mi>y<\/mi><mo>=<\/mo><msub><mi>f<\/mi><mi>\u03b8<\/mi><\/msub><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>x<\/mi><mo separator=\"true\">,<\/mo><mi>z<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">y=f_\\theta(x,z)<\/annotation><\/semantics><\/math>, we can decompose its sampled heightfield into a spatial frequency components. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This analysis shows whether the learned surface is dominated by broad, long-wavelength deformation or also contains shorter-wavelength local detail. It also provides a baseline for evaluating how positional encoding changes the frequency content of the terrain.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We sample the trained MLP on an <math><semantics><mrow><mi>N<\/mi><mo>\u00d7<\/mo><mi>N<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">N\\times N<\/annotation><\/semantics><\/math> grid and denote the resulting heights by <math><semantics><msub><mi>y<\/mi><mrow><mi>m<\/mi><mo separator=\"true\">,<\/mo><mi>n<\/mi><\/mrow><\/msub><annotation encoding=\"application\/x-tex\">y_{m,n}<\/annotation><\/semantics><\/math>. Before computing the Fourier transform, we remove the mean height,<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mover><mi>y<\/mi><mo stretchy=\"true\">~<\/mo><\/mover><mrow><mi>m<\/mi><mo separator=\"true\">,<\/mo><mi>n<\/mi><\/mrow><\/msub><mo>=<\/mo><msub><mi>y<\/mi><mrow><mi>m<\/mi><mo separator=\"true\">,<\/mo><mi>n<\/mi><\/mrow><\/msub><mo>\u2212<\/mo><mover><mi>y<\/mi><mo stretchy=\"false\" class=\"tml-xshift\">\u203e<\/mo><\/mover><mo separator=\"true\">,<\/mo><mspace width=\"1em\"><\/mspace><mtext>where<\/mtext><mspace width=\"1em\"><\/mspace><mover><mi>y<\/mi><mo stretchy=\"false\" class=\"tml-xshift\">\u203e<\/mo><\/mover><mo>=<\/mo><mfrac><mn>1<\/mn><msup><mi>N<\/mi><mn>2<\/mn><\/msup><\/mfrac><mrow><munderover><mo>\u2211<\/mo><mrow><mi>m<\/mi><mo>=<\/mo><mn>0<\/mn><\/mrow><mrow><mi>N<\/mi><mo>\u2212<\/mo><mn>1<\/mn><\/mrow><\/munderover><\/mrow><mrow><munderover><mo>\u2211<\/mo><mrow><mi>n<\/mi><mo>=<\/mo><mn>0<\/mn><\/mrow><mrow><mi>N<\/mi><mo>\u2212<\/mo><mn>1<\/mn><\/mrow><\/munderover><\/mrow><msub><mi>y<\/mi><mrow><mi>m<\/mi><mo separator=\"true\">,<\/mo><mi>n<\/mi><\/mrow><\/msub><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">\\widetilde y_{m,n} = y_{m,n}-\\bar y, \\quad \\text{where} \\quad \\bar{y}\n=\n\\frac{1}{N^2}\n\\sum_{m=0}^{N-1}\n\\sum_{n=0}^{N-1}\ny_{m,n}.<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">This removes the constant vertical offset and leaves only spatial variation. The Fourier coefficients are<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>Y<\/mi><mrow><mi>p<\/mi><mo separator=\"true\">,<\/mo><mi>q<\/mi><\/mrow><\/msub><mo>=<\/mo><mrow><munderover><mo>\u2211<\/mo><mrow><mi>m<\/mi><mo>=<\/mo><mn>0<\/mn><\/mrow><mrow><mi>N<\/mi><mo>\u2212<\/mo><mn>1<\/mn><\/mrow><\/munderover><\/mrow><mrow><munderover><mo>\u2211<\/mo><mrow><mi>n<\/mi><mo>=<\/mo><mn>0<\/mn><\/mrow><mrow><mi>N<\/mi><mo>\u2212<\/mo><mn>1<\/mn><\/mrow><\/munderover><\/mrow><msub><mover><mi>y<\/mi><mo stretchy=\"true\">~<\/mo><\/mover><mrow><mi>m<\/mi><mo separator=\"true\">,<\/mo><mi>n<\/mi><\/mrow><\/msub><mrow><mi>exp<\/mi><mo>\u2061<\/mo><\/mrow><mrow><mo fence=\"true\" form=\"prefix\">[<\/mo><mo>\u2212<\/mo><mn>2<\/mn><mi>\u03c0<\/mi><mi>i<\/mi><mrow><mo fence=\"true\" form=\"prefix\">(<\/mo><mfrac><mrow><mi>p<\/mi><mi>m<\/mi><\/mrow><mi>N<\/mi><\/mfrac><mo>+<\/mo><mfrac><mrow><mi>q<\/mi><mi>n<\/mi><\/mrow><mi>N<\/mi><\/mfrac><mo fence=\"true\" form=\"postfix\">)<\/mo><\/mrow><mo fence=\"true\" form=\"postfix\">]<\/mo><\/mrow><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">Y_{p,q} = \\sum_{m=0}^{N-1}\\sum_{n=0}^{N-1}\\widetilde y_{m,n}\\exp\\left[-2\\pi i\\left(\\frac{pm}{N}+\\frac{qn}{N}\\right)\\right].<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">with power,<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>P<\/mi><mrow><mi>p<\/mi><mo separator=\"true\">,<\/mo><mi>q<\/mi><\/mrow><\/msub><mo>=<\/mo><msup><mrow><mo fence=\"true\" form=\"prefix\">|<\/mo><msub><mi>Y<\/mi><mrow><mi>p<\/mi><mo separator=\"true\">,<\/mo><mi>q<\/mi><\/mrow><\/msub><mo fence=\"true\" form=\"postfix\">|<\/mo><\/mrow><mn>2<\/mn><\/msup><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">P_{p,q} = \\left|Y_{p,q}\\right|^2.<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">After shifting the zero-frequency coefficient to the center, coefficients near the center describe broad terrain deformation, while coefficients farther away describe finer spatial variation. We display <math><semantics><mrow><msub><mi>log<\/mi><mn>10<\/mn><\/msub><mo>\u2061<\/mo><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msub><mi>P<\/mi><mrow><mi>p<\/mi><mo separator=\"true\">,<\/mo><mi>q<\/mi><\/mrow><\/msub><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\log_{10}(P_{p,q})<\/annotation><\/semantics><\/math>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To obtain the radial power spectrum, we group the coefficients by normalized radial frequency,<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>\u03c1<\/mi><mrow><mi>p<\/mi><mo separator=\"true\">,<\/mo><mi>q<\/mi><\/mrow><\/msub><mo>=<\/mo><mfrac><msqrt><mrow><msubsup><mi>f<\/mi><mi>p<\/mi><mn>2<\/mn><\/msubsup><mo>+<\/mo><msubsup><mi>f<\/mi><mi>q<\/mi><mn>2<\/mn><\/msubsup><\/mrow><\/msqrt><mstyle scriptlevel=\"0\" displaystyle=\"true\"><munder><mi>max<\/mi><mrow><mi>u<\/mi><mo separator=\"true\">,<\/mo><mi>v<\/mi><\/mrow><\/munder><mo>\u2061<\/mo><mspace width=\"0.1667em\"><\/mspace><msqrt><mrow><msubsup><mi>f<\/mi><mi>u<\/mi><mn>2<\/mn><\/msubsup><mo>+<\/mo><msubsup><mi>f<\/mi><mi>v<\/mi><mn>2<\/mn><\/msubsup><\/mrow><\/msqrt><\/mstyle><\/mfrac><mo separator=\"true\">,<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\rho_{p,q} = \\frac{\\sqrt{f_p^2+f_q^2}}{\\displaystyle\\max_{u,v}\\sqrt{f_u^2+f_v^2}},<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">where <math><semantics><msub><mi>f<\/mi><mi>p<\/mi><\/msub><annotation encoding=\"application\/x-tex\">f_p<\/annotation><\/semantics><\/math> and <math><semantics><msub><mi>f<\/mi><mi>q<\/mi><\/msub><annotation encoding=\"application\/x-tex\">f_q<\/annotation><\/semantics><\/math> are the frequencies along the x &#8211; and z &#8211; directions. We then average the power of coefficients with similar radial frequencies. This removes directional information and shows how the spectral power changes with overall frequency magnitude.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To compare broad deformation with finer geometric variation, we divide the normalized frequency range at <math><semantics><mrow><msub><mi>\u03c1<\/mi><mrow><mi>p<\/mi><mo separator=\"true\">,<\/mo><mi>q<\/mi><\/mrow><\/msub><mo>=<\/mo><mn>0.25<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">\\rho_{p,q}=0.25<\/annotation><\/semantics><\/math>:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mtext>low&nbsp;frequency:&nbsp;<\/mtext><msub><mi>\u03c1<\/mi><mrow><mi>p<\/mi><mo separator=\"true\">,<\/mo><mi>q<\/mi><\/mrow><\/msub><mo>\u2264<\/mo><mn>0.25<\/mn><mo separator=\"true\">,<\/mo><mspace width=\"2em\"><\/mspace><mtext>high&nbsp;frequency:&nbsp;<\/mtext><msub><mi>\u03c1<\/mi><mrow><mi>p<\/mi><mo separator=\"true\">,<\/mo><mi>q<\/mi><\/mrow><\/msub><mo>&gt;<\/mo><mn>0.25.<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">\\text{low frequency: } \\rho_{p,q}\\leq 0.25,\\qquad\\text{high frequency: } \\rho_{p,q}&gt;0.25.<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">This cutoff separates the region near the frequency origin from the higher-frequency portion of the spectrum.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"455\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-19.10.41-1024x455.png\" alt=\"\" class=\"wp-image-910\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-19.10.41-1024x455.png 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-19.10.41-300x133.png 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-19.10.41-768x341.png 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-19.10.41-1536x682.png 1536w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-19.10.41-1200x533.png 1200w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-13-at-19.10.41.png 1976w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><strong>Fourier spectrum of the MLP heightfield.<\/strong> <br>The 2D log-power spectrum (left); radial power spectrum (right). The dashed line marks the normalized frequency cutoff at (0.25).<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The radial power decreases rapidly with frequency. Nearly all spectral power lies below the <math><semantics><mn>0.25<\/mn><annotation encoding=\"application\/x-tex\">0.25<\/annotation><\/semantics><\/math> cutoff, while the high-frequency contribution rounds to <math><semantics><mrow><mn>0.00<\/mn><mi>%<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">0.00\\%<\/annotation><\/semantics><\/math>. This shows that the learned terrain is dominated by large-scale, low-frequency deformation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So what does this tell us for the inverse problem? The plain MLP hits its distance targets almost entirely through broad, large-scale deformation rather than fine local adjustments, which is why the surface stays free of vertex-level noise even while moving large regions of the terrain. The analysis does not change the geometry itself; it pins down the limitation of this representation, that it has essentially no high-frequency content, and that is exactly what motivates positional encoding, which we turn to next to add short-wavelength detail when a target needs it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Fix: Positional Encoding<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The plain MLP has a built-in weakness known as <strong>spectral bias<\/strong>. Networks pick up smooth, low-frequency shapes almost immediately, but sharp, high-frequency detail comes very slowly, if it comes at all. So when the target we want has sharp features, like corners or a thin ridge, the plain MLP barely budges. There is almost no gradient pushing it toward that high-frequency shape, and it just stays smooth and flat and never gets near the target.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The fix is <strong>positional encoding<\/strong>. Instead of handing the network the raw coordinates and waiting for it to build high frequencies by itself (it will not), we give it those frequencies up front. Before the MLP sees anything, we expand each coordinate into a set of sinusoids at rising frequencies:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>\u03b3<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>x<\/mi><mo separator=\"true\">,<\/mo><mi>z<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>=<\/mo><mo fence=\"false\" symmetric=\"true\" minsize=\"1.2em\" maxsize=\"1.2em\">[<\/mo><mspace width=\"0.1667em\"><\/mspace><mi>x<\/mi><mo separator=\"true\">,<\/mo><mspace width=\"0.2778em\"><\/mspace><mi>z<\/mi><mo separator=\"true\">,<\/mo><mspace width=\"0.2778em\"><\/mspace><mrow><mspace width=\"0.1667em\"><\/mspace><mi>sin<\/mi><mo>\u2061<\/mo><\/mrow><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>\u03c0<\/mi><mi>x<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo separator=\"true\">,<\/mo><mspace width=\"0.2778em\"><\/mspace><mrow><mspace width=\"0.1667em\"><\/mspace><mi>cos<\/mi><mo>\u2061<\/mo><\/mrow><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>\u03c0<\/mi><mi>x<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo separator=\"true\">,<\/mo><mspace width=\"0.2778em\"><\/mspace><mrow><mspace width=\"0.1667em\"><\/mspace><mi>sin<\/mi><mo>\u2061<\/mo><\/mrow><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mn>2<\/mn><mi>\u03c0<\/mi><mi>x<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo separator=\"true\">,<\/mo><mspace width=\"0.2778em\"><\/mspace><mrow><mspace width=\"0.1667em\"><\/mspace><mi>cos<\/mi><mo>\u2061<\/mo><\/mrow><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mn>2<\/mn><mi>\u03c0<\/mi><mi>x<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo separator=\"true\">,<\/mo><mspace width=\"0.2778em\"><\/mspace><mo>\u2026<\/mo><mo separator=\"true\">,<\/mo><mspace width=\"0.2778em\"><\/mspace><mrow><mspace width=\"0.1667em\"><\/mspace><mi>sin<\/mi><mo>\u2061<\/mo><\/mrow><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>\u03c0<\/mi><mi>z<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo separator=\"true\">,<\/mo><mspace width=\"0.2778em\"><\/mspace><mrow><mspace width=\"0.1667em\"><\/mspace><mi>cos<\/mi><mo>\u2061<\/mo><\/mrow><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>\u03c0<\/mi><mi>z<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo separator=\"true\">,<\/mo><mspace width=\"0.2778em\"><\/mspace><mo>\u2026<\/mo><mspace width=\"0.1667em\"><\/mspace><mo fence=\"false\" symmetric=\"true\" minsize=\"1.2em\" maxsize=\"1.2em\">]<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\gamma(x,z) = \\big[\\,x,\\; z,\\; \\sin(\\pi x),\\; \\cos(\\pi x),\\; \\sin(2\\pi x),\\; \\cos(2\\pi x),\\; \\ldots,\\; \\sin(\\pi z),\\; \\cos(\\pi z),\\; \\ldots\\,\\big]<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">and feed this encoded vector to the same MLP:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>y<\/mi><mo>=<\/mo><mtext>scale<\/mtext><mo>\u22c5<\/mo><mrow><mi>tanh<\/mi><mo>\u2061<\/mo><\/mrow><mo fence=\"false\" symmetric=\"true\" minsize=\"1.2em\" maxsize=\"1.2em\">(<\/mo><mtext>MLP<\/mtext><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>\u03b3<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>x<\/mi><mo separator=\"true\">,<\/mo><mi>z<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo fence=\"false\" symmetric=\"true\" minsize=\"1.2em\" maxsize=\"1.2em\">)<\/mo><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">y = \\text{scale}\\cdot\\tanh\\big(\\text{MLP}(\\gamma(x,z))\\big).<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Now that the high frequencies are already sitting in the input, the network does not have to conjure them up, and it can actually draw sharp terrain. The number of bands <math><semantics><mi>L<\/mi><annotation encoding=\"application\/x-tex\">L<\/annotation><\/semantics><\/math> is the only real knob here, and more bands buys you finer detail. We build <math><semantics><mrow><mi>\u03b3<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">\\gamma<\/annotation><\/semantics><\/math> with iskra&#8217;s <code>HarmonicEmbedding<\/code>, so none of this adds any extra learnable parameters.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Result 1: sharper contours for the same budget<\/strong><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">With the same height cap and the same training budget, the plain MLP gives us round, washed-out contours, while the positional-encoding one actually lands on the sharp target shape we asked for. The top-down view is where the gap really shows.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1458\" height=\"1237\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/pe_polyscope-1.png\" alt=\"\" class=\"wp-image-828\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/pe_polyscope-1.png 1458w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/pe_polyscope-1-300x255.png 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/pe_polyscope-1-1024x869.png 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/pe_polyscope-1-768x652.png 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/pe_polyscope-1-1200x1018.png 1200w\" sizes=\"auto, (max-width: 1458px) 100vw, 1458px\" \/><figcaption class=\"wp-element-caption\">Plain MLP (round, blurred contours) vs positional-encoding MLP (sharp target contours), oblique and top view.<\/figcaption><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Result 2: the target at a smaller height budget<\/strong><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">We also swept the height cap directly. As we shrink <math><semantics><mtext>scale<\/mtext><annotation encoding=\"application\/x-tex\">\\text{scale}<\/annotation><\/semantics><\/math>, the terrain has less room to move, so reaching the target distance gets harder. The positional-encoding MLP still converges all the way down to scale 0.1, whereas the plain MLP gives up somewhere below 0.2. So positional encoding gets to the same target on roughly half the height budget.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1275\" height=\"825\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/scale_sweep-1.png\" alt=\"\" class=\"wp-image-825\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/scale_sweep-1.png 1275w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/scale_sweep-1-300x194.png 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/scale_sweep-1-1024x663.png 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/scale_sweep-1-768x497.png 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/scale_sweep-1-1200x776.png 1200w\" sizes=\"auto, (max-width: 1275px) 100vw, 1275px\" \/><figcaption class=\"wp-element-caption\">Final RMSE to the target distance vs height cap, plain MLP vs positional-encoding MLP. PE converges down to scale 0.1; the plain MLP needs scale of at least 0.2.<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Future work<\/h2>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>What is next: MLP-derivative smoothness<\/strong><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Since the terrain is now a proper continuous function, the smoothness question gets cleaner. The mesh-based terms we used earlier, penalizing height differences between neighbors or the bending between adjacent face normals, are really measuring one particular triangulation. Refine the mesh and the same surface gives a different penalty, which always felt slightly off, since what we actually care about is whether the underlying surface is smooth. With the MLP we can ask that directly. Because the height is a differentiable function of position, we hand the coordinates to autograd, get the true gradient of the surface at any point, and penalize its magnitude:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>L<\/mi><mrow><mtext><\/mtext><mi>deriv<\/mi><\/mrow><\/msub><mo>=<\/mo><mfrac><mn>1<\/mn><mrow><mi>|<\/mi><mi>V<\/mi><mi>|<\/mi><\/mrow><\/mfrac><mrow><munder><mo>\u2211<\/mo><mi>i<\/mi><\/munder><\/mrow><msup><mrow><mo fence=\"true\" form=\"prefix\">\u2016<\/mo><mo>\u2207<\/mo><msub><mi>f<\/mi><mi>\u03b8<\/mi><\/msub><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msub><mi>x<\/mi><mi>i<\/mi><\/msub><mo separator=\"true\">,<\/mo><msub><mi>z<\/mi><mi>i<\/mi><\/msub><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo fence=\"true\" form=\"postfix\">\u2016<\/mo><\/mrow><mn>2<\/mn><\/msup><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">L_{\\mathrm{deriv}} = \\frac{1}{|V|}\\sum_{i}\\left\\lVert \\nabla f_\\theta(x_i,z_i)\\right\\rVert^2.<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Unlike the mesh-based smoothness terms, this one does not depend on the mesh at all. It reads the smoothness of the surface itself, so training on a coarse grid and evaluating on a fine one gives a consistent notion of smooth either way. The next step is to run the same accuracy-versus-smoothness sweep we did for the mesh terms, but with this derivative penalty, and see where it lands on the trade-off curve. Our hunch is that it gives a gentler, more uniform surface for the same distance error, since it is not chasing per-triangle artifacts.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A couple of other directions we want to chase from here:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Bringing surface normals into the objective.<\/strong> Regularizing or matching normals, rather than only distances, is a natural next lever and something our smoothness experiments already hinted at.<\/li>\n\n\n\n<li><strong>Scaling to many sources and harder targets.<\/strong> Our tests so far used one or two sources and fairly simple contours. Pushing to many sources and genuinely complex target shapes is where the positional-encoding advantage should matter most.<\/li>\n\n\n\n<li><strong>Combining positional encoding with the derivative smoothness.<\/strong> The two ideas are independent, so pairing them should give sharp targets on a clean, mesh-independent surface.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Reference<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Edelstein, M., Guillen, N., Solomon, J., and Ben-Chen, M. \u201cA Convex Optimization Framework for Regularized Geodesic Distances.\u201d <em>SIGGRAPH 2023 Conference Proceedings<\/em>, 2023. <a href=\"https:\/\/doi.org\/10.1145\/3588432.3591523\">https:\/\/doi.org\/10.1145\/3588432.3591523<\/a><\/li>\n\n\n\n<li>Dodik, A., Mahmoud, A. H., and Solomon, J. \u201cIskra: A System for Inverse Geometry Processing.\u201d 2026. <a href=\"https:\/\/arxiv.org\/abs\/2602.12105\">https:\/\/arxiv.org\/abs\/2602.12105<\/a><\/li>\n\n\n\n<li>Iskra source code: <a href=\"https:\/\/github.com\/anadodik\/iskra\">https:\/\/github.com\/anadodik\/iskra<\/a><\/li>\n<\/ol>\n\n","protected":false},"excerpt":{"rendered":"<p>Mentor(s): Dr. Ahmed Mahmoud [MIT], Ana Dodik [MIT] Fellow(s): Gokul Adithya Suresh [RVCE], Yufan (Diana) Hu [Macalester College] Volunteer: Ruyu Yan [Princeton University] Introduction When we think about the shortest path between two points, we usually imagine a straight line (a Euclidean distance). But once those points lie on a curved surface, the notion of [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[110],"tags":[],"ppma_author":[19,104],"class_list":["post-749","post","type-post","status-publish","format-standard","hentry","category-research"],"authors":[{"term_id":19,"user_id":0,"is_guest":1,"slug":"cap-adigokul05","display_name":"adigokul05","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/?s=96&d=mm&r=g","author_category":"","first_name":"","last_name":"","user_url":"","job_title":"","description":""},{"term_id":104,"user_id":0,"is_guest":1,"slug":"cap-yhu1","display_name":"yhu1","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/?s=96&d=mm&r=g","author_category":"","first_name":"","last_name":"","user_url":"","job_title":"","description":""}],"_links":{"self":[{"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/posts\/749","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/comments?post=749"}],"version-history":[{"count":10,"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/posts\/749\/revisions"}],"predecessor-version":[{"id":1009,"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/posts\/749\/revisions\/1009"}],"wp:attachment":[{"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/media?parent=749"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/categories?post=749"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/tags?post=749"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/ppma_author?post=749"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}