Cosine Interpolation

Started by
3 comments, last by Eagle11 15 years ago
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?
Advertisement
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 valuesfloat i; // Interpolation amountfloat 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.
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!
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.
Thanks a ton for the explanation! Very helpful!

This topic is closed to new replies.

Advertisement