Using noise to generate marching cube terrain? - c#

I've succesfully made a marching cubes class in C# XNA and am using Lib noise to generate 3d perlin noise, but when I tried to generate terrain using the values of the perlin noise as the densities for my marching cubes it generates a large chunk of marching cubes with seemingly random triangles inside of them. I separated the marching cube and gave it arbitrary 3d arrays of densities to run through so I could make sure everything was working and it looked fine, and I had the terrain generation code generate normal cubes and I got a normal looking terrain, but the problem is the corners of each cube gets values from the noise are always switching from negative to positive so fast that the marching cubes just look like a jumbled mess.
This is the code I'm using to generate the terrain:
public MarchingCube[, ,] getTerrainChunk(int size, int stepsize)
{
MarchingCube[, ,] temp = new MarchingCube[size / stepsize, size / stepsize, size / stepsize];
for (int x = 0; x < size; x += stepsize)
{
for (int y = 0; y <size; y += stepsize)
{
for (int z = 0; z < size; z += stepsize)
{
Vector3[] corners = { new Vector3(x,y,z), new Vector3(x,y+stepsize,z),new Vector3(x+stepsize,y+stepsize,z),new Vector3(x+stepsize,y,z), new Vector3(x,y,z+stepsize), new Vector3(x,y+stepsize,z+stepsize),
new Vector3(x+stepsize,y+stepsize,z+stepsize), new Vector3(x+stepsize,y,z+stepsize)};
float[] densities = { GetDensity(corners[0]),GetDensity(corners[1]),GetDensity(corners[2]),GetDensity(corners[3]),GetDensity(corners[4]),
GetDensity(corners[5]),GetDensity(corners[6]),GetDensity(corners[7])};
if (x == 0 && y == 0 && z == 0)
{
temp[x / stepsize, y / stepsize, z / stepsize] = new MarchingCube(densities, corners, device);
}
else
temp[x / stepsize, y / stepsize, z / stepsize] = new MarchingCube(densities, corners);
}
}
}
return temp;
}
and the GetDensity method is as follows:
private float GetDensity(Vector3 point)
{
return (float)terrain.GetValue(point));
}
I think the problem is that I'm not using the noise correctly to get the densities of the marching cubes corners, but so far my google search results haven't been much help at all. So does anyone know how to use the noise properly to generate smooth looking terrain? Or what else may be causing this problem?
I posted screen shots at http://imgur.com/a/D1uMC , the first two are shown using marching cubes and the last one is normal cubes.
Edit:
So from the pictures I figured out that the marching cubes are actually working how they are supposed to with the values they are being given, the problem is defiantly with my noise and how I"m using it. Does anyone know any good resources for 3d terrain generation based on noise?

The problem was how I was reading in the noise, by default I was incredibly "zoomed out" so by changing stepsize to a float and then using smaller increments I was able to get the desired results. Basically "zoomed in" on the area of noise.

Related

Object spawn on procedural terrain spawns on unspecified area

