Advertisement

Questions about a scaling up, down, and resetting using a scale matrix

Started by November 25, 2017 05:59 PM
12 comments, last by noodleBowl 7 years, 2 months ago

Hey I have a quick question about a scale matrices

I have my matrices in column major order and I know a scale matrix looks like:


// Column order matrix. Data arragned in an array:
0 4 8  12
1 5 9  13
2 6 10 14
3 7 11 15

//Scale Matrix
x 0 0 0
0 y 0 0
0 0 z 0
0 0 0 1

And I know I need to multiple my matrix by the scale matrix in order to scale by the x, y, z values. But how am I supposed to actually scale my matrix back down/set it to a scale factor?

I have this code that is to scale my matrix the along the X access


//Scale the current matrix by X amount
void Matrix4::scaleX(float x)
{
	data[0] *= x;
	data[1] *= x;
	data[2] *= x;
	data[3] *= x;
}

And if I were to do something like this I could scale my matrix along the X axis by 2


//Scale the matrix along the x axis by 2
myMatrix.scaleX(2.0f);

But I'm honestly not sure how I'm supposed to scale back down from this point. I was hoping for something like:


//Scale the matrix along the x axis by 2
myMatrix.scaleX(2.0f);

//Scale the matrix down along the x axis by 2
myMatrix.scaleX(-2.0f);

But based on the current math that is not how it works

Also how should I go about "resetting/setting" a scale matrix to a certain value? Say I have a matrix that is at scale factor 1. Then over time I want to scale it up until it hits a scale factor of 5. Then later after some other conditions are met I want to reset/set the scale factor to 2
 

The inverse of multiplying by 2 is multiplying by 1/2, not -2.

Advertisement
2 hours ago, alvaro said:

The inverse of multiplying by 2 is multiplying by 1/2, not -2.

I don't think I explained myself very well here. If I scale up by 2 then yes if I want to get back to my original number I would multiple by 0.5

I guess I'm really looking for a way to scale up / down easily using matrices. Easily in the sense that I have a single value that controls the scale factor I need to use. Example, something like:


//Declares somewhere, global or class memeber
float scaleFactor = 1.0f;

//Somewhere else in code
if(keyDown(W_KEY))
  scaleFactor += 0.5f

if(keyDown(S_KEY))
  scaleFactor -= 0.5f

//Scale the matrix up or down along the X axis based on the scaleFactor value
//See above post for implementation details
myMatrix.scaleX(scaleFactor); 

I'm not sure if adding / subtracting to a scale factor is possible using a scale matrix since it is multiplicative. Kind of throws me off since multiplying by a translation or rotation matrix seems to be additive

I am having a hard time understanding what your problem is. But perhaps we can get to the bottom of it if by looking at your last line: "[...] multiplying by a translation or rotation matrix seems to be additive".

I can see how translations might seem additive: There is a very natural way to describe the translation using a vector, and composition of translations corresponds to addition of vectors.

In the case of rotations, they seem additive if you are thinking about them in terms of angles. Now, anything multiplicative can be made to look additive if you use logarithms, because log(a*b) = log(a) + log(b). In some sense, angles are logarithms of the transformations they represent.

Now, scaling things is the prototypical example of multiplication, and you generally want to use multiplicative updates. In the bit of code you posted, if you hit "S" twice the object will collapse to a point because you made the scaling factor zero. Also, if you have made the object very big already, hitting "W" again will have an almost imperceptible effect. My guess is that that is not what you wanted. An alternative would be to multiply the scale factor by some constant (say, 1.2) when you hit "W" and divide by the same constant when you hit "S". Try that and see if that's closer to the behavior you want.

As a side note, you could keep track of the logarithm of the scaling factor instead. To get the same effect, you would be adding log(1.2) [about 0.1823] or subtracting log(1.2) to a variable named "log_scale", and then the scaling factor would be computed as "exp(log_scale)". That's rather awkward and I don't recommend it, but it would make scaling seem additive. It gives me a similar feeling to code that uses angles. :)

 

11 hours ago, alvaro said:

Now, scaling things is the prototypical example of multiplication, and you generally want to use multiplicative updates. In the bit of code you posted, if you hit "S" twice the object will collapse to a point because you made the scaling factor zero. Also, if you have made the object very big already, hitting "W" again will have an almost imperceptible effect. My guess is that that is not what you wanted. An alternative would be to multiply the scale factor by some constant (say, 1.2) when you hit "W" and divide by the same constant when you hit "S". Try that and see if that's closer to the behavior you want.

