Jump to content
  • Advertisement
Outliner

Marching Cubes with Multiple Materials

Recommended Posts

Consider how one makes terrain using marching cubes. By having a grid of floats we can represent a continuous field that marching cubes will interpolate and turn into a nice smooth isosurface for the player to walk around on. This is easy and excellent for creating mountains and valleys and so on, but what if we want more variety in our game? A game is not normally made of just grass and sky. Maybe some places should be sand, or water, or road. How could that be worked into the mesh that we're getting from marching cubes?

The obvious approach seems to be to have multiple fields, so each point on the grid has a certain level of sand, soil, rock, water, and so on. Then we modify the marching cubes algorithm to look for transitions between materials, so it puts a surface between areas of mostly one material and areas that are mostly other materials. We'd also want to keep track of when these surfaces touch the air, because that's the only time when we'd actually want to triangulate and render the surfaces.

Suddenly the delightfully simple marching cubes algorithm is looking a lot less obvious. Has anything like this ever been done? Does anyone have any tips? Is this the right approach?

Edit: Embarrassing mistake! I didn't think of phrasing the problem as "multiple materials" until I went to post this question, but now that I have I see there are plentiful google results for marching cubes with multiple materials. I'm still interested in any tips and advice, but now I have other resources to help with this problem.

From the Google results, this paper looks especially interesting: Automatic 3D Mesh Generation for A Domain with Multiple Materials

Edited by Outliner

Share this post


Link to post
Share on other sites
Advertisement

That paper seems about right. Basically in the simple form of marching cubes you create a surface in every cube that contains a sign-change. To add materials, you need to also produce a surface in every cube that contains a material change.

Share this post


Link to post
Share on other sites
19 minutes ago, LifeIsGood said:

The paper on Dual Contouring also provides a solution to realize multiple materials.

This paper? Dual Contouring of Hermite Data by Tao Ju et al.

If I have this right, then the essence of the problem is to convert the marching cubes algorithm from an algorithm where the corners of each cube can only be positive or negative, into an algorithm where the corners of each cube can take anywhere up to 8 distinct values. If I have this right, then while ordinary marching cubes deals with 2^8 possibilities, the multiple material marching cubes needs to deal with a far greater number of possibilities. It's not as many as 8^8, but it's still quite a few. Hopefully one of these papers will go into a way to enumerate the possibilities.

Supposing that we're dealing with sand, rock, and air, then each corner has three numbers, one for each material, and the material of the corner is whichever has the greatest number. When two adjacent corners have distinct materials, then the algorithm will put a vertex somewhere on the edge between the corners. Supposing that the one of the corners is sand and the other is rock, we can linearly interpolate the values of sand and rock between the two corners, and the position of the vertex should be the point at which we switch from sand having the greatest value to rock having the greatest value. It's possible that if we interpolated the air value we'd find that air has the greatest value somewhere along the edge, but it's probably fair to just ignore that.

I should focus on implementing a multiple material marching squares as a starting point, then move up to marching cubes once I'm confident I've got all the details worked out for the 2D version. Unfortunately it's not so easy to find resources for the 2D case.

 

Share this post


Link to post
Share on other sites
27 minutes ago, Outliner said:

If I have this right, then the essence of the problem is to convert the marching cubes algorithm from an algorithm where the corners of each cube can only be positive or negative, into an algorithm where the corners of each cube can take anywhere up to 8 distinct values. If I have this right, then while ordinary marching cubes deals with 2^8 possibilities, the multiple material marching cubes needs to deal with a far greater number of possibilities. It's not as many as 8^8, but it's still quite a few. Hopefully one of these papers will go into a way to enumerate the possibilities.

Their is a more straightforward way of dealing with this. For any one material, there are at most 2^8 possible configurations. And there are at most 8 possible materials that could intersect in any one cell.

Worst case is you have to run the exact same marching cubes algorithm 8 times (once for each material present in the current cube). The tables don't expand any, if you don't mind hard transitions between materials.

Share this post


Link to post
Share on other sites
2 minutes ago, swiftcoder said:

Worst case is you have to run the exact same marching cubes algorithm 8 times (once for each material present in the current cube).

