I need a little help,
I have this Perlin noise function, but I don't know how to properly create offsets.
I am using this to create infinite terrain generation and when I use this script it the noise values of individual chunks don't fit together properly. And they create holes.
Is there a way of fixing this ?
public float[,] GenerateNoise(int chunkSize, int octaves, string seed, float noiseScale, float persistence, float lacunarity, Vector2 offset)
{
if (noiseScale <= 0)
{
noiseScale = 0.0001f;
}
float halfWidth = chunkSize / 2f;
float halfHeight = chunkSize / 2f;
float[,] noiseMap = new float[chunkSize, chunkSize];
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 / chunkSize , offset_Y / chunkSize);
}
for (int x = 0; x < chunkSize; x++)
{
for (int y = 0; y < chunkSize; 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 * frequency;
float sampleY = (y - halfHeight) / noiseScale * frequency + octavesOffset[i].y * frequency;
float noiseValue = Mathf.PerlinNoise(sampleX, sampleY);
noiseHeight += noiseValue * amplitude;
noiseHeight -= superpositionCompensation;
amplitude *= persistence;
frequency *= lacunarity;
superpositionCompensation = amplitude / 2;
}
noiseMap[x, y] = Mathf.Clamp01(noiseHeight);
}
}
return noiseMap;
}
It is quite simple actually, just add the chunk x,y coordinates to Mathf.PerlinNoise. Taking your code as an example, you can:
Pass chunkPosition as an argument to it:
public float[,] GenerateNoise(Vector2 chunkPos, int chunkSize, int octaves, string seed, float noiseScale, float persistence, float lacunarity, Vector2 offset)
Add it to Mathf.PerlinNoise invocation:
float noiseValue = Mathf.PerlinNoise(sampleX + chunkPos.x, sampleY + chunkPos.y);
Then make sure to generate each chunk with an appropriate chunkPos, where chunkPos can be its transform.position or whatever coordinates you have.
That's it.
Related
I'm trying to program a Compute-Shader Perlin Noise code based on a working C# code.
The problem is that i only got smooth dots.
Left C# Working, Right Compute-Shader
with this values for both
If i lower the frequency the dots get bigger:
This is the code i'm using
#pragma kernel CSMain
RWTexture2D<float> Result;
RWStructuredBuffer<float> resfloat;
float res;
float frequency;
float octaves;
float lacunarity;
float persistence;
StructuredBuffer<float3> gradients3D;
StructuredBuffer<int> hash;
float lerp(float v0, float v1, float t);
float Dot(float3 g, float x, float y, float z);
float Smooth(float t);
float Perlin3D(float3 v, float frequency);
float noise(float3 v, float frequency, int octaves, float lacunarity, float persistence);
int hashMask = 255;
int gradientsMask3D = 15;
[numthreads(8, 8, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
float3 v = float3(id.x, id.y, id.z) / res;
float h = 0.0;
h = noise(v, frequency, octaves, lacunarity, persistence);
Result[id.xy] = float4(h, 0, 0, 0);
resfloat[id.x + id.y * res] = h;
}
float lerp(float v0, float v1, float t) {
return v0 + t * (v1 - v0);
}
float Smooth(float t) {
return t * t * t * (t * (t * (float) 6 - (float) 15) + (float) 10);
}
float Perlin3D(float3 v, float frequency) {
v *= frequency;
int ix0 = (int) floor(v.x);
int iy0 = (int) floor(v.y);
int iz0 = (int) floor(v.z);
float tx0 = v.x - ix0;
float ty0 = v.y - iy0;
float tz0 = v.z - iz0;
float tx1 = tx0 - (float) 1;
float ty1 = ty0 - (float) 1;
float tz1 = tz0 - (float) 1;
ix0 &= hashMask;
iy0 &= hashMask;
iz0 &= hashMask;
int ix1 = ix0 + (float) 1;
int iy1 = iy0 + (float) 1;
int iz1 = iz0 + (float) 1;
int h0 = hash[ix0];
int h1 = hash[ix1];
int h00 = hash[h0 + iy0];
int h10 = hash[h1 + iy0];
int h01 = hash[h0 + iy1];
int h11 = hash[h1 + iy1];
float3 g000 = gradients3D[hash[h00 + iz0] & gradientsMask3D];
float3 g100 = gradients3D[hash[h10 + iz0] & gradientsMask3D];
float3 g010 = gradients3D[hash[h01 + iz0] & gradientsMask3D];
float3 g110 = gradients3D[hash[h11 + iz0] & gradientsMask3D];
float3 g001 = gradients3D[hash[h00 + iz1] & gradientsMask3D];
float3 g101 = gradients3D[hash[h10 + iz1] & gradientsMask3D];
float3 g011 = gradients3D[hash[h01 + iz1] & gradientsMask3D];
float3 g111 = gradients3D[hash[h11 + iz1] & gradientsMask3D];
float v000 = dot(g000, float3(tx0, ty0, tz0));
float v100 = dot(g100, float3(tx1, ty0, tz0));
float v010 = dot(g010, float3(tx0, ty1, tz0));
float v110 = dot(g110, float3(tx1, ty1, tz0));
float v001 = dot(g001, float3(tx0, ty0, tz1));
float v101 = dot(g101, float3(tx1, ty0, tz1));
float v011 = dot(g011, float3(tx0, ty1, tz1));
float v111 = dot(g111, float3(tx1, ty1, tz1));
float tx = Smooth(tx0);
float ty = Smooth(ty0);
float tz = Smooth(tz0);
return lerp(
lerp(lerp(v000, v100, tx), lerp(v010, v110, tx), ty),
lerp(lerp(v001, v101, tx), lerp(v011, v111, tx), ty),
tz);
}
float noise(float3 v, float frequency, int octaves, float lacunarity, float persistence)
{
float sum = Perlin3D(v, frequency);
float amplitude = 1;
float range = 1;
for (int o = 1; o < octaves; o++) {
frequency *= lacunarity;
amplitude *= persistence;
range += amplitude;
sum += Perlin3D(v, frequency) * amplitude;
}
return sum / range;
}
This is the C# working code
And this is the C# code that calls the shader
I already checked that the StructuredBuffer (gradients3D and hash), and the float params are correctly loaded .
Any ideas?
The problem was that for some reason, when the function Perlin3D, used hashMask and gradientsMask3D, they had a 0.
So i moved the definition to the function:
float Perlin3D(float3 v, float frequency)
{
int hashMask = 255;
int gradientsMask3D = 15;
v *= frequency;
int ix0 = (int) floor(v.x);
There is a code for a drawing circle with LineRenderer.
but I want to draw multiple circles with different radius, I used "for loop" but there is one circle instead of multiple
public float ThetaScale = 0.01f;
public float radius = 3f;
private int Size;
private LineRenderer LineDrawer;
private float Theta = 0f;
void Start ()
{
LineDrawer = GetComponent<LineRenderer>();
}
void Update ()
{
Theta = 0f;
Size = (int)((1f / ThetaScale) + 1f);
LineDrawer.SetVertexCount(Size);
for (int l = 0; l < 5; l++)
{
for(int i = 0; i < Size; i++)
{
Theta += (2.0f * Mathf.PI * ThetaScale);
float x = l * radius * Mathf.Cos(Theta);
float y = l * radius * Mathf.Sin(Theta);
LineDrawer.SetPosition(i, new Vector3(x, 0, y));
}
}
}
In every loop you always overwrite the same positions indices in the same line renderer. So you will always only have the last circle.
Note that it is also quite expensive to use SetPoisition repeatedly. As it says in the API you should rather work on an array and then use SetPoisitions to assign all positions at once.
One thing is a bit unclear though: If you use one single LineRenderer you won't get independent circles but they will always be connected at some point. Otherwise you would need 5 separated LineRenderer instances.
Option A: 5 circles but connected to each other since part of a single LineRenderer
void Start ()
{
LineDrawer = GetComponent<LineRenderer>();
LineDrawer.loop = false;
Theta = 0f;
// Use one position more to close the circle
Size = (int)((1f / ThetaScale) + 1f) + 1;
LineDrawer.positionCount = 5 * Size;
var positions = new Vector3[5 * Size];
for (int l = 0; l < 5; l++)
{
for(int i = 0; i < Size; i++)
{
Theta += (2.0f * Mathf.PI * ThetaScale);
float x = l * radius * Mathf.Cos(Theta);
float y = l * radius * Mathf.Sin(Theta);
positions[5 * l + i] = new Vector3(x, 0, y);
}
}
LineDrawer.SetPositions(positions);
}
Option B: 5 separated circles in 5 separated LineRenderers
// Drag 5 individual LineRenderer here via the Inspector
public LineRenderer[] lines = new LineRenderer[5];
void Start ()
{
foreach(var line in lines)
{
line.loop = true;
Theta = 0f;
Size = (int)((1f / ThetaScale) + 1f);
line.positionCount = Size;
var positions = new Vector3[Size];
for(int i = 0; i < Size; i++)
{
Theta += (2.0f * Mathf.PI * ThetaScale);
float x = l * radius * Mathf.Cos(Theta);
float y = l * radius * Mathf.Sin(Theta);
positions[5 * l + i] = new Vector3(x, 0, y);
}
line.SetPositions(positions);
}
}
You missed few details here and there. Here, this will work:
using UnityEngine;
[ExecuteAlways]
[RequireComponent( typeof(LineRenderer) )]
public class CircularBehaviour : MonoBehaviour
{
[SerializeField][Min(3)] int _numSegments = 16;
[SerializeField][Min(1)] int _numCircles = 5;
[SerializeField] float _radius = 3f;
LineRenderer _lineRenderer;
void Awake ()
{
_lineRenderer = GetComponent<LineRenderer>();
_lineRenderer.loop = false;
_lineRenderer.useWorldSpace = false;
}
void Update ()
{
const float TAU = 2f * Mathf.PI;
float theta = TAU / (float)_numSegments;
int numVertices = _numSegments + 1;
_lineRenderer.positionCount = numVertices * _numCircles;
int vert = 0;
for( int l=1 ; l<=_numCircles ; l++ )
{
float r = _radius * (float)l;
for( int i=0 ; i<numVertices ; i++ )
{
float f = theta * (float)i;
Vector3 v = new Vector3{ x=Mathf.Cos(f) , y=Mathf.Sin(f) } * r;
_lineRenderer.SetPosition( vert++ , v );
}
}
}
}
But
as #derHugo explained, this is not what you're looking for exactly as all circles will be drawn connected.
I made a Noise class using the Perlin Noise from Unity like this:
public static float[,] GetNoise(Vector2Int initialOffset, float scale, float persistance, float lacunarity, int octaves)
{
float[,] noiseMap = new float[Chunk.width, Chunk.height];
float maxHeight = 0;
float minHeight = 0;
for (int y = 0; y < Chunk.height; y++)
{
for (int x = 0; x < Chunk.width; x++)
{
float amplitude = 1;
float frequency = 1;
float noiseHeight = 0;
for (int oc = 0; oc < octaves; oc++)
{
float coordX = (x + initialOffset.x) / scale * frequency;
float coordY = (y + initialOffset.y) / scale * frequency;
float perlin = Mathf.PerlinNoise(coordX, coordY) * 2 - 1;
noiseHeight += perlin * amplitude;
amplitude *= persistance;
frequency *= lacunarity;
}
if (noiseHeight < minHeight)
{
minHeight = noiseHeight;
}
if (noiseHeight > maxHeight)
{
maxHeight = noiseHeight;
}
noiseMap[x, y] = noiseHeight;
}
}
for (int y = 0; y < Chunk.height; y++)
{
for (int x = 0; x < Chunk.width; x++)
{
noiseMap[x, y] = Mathf.InverseLerp(minHeight, maxHeight, noiseMap[x, y]);
}
}
return noiseMap;
}
However this code is giving me repeating patterns like this:
What am I doing wrong? Or there is no way to get rid of the patterns?
I got it working, not very well, but working. The way I did was I generate the height map for every tile in the chunk, then I did some random placing of tiles, while having in account the height map. Something like this:
if (heightMap[x, y] < 0.3 && Random.value < 0.5)
// Add tile
This way I got this result:
EDIT:
Doing some more research about Perlin Noise I found out that it just doesn't like negative coords for some reason, so I did this way, hope this helps someone!
so .. fixed the negative coords like this:
//account for negatives (ex. -1 % 256 = -1, needs to loop around to 255)
if (noiseOffset.x < 0)
noiseOffset = new Vector2(noiseOffset.x + noiseRange.x, noiseOffset.y);
if (noiseOffset.y < 0)
noiseOffset = new Vector2(noiseOffset.x, noiseOffset.y + noiseRange.y);
I am currently making a climbing detection system that follows the player around and detects the closest point the player can climb. I have a game object that casts rays according to the players position. The rays move with the player but do not rotate around. How can I make them rotate with the player ?
This is the code that draws the rays
void Update()
{
//Debug.DrawRay();
raySpace = surfaceWidth / definition;
raySpaceDividedByTwo = raySpace / 2;
surfaceWidthDividedByTwo = surfaceWidth / 2;
for (int i = 0; i < definition; i++)
{
for (int y = 0; y < definition; y++)
{
raycastCoordinates = new Vector3(
(this.transform.position.x + (i * raySpace) + raySpaceDividedByTwo) - surfaceWidthDividedByTwo,
this.transform.position.y,
(this.transform.position.z + (y * raySpace) + raySpaceDividedByTwo) - surfaceWidthDividedByTwo);
//offset = Quaternion.AngleAxis(Input.GetAxis("Mouse X") * turnSpeed, Vector3.up) * offset;
//raycastCoordinates = Quaternion.AngleAxis(this.transform.rotation.y, Vector3.up) * raycastCoordinates;
raycastUpHitsList.Add(Physics.RaycastAll(raycastCoordinates, Vector3.up, detectionHeight, LayerMask.GetMask("Water")));
Debug.DrawRay(raycastCoordinates, Vector3.up * detectionHeight, Color.green, 0.1f);
}
}
}
This should work:
void Update()
{
//Debug.DrawRay();
raySpace = surfaceWidth / definition;
raySpaceDividedByTwo = raySpace / 2;
surfaceWidthDividedByTwo = surfaceWidth / 2;
for (int i = 0; i < definition; i++)
{
for (int y = 0; y < definition; y++)
{
raycastCoordinates = new Vector3(
(this.transform.position.x + (i * raySpace) + raySpaceDividedByTwo) - surfaceWidthDividedByTwo,
this.transform.position.y,
(this.transform.position.z + (y * raySpace) + raySpaceDividedByTwo) - surfaceWidthDividedByTwo) * transform.forward;//if this doesn't work, try transform.right instead
//offset = Quaternion.AngleAxis(Input.GetAxis("Mouse X") * turnSpeed, Vector3.up) * offset;
//raycastCoordinates = Quaternion.AngleAxis(this.transform.rotation.y, Vector3.up) * raycastCoordinates;
raycastUpHitsList.Add(Physics.RaycastAll(raycastCoordinates, Vector3.up, detectionHeight, LayerMask.GetMask("Water")));
Debug.DrawRay(raycastCoordinates, Vector3.up * detectionHeight, Color.green, 0.1f);
}
}
}
When creating the mesh, I had a problem: both in the game, and in the scene appears a large triangular polygon. The problem is that with a system size of 250 by 250 points there aren't this polygon. At a size of 400 by 400 points, it already appears.
What I did:
void Start () {
MeshFilter mf = GetComponent<MeshFilter>();
Mesh mesh = mf.mesh;
int size = 400;
Vector3[] vertices = new Vector3[size * size];
Vector3[] normals = new Vector3[size * size];
Vector2[] uvc = new Vector2[size * size];
int c = 0;
for (int i =0; i < size; i++)
{
for(int j =0; j < size; j++)
{
vertices[c] = new Vector3(i, j, Mathf.Sin(j+i+Mathf.PI));
normals[c] = -Vector3.forward;
float mult = 1.0f / ((float)(size));
uvc[c] = new Vector2(((float)(i))*mult, ((float)(j)) * mult);
c++;
}
};
int[] triangles = new int[(size-1)*(size-1)*6];
int counter = 0;
{
for (int i = 0; i < size - 1; i++)
{
for (int j = 0; j < size - 1; j++)
{
setTriangle(ref triangles, ref counter, i * size + j);
setTriangle(ref triangles, ref counter, (i + 1) * size + j);
setTriangle(ref triangles, ref counter, i * size + j + 1);
setTriangle(ref triangles, ref counter, (i + 1) * size + j);
setTriangle(ref triangles, ref counter, (i + 1) * size + j + 1);
setTriangle(ref triangles, ref counter, i * size + j + 1);
};
}
};
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.normals = normals;
mesh.uv = uvc;
mesh.RecalculateNormals();
gameObject.transform.position = new Vector3(-55, -60, 90);
void setTriangle(ref int[] triangle, ref int index, int num)
{
triangle[index++] = num;
}
And what I have in results. The first picture is with 250 by 250 points. The second is with 400 by 400. The third is just bigger picture
I think you are over the max. number of vertices for the mesh when the size is 400x400.
The default max. number of vertices for a mesh is 65535. If you want to have more, then you have to set:
mesh.indexFormat = Rendering.IndexFormat.UInt32;
but this is not guaranteed to be supported in all platforms.