i want to change the texture of my terrain with certain texture. i got confuse to set the splatmapdata, anyone can help me out??
private void ChangeTexture(Vector3 WorldPos)
{
print ("changeTexture");
int mapX = (int)(((WorldPos.x - terrainPos.x) / terrainData.size.x) * terrainData.alphamapWidth);
int mapZ = (int)(((WorldPos.z - terrainPos.z) / terrainData.size.z) * terrainData.alphamapHeight);
float[,,] splatmapData = terrainData.GetAlphamaps(3, 3, 15, 15);
terrainData.SetAlphamaps (mapX, mapZ, splatmapData);
terrain.Flush ();
}
The data returned by GetAlphamaps
The returned array is three-dimensional - the first two dimensions represent x and y coordinates on the map, while the third denotes the splatmap texture to which the alphamap is applied.
Or in simple words a float[x, y, l] where
x = width in pixels
y = height in pixels
l = Texture-Layer
So lets say you want to set it to a certain texture at this pixel coordinates what you do is
set the weight for the texture's layer to 1
set all other layers weight to 0
So let's say you have e.g. 3 Layers and you want the second one (= index 1) to be the full weighted texture:
float[,,] splatmapData = terrainData.GetAlphamaps(mapX, mapZ, 15, 15);
// Iterate over x-y coordinates within the array
for(var y = 0; i < 15; y++)
{
for(var x = 0; x < 15; x++)
{
// Set first layers weight to 0
splatmapData[x, y, 0] = 0;
// Set second layer's weight to 1
splatmapData[x, y, 1] = 1;
// Set third layer's weight to 0
splatmapData[x, y, 2] = 0;
}
}
terrainData.SetAlphamaps(mapX, mapZ, splatmapData);
I would then implement an enum for the layers like let's say
public enum TerrainLayer
{
Default = 0,
Green,
Red
}
so you can simply pass the according layer index as a parameter - a bit more secure than passing in the int values themselves:
private void ChangeTexture(Vector3 worldPos, TerrainLayer toLayer)
{
print ("changeTexture");
int mapX = (int)(((worldPos.x - terrainPos.x) / terrainData.size.x) * terrainData.alphamapWidth);
int mapZ = (int)(((worldPos.z - terrainPos.z) / terrainData.size.z) * terrainData.alphamapHeight);
float[,,] splatmapData = terrainData.GetAlphamaps(mapX, mapZ, 15, 15);
for(var z = 0; z < 15; z++)
{
for(var x = 0; x < 15; x++)
{
// This ofcourse would be more efficient if you do this only once
// e.g. in Awake since the enum won't change on runtime
var values = (TerrainLAyer[])Enum.GetValues(typeof(TerrainLayer));
// Iterate through the enum and
for(var l = 0; l < values.Length; l++)
{
// set all layers to 0 except the toLayer
splatmapData[x, z, l] = values[l] == toLayer ? 1 : 0;
}
}
}
terrainData.SetAlphamaps (mapX, mapZ, splatmapData);
terrain.Flush ();
}
Now you would simply call it e.g.
ChangeTexture(somePosition, TerrainLayer.Green);
Related
I want to create a world generation using two noise maps (altitude noise and moisture noise). I create maps of these noises and set them in the inspector to values between 0 and 1.
I want to get result like this:
If Elevation < 1000
{
If Moisture < 50: Desert
Else Forest
}
And it seems that I did it as it should, but for some reason the generation does not work correctly:
Here is my code:
for (int x=0; x < tilemap.Width; x++)
{
for (int y = 0; y < tilemap.Height; y++)
{
// Get height at this position
var height = noiseMap[y * tilemap.Width + x];
var moisureHeight = moisureMap[y * tilemap.Width + x];
// Loop over our configured tile types
for (int i = 0; i < TileTypes.Length; i++)
{
var TileType = TileTypes[i];
for (var j = 0; j < TileType.MoisureValues.Length; j++)
{
// If the height is smaller or equal then use this tiletype
if (height <= TileType.Height)
{
if (moisureHeight <=TileType.MoisureValues[j].MoisureHeight)
{
tilemap.SetTile(x, y, (int)TileType.MoisureValues[j].GroundTile);
break;
}
}
}
}
}
}
I'm using Unity, and I'm making a generator in which the user inputs a degree of a polynomial plus all of its coefficients. For example, I can have degree 3 and [1,2,3,4], which should be 1x^3 + 2x^2 + 3x^1 + 4x^0.
Here's what I have:
int[] coef = TitleToGame.coeficients;
for (int x = -10; x <= 10; x++)
{
float y = 0;
for (int i = 0; i < TitleToGame.degree - 1; i++)
{
if (i == 0)
{
y = coef[TitleToGame.degree] * Mathf.Pow(x, i);
}
else
{
y += coef[TitleToGame.degree - i] * Mathf.Pow(x, i);
}
Instantiate(block, new Vector3(x, y, 5), Quaternion.identity);
}
}
I'm trying to generate blocks from domain -10 to 10. However, the result looks a bit funky.
Degree 3 with [1,1,1,1] shows a parabola with an extra linear line:
Degree 2 shows a linear line (with an extra unwanted flat line), 1 doesn't show anything, and 4 also shows a parabola. What am I doing wrong?
You're calling Instantiate in the inner for loop, when I persume you want to call it in the outer loop (from -10 to 10).
Also, you probably don't need that if-else statement. Both lines do the same thing.
for (int i = 0; i < TitleToGame.degree - 1; i++)
{
y += coef[TitleToGame.degree - i] * Mathf.Pow(x, i);
}
Instantiate(block, new Vector3(x, y, 5), Quaternion.identity);
While moving forward with my algorithm for Ordered Dithering I got a problem, mainly I don't really know what col[levels] might be.
Here is the pseudocode
k - number of color values per channel
n - size of the threshold Bayers Matrix
My code which works somehow fine for K = 2, but it doesn't return correct result image when K = 3, K = 4 and so on
UPDATED CODE
class OrderedDithering
{
private float[,] bayerMatrix;
private float[,] dither2x2Matrix =
new float[,] { { 1, 3 },
{ 4, 2 } };
private float[,] dither3x3Matrix =
new float[,] { { 3, 7, 4 },
{ 6, 1, 9 },
{ 2, 8, 5 } };
public BitmapImage OrderedDitheringApply(BitmapImage FilteredImage, int valuesPerChannel, int thresholdSize)
{
Bitmap bitmap = ImageConverters.BitmapImage2Bitmap(FilteredImage);
if (thresholdSize == 2)
{
bayerMatrix = new float[2, 2];
for (int i = 0; i < 2; ++i)
for (int j = 0; j < 2; ++j)
bayerMatrix[i,j] = dither2x2Matrix[i,j] / 5;
}
else
{
bayerMatrix = new float[3, 3];
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
bayerMatrix[i, j] = dither3x3Matrix[i, j] / 10;
}
for (int i = 0; i < bitmap.Width; ++i)
for(int j = 0; j < bitmap.Height; ++j)
{
Color color = bitmap.GetPixel(i, j);
double r = Scale(0, 255, 0, 1, color.R);
double g = Scale(0, 255, 0, 1, color.G);
double b = Scale(0, 255, 0, 1, color.B);
int counter = 0;
counter += Dither(valuesPerChannel, r, thresholdSize, i, j);
counter += Dither(valuesPerChannel, g, thresholdSize, i, j);
counter += Dither(valuesPerChannel, b, thresholdSize, i, j);
if (counter == 0)
bitmap.SetPixel(i, j, Color.FromArgb(0,0,0));
else
bitmap.SetPixel(i, j, Color.FromArgb(255/counter, 255/counter, 255/counter));
}
return ImageConverters.Bitmap2BitmapImage(bitmap);
}
public int Dither(int valuesPerChannel, double colorIntensity, int thresholdSize, int i, int j)
{
double tempValue = (double)(Math.Floor((double)((valuesPerChannel - 1) * colorIntensity)));
double re = (valuesPerChannel - 1) * colorIntensity - tempValue;
if (re >= bayerMatrix[i % thresholdSize, j % thresholdSize])
return 1;
else
return 0;
}
public double Scale(double a0, double a1, double b0, double b1, double a)
{
return b0 + (b1 - b0) * ((a - a0) / (a1 - a0));
}
}
If you just need a dithering library that works with System.Drawing and supports ordered dithering feel free to use mine. It has an easily usable OrderedDitherer class.
But if you are just playing for the sake of curiosity, and would like to improve your algorithm, here are some comments:
You now always set the pixels black or white (btw, you can use just Color.Black instead of FromArgb(0, 0, 0), for example), so it cannot work for more colors
It is unclear from the provided code whether a target palette belongs to colorsNum but basically you need to quantize all dithered pixels to the nearest transformed color.
So instead of working by brightness you should apply the ordered matrix for each color channel of each pixels (between the 0..255 range), and pick a color from the target palette for the result. The 'nearest color' can have more interpretation but generally an euclidean search will do it.
Try to avoid Bitmap.SetPixel/GetPixel as they are terribly slow and SetPixel does not even work with bitmaps that have indexed palette PixelFormat.
Your code does not show the values of your matrix but the values should also be calibrated for the palette you use. The typical default values are good for a BW palette but they are probably too strong for a 256 color palette (the dithering "jumps over" more shades and introduces rather just noise than nice gradients). See this page for more info.
Feel free to explore the linked code base. The Dither extension method is the starting point for dithering a Bitmap in-place, and here are the OrderedDitherer constructor, the strength calibration for the min/max values of the matrix and a fairly fast nearest color search either by RGB channels or by brightness.
I've created a 2d array of 1024 x 1024 values ranging from -1 to 1, but I do not know how I am supposed to change this to a greyscale image.
What I have been doing is assigning a certain color to certain values, but this is not what I was going for.
What I have:
Specific ranges of values between -1 and 1 are mapped to distinct colors in a noncontinuous way (see the code snippet below)
What I want:
Values between -1 and 1 are mapped to greyscale varying uniformly from black at -1 to white at 1 or vice-versa
Code for the current version
private void button1_Click(object sender, EventArgs e)
{
sw.Start();
LibNoise.Perlin perlinMap = new LibNoise.Perlin();
perlinMap.Lacunarity = lacunarity + 0.01d;
perlinMap.NoiseQuality = LibNoise.NoiseQuality.High;
perlinMap.OctaveCount = octaveCount;
perlinMap.Persistence = persistence;
perlinMap.Frequency = frequency;
perlinMap.Seed = 1024;
if (radioButton1.Checked)
perlinMap.NoiseQuality = LibNoise.NoiseQuality.Low;
else if (radioButton2.Checked)
perlinMap.NoiseQuality = LibNoise.NoiseQuality.Standard;
else if (radioButton3.Checked)
perlinMap.NoiseQuality = LibNoise.NoiseQuality.High;
double sample = trackBar6.Value * 10;
double[,] perlinArray = new double[resolutieX, resolutieY];
for (int x = 0; x < resolutieX; x++)
{
for (int y = 0; y < resolutieY; y++)
{
perlinArray[x, y] = perlinMap.GetValue(x / sample, y / sample, 1d);
}
}
draw(perlinArray);
textBox12.Text = sw.ElapsedMilliseconds.ToString() + "ms";
sw.Reset();
}
public void draw(double[,] array)
{
Color color = Color.DarkBlue;
// Bitmap b = new Bitmap(1, 1);
Color[,] colorArray = new Color[resolutieX, resolutieY];
Bitmap afbeelding = new Bitmap( 1024, 1024);
// int tileSize = 1024 / resolutieY;
for (int y = 1; y < resolutieY; y++)
{
for (int x = 1; x < resolutieX; x++)
{
colorArray[x, y] = array[x, y] <= 0.0 ? Color.DarkBlue :
array[x, y] <= 0.1 ? Color.Blue :
array[x, y] <= 0.2 ? Color.Beige :
array[x, y] <= 0.22 ? Color.LightGreen :
array[x, y] <= 0.40 ? Color.Green :
array[x, y] <= 0.75 ? Color.DarkGreen :
array[x, y] <= 0.8 ? Color.LightSlateGray :
array[x, y] <= 0.9 ? Color.Gray :
array[x, y] <= 1.0 ? Color.DarkSlateGray :
Color.DarkSlateGray;
// colorArray[]
// afbeelding.SetPixel(x, y, color);
}
}
for (int y = 1; y < resolutieY; y++)
{
for (int x = 1; x < resolutieX; x++)
{
afbeelding.SetPixel(x, y, colorArray[x, y]);
}
}
pictureBox1.Image = afbeelding;
}
Ohhh, lovely fractals... :)
As you are using a 2d vector from -1 to 1, you have to recalculate it to 0..255. Your function is
f(x) = 255 * (x+1)/2
Then all you have to do, is to create a 2D Color vector with f(x)
foreach (int value in 2dVector)
{
2dColorVector.add(new Color.fromArgb(255, f(x), f(x), f(x));
}
Is pseudocode, but i think you could understand it clearly :)
You can get a grayscale image by setting the primary colors (RGB) to equal values.
One way of achieving this is to make a function that calculates the average of the RGB components of the colors you have, and then set each of the components to the average value.
Example - Average method:
Color ToGrayscale(Color c)
{
int avg = (c.R + c.G + c.B)/3;
return Color.FromArgb(avg, avg, avg);
}
Then apply that function for each output pixel:
for (int y = 1; y < resolutieY; y++)
{
for (int x = 1; x < resolutieX; x++)
{
afbeelding.SetPixel(x, y, ToGrayscale(colorArray[x, y]));
}
}
Luminosity
A more sophisticated version of grayscaling is the Luminosity method. It also averages the values, but it is a weighted average that takes human perception into account.
The formula is: 0.2126*Red + 0.7152*Green + 0.0722*Blue.
You can see how the formula is weighted towards which colors the human eye is most sensitive to.
To see if this alternate approach looks better for your project you simply use the Luminosity formula for calculating the average instead:
Color ToGrayscaleLuminosity(Color c)
{
var avg = (int)Math.Round(0.2126 * c.R + 0.7152 * c.G + 0.0722 * c.B);
return Color.FromArgb(avg, avg, avg);
}
I have this code:
int tX = 1;
for (int y = 0; y < ROWS; y++)
{
for (int x = 0; x < tX; x++)
{
Tile t = new Tile()
{
Texture = tile,
Position = new Microsoft.Xna.Framework.Point(x, y),
Troops = rnd.Next(1, 4),
OwnedByPlayerIndex = 0
};
t.Tap += tile_Tap;
if (t.Position.Y < ROWS)
tiles.Add(t);
}
tX += 2;
tX = (int)MathHelper.Clamp(tX, 0, COLS);
}
And what im trying to do is create a map within a rect, limiting the map by number of rows and cols.
But it does not work as it does not follow up and finishes the last corner, leaving it uncomplete
You appear to have some redudant logic in there, and your inner loop is not iterating over all columns. If you want to fill the entire rectangle then you don't need tX at all. Example:
for (int y = 0; y < ROWS; y++)
{
for (int x = 0; x < COLS; x++)
{
Tile t = new Tile()
{
Texture = tile,
Position = new Microsoft.Xna.Framework.Point(x, y),
Troops = rnd.Next(1, 4),
OwnedByPlayerIndex = 0
};
t.Tap += tile_Tap;
tiles.Add(t);
}
}
Additionally, from your screenshot it looks like if the entire bottom row were filled then the lower staggered hexes would overlap the bottom of the red rectangle. If you don't want to add those lower hexes then you'll need to add them conditionally:
...
if (y > 0 || 0 == (x & 1))
tiles.Add(t);