Just running the marching cubes algorithm for each material doesn't seem like it would work. Hard transitions are exactly what I want, since that's easier to render than fading between materials, but if we just do 8 separate marching cubes, then what would prevent the materials from clipping into each other?

Suppose we have three materials on the corners of a cube, then it seems they ought to divide the cube among themselves around a point in the middle. Using three separate marching cube procedures the materials might end up leaving an empty space in the middle, or they might end up clipping into each other so some parts of the cube are claimed by more than one material, but it seems impossible for the cube to be properly partitioned among the materials. The geometry of the situation is more complicated than single-material marching cubes ever needs to deal with.

As it happens, I think I've found the actual number of possibilities for multi-material cubes. It's called the 8th Bell number, and it is 4140. It's roughly 16 times the 256 possibilities of single-material marching cubes, but still well within the range of a 16-bit integer.

Share this post


Link to post
Share on other sites

Finding a good index for the lookup table is tricky. There are 4140 cases for a cube with various materials on its corners, but how should we number those cases? There are 28 comparisons we could do between the corners of a cube, and if we give each comparison a bit in the index, then we end up with an array of 268,435,456 elements, of which only 4140 will ever be used.

Here's a blog entry about generating lists of set partitions. It generates partitions in a particular order, but it's not clear how to reverse the process and generate a number when given a partition. Enumerating set partitions with Bell numbers and Stirling numbers of the second kind.

Share this post


Link to post
Share on other sites
18 hours ago, Outliner said:

but if we just do 8 separate marching cubes, then what would prevent the materials from clipping into each other?

Depends how watertight/deterministic your marching cubes is, I guess. I'll have to give this a try at some point.

Share this post


Link to post
Share on other sites
10 minutes ago, swiftcoder said:

Depends how watertight/deterministic your marching cubes is, I guess.

If I understand this correctly, then the issue isn't about floating point errors or deterministic behavior. Even with absolute precision there would still be no way to avoid clipping except by having large gaps between materials. For example:

59c01e839184b_MarchingSquaresGap.gif.e8400bb72ca14365193b06a2c681d6b0.gif

On the left we have a Marching Squares square with three materials inserted with three separate single-material marching squares passes. Unfortunately there is a gap, so picture grass, sand, and water meeting at a point that includes a hole to infinity. If we want to fill that gap, we're forced to do something like shown on the right, where we increase the magnitude of one of the materials, but if we increase any of the materials even slightly it will not only start to fill the gap, but also start to overlap with the other materials.

Share this post


Link to post
Share on other sites

In the past, I never bothered with marching different meshes for different terrain materials. I just marches the terrain as a single mesh, then used vertex colors (generated after marching the surface, using various techniques) to blend between terrain textures in the shader. Something like this (very quick example):

797O5Ok.png

With a tri-planar shader that displays different textures for the top surface than what it displays for the side surfaces, then you can just paint the v-colors (either procedurally, or by hand if that is your wish, in a post-process step) for different materials, and the shader will handle blending between the types and applying the tri-planar projection. A single color layer provides for 5 base terrain materials, if you count black(0,0,0,0) as one material, red(1,0,0,0), green(0,1,0,0), blue(0,0,1,0) and alpha(0,0,0,1) as the others. Provide another RGBA v-color layer and you can bump that to 9.

Doing it this way, you don't have to be content with sharp edges between terrain types, since the shader is content to smoothly blend between materials as needed, and you don't deal with the hassle of marching multiple terrain meshes.

Share this post


