Unity - How to create circular gradient? - c#

I need a small help, I am trying to create an Island generator using Unity.
However I do not know how to make circular fall Off map.But instead I manage to create an island that is more of a box shaped island.
This is what I am doing:
I am generating Height map using Perlin noise
I will generate FallOff map and substract it from Perlin noise
The Island getsgenerated, but instead of having circular shape the
island hasbox-like shape
I want to create this
This is my Perlin noise function
public float[,] GenerateNoise(int mapSize,int octaves, string seed, float noiseScale, float persistence, float lacunarity, Vector2 offset)
{
if (noiseScale <= 0)
{
noiseScale = 0.0001f;
}
float halfWidth = mapSize / 2f;
float halfHeight = mapSize / 2f;
float[,] noiseMap = new float[mapSize + 1, mapSize + 1];
System.Random rand = new System.Random(seed.GetHashCode());
//Octaves offset
Vector2[] octavesOffset = new Vector2[octaves];
for (int i = 0; i < octaves; i++)
{
float offset_X = rand.Next(-100000, 100000) + offset.x;
float offset_Y = rand.Next(-100000, 100000) + offset.y;
octavesOffset[i] = new Vector2(offset_X / mapSize, offset_Y / mapSize);
}
for (int x = 0; x < mapSize; x++)
{
for (int y = 0; y < mapSize; y++)
{
float amplitude = 1;
float frequency = 1;
float noiseHeight = 0;
float superpositionCompensation = 0;
for (int i = 0; i < octaves; i++)
{
float sampleX = (x - halfWidth) / noiseScale * frequency + octavesOffset[i].x;
float sampleY = (y - halfHeight) / noiseScale * frequency + octavesOffset[i].y;
float perlinValue = Mathf.PerlinNoise(sampleX, sampleY);
noiseHeight += perlinValue * amplitude;
noiseHeight -= superpositionCompensation;
amplitude *= persistence;
frequency *= lacunarity;
superpositionCompensation = amplitude / 2;
}
noiseMap[x, y] = Mathf.Clamp01(noiseHeight);
}
}
return noiseMap;
}
And this is my FallOff map function
public float[,] GenerateFallOffMap(int mapSize)
{
float[,] fallOffMap = new float[mapSize , mapSize];
for (int x = 0; x < mapSize; x++)
{
for (int y = 0; y < mapSize; y++)
{
int index = x * mapSize + y;
float fallOff_A = x / (float)mapSize * 2 - 1;
float fallOff_B = y / (float)mapSize * 2 - 1;
float value = Mathf.Max(Mathf.Abs(fallOff_A), Mathf.Abs(fallOff_B));
fallOffMap[x,y] = Evaluate(value);
}
}
return fallOffMap;
}
static float Evaluate(float value)
{
float a = 3;
float b = 2.2f;
return Mathf.Pow(value, a) / (Mathf.Pow(value, a) + Mathf.Pow(b - b * value, a));
}

Use the distance from the center of the grid as the parameter to calculate the falloff value.
/// value - The calculated value to process
/// radius - The distance from center to calculate falloff distance
/// x - The x-coordinate of the value position
/// y - The y-coordinate of the value position
/// cx - The x-coordinate of the center position
/// cy - The y-coordinate of the center position
public float RadialFallOff(float value, float radius, int x, int y, float cx, float cy)
{
float dx = cx - x;
float dy = cy - y;
float distSqr = dx * dx + dy * dy;
float radSqr = radius * radius;
if (distSqr > radSqr) return 0f;
return value;
}
This will result in a hard cutoff at radius. If you want a softer transition along the edges, you can use an innerRadius and an outerRadius to produce a feathered effect:
/// value - The calculated value to process
/// innerRadius - The distance from center to start feathering
/// outerRadius - The distance from center to fully fall off
/// x - The x-coordinate of the value position
/// y - The y-coordinate of the value position
/// cx - The x-coordinate of the center position
/// cy - The y-coordinate of the center position
public float FeatheredRadialFallOff(float value, float innerRadius, float outerRadius, int x, int y, float cx, float cy)
{
float dx = cx - x;
float dy = cy - y;
float distSqr = dx * dx + dy * dy;
float iRadSqr = innerRadius * innerRadius;
float oRadSqr = outerRadius * outerRadius;
if (distSqr >= oRadSqr) return 0f;
if (distSqr <= iRadSqr) return value;
float dist = Mathf.Sqr(distSqr);
float t = Mathf.InverseLerp(innerRadius, outerRadius, dist);
// Use t with whatever easing you want here, or leave it as is for linear easing
return value * t;
}
You can use it like such:
float value = Mathf.Max(Mathf.Abs(fallOff_A), Mathf.Abs(fallOff_B));
value = Evaluate(value)
fallOffMap[x,y] = RadialFalloff(value, someRadius, x, y, mapSize / 2f, mapSize / 2f);
// or
fallOffMap[x,y] = FeatheredRadialFalloff(value, someInnerRadius, someOuterRadius, x, y, mapSize / 2f, mapSize / 2f);

