Aligning simplex noise-generated terrain

I have tried to generate terrain (the standard Unity terrain) through scripts, using simplex noise.
However, when I generate more than one segment of terrain, they don’t quite align at the edges (see picture).
46330-seams.jpg

I believe it is a problem with the part of the code that generates the hight map, but I can’t really figure out what is wrong.
This is that part of the code (the terrain segments are loaded into an array named terrains):
foreach(Terrain terrain in terrains){
tData = terrain.terrainData;

			xRes = tData.heightmapWidth;
			yRes = tData.heightmapHeight;
			

			heights = tData.GetHeights(0, 0, xRes, yRes);
			
			for (int y = 0; y < yRes; y++) {
				for (int x = 0; x < xRes; x++) {
					float xpos = x+terrain.transform.position.x; //Is this wrong?
					float ypos = y; //Terrain plane will always be at y 0, so no need to add position here
					float height = SimplexNoise.Noise.Generate((xpos*strength), (ypos*strength))*res;
					heights[x,y] = height;
				}
			}
			
			tData.SetHeights(0, 0, heights);

I had an issue much like this recently (excepted I was using procedural meshes rather than terrains.) How I fixed it was to add half of the width and depth of the mesh into the noise function. So change “(xposstrength)" and the y variant to "(xposstrength) + (widthOfTerrain/2)”.

Sorry for the lack of formatting and proper code examples but I’m on my phone. I’ll try to edit this later to make it more understandable.

I figured out one solution, although it creates a weirdly symmetrical alignment… it can be tweaked by making the noise sampling more complex and/or layering noise values.

Here is some sample code:

Basically i’m using a square terrain “chunk”, position is provided as the chunks Vector3 location.
heightmapResolution is equal to terrainData.heightmapHeight & terrainData.heightmapWidth.
The code can be simplified / cleaned up a bit, it has some legacy things from previous implementations.

      // yRes = terrainData.heightmapHeight;
      // xRes = terrainData.heightmapWidth;
      // yRes = heightmapResolution;
      // xRes = heightmapResolution;
      // print("yRes " + yRes);
      // print("xRes " + xRes);

      float terrainSize = chunkLength;
      print("terrainSize " + terrainSize);

      float offset = 1F;

      float startXNoise = (position.x / chunkLength) * (heightmapResolution -1F);
      float startZNoise = (position.z / chunkLength) * (heightmapResolution -1F);
      print("startXNoise, startZNoise " + startXNoise + " " + startZNoise);

      float xS = 0;
      for (int x = 0; x < heightmapResolution; x++) {

        float zS = 0;
        for (int z = 0; z < heightmapResolution; z++) {

          float xCoord = (startXNoise + xS) / (heightmapResolution) * 10;
          float yCoord = (startZNoise + zS) / (heightmapResolution) * 10;
          // float xCoord = (startXNoise + xS) / (heightmapResolution - 1F) * 10;
          // float yCoord = (startZNoise + zS) / (heightmapResolution - 1F) * 10;

          // SIMPLE NOISE
          float combinedNoiseValues = Mathf.PerlinNoise(xCoord, yCoord);

          // set height value
          heights[z,x] = combinedNoiseValues;

          zS += offset;
        }

        xS += offset;
      }

      terrainData.SetHeights(0, 0, heights);

Purchase Stitchscape.