Link to post
Share on other sites

  • Advertisement
  • Advertisement
  • Popular Tags

  • Popular Now

  • Advertisement
  • Similar Content

    • By CocoaColetto
      I am basically brand new to the gaming industry business wise although I have been a gamer for years. I officially started my game publishing company, and being as though I am only 20, I have no connects to the gaming industry. Of course, I'm still going to do more internet research, but I thought why not ask folks who may have business hands in the gaming community? If anyone is questioning, my game prototype is basically done (I designed it myself) and its very detailed and I am going to start searching for a team to help me build it. Thank you. 
    • By Shtabbbe
      I've had a game idea for a while, and I wanted to finally try to create it.
      Its a 2D open-world tile-based MMO. The concept is it is one world and multiplayer only, so everyone shares one world no matter region, platform, etc.
      I am having problems finding out what to use to start development, I tried Unity but saw some of the negatives and refrained and now im stuck, could anyone recommend some intermediate friendly 2D engines that can support what I am looking for? Preferably in languages that are or are somewhat like Java, C#, Python, JavaScript, Lua.
      Thanks for your help, im very new at this if you cant tell
    • By Nikita Sidorenko
      I'm making render just for fun (c++, opengl)
      Want to add decals support. Here what I found
      A couple of slides from doom
      http://advances.realtimerendering.com/s2016/Siggraph2016_idTech6.pdf Decals but deferred 
      http://martindevans.me/game-development/2015/02/27/Drawing-Stuff-… space-Decals/
      No implementation details here
      https://turanszkij.wordpress.com/2017/10/12/forward-decal-rendering/
      As I see there should be a list of decals for each tile same as for light sources. But what to do next?
      Let assume that all decals are packed into a spritesheet. Decal will substitute diffuse and normal.
      - What data should be stored for each decal on the GPU? 
      - Articles above describe decals as OBB. Why OBB if decals seem to be flat?
      - How to actually render a decal during object render pass (since it's forward)? Is it projected somehow? Don't understand this part completely.
      Are there any papers for this topic?
    • By Ming-Lun "Allen" Chou
      Here is the original blog post.
      Edit: Sorry, I can't get embedded LaTeX to display properly.
      The pinned tutorial post says I have to do it in plain HTML without embedded images?
      I actually tried embedding pre-rendered equations and they seemed fine when editing, 
      but once I submit the post it just turned into a huge mess.
      So...until I can find a proper way to fix this, please refer to the original blog post for formatted formulas.
      I've replaced the original LaTex mess in this post with something at least more readable.
      Any advice on fixing this is appreciated.
      This post is part of my Game Math Series.
      Source files are on GitHub.
      Shortcut to sterp implementation.
      Shortcut to code used to generate animations in this post.
      An Alternative to Slerp
      Slerp, spherical linear interpolation, is an operation that interpolates from one orientation to another, using a rotational axis paired with the smallest angle possible.
      Quick note: Jonathan Blow explains here how you should avoid using slerp, if normalized quaternion linear interpolation (nlerp) suffices. Long store short, nlerp is faster but does not maintain constant angular velocity, while slerp is slower but maintains constant angular velocity; use nlerp if you’re interpolating across small angles or you don’t care about constant angular velocity; use slerp if you’re interpolating across large angles and you care about constant angular velocity. But for the sake of using a more commonly known and used building block, the remaining post will only mention slerp. Replacing all following occurrences of slerp with nlerp would not change the validity of this post.
      In general, slerp is considered superior over interpolating individual components of Euler angles, as the latter method usually yields orientational sways.
      But, sometimes slerp might not be ideal. Look at the image below showing two different orientations of a rod. On the left is one orientation, and on the right is the resulting orientation of rotating around the axis shown as a cyan arrow, where the pivot is at one end of the rod.

      If we slerp between the two orientations, this is what we get:

      Mathematically, slerp takes the “shortest rotational path”. The quaternion representing the rod’s orientation travels along the shortest arc on a 4D hyper sphere. But, given the rod’s elongated appearance, the rod’s moving end seems to be deviating from the shortest arc on a 3D sphere.
      My intended effect here is for the rod’s moving end to travel along the shortest arc in 3D, like this:

      The difference is more obvious if we compare them side-by-side:

      This is where swing-twist decomposition comes in.
       
      Swing-Twist Decomposition
      Swing-Twist decomposition is an operation that splits a rotation into two concatenated rotations, swing and twist. Given a twist axis, we would like to separate out the portion of a rotation that contributes to the twist around this axis, and what’s left behind is the remaining swing portion.
      There are multiple ways to derive the formulas, but this particular one by Michaele Norel seems to be the most elegant and efficient, and it’s the only one I’ve come across that does not involve any use of trigonometry functions. I will first show the formulas now and then paraphrase his proof later:
      Given a rotation represented by a quaternion R = [W_R, vec{V_R}] and a twist axis vec{V_T}, combine the scalar part from R the projection of vec{V_R} onto vec{V_T} to form a new quaternion: T = [W_R, proj_{vec{V_T}}(vec{V_R})]. We want to decompose R into a swing component and a twist component. Let the S denote the swing component, so we can write R = ST. The swing component is then calculated by multiplying R with the inverse (conjugate) of T: S= R T^{-1} Beware that S and T are not yet normalized at this point. It's a good idea to normalize them before use, as unit quaternions are just cuter. Below is my code implementation of swing-twist decomposition. Note that it also takes care of the singularity that occurs when the rotation to be decomposed represents a 180-degree rotation. public static void DecomposeSwingTwist ( Quaternion q, Vector3 twistAxis, out Quaternion swing, out Quaternion twist ) { Vector3 r = new Vector3(q.x, q.y, q.z); // singularity: rotation by 180 degree if (r.sqrMagnitude < MathUtil.Epsilon) { Vector3 rotatedTwistAxis = q * twistAxis; Vector3 swingAxis = Vector3.Cross(twistAxis, rotatedTwistAxis); if (swingAxis.sqrMagnitude > MathUtil.Epsilon) { float swingAngle = Vector3.Angle(twistAxis, rotatedTwistAxis); swing = Quaternion.AngleAxis(swingAngle, swingAxis); } else { // more singularity: // rotation axis parallel to twist axis swing = Quaternion.identity; // no swing } // always twist 180 degree on singularity twist = Quaternion.AngleAxis(180.0f, twistAxis); return; } // meat of swing-twist decomposition Vector3 p = Vector3.Project(r, twistAxis); twist = new Quaternion(p.x, p.y, p.z, q.w); twist = Normalize(twist); swing = q * Quaternion.Inverse(twist); } Now that we have the means to decompose a rotation into swing and twist components, we need a way to use them to interpolate the rod’s orientation, replacing slerp.
      Swing-Twist Interpolation
      Replacing slerp with the swing and twist components is actually pretty straightforward. Let the Q_0 and Q_1 denote the quaternions representing the rod's two orientations we are interpolating between. Given the interpolation parameter t, we use it to find "fractions" of swing and twist components and combine them together. Such fractiona can be obtained by performing slerp from the identity quaternion, Q_I, to the individual components. So we replace: Slerp(Q_0, Q_1, t) with: Slerp(Q_I, S, t) Slerp(Q_I, T, t) From the rod example, we choose the twist axis to align with the rod's longest side. Let's look at the effect of the individual components Slerp(Q_I, S, t) and Slerp(Q_I, T, t) as t varies over time below, swing on left and twist on right:
      And as we concatenate these two components together, we get a swing-twist interpolation that rotates the rod such that its moving end travels in the shortest arc in 3D. Again, here is a side-by-side comparison of slerp (left) and swing-twist interpolation (right):

      I decided to name my swing-twist interpolation function sterp. I think it’s cool because it sounds like it belongs to the function family of lerp and slerp. Here’s to hoping that this name catches on.
      And here’s my code implementation:
      public static Quaternion Sterp ( Quaternion a, Quaternion b, Vector3 twistAxis, float t ) { Quaternion deltaRotation = b * Quaternion.Inverse(a); Quaternion swingFull; Quaternion twistFull; QuaternionUtil.DecomposeSwingTwist ( deltaRotation, twistAxis, out swingFull, out twistFull ); Quaternion swing = Quaternion.Slerp(Quaternion.identity, swingFull, t); Quaternion twist = Quaternion.Slerp(Quaternion.identity, twistFull, t); return twist * swing; } Proof
      Lastly, let’s look at the proof for the swing-twist decomposition formulas. All that needs to be proven is that the swing component S does not contribute to any rotation around the twist axis, i.e. the rotational axis of S is orthogonal to the twist axis. Let vec{V_{R_para}} denote the parallel component of vec{V_R} to vec{V_T}, which can be obtained by projecting vec{V_R} onto vec{V_T}: vec{V_{R_para}} = proj_{vec{V_T}}(vec{V_R}) Let vec{V_{R_perp}} denote the orthogonal component of vec{V_R} to vec{V_T}: vec{V_{R_perp}} = vec{V_R} - vec{V_{R_para}} So the scalar-vector form of T becomes: T = [W_R, proj_{vec{V_T}}(vec{V_R})] = [W_R, vec{V_{R_para}}] Using the quaternion multiplication formula, here is the scalar-vector form of the swing quaternion: S = R T^{-1} = [W_R, vec{V_R}] [W_R, -vec{V_{R_para}}] = [W_R^2 - vec{V_R} ‧ (-vec{V_{R_para}}), vec{V_R} X (-vec{V_{R_para}}) + W_R vec{V_R} + W_R (-vec{V_{R_para}})] = [W_R^2 - vec{V_R} ‧ (-vec{V_{R_para}}), vec{V_R} X (-vec{V_{R_para}}) + W_R (vec{V_R} -vec{V_{R_para}})] = [W_R^2 - vec{V_R} ‧ (-vec{V_{R_para}}), vec{V_R} X (-vec{V_{R_para}}) + W_R vec{V_{R_perp}}] Take notice of the vector part of the result: vec{V_R} X (-vec{V_{R_para}}) + W_R vec{V_{R_perp}} This is a vector parallel to the rotational axis of S. Both vec{V_R} X(-vec{V_{R_para}}) and vec{V_{R_perp}} are orthogonal to the twist axis vec{V_T}, so we have shown that the rotational axis of S is orthogonal to the twist axis. Hence, we have proven that the formulas for S and T are valid for swing-twist decomposition. Conclusion
      That’s all.
      Given a twist axis, I have shown how to decompose a rotation into a swing component and a twist component.
      Such decomposition can be used for swing-twist interpolation, an alternative to slerp that interpolates between two orientations, which can be useful if you’d like some point on a rotating object to travel along the shortest arc.
      I like to call such interpolation sterp.
      Sterp is merely an alternative to slerp, not a replacement. Also, slerp is definitely more efficient than sterp. Most of the time slerp should work just fine, but if you find unwanted orientational sway on an object’s moving end, you might want to give sterp a try.
    • By 3dmodelerguy
      A few questions about some c++ code
      So I am starting to get back into c++ after about 12 - 14 years away from it (and even back then, my level of knowledge was maybe a little above beginner) to do some game / SDL programming. I was following a tutorial to get at least a basic starting point for an entity component system and it works however there was some code that I don't quite understand even after looking around little.
      First pice of code is:
      T* component(new T(std::forward<TArguments>(arguments)...)); This seems to be assigning the `component` with the results of what is in the parentheses though normally I would expect this:
      T* component = new T(std::forward<TArguments>(arguments)...); Is this just syntax preference or does the compiler do something different with the parentheses (it is weird to me as when I see that, I think it is a function call)?
      The second piece of code I think I understand the general idea of what it is doing but some of the specific are escaping me:
      template <typename T, typename... TArguments> T& Entity::addComponent(TArguments&&... arguments) {   T* component = new T(std::forward<TArguments>(arguments)...); So from my understanding, the first line would basically take this:
      entity->addComponent<TransformComponent, int, int, int, int>(x, y, width, height); and take of the first item in the template and assign the to T and then "group" (not sure the correct term) the rest of the items as a collection of some sort and then the `...` on the second line would group the arguments (that would need to match the template group) that were passed in. Then the third line is effectively converting the template / passed in arguments to be called like this:
      TransformComponent* component = new TransformComponent(x, y, width, height); The parts that are a bit confusing to me is first the `&&`. From what I have read (from stack overflow), that symbol means rvalue reference or reference to an argument that is about to be destroyed. Not quite sure what it means by it about to be destroyed.
      The second part, which I think related to using `&&`, is the `std::forward<TArguments>`. The explainations that I have found so far as are bit confusing to me.
      I will continue to try to find the answer to these confusions but I though maybe someone here might have an explanation that might make more sense to me. I would also consider it quite possible that there is some prerequisite knowledge that I might not have (I mean I think I have a decent understanding of pointers and references) so if there is other stuff I should looking into, that would be great too.
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!