Related

How can i find the middle of a Hexgrid?

So ive created a code which will use my Standard hexagon Node from Godot and istance it for a given size into a hexgrid, now i wanted to find the exact middle of this Hexgrid to position my player there eg. move the Nodes along the X and Z axis to the standard position of the map 0,0,0.
I thought the exact middle of the Hexgrid is Axis X - Size of the X Axis / 2, Axis Z - Size of the Y Axis / 2.
But this just doesnt seem to work. Any suggestions on how to finde the middle of the Hexgrid and how to move it along the axis?
The Code for the middle looks like this:
public void GenerateGrid()
{
for (int y = 0; y < gridSize.Y; y++)
{
for (int x = 0; x < gridSize.X; x++)
{
Vector3 newPos = GetPositionFromCurrentHex(new Vector2(x, y));
Node3D generator_scene = (Node3D) ResourceLoader.Load<PackedScene>(tilePath).Instantiate();
generator_scene.Position = new Vector3(newPos.X - (gridSize.X / 2.0f), newPos.Y, newPos.Z - (gridSize.Y/ 2.0f));
if (isFlatTopped)
{
generator_scene.RotateY(Mathf.DegToRad(90));
}
AddChild(generator_scene);
}
}
}
And for the Calculation of the Hexgrid:
public Vector3 GetPositionFromCurrentHex(Vector2 coordinate)
{
float col = coordinate.X;
float row = coordinate.Y;
float width;
float height;
float xPos;
float yPos;
bool shouldOffset;
float horizontalDistance;
float verticalDistance;
float offset;
float size = outerSize;
if (!isFlatTopped)
{
shouldOffset = (row % 2) == 0;
width = Mathf.Sqrt(3) * size;
height = 2f * size;
horizontalDistance = width;
verticalDistance = height * (3f / 4f);
offset = (shouldOffset) ? width / 2 : 0;
xPos = (col * (horizontalDistance)) + offset;
yPos = (row * verticalDistance);
}
else
{
shouldOffset = (col % 2) == 0;
width = 2f * size;
height = Mathf.Sqrt(3f) * size;
horizontalDistance = width * (3f / 4f);
verticalDistance = height;
offset = (shouldOffset) ? height / 2 : 0;
xPos = (col * (horizontalDistance));
yPos = (row * verticalDistance) - offset;
}
return new Vector3(xPos, 0, yPos);
}
Answer depends on how you define center in different cases, for example
Is your hexgrid flat top or pointy top?
Is the topmost row of your hexgrid also leftmost or not (offset of rows)
Do you expect your "center" of the hexgrid to be also center of an hexagon, or it can be a point along edge of some hexagons.
If the number of rows and columns are odd, you can get away with calculating the coordinates of 4 corner points (topleft, topright, botleft, botright) and average them. If your grid is pointy-top this is also center of the middle hexagon, if your grid is flat-top it is on the edge between two hexagons.

How to create a 3D mesh from a heightmap represented as a float array

