- Home /
SetAlphamaps with multiple terrains at runtime
I'm trying to use the SetAlphamaps function on multiple terrains at run-time, but I'm running into an issue. Here's a rundown of what I have;
I'm generating 4 terrain tiles at run-time, and generating heightmaps via perlin noise. After this I am setting the splatmaps based off of height and slope.
This all works perfectly fine when I do it for one terrain. Now that I am using multiple terrain tiles, when I use SetAlphamaps it sets the the alphamap for all the terrains to the same one. It seems as if the terrainData is static or something.
Here is a relevant code snippet:
void genTextures(TerrainData terrain)
{
int xRes = terrain.heightmapHeight;
int zRes = terrain.heightmapHeight;
float[,] heights = terrain.GetHeights(0, 0, xRes, zRes);
float[, ,] splatmapData = new float[terrain.alphamapWidth, terrain.alphamapHeight, terrain.alphamapLayers];
Vector4 peak = new Vector4 (1, 0, 0, 0);
Vector4 grass = new Vector4 (0, 1, 0, 0);
Vector4 dirt = new Vector4 (0, 0, 1, 0);
Vector4 cliff = new Vector4 (0, 0, 0, 1);
Vector4 splat = dirt;
int maxHeight = 600;
int layer1 = 250;
int layer2 = 175;
for (int i = 0; i < terrain.alphamapHeight; i++)
{
for (int j = 0; j < terrain.alphamapWidth; j++)
{
Vector2 normalizedPos = new Vector2(Mathf.InverseLerp(0.0f, terrain.alphamapHeight, j), Mathf.InverseLerp(0.0f, terrain.alphamapWidth, i));
Vector3 angle = terrain.GetInterpolatedNormal(normalizedPos.x, normalizedPos.y);
float cliffAngle = Vector3.Angle(Vector3.up, angle);
float slope1 = 67f;
float slope2 = 63f;
if(heights[i, j] > layer1)
{
Vector3 temp = Vector4.Lerp (grass, peak, (heights[i, j] - layer1) / (maxHeight - layer1));
if(cliffAngle > slope2)
{
splat = Vector4.Lerp (temp, cliff, (cliffAngle - slope2) / (3 * (slope1 - slope2)));
}
else
{
splat = temp;
}
}
else if(heights[i, j] > layer2)
{
Vector3 temp = Vector4.Lerp (dirt, grass, (heights[i, j] - layer2) / (layer1 - layer2));
if(cliffAngle > slope2)
{
splat = Vector4.Lerp (temp, cliff, (cliffAngle - slope2) / (3 * (slope1 - slope2)));
}
else
{
splat = temp;
}
}
else
{
if(cliffAngle > slope2)
{
splat = Vector4.Lerp (dirt, cliff, (cliffAngle - slope2) / (3 * (slope1 - slope2)));
}
else
{
splat = Vector4.Lerp (dirt, dirt, (heights[i, j] - layer1) / (maxHeight - layer1));
}
}
splat.Normalize();
splatmapData [i, j, 0] = splat.x;
splatmapData [i, j, 1] = splat.y;
splatmapData [i, j, 2] = splat.z;
splatmapData [i, j, 3] = splat.w;
}
}
terrain.SetAlphamaps(0, 0, splatmapData);
}
This function is called in a for loop that cycles through each terrain object, passing in the relevant TerrainData object. This is exactly the same way I set my heightmap for each Terrain, so I'm confused as to why the alphamaps would not work the same.
Any help is appreciated. If I need to explain further, please let me know. Here is a screenshot showing that the alphamap is the same across 4 terrain tiles.
EDIT
Ok, as per my last comment, here is some additional code that may be involved.
TerrainData terdata;
terdata = Resources.Load("template") as TerrainData;
int terRes = 2000;
int terrainArraySize = 2;
for(int i = 0; i < terrainArraySize; i++)
{
for(int j = 0; j < terrainArraySize; j++)
{
file = file + (j + (i * terrainArraySize));
terrainsdataArray[i, j] = (TerrainData) Object.Instantiate(terdata);
terrainArray[i, j] = Terrain.CreateTerrainGameObject(terrainsdataArray[i, j]);
terrainArray[i, j].transform.Translate((((j) * terRes) + terrainArray[i, j].transform.position.x),
(terrainArray[i,j].transform.position.y),
(((i) * terRes) + terrainArray[i, j].transform.position.z),
Space.World);
terrainArray[i, j].GetComponent<Terrain>().basemapDistance = 1000;
genTerrainHeightmap(terrainsdataArray[i,j], j, i);
}
}
Basically I have an empty terrain asset saved in the resources folder. I load that in ('template') and then go through an a nested for loop, instantiating copies of it that I then change at runtime. It appears to me that the problem is with the saved terrain. A child of that asset is called SplatAlpha0. This is an image with RGBA channels. I'm pretty sure that this is where the splats are saved, and helps to make the baseMap. The issue I believe is that when I use the SetAlphamaps function, it is writing to this one file, but all the terrain objects use this file.
My idea is to create multiple image files, and somehow tell the terrain to use these files rather than the default. If this is not possible, or if there is a better idea someone has, please let me know. The end goal is to make endless terrain, but for initial release, I may need to limit the number of terrain tiles.
I'm still at a loss on this one, any help or suggestions would be appreciated. I've considered looking into writing custom shaders to handle all terrain textures, but I've never written shaders, so this would be a huge roadblock in development if I need to write my own custom terrain shaders.
Ok, I've been looking into this one further, and believe that I've at least deter$$anonymous$$ed the initial problem. I'll edit the question so I can include some additional code that I think is involved.
As a workaround I just created several terrain objects manually, then edit those at runtime, as they all have their own SplatAlpha0. Unfortunately this is only a temporary workaround, as it will not allow 'endless' terrain. Any help at all on this one would be greatly appreciated. Thanks!
I've found that I need to manually copy my "prefab" terraindata to a new terraindata for each terrain.
// Hand build our terrain data for each chunk, otherwise all terrain chunks reference the same terraindata unityTerrainChunkData = new TerrainData();
unityTerrainChunkData.SetDetailResolution(terrainChunkDataPrefab.detailResolution, 8);
unityTerrainChunkData.heightmapResolution = terrainChunkDataPrefab.heightmapResolution;
unityTerrainChunkData.alphamapResolution = terrainChunkDataPrefab.alphamapResolution;
unityTerrainChunkData.base$$anonymous$$apResolution = terrainChunkDataPrefab.base$$anonymous$$apResolution;
unityTerrainChunkData.size = terrainChunkDataPrefab.size;
// Copy texture assignments
unityTerrainChunkData.splatPrototypes = terrainChunkDataPrefab.splatPrototypes;
// Copy Tree prototypes
unityTerrainChunkData.treePrototypes = terrainChunkDataPrefab.treePrototypes;
//unityTerrainChunkData.treeInstances = terrainChunkDataPrefab.treeInstances;
// Copy detail prototypes
unityTerrainChunkData.detailPrototypes = terrainChunkDataPrefab.detailPrototypes;
// Create the new terrain using the new terraindata unityTerrainChunk = Terrain.CreateTerrainGameObject(unityTerrainChunkData).GetComponent();
This is only partially true. You can create multiple terrains with different height values all based off of one Terrain object. Unfortunately, I've found, when it comes to the textures, all the terrains will use the same SplatAlpha0 image. Unless there is someway to force the terrain to use a different alphamap...
Answer by Logopolis · Mar 03, 2015 at 09:11 AM
I just faced this same problem, and while this question was asked long ago, I thought I'd post how I solved it here in case anyone else runs into the same problem.
Instantiating a new TerrainData wasn't enough, in my case. Doing that allowed me to set different height values for the different domains, but they still all used the same splat maps. To have them use different splat maps, I had to create an individual SplatPrototypes array for each individual TerrainData, and fill it with the SplatPrototype textures. Here's the code that I wrote to do this:
terrainPatches[x,z] = (Terrain) Instantiate(terrainPrefab, new Vector3(0, 0, 0), Quaternion.identity);
terrainPatches[x,z].terrainData = new TerrainData();
terrainPatches[x,z].terrainData.heightmapResolution = 129;
terrainPatches[x,z].terrainData.size = new Vector3(200,80,200);
terrainPatches[x,z].terrainData.alphamapResolution = 128;
terrainPatches[x,z].terrainData.baseMapResolution = 256;
terrainPatches[x,z].terrainData.SetDetailResolution(256, 8);
SplatPrototype[] splatPrototypes = new SplatPrototype[10];
for (int i = 0; i < 10; i++) {
splatPrototypes[i] = new SplatPrototype();
splatPrototypes[i].tileOffset = new Vector2(0,0);
splatPrototypes[i].tileSize = new Vector2(20,20);
switch (i) {
case 0: splatPrototypes[i].texture = terrainTexture0; splatPrototypes[i].normalMap = rockNormalMap; break;
case 1: splatPrototypes[i].texture = terrainTexture1; break;
case 2: splatPrototypes[i].texture = terrainTexture2; break;
case 3: splatPrototypes[i].texture = terrainTexture3; splatPrototypes[i].normalMap = grassNormalMap; break;
case 4: splatPrototypes[i].texture = terrainTexture4; splatPrototypes[i].normalMap = rockNormalMap; break;
case 5: splatPrototypes[i].texture = terrainTexture5; splatPrototypes[i].normalMap = rockNormalMap; break;
case 6: splatPrototypes[i].texture = terrainTexture6; break;
case 7: splatPrototypes[i].texture = terrainTexture7; break;
case 8: splatPrototypes[i].texture = terrainTexture8; break;
case 9: splatPrototypes[i].texture = terrainTexture9; break;
}
}
terrainPatches[x,z].terrainData.splatPrototypes = splatPrototypes;
Your answer
Follow this Question
Related Questions
Generate Alpha Map Procedurally... 1 Answer
A question for someone who understands alphamaps. 0 Answers
Terrain texture low quality 0 Answers
Blob Shadow and Trees Using Terrain Cause Issues 0 Answers
create terrain using script 1 Answer