Sign in to follow this  
Eagle11

Cosine Interpolation

Recommended Posts

Hey, I have a couple questions about Cosine Interpolation. I am going through "Programming an RTS game with Direct3D" and I have reached the second example in the 4th chapter. I understand most of it after spending hours pouring over the code, and looking up D3D functions and, parameters. I have a couple problems with the random heightmap generation though. I don't understand Cosine Interpolation. In the code there is a function like this:
float CosInterpolate(float v1, float v2, float a)
{
	float angle = a * D3DX_PI;
	float prc = (1.0f - cos(angle)) * 0.5f;
	return  v1*(1.0f - prc) + v2*prc;
}
I have a hard time understanding what this function accomplishes along with how the math works and what the each of the parameters are. Can someone help me out a bit?

Share this post


Link to post
Share on other sites
Firstly, if you haven't already, I'd suggest you read up on and play around with interpolation as it's an incredibly useful and pretty common technique.

Linear interpolation is where you have two values, in this case v1 and v2, and you can specify a value between 0.0 and 1.0 that says how much to interpolate between the two values.

E.g. Interpolating 0.0 between v1 and v2, would be equal to v1. Interpolating 1.0 would be equal to v2. Interpolating 0.5 would be equal to 0.5 * v1 added to 0.5 * v2.

This can be written in code as:

float v1, v2; // Interpolating values
float i; // Interpolation amount

float result = v1 * (1.0f - i) + v2 * i;


Essentially saying that you take the interpolation amount of one value, and the opposite amount from the other value - Assuming that the interpolation amount goes from 0.0 to 1.0.

What your function does is the same process, but rather than a linear interpolation line between the two values, it uses the cosine curve instead. The first two lines turn your interpolation value into an angle to sample from the cosine wave, and then sample from the wave, but converting the scale run between 0 and 1 instead of the wave's usual -1 to 1.

Cosine wave goes from -1 to 1.
Adding 1 changes it to 0 to 2.
Halving changes it to 0 to 1.


The last part of the function then performs a normal linear interpolation, but using the value from the cosine wave instead of the value,"a", that you passed into the function.



Sorry if that's a little unclear. I'd recommend you experiment a little with linear interpolation, and have a look at the cosine wave and hopefully the point of the function should be come a little more clear.

Share this post


Link to post
Share on other sites
Ok, so if I understand correctly, what this function does is find a point on the curve between v1 and v2. The greater 'a' is the closer the point is to v2.
Is that correct?

The next thing that I need to know is why we use it. It is used in creating a random heightmap. I can't understand why though. Here is the code:


HRESULT HEIGHTMAP::CreateRandomHeightMap(int seed, float noiseSize, float persistence, int octaves)
{
if(m_pHeightMapTexture != NULL){m_pHeightMapTexture->Release(); m_pHeightMapTexture = NULL;}
m_pDevice->CreateTexture(m_size.x, m_size.y, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &m_pHeightMapTexture, NULL);

D3DLOCKED_RECT lock;
m_pHeightMapTexture->LockRect(0, &lock, NULL, NULL);

//For each map node
for(int y=0;y<m_size.y;y++)
for(int x=0;x<m_size.x;x++)
{
//Scale x & y to the range of 0.0 - m_size
float xf = ((float)x / (float)m_size.x) * noiseSize;
float yf = ((float)y / (float)m_size.y) * noiseSize;

float total = 0;

// For each octave
for(int i=0;i<octaves;i++)
{
//Calculate frequency and amplitude (different for each octave)
float freq = pow(2.0f, i);
float amp = pow(persistence, i);

//Calculate the x,y noise coordinates
float tx = xf * freq;
float ty = yf * freq;
int tx_int = tx;
int ty_int = ty;

//Calculate the fractions of x & y
float fracX = tx - tx_int;
float fracY = ty - ty_int;

//Get the noise of this octave for each of these 4 points
float v1 = Noise(tx_int + ty_int * 57 + seed);
float v2 = Noise(tx_int+ 1 + ty_int * 57 + seed);
float v3 = Noise(tx_int + (ty_int+1) * 57 + seed);
float v4 = Noise(tx_int + 1 + (ty_int+1) * 57 + seed);

//Smooth in the X-axis
float i1 = CosInterpolate(v1 , v2 , fracX);
float i2 = CosInterpolate(v3 , v4 , fracX);

//Smooth in the Y-axis
total += CosInterpolate(i1 , i2 , fracY) * amp;
}

int b = 128 + total * 128.0f;
if(b < 0)b = 0;
if(b > 255)b = 255;

BYTE *bDest = (BYTE*)lock.pBits;
bDest += y * lock.Pitch + x;
*bDest = b;

//Save to heightMap
m_pHeightMap[x + y * m_size.x] = ((float)b / 255.0f) * m_maxHeight;
}

m_pHeightMapTexture->UnlockRect(0);

return S_OK;
}


Thanks for the help!

Share this post


Link to post
Share on other sites
Quote:
Original post by Eagle11
Ok, so if I understand correctly, what this function does is find a point on the curve between v1 and v2. The greater 'a' is the closer the point is to v2.
Is that correct?


Yep, that's exactly right!

Sorry I don't have time to read the code, but I'd assume it's used when generating a heightmap because; If you imagine a linear interpolation between points (or the noise or whatever is used to generate the points, as noise is generally.. well, noisy! and so lots of random spiking values), you'd end up with very sharp lines, so points going up and down with big spikes everywhere kind of like:

/\/\/\/\/

for your surface if you sampled along it. But if you use a cosine interpolation instead to generate the points, you'd get smooth tops and bottoms to the line generated instead, so something more like the above, but curved instead of the sharp points with a sudden change in direction. So basically just to give a more pleasing visual result I'd assume.

Again sorry I don't have time to read it and try and look more into the actual reasoning, but just as a comparison, try changing the function yourself to linear interpolation instead and see how the heightmap looks.

So the consine interpolation function from before would become a simplified:


float LinearInterpolate(float v1, float v2, float a)
{
return v1*(1.0f - a) + v2*a;
}



Presumably with a heightmap it's something along the lines of what I said, but changing it yourself is probably the quickest way to see! And of course if you're curious, try writing your own otehr interpolation functions and see what happens.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this