Issues wrapping my brain around this math
This is a problem for you math wizards that I have been stumped on for a while even though I feel like I should be able to solve it.
What I am doing : I am taking an image, or rather, float[,] values from an image and putting them into an array. From that array I am "upressing" the values so that the range of values turns into a larger pool. From there I am dividing it up into chunks. This is for terrain generation.
So for this example.
N = 512 (the original input dimensions. 512*512)
Up = 5 (the upres amount)
Cs = 512 (The chunk dimensions. 512*512)
Ms = N * Up (The total dimensions of the upresed array. 2560*2560)
Gs = Ms/Cs (The grid dimensions. 5*5 chunks)
Currently I am upresing the entire thing all at once. What I want to be able to do is upress only the necessary areas. As you can imagine, upressing a 512*512 image 50 times (25600*25600) would take a while to process a lot of unnecessary data...especially when its going to be representing terrain that the player wont be near.
Where I fail: I need to be able to access the specific values that are getting upresed into the chunk dimensions (512x512) from the original input (N) and upres only those. I cant have float values and I cant be rounding. The values exist and are being upressed, I just cant think of a way to reverse this process.
Conclusion: I want to collect only the necessary values from the original input and pass those to be upresed instead of unpresing the entire original input array.
Here is the code I am currently working with.
float[,] originalInput; // an array containing the original values, im using 512*512 (N)
int upres = 5; // how many times the original values are upres (Up)
originalInput = UpresHeights(originalInput, upres); //upres the input
int mapsize = originalInput.GetLength(0); // (Ms)
int chunkSize = 512; //(Cs)
int gridSize = (int)Mathf.Round((mapsize / (float)chunkSize)); //Grid size (Gs)
float[,] UpresHeights(float[,] oldHeights, int upres)
{
//Duplicate the array input array
int originalHeight = oldHeights.GetLength(0);
float[,] heights = new float[originalHeight, originalHeight];
for (int y = 0; y < originalHeight; y++)
{
for (int x = 0; x < originalHeight; x++)
{
float val = oldHeights[x, y];
heights[x, y] = val;
}
}
//Upres the new array
int heightsLength = originalHeight;
int resolution = (heightsLength * upres);
float[,] newHeights = new float[resolution, resolution];
for (int y = 0; y < originalHeight; y++)
{
for (int x = 0; x < originalHeight; x++)
{
//The original input is tileable, so the following
//just makes sure to sample from the opposite side
//of the array if at the border
float right = 0.0f;
if (x == originalHeight - 1)
{
right = heights[0, y];
}
else {
right = heights[x + 1, y];
}
float top = 0.0f;
if (y == originalHeight - 1)
{
top = heights[x, 0];
}
else {
top = heights[x, y + 1];
}
for (int xx = 0; xx < upres; xx++)
{
for (int yy = 0; yy < upres; yy++)
{
//smooth the values (could use vector2.distance,
//but this isnt important right now)
//not that important since blur is applied later
float a = Mathf.Lerp(heights[x, y], right, (xx / (float)upres));
float b = Mathf.Lerp(heights[x, y], top, (yy / (float)upres));
newHeights[x * upres + xx, y * upres + yy] = (a + b) / 2f;
}
}
}
}
return newHeights;
}
Usually helps to draw a picture, pick some arbitrary spot, hand-make the numbers, and compare to your code-idea. For example, pixel [3,2] is probably easy to hand-translate into the correct $$anonymous$$i-grid(?) and check. Then hand-compute for [4,2] and see if they actually fit together.
The other trick is is to think up tests using colors or drawing lines. Often they show you where your math is junk, and sometimes you can program them faster than you can hand-work the equations.
Answer by b1gry4n · Oct 02, 2016 at 07:34 AM
Heres the solution.
I needed to create two new variables
//The sample area "per chunk" of the lower resolution array
lowResWidth = (N) / (Gs);
//the value of a step. Since its being upresed and between each value in //the array many other values are being made, this would be a float
//example: between value 1 and value 2, theres 5 values with a pixel
//step of 0.2
pixelstep = lowResWidth / (CS);
My grid was already set up with x and y coordinates, so i needed to find the start value of the x and y in relation to the "downscaled" version
float startX = lowResWidth * gridpoint.x;
float startY = lowResWidth * gridpoint.y;
I created a new function to return an upresed array from that specific area of the downscaled array called with
float[,] segment = UpresSeg(originalValues(N), chunkDimensions(Cs), startX, startY, gridPoint.x, gridPoint.y);
and the meat:
float[,] UpresSeg(float[,] originalHeight, int resolution, float startX, float startY, int gridX, int gridY)
{
//create a new array to return
float[,] returnThis = new float[resolution, resolution];
//Since its possible to start on a float (example 1.2), we need to floor the original start values
int origStartX = Mathf.FloorToInt(startX);
int origStartY = Mathf.FloorToInt(startY);
//we also need to subtract the original start floored value from the given start so we can retieve our
//xstep and ystep starting points
float xStep = startX - origStartX;
float yStep = startY - origStartY;
int xCounter = 0;
int yCounter = 0;
for (int y = 0; y < resolution; y++)
{
//reset the x counter for the new row
xCounter = 0;
for (int x = 0; x < resolution; x++)
{
int origX = Mathf.FloorToInt(gridX * lowResWidth) + xCounter;
int origY = Mathf.FloorToInt(gridY * lowResWidth) + yCounter;
float right = 0.0f;
float top = 0.0f;
int xMod = 0;
int yMod = 0;
if (origX < originalHeight.GetLength(0) - 1)
{
xMod = origX + 1;
}
else {
xMod = originalHeight.GetLength(0) - 1;
}
right = originalHeight[xMod, origY];
if (origY < originalHeight.GetLength(1) - 1)
{
yMod = origY + 1;
}
else {
yMod = originalHeight.GetLength(1) - 1;
}
top = originalHeight[origX, yMod];
float a = Mathf.Lerp(originalHeight[origX, origY], right, xStep);
float b = Mathf.Lerp(originalHeight[origX, origY], top, yStep);
returnThis[x, y] = (a + b) / 2f;
xStep += pixelstep;
if (xStep >= pixelstep * tileSettings.upres)
{
xStep = 0.0f;
xCounter++;
}
}
yStep += pixelstep;
if (yStep >= pixelstep * tileSettings.upres)
{
yStep = 0.0f;
yCounter++;
}
}
return returnThis;
}
There are some new issues that arise for me, but those are related to my generation script and what it does after this step. This might not be the best way to do this or even the "smartest" way, but it works
Answer by Bunny83 · Oct 02, 2016 at 01:42 AM
Well, it all boils down how you want to interpolate the values across your new resolution. What you do currently is kinda strange:
top----unused
| |
| |
x/y----right
Currently you ignore the height at "unused" and only interpolate in the lower left triangle.
Maybe you want a bilinear interpolation?
As for how to find the right values, first need some additional information: The chunk x and y position in "chunk coordinates". So the first chunk would have (0, 0) the second on the x axis would have (1, 0). The last chunk would be (4,4)
Now you could split the problem into smaller pieces but the easiest way is to simply iterate through your target array and work out the 4 required source coordinates.
// chunk coordinate
int cx;
int cy;
// this is the offset of this chunk inside the source image
Vector2 offset = new Vector2(cx * Up, cy * Up);
for(int y = 0; y < chunkSize; y++)
{
for(int x = 0; x < chunkSize; x++)
{
// "p" is the exact point inside the source image including fraction
var p = offset + new Vector2(x,y) / Up;
// get the bottom left and upper right corners
int x0 = Mathf.FloorToInt(p.x);
int y0 = Mathf.FloorToInt(p.y);
int x1 = (x0 >= originalHeight - 1)?0:x0 + 1;
int y1 = (y0 >= originalHeight - 1)?0:y0 + 1;
var t = p - new Vector2(x0, y0); // extract the fractional part
float r0 = Mathf.Lerp(heights[x0, y0], heights[x1, y0], t.x);
float r1 = Mathf.Lerp(heights[x0, y1], heights[x1, y1], t.x);
newHeights[x,y] = Mathf.Lerp(r0, r1, t.y);
}
}
Note: I've written this from scratch without testing or syntax checking, so use at your own risk ^^.
I upvoted because while this isnt exactly what I was going for (i do things really weird after this step so i need it a specific way) it gave me the idea of dividing the original input, retrieving the fraction and rounding it up. It needs to be rounded up since the returned value could be a float, i need the "pixels between the pixels" that havent been created yet. That way I can plug in an array of values Ive collected from the "originalinput" and get an upresd version
I will post a solution...if it works, when i get it up and running
Just wanted to clarify why I ignore the top right. The way I am setting up my tiles, that top right value ends up being the value for the tile that sits to the current tiles top right. it isnt ignored, it just belongs to a different tile in the grid. tiles that reach the end of the grid look to the tiles at the "looped" side. every edge is calculated/used
After this process each tile sets its top and right edge heights to match the heights of its top and right neighbor. Another pass is done for all 4 tiles that share a common corner height, the value is averaged and set to those tiles.
Your answer
Follow this Question
Related Questions
How to Instantiate from mouse pos? 0 Answers
Relative Position Not Constant? 0 Answers
Calculating laser energy 1 Answer
2D kinematics 1 Answer