I am attempting to understand how a 3D mesh has been constructed from a heightmap stored as a one dimensional float array. The only examples I have seen previously have made use of a 2D float array, and I am struggling to wrap my head around the math involved here. Any insight into it would be appreciated. I have commented the code which I do not quite understand yet for your convenience.
Source of code: https://github.com/SebLague/Hydraulic-Erosion
public void ContructMesh () {
Vector3[] verts = new Vector3[mapSize * mapSize];
int[] triangles = new int[(mapSize - 1) * (mapSize - 1) * 6];
int t = 0;
//Note that default mapSize is 255
for (int i = 0; i < mapSize * mapSize; i++) {
//Following code is not properly understood
int x = i % mapSize;
int y = i / mapSize;
int meshMapIndex = y * mapSize + x;
Vector2 percent = new Vector2 (x / (mapSize - 1f), y / (mapSize - 1f));
Vector3 pos = new Vector3 (percent.x * 2 - 1, 0, percent.y * 2 - 1) * scale;
pos += Vector3.up * map[meshMapIndex] * elevationScale; //Elevation scale is 20 by default
verts[meshMapIndex] = pos;
//End of misunderstood code
if (x != mapSize - 1 && y != mapSize - 1) {
t = (y * (mapSize - 1) + x) * 3 * 2;
triangles[t + 0] = meshMapIndex + mapSize;
triangles[t + 1] = meshMapIndex + mapSize + 1;
triangles[t + 2] = meshMapIndex;
triangles[t + 3] = meshMapIndex + mapSize + 1;
triangles[t + 4] = meshMapIndex + 1;
triangles[t + 5] = meshMapIndex;
t += 6;
}
}
Mesh mesh = new Mesh();
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
mesh.vertices = verts;
mesh.triangles = triangles;
mesh.RecalculateNormals ();
What specifically do you not understand?
int x = i % mapSize; // Get the x location of the current point
int y = i / mapSize; // Get the y location of the current point
// This should be equal to i, IDK why this is even calculated
int meshMapIndex = y * mapSize + x;
// How far along either direction are we?
Vector2 percent = new Vector2 (x / (mapSize - 1f), y / (mapSize - 1f));
// Make a new vector that scales the X and Y coordinates up.
// The Y coordinate is set to the Z element in this vector
// Presumably because whatever you use to render uses the Y element as "up"
// And the X-Z plane is the horizontal plane
// Also normalize X and Z to lie between -1*scale and 1*scale
Vector3 pos = new Vector3 (percent.x * 2 - 1, 0, percent.y * 2 - 1) * scale;
// Add the value at the current index, times the scale, as the Y element of pos
pos += Vector3.up * map[meshMapIndex] * elevationScale; //Elevation scale is 20 by default
// The X-Z values of pos give you the location of the vertex in the horizontal plane
// The Y value of pos gives you the height
// save the newly calculated pos in verts
verts[meshMapIndex] = pos;

Calculation island error with perlin noise

I'm developing a small project, which is a grid of 100 x 100 hexagons.
In the script below, I paint my hexagons with the perlin noise, but the format I want to island does not go away.
I'll leave my code and 2 examples as my map stays and how I wish it to stay.
My island
My Island
As i need
Im Need
int getColor(float x, float z)
{
xTO = (int)x / terrainWidth - 30;
zTO = (int)z / terrainHeight - 30;
float v = Mathf.PerlinNoise((xTO + x + seed) * freq, (zTO + z) * freq);
// v += 0.001f;
float form = formWorld(x, z);
if (v < 0.25f)
{
//water
return 0;
}
else if (v < 0.5f)
{
//sand
return 1;
}
else if (v < 0.75f)
{
//grass
return 2;
}
else
{
//Trees / Forest
MakeNewTree(new Vector3(xx, 0, z * 7.5f));
return 2;
}
}
If you want your image to look more like the second one, the best option is going to be adding a circular gradient which offsets your Perlin Noise.
The easiest way to do this is to measure the distance from the center and combine that with the perlin noise.
Here's some untested code.
int getColor(float x, float z)
{
xTO = (int)x / terrainWidth - 30;
zTO = (int)z / terrainHeight - 30;
float v = Mathf.PerlinNoise((xTO + x + seed) * freq, (zTO + z) * freq);
// v += 0.001f;
v -= CircleOffset(x,z)/2; //Change the two to make the island bigger.
float form = formWorld(x, z);
if (v < 0.25f)
{
//water
return 0;
}
else if (v < 0.5f)
{
//sand
return 1;
}
else if (v < 0.75f)
{
//grass
return 2;
}
else
{
//Trees / Forest
MakeNewTree(new Vector3(xx, 0, z * 7.5f));
return 2;
}
}
float CircleOffset(float x, float y)
{
Vector2 center = new Vector2(terrainWidth/2,terrainHeight/2);
float distance = Mathf.Sqrt((center.x - x)*(center.x - x) + (center.y - y) * (center.y - y));
return distance/terrainWidth;
}
Hope this helps!

Formula to create a Radial Gradient

I need to write my own Radial Gradient generator (without using something like RadialGradientBrush).
Currently, my code looks like this:
public float[,] radGrad(int width, int height, float threshold) {
float[,] grad = new float[width, height];
float cX = width * 0.5f;
float cY = height * 0.5f;
for (int y = 0; y < height; ++y) {
for(int x = 0; x < width; ++x) {
float pixel = (Math.Max(y, cY) - Math.Min(y, cY)) / (Math.Max(x, cX) - Math.Min(x, cX));
pixel = fLerp(pixel, 0, 255);
grad[x, y] = pixel;
}
}
return grad;
}
And it produces this result:
How do I go about correcting this to make it look like the expected result:
--
Edit:
Getting closer with advice from #31eee384...
public float[,] radGrad(int width, int height, float threshold) {
float[,] grad = new float[width, height];
float cX = width * 0.5f;
float cY = height * 0.5f;
for (int y = 0; y < height; ++y) {
for(int x =0; x < width; ++x) {
float distFromCenter = distance(cX, cY, x, y);
float pixel = (Math.Max(distFromCenter, cY) - Math.Min(distFromCenter, cY)) / (Math.Max(distFromCenter, height) - Math.Min(distFromCenter, height));
pixel = fLerp(pixel, 0, 255);
grad[x, y] = pixel;
}
}
return grad;
}
Just need to figure out why it's adding white around the edges.
I believe you should sin!
public float[,] radGrad(int width, int height, float threshold) {
float[,] grad = new float[width, height];
int cX = width * 0.5f;
int cY = height * 0.5f;
for (int y = 0; y < cx; ++y) {
for(int x = 0; x < cy; ++x) {
float pixel = (Math.Sin(x / y));
pixel = fLerp(pixel, 0, 255);
grad[cx + x, cy + y] = pixel;
grad[cx + x, cy - y] = pixel;
grad[cx - x, cy + y] = pixel;
grad[cx - x, cy - y] = pixel;
}
}
return grad;
}
Far from perfect but should get you on the right path.

C# DirectX Circle Drawing not working correctly

I've been trying to recode a C++ DirectX code to C# that would help me with Drawing a perfect circle. Currently I have this code that i translated by myself:
private void Circle(int X, int Y, int radius, int numSides, Color color)
{
Vector2[] Line = new Vector2[128];
float Step = (float)(Math.PI * 2.0 / numSides);
int Count = 0;
for (float a = 0; a < Math.PI * 2.0; a += Step)
{
float X1 = (float)(radius * Math.Cos(a) + X);
float Y1 = (float)(radius * Math.Sin(a) + Y);
float X2 = (float)(radius * Math.Cos(a + Step) + X);
float Y2 = (float)(radius * Math.Sin(a + Step) + Y);
Line[Count].X = X1;
Line[Count].Y = Y1;
Line[Count + 1].X = X2;
Line[Count + 1].Y = Y2;
Count += 2;
}
line.Begin();
line.Draw(Line, color);
line.End();
}
The problem is that the circle is drawn but also a Line from a point in the circle to the left top corner, like this.
Don't iterate with a floating point variable. They might get imprecise during the iteration. In your case, the last step is probably very close behind the upper bound (instead of hitting it exactly). So it won't get calculated and left as the default (0, 0).
So use an integer iteration variable:
for (int i = 0; i < numSides; ++i)
{
float a = i * Step;
...
}
Then, you can also get rid of Count.
Furthermore, you should make your coordinate buffer dynamic:
Vector2[] Line = new Vector2[2 * numSides];

Categories