2

I was wondering, how would you, mathematically speaking, generate x points at random positions on a 3D surface, knowing the number of triangle polygons composing the surface (their dimensions, positions, normals, etc.)? In how many steps would you proceed?

I'm trying to create a "scatterer" in Maya (with Python and API), but I don't even know where to start in terms of concept. Should I generate the points first, and then check if they belong to the surface? Should I create the points directly on the surface (and how, in this case)?

Edit: I want to achieve this without using 2D projection or UVs, as far as possible.

Cœur
  • 37,241
  • 25
  • 195
  • 267
UKDP
  • 226
  • 5
  • 21
  • how does the surface look like? I assume you want the point to be random, independently of your local triangle density. I had once to do something similar and one way to go about it is to generate random straight lines and then "intersect" them with the surface, i.e. take the vertex that are nearest to the line, by projecting onto the perpendicular plane and then minimizing the sum of the squares of the components of the projected distance. Now the thing is, how to generate random straight lines... that depends on the kind of surfaces you have – Daniele Bernardini Mar 04 '16 at 16:24

4 Answers4

2

If the constraint is that all of the output points be on the surface, you want a consistent method of addressing the surface itself rather than worrying about the 3d > surface conversion for your points.

The hacktastic way to do that would be to create a UV map for your 3d object, and then scatter points randomly in 2 dimensions (throwing away points which happened not to land inside a valid UV shell). Once your UV shells are filled up as much as you'd like, you can convert your UV points to barycentric coordinates to convert those 2-d points back to 3-d points: effectively you say "i am 30% vertex A, 30 % vertex B, and 40% vertex C, so my position is (.3A + .3B + .4C)

Besides simplicity, another advantage of using is UV map is that it would allow you to customize the density and relative importance of different parts of the mesh: a larger UV face will get a lot of scattered points, and a smaller one fewer -- even if that doesn't match the physical size or the faces.

Going to 2D will introduce some artifacts because you probably will not be able to come up with a UV map that is both stretch-free and seam-free, so you'll get variations in the density of your scatter because of that. However for many applications this will be fine, since the algorithm is really simple and the results easy to hand tune.

I have not used this one but this looks like it's based on this general approach: http://www.shanemarks.co.za/uncategorized/uv-scatter-script/

If you need a more mathematically rigorous method, you'd need a fancier method of mesh parameterization : a way to turn your 3-d collection of triangles into a consistent space. There is a lot of interesting work in that field but it would be hard to pick a particular path without knowing the application.

theodox
  • 12,028
  • 3
  • 23
  • 36
  • Hey, thank you for your answer! The thing is -- I should have added this in my post -- I want to avoid to modify UVs because the mesh could already have UVs and I don't want to modify them. – UKDP Mar 08 '16 at 13:37
  • 1
    You can add a custom UV channel and then delete it when you are done – theodox Mar 08 '16 at 20:04
2

You should compute the area of each triangle, and use those as weights to determine the destination of each random point. It is probably easiest to do this as a batch operation:

def sample_areas(triangles, samples):
  # compute and sum triangle areas
  totalA = 0.0
  areas = []
  for t in triangles:
    a = t.area()
    areas.append(a)
    totalA += a

  # compute and sort random numbers from [0,1)
  rands = sorted([random.random() for x in range(samples)])

  # sample based on area
  area_limit = 0.0
  rand_index = 0
  rand_value = rands[rand_index]
  for i in range(len(areas)):
    area_limit += areas[i]
    while rand_value * totalA < area_limit:
      # sample randomly over current triangle
     triangles[i].add_random_sample()

      # advance to next sorted random number
      rand_index += 1;
      if rand_index >= samples:
        return
      rand_value = rands[rand_index]

Note that ridged or wrinkled regions may appear to have higher point density, simply because they have more surface area in a smaller space.

comingstorm
  • 25,557
  • 3
  • 43
  • 67
  • Thanks for your answer, I'm trying to rewrite you pseudo in python code but I have issues "translating" the loop part, could you please give me more details or comments to explain how it works? Thank you again – UKDP Mar 08 '16 at 13:35
  • 1
    I have rewritten the pseudocode in python. Untested, so worth what you paid for it... – comingstorm Mar 08 '16 at 17:39
2
  1. Pick 2 random edges from random triangle.
  2. Create 2 random points on edges.
  3. Create new random point between them.

My ugly mel script:

//Select poly and target object
{
$sel = `ls -sl -fl`; select $sel[0];
polyTriangulate -ch 0;
$poly_s = `polyListComponentConversion -toFace`;$poly_s = `ls -fl $poly_s`;//poly flat list
int $numPoly[] = `polyEvaluate -fc`;//max random from number of poly
int $Rand = rand($numPoly[0]);//random number
$vtx_s =`polyListComponentConversion -tv $poly_s[$Rand]`;$vtx_s=`ls- fl $vtx_s`;//3 vertex from random poly flat list
undo; //for polyTriangulate

vector $A = `pointPosition $vtx_s[0]`;
vector $B = `pointPosition $vtx_s[1]`;
vector $C = `pointPosition $vtx_s[2]`;
vector $AB = $B-$A; $AB = $AB/mag($AB); //direction vector and normalize
vector $AC = $A-$C; $AC = $AC/mag($AC); //direction vector and normalize
$R_AB = mag($B-$A) - rand(mag($B-$A)); vector $AB = $A + ($R_AB * $AB);//new position
$R_AC = mag($A-$C) - rand(mag($A-$C)); vector $AC = $C + ($R_AC * $AC);//new position
vector $ABC = $AB-$AC; $ABC = $ABC/mag($ABC); //direction vector and normalize
$R_ABC = mag($AB-$AC) - rand(mag($AB-$AC)); //random
vector $ABC = $AC + ($R_ABC * $ABC);
float $newP2[] = {$ABC.x,$ABC.y,$ABC.z};//back to float

move $newP2[0] $newP2[1] $newP2[2] $sel[1];
select -add $sel[1];
}

PS UV method is better

SAF
  • 309
  • 2
  • 13
1

Here is pseudo code that might be a good starting point:

  1. Let N = no of vertices of 3D face that you are working with.
  2. Just generate N random numbers, compute their sum, divide each one by the sum. Now you have N random number whose sum is = 1.0.
  3. Using above random numbers, take a linear combination of 3D vertices of the 3D face that you are interested in. This should give you a random 3D point on the face.
  4. Repeat till you get sufficient no. of random points on the 3D face.
vcp
  • 962
  • 7
  • 15