I went back and tried this, but I don't think it will work :(


//Declares somewhere, global or class memeber
float scaleFactor = 1.0f;

//Somewhere else in code
if(keyDown(W_KEY))
  scaleFactor *= 1.2f

if(keyDown(S_KEY))
  scaleFactor /= 1.2f

//Scale the matrix up or down along the X axis based on the scaleFactor value
//See above post for implementation details
myMatrix.scaleX(scaleFactor); 

The scenario goes like this:
1. Press the W key. scaleFactor now has a value of 1.2f. The matrix is scaled up by 1.2f just like we wanted
2. Press the W key again. scaleFactor now has a value of 1.44f. The matrix is scaled up by 1.44f. This is a issue because we will now scale by double the amount we wanted
3. Press the S key. scaleFactor is now 1.2f. The matrix is scaled up by 1.2f. This is definitely an issue. We intended to scale down by 1.2f, but because the value for scaleFactor is 1.2f we will actually scale up our matrix by 1.2f

 

Oh, you have a more basic problem. I imagined you were building your matrix from scratch every time. You should make the scaleFactor equal to 1.2 when `W' is pressed, and 1/1.2 when 'S' is pressed, 1 otherwise. Either that, or build the matrix from scratch every time.

Advertisement

Normalize the x vector, and then multiply it with the scale factor.

6 hours ago, alvaro said:

Oh, you have a more basic problem. I imagined you were building your matrix from scratch every time. You should make the scaleFactor equal to 1.2 when `W' is pressed, and 1/1.2 when 'S' is pressed, 1 otherwise. Either that, or build the matrix from scratch every time.

This will totally work! I think I will have to rebuild the matrix from scratch if I need to keep track of how much I scaled my matrix by. That or have another variable

5 hours ago, Hodgman said:

Normalize the x vector, and then multiply it with the scale factor.

Can you explain this? I'm curious, but I don't know what you really mean by normalize the X vector since I'm just using a single value and I don't know how you are supposed to normalize a single value

Also just a side question for confirmation, I didn't mess up the scale matrix math did I? The whole not adding or subtracting to an accumulator value and then passing that value to my functions makes me feel like I did it wrong


void Matrix4::scale(float x, float y, float z)
{
	data[0] *= x;
	data[1] *= x;
	data[2] *= x;
	data[3] *= x;

	data[4] *= y;
	data[5] *= y;
	data[6] *= y;
	data[7] *= y;

	data[8]  *= z;
	data[9]  *= z;
	data[10] *= z;
	data[11] *= z;
}

 

21 minutes ago, noodleBowl said:

Can you explain this? I'm curious, but I don't know what you really mean by normalize the X vector since I'm just using a single value and I don't know how you are supposed to normalize a single value

In your matrix, elements 0, 1, 2 and 3 are the X basis vector. If you want to scale that vector, you can multiply it with a single (scalar) value. If you want to set the scale of the X axis to some new value, you can first normalize the x basis vector (which sets the scale / length to 1.0), and then multiply it by your scalar.

54 minutes ago, Hodgman said:

In your matrix, elements 0, 1, 2 and 3 are the X basis vector. If you want to scale that vector, you can multiply it with a single (scalar) value

Isnt this what I'm already doing?

55 minutes ago, Hodgman said:

If you want to set the scale of the X axis to some new value, you can first normalize the x basis vector (which sets the scale / length to 1.0), and then multiply it by your scalar.

So then I'm looking at something like this then?:


float length = sqrt(data[0] * data[0] + data[1] * data[1] + data[2] * data[2] + data[3] * data[3]);
data[0] = (data[0]/length) * newScaleValue;
data[1] = (data[1]/length) * newScaleValue;
data[2] = (data[2]/length) * newScaleValue;
data[3] = (data[3]/length) * newScaleValue;

Does just computing the length on the first 3 columns give me back the current scale values for x, y, z?
Can I just extract everything from the matrix then? Not just scale but rotation and transform too. Is it worth it (accurate)?

Assuming the answer to my first question is yes, you wouldn't be able to get an accurate scale value back if the scale was negative. Which in my mind is "wrong" since we are missing the sign information. Only makes me wonder what I wouldn't / couldn't get back trying to extract the rotation and transform values. Mainly just the rotation values since I'm pretty sure the last column of the matrix is everything needed for the transform values

This topic is closed to new replies.

Advertisement