I've managed to put together a procedural terrain with defined regions and I am looking to procedurally place objects within the world within these regions. The regions are defined by their height and I am trying to utilise this to correctly place certain objects in certain regions however my result seems to come out slightly odd where objects are able to spawn outside the defined region height. I am using an AnimationCurve as a mesh height curve to prevent water areas from becoming terrain like. I am unsure if this is causing the issue behind in the correct placement. Would appreciate any insight into where I might be going wrong
Defined regions:
The Rock region is defined with a height of 0.7 and I try to spawn trees on the map only at a Rock location
Spawning object (Spawn 10) at rock location
int amount = 0;
for (int y = 0; y < mapHeight; y++)
{
if(amount < 10)
{
for (int x = 0; x < mapWidth; x++)
{
float currentHeight = noiseMap[x, y];
if(currentHeight.ToString("f1") == (0.7f).ToString())
{
Debug.Log(currentHeight.ToString("f1"));
Vector3 spawnPosition = new Vector3(Random.Range((x), (mapWidth / 2)), currentHeight, Random.Range(y, (mapHeight / 2)));
var block = Instantiate(AssetsToSpawn[0].AssetPrefab, spawnPosition, Quaternion.identity);
block.transform.SetParent(this.transform);
amount++;
break;
}
}
} else
{
return;
}
Result
Some seem to spawn in the right location albeit looking slightly weird but the one on the far left is finding itself on flat land, with water and sand; an area not defined as 0.7 or Rock type.
I think the issue lies in the line
Vector3 spawnPosition = new Vector3(Random.Range((x), (mapWidth / 2)), currentHeight, Random.Range(y, (mapHeight / 2)));
you seem to already iterate your map grid using x and y so why pick random positions on your map that might be anywhere between this current position and the center of the map?
I think you would rather want a random position within the current field and do e.g.
Vector3 spawnPosition = new Vector3(x + Random.Range(-0.5f, 0.5f), currentHeight, y + Random.Range(-0.5f, 0.5f));
Besides that why go through strings in
if(currentHeight.ToString("f1") == (0.7f).ToString())
I see that it's probably for the rounding but I would still prefer to rather do e.g.
if(Mathf.Abs(currentHeight - 0.7f) <= 0.05f)
which would have about the same effect but the threshold is better to control.
However, sounds to me like rock rather would be anything between 0.49 and 0.7 actually so actually it should be
if(currentHeight > 0.49f && currentHeight <= 0.7f)
Finally, unless you store somewhere which map position you already populated with a tree your outer for loop will always over and over enter at the exact same grid position, the first one that is encountered to fulfill your inner loop's condition!
So far you where always using the exact se position for all 10 trees, only the random position caused that it didn't seem so.

Minecraft like terrain in unity3d

I'm a fan of Minecraft's old terrain generation with amazing overhangs, mountains and generally interesting worlds. My problem is that right now I'm using perlin noise, which while good for smooth terrain doesn't really give sporadic jumps that would allow mountains in a mostly flat area.
On top of that with the method I'm using gets 2d perlin noise, puts it in an array and then gets every Y value under it and sets it to a block; This stops generation of overhangs like this: Old Minecraft Terrain Image
Right now I have this:
public class GenerateIdMap : MonoBehaviour {
[Serializable] public class IBSerDict : SerializableDictionaryBase<int, byte> {};
public int size = 60;
public int worldHeight = 3;
public float perlinScale = 15f;
public int seed;
public int heightScale = 10;
public int maxHeight = 256;
public IBSerDict defaultBlocks = new IBSerDict();
void Start()
{
if (seed != 0) seed = (int)Network.time * 10;
CreateMap();
}
byte[,,] CreateMap()
{
byte[,,] map = new byte[size, maxHeight, size];
for (int x = 0; x < size; x++)
{
for (int z = 0; z < size; z++)
{
int y = (int)(Mathf.PerlinNoise((x + seed) / perlinScale, (z + seed) / perlinScale) * heightScale) + worldHeight;
y = Mathf.Clamp(y, 0, maxHeight-1);
while (y > 0)
{
map[x, y, z] = GetBlockType(y);
y--;
}
}
}
return map;
}
byte GetBlockType(int y)
{
SortedDictionary<int, byte> s_defaultBlocks = new SortedDictionary<int, byte>(defaultBlocks);
foreach (var item in s_defaultBlocks.OrderBy(key => key.Key))
{
if (y <= item.Key)
{
print(item.Value);
return item.Value;
}
}
return 0;
} }
The GetBlockType function is new and for getting the default block at that height, I'll fix it up later but it works for now. If you instantiate a prefab at that vector3 you would see terrain. Can someone help me figure out how to make better terrain? Thanks in advance!
Both of your problems should be tackled individually.
The first issue regarding the lack of variation in the generated values can usually be fixed in one of two ways, the first way is to modify the input into the perlin noise, i.e. the octaves and persistance and the second is to mix the output of multiple functions and even use the output of one function as the input to another. By functions, I mean Perlin/Simplex/Voronoi etc.
With the former method, as you mentioned, it can be pretty difficult to get terrain with interesting features over a large area (the generated values are homogeneous), but by playing with the coordinate range and octaves/persistance, it can be possible. The second approach is probably recommended however, because by mixing the inputs and outputs of different functions you can get some really interesting shapes (Voronoi produces circular crator-like shapes).
In order to fix the problem you are having with the overhangs, you would need to change your approach to generating the world slightly. Currently, you are just generating the height values of the terrain and assigning each of those values to give you the terrain surface only. What you ideally would want to do is, generate a pseudo-random value to use as a pass flag for each of the blocks in the 3d space (also those underground). The flag would indicate whether a block should be placed or not in the 3d world.
This is slower, but would generate caves and overhangs as you need.

Getting Number of Cubes with Integer Coordinates with Certain Distance from Origin

I am working on a voxel system for my game that uses dynamic loading of chunks. To optimize it, I have a pool of chunks and a render distance, and what I want to do is fill the pool with a proper amount of chunks. So, I need a way to find that amount. I have tried the following but it seems very inefficient.
private void CreatePool()
{
int poolSize = 0;
for (int x = -m_RenderDistance; x <= m_RenderDistance; x++) {
for (int y = -m_RenderDistance; y <= m_RenderDistance; y++) {
for (int z = -m_RenderDistance; z <= m_RenderDistance; z++) {
if (Position3Int.DistanceFromOrigin(new Position3Int(x, y, z)) <= m_RenderDistance)
poolSize++;
}
}
}
}
More formally, the question involes finding the amount of unique cubes with integer coorindates with a certain distance from the origin.
If you think there is a better way to approach this or I am doing something fundamentally wrong, let me know.
Thanks,
Quintin
I assume its the distance check that you think is inefficient? What you've got shouldn't be too bad if you're just getting the count on Start() or Awake().
Draco18s solution is fine if you are okay with a cubed result. If you want a spherical result without a distance check, you can try some formulation of the volume of a sphere: 4/3*PI*r^3
checkout Bresenham's circle.
Here's a approximation algorithm for a filled 3d Bresenham Circle that I have. It is very similar to what you have already, just with a more effecient squared dist check and a minor adjustment to get a more attractive bresenham-looking circle):
public static List<Vector3> Get3DCircleKeys(int radius){
List<Vector3> keys = new List<Vector3>();
for(int y=-radius; y<=radius; y++){
for(int x=-radius; x<=radius; x++){
for(int z =-radius; z<=radius; z++){
// (+ radius*.08f) = minor modification to match Bresenham result
if(x*x+y*y+z*z <= radius*radius + radius*.08f){
keys.Add(new Vector3(x,y,z));
}
}
}
}
return keys;
}
This, however, will deliver a different count than the volume of sphere would give you, but with some tweaking to it or to the sphere volume calculation, it could be good enough, or at least, more efficient than instantiating a full volume of a cube, where many of the voxels will be outside of the bounds of the render distance.

Map Generation with perlin noise lags after 256 x 256 cube array

I have a Unity project in which I have the necessary code to create a texture anywhere from 2x2 to 512x512 and fill it with perlin noise. I am using this texture to populate a map of blocks (think minecraft) .
The map generates correctly, but Unity starts lagging with that many cubes?
This is an image of the generated map, 256x256.
Any leads in the right direction would be appreciated, I'm just stumped on how to get passed this.
This is the method to populate the map
private void GenerateMap ()
{
for (int x = 0; x < creator.resolution; x++) {
for (int z = 0; z < creator.resolution; z++) {
Object newCube = Instantiate (cube, new Vector3(x, Mathf.Round (texture.GetPixel (x, z).b * 10), z), Quaternion.identity);
newCube.name = "Cube: " + x + ", " + z;
}
}
Debug.Log ("Finished generating world");
}
Probably you should try to load them dynamically as minecraft does to not having to process all the cubes on each tick.
You cannot have that many gameobjects in scene.
Should create mesh chunks (so one mesh piece basically replaces hundreds of cubes)
Browse through the huge minecraft-thread in unity forums: (plenty of explanations and samples too)
http://forum.unity3d.com/threads/after-playing-minecraft.63149/
Or just search for unity minecraft terrain, there are many tutorials for it.

Trying to accurately measure 3D distance from a 2D image

I am trying to extract out 3D distance in mm between two known points in a 2D image. I am using square AR markers in order to get the camera coordinates relative to the markers in the scene. The points are the corners of these markers.
An example is shown below:
The code is written in C# and I am using XNA. I am using AForge.net for the CoPlanar POSIT
The steps I take in order to work out the distance:
1. Mark corners on screen. Corners are represented in 2D vector form, Image centre is (0,0). Up is positive in the Y direction, right is positive in the X direction.
2. Use AForge.net Co-Planar POSIT algorithm to get pose of each marker:
float focalLength = 640; //Needed for POSIT
float halfCornerSize = 50; //Represents 1/2 an edge i.e. 50mm
AVector[] modelPoints = new AVector3[]
{
new AVector3( -halfCornerSize, 0, halfCornerSize ),
new AVector3( halfCornerSize, 0, halfCornerSize ),
new AVector3( halfCornerSize, 0, -halfCornerSize ),
new AVector3( -halfCornerSize, 0, -halfCornerSize ),
};
CoplanarPosit coPosit = new CoplanarPosit(modelPoints, focalLength);
coPosit.EstimatePose(cornersToEstimate, out marker1Rot, out marker1Trans);
3. Convert to XNA rotation/translation matrix (AForge uses OpenGL matrix form):
float yaw, pitch, roll;
marker1Rot.ExtractYawPitchRoll(out yaw, out pitch, out roll);
Matrix xnaRot = Matrix.CreateFromYawPitchRoll(-yaw, -pitch, roll);
Matrix xnaTranslation = Matrix.CreateTranslation(marker1Trans.X, marker1Trans.Y, -marker1Trans.Z);
Matrix transform = xnaRot * xnaTranslation;
4. Find 3D coordinates of the corners:
//Model corner points
cornerModel = new Vector3[]
{
new Vector3(halfCornerSize,0,-halfCornerSize),
new Vector3(-halfCornerSize,0,-halfCornerSize),
new Vector3(halfCornerSize,0,halfCornerSize),
new Vector3(-halfCornerSize,0,halfCornerSize)
};
Matrix markerTransform = Matrix.CreateTranslation(cornerModel[i].X, cornerModel[i].Y, cornerModel[i].Z);
cornerPositions3d1[i] = (markerTransform * transform).Translation;
//DEBUG: project corner onto screen - represented by brown dots
Vector3 t3 = viewPort.Project(markerTransform.Translation, projectionMatrix, viewMatrix, transform);
cornersProjected1[i].X = t3.X; cornersProjected1[i].Y = t3.Y;
5. Look at the 3D distance between two corners on a marker, this represents 100mm. Find the scaling factor needed to convert this 3D distance to 100mm. (I actually get the average scaling factor):
for (int i = 0; i < 4; i++)
{
//Distance scale;
distanceScale1 += (halfCornerSize * 2) / Vector3.Distance(cornerPositions3d1[i], cornerPositions3d1[(i + 1) % 4]);
}
distanceScale1 /= 4;
6. Finally I find the 3D distance between related corners and multiply by the scaling factor to get distance in mm:
for(int i = 0; i < 4; i++)
{
distance[i] = Vector3.Distance(cornerPositions3d1[i], cornerPositions3d2[i]) * scalingFactor;
}
The distances acquired are never truly correct. I used the cutting board as it allowed me easy calculation of what the distances should be. The above image calculated a distance of 147mm (expected 150mm) for corner 1 (red to purple). The image below shows 188mm (expected 200mm).
What is also worrying is the fact that when measuring the distance between marker corners sharing an edge on the same marker, the 3D distances obtained are never the same. Another thing I noticed is that the brown dots never seem to exactly match up with the colored dots. The colored dots are the coordinates used as input to the CoPlanar posit. The brown dots are the calculated positions from the center of the marker calculated via POSIT.
Does anyone have any idea what might be wrong here? I am pulling out my hair trying to figure it out. The code should be quite simple, I don't think I have made any obvious mistakes with the code. I am not great at maths so please point out where my basic maths might be wrong as well...
You are using way to many black boxes in your question. What is the focal length in the second step? Why go through ypr in step 3? How do you calibrate? I recommend to start over from scratch without using libraries that you do not understand.
Step 1: Create a camera model. Understand the errors, build a projection. If needed apply a 2d filter for lens distortion. This might be hard.
Step 2: Find you markers in 2d, after removing lens distortion. Make sure you know the error and that you get the center. Maybe over multiple frames.
Step 3: Un-project to 3d. After 1 and 2 this should be easy.
Step 4: ???
Step 5: Profit! (Measure distance in 3d and know your error)
I think you need to have 3D photo (two photo from a set of distance) so you can get the parallax distance from image differences

Categories