Learning game programming (part 2) (math) [closed] - math

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
So, it's been a few months since I wrote this question, since then I've toyed with "raw" C++ D3D, The Ogre and Irrlicht graphics engines and lately Microsoft XNA. I've built a few 2D games (mostly replicas of old stuff like tetris, astreoids, etc.) and made some (very) small steps into the 3D world in the above mentioned technologies.
I have little to no trouble creating the actual game logic, abstracting away object interactions to allow me to plug in different forms of control (computer, player, over network. etc.), doing threading or any of the other stuff I'm used to from my day to day work - which feels perfectly natural to me. I messed around very little with HLSL and particle effects (very very basic).
But 3D math involving Matrices and Vectors (and Quaternions(?) in Ogre3D, are these really needed?)... really gets me, I can follow examples (e.g. the Learning XNA 3.0 book I bought from O'Reilly, which is an awesome book btw) and I understand why and how something happens in the example, but when I try to do something myself I feel that I'm lacking the understanding of this type of math to be able to really get it and make it work by myself.
So I'm looking for resources on learning 3D math (mostly) and some Shader/Particle Effects books. I would prefer resources that are pedagogic and take it slow above something like a doctors thesis on vector math which will be way over my head. The ideal resource would be something that demonstrates it all in D3D.

Ok, a quick course in Matrix/Vector calculation:
A matrix is a collection of numbers ordered in a rectangular grid like:
[ 0, 1, 2 ]
[ 2, 3, 5 ]
[ 2, 1, 3 ]
[ 0, 0, 1 ]
The above matrix has 4 rows and 3 columns and as such is a 4 x 3 matrix.
A vector is a matrix with 1 row (a row vector) or 1 column (a column vector).
Normal numbers are called scalars to contrast with matrices.
It is also common to use capital letters for matrices and lowercase letters for scalars.
We can do basic calculation with matrices but there are some conditions.
Addition
Matrices can be added if they have the same dimensions. So a 2x2 matrix can be added to a 2x2 matrix but not to a 3x5 matrix.
[ 1, 2 ] + [ 2, 5 ] = [ 3, 7 ]
[ 2, 4 ] [ 0, 3 ] [ 2, 7 ]
You see that by addition each number at each cell is added to the number on the same position in the other matrix.
Matrix multiplication
Matrices can be multiplied, but this is a bit more complex. In order to multiply matrix A with matrix B, you need to multiply the numbers in each row if matrix A with each column in matrix B. This means that if you multiply an a x b matrix with a c x d matrix, b and c must be equal and the resulting matrix is a x d:
[1,2,3] x [4,6] = [1x4+2x2+3x2, 1x6+2x1+3x3 ] = [4+4+6, 6+2+9 ] = [14, 20]
[1,4,5] [2,1] [1x4+4x2+5x2, 1x6+4x1+5x3 ] [4+8+10, 6+4+15 ] [22, 25]
[2,3]
As you can see, with matrixes, A x B differs from B x A.
Matrix scalar multiplication
You can multiply a matrix with a scalar. In that case, each cell is multiplied with that number:
3 x [1,2] = [ 3, 6]
[4,7] [12,21]
Inverting a matrix
Matrix division is not possible, but you can create an inversion of a matrix such that A x A-inv is a matrix with all zero's except for that main diagonal:
[ 1, 0, 0 ]
[ 0, 1, 0 ]
[ 0, 0, 1 ]
Inverting a matrix can only be done with square matrices and it is a complex job that does not neccesary have a result.
Start with matrix A:
[ 1, 2, 3 ]
A = [ 1, 3, 4 ]
[ 2, 5, 1 ]
We add 3 extra columns and fill them with the unit matrix:
[ 1, 2, 3, 1, 0, 0 ]
[ 1, 3, 4, 0, 1, 0 ]
[ 2, 5, 1, 0, 0, 1 ]
Now we start with the first column. We need to subtract the first row from each other row such that the first column contains only zeroes except for the first row.
In order to do that we subtract the first row once from the second and twice from the third:
[ 1, 2, 3, 1, 0, 0 ]
[ 0, 1, 1,-1, 1, 0 ]
[ 0, 1,-5,-2, 0, 1 ]
Now we repeat this with the second column (twice from the first row and once from the third)
[ 1, 0, 1, 3,-2, 0 ]
[ 0, 1, 1,-1, 1, 0 ]
[ 0, 0,-6,-1,-1, 1 ]
For the third column, we have a slight problem. The pivot number is -6 and not 1. But we can solve this by multiplying the entire row with -1/6:
[ 1, 0, 1, 3, -2, 0 ]
[ 0, 1, 1, -1, 1, 0 ]
[ 0, 0, 1, 1/6, 1/6, -1/6 ]
And now we can subtract the third row from the first and the second:
[ 1, 0, 0, 17/6,-13/6, 1/6 ]
[ 0, 1, 0, -7/6, 5/6, 1/6 ]
[ 0, 0, 1, 1/6, 1/6, -1/6 ]
Ok now we have the inverse of A:
[ 17/6,-13/6, 1/6 ]
[ -7/6, 5/6, 1/6 ]
[ 1/6, 1/6, -1/6 ]
We can write this as:
[ 17,-13, 1 ]
1/6 * [ -7, 5, 1 ]
[ 1, 1, -1 ]
[ 1, 2, 3 ] [ 17,-13, 1 ] [ 6, 0, 0 ] [ 1, 0, 0 ]
A = [ 1, 3, 4 ] x [ -7, 5, 1 ] x 1/6 = 1/6 x [ 0, 6, 0 ] = [ 0, 1, 0 ]
[ 2, 5, 1 ] [ 1, 1, -1 ] [ 0, 0, 6 ] [ 0, 0, 1 ]
Hope this helps a bit.

Fredrik - the short answer is that, yes, you must learn Matrices and Vectors as they are the mathematical underpinnings for 3D work.
While Linear algebra is definitely not doctorate-level mathematics, it will take a bit of work. To get started, check out this book on Amazon: it looks like it is exactly what you are looking for. I haven't read this particular book (the one I used in grad school is a bit out of date) but it is particularly well rated.
One other thing: there are various 3D modeling engines that do this work for you on the market. The most famous of these is arguably the Source Engine from Valve. You can use this Engine (built for HalfLife2 & CounterStrike) to create some pretty sophisticated games while working above the level of 3D modeling. In fact, one of the most popular games on the Steam network, Garry's mod started with someone just playing with cool things you can do with the Steam Engine. Here's a link to a site that provides tutorials for building your own worlds using the Source Engine in case you are interested.

You definitely need to learn linear algebra. MIT released the whole class on youtube, for free. You can start from here. It's not that difficult, believe me! Have fun ;)

'Mathematics for Computer Graphics Applications' is an introductory level textbook and takes a classroom appropriate approach to all of the basic math that you need to be acquainted with for 3D programming (matrices and vectors mostly)
And a note regarding quaternions: They're very useful for certain applications. SLERP (Spherical Linear intERPolation) can be very handy for producing smooth/appealing camera movements (among other things). SLERP is a pain (expensive) to do with matrices, but cheap and easy with Quaternions. Learn to use and love them - even if you don't fully understand them.

For vectors specifically, an introductory text or course on linear algebra should be able to get you up to speed fairly quickly.

Related

Using gather() to retrieve rows from 3d tensor with 2d tensor in Pytorch

I'm new to Pytorch and having an issue with the gather() function:
I have a 3d tensor, x[i,j,k]:
x=tensor([[[1,2,3],
[4,5,6],
[7,8,9]],
[[10,11,12],
[13,14,15],
[16,17,18]]])
I have an index tensor:
index=tensor([[1,2,0]])
I want to use the values of index to iterate over x[j] and fetch the (complete) rows. I've tried gather() with all dims, squeezing, unsqueezing and it never seems to get the output I'm looking for, which would be:
output=tensor([[[4,5,6],
[7,8,9],
[1,2,3]],
[[13,14,15],
[16,17,18],
[10,11,12]]])
I've also tried repeating the values of index to get the same shape as x but it did not work.
I know I can do this with an if loop, but I'm pretty sure I can do it with gather() as well. Thanks for the help
Let us set up the two tensors x and index:
>>> x = torch.arange(1,19).view(2,3,3)
>>> index = torch.tensor([[1,2,0]])
What you are looking for is the torch.gather operation:
out[i][j][k] = x[i][index[i][j][k]][k]
In other to apply this function, you need to expand index to the same shape as out. Additionally, a transpose operation is required to flip your original index tensor.
>>> i = index.T.expand_as(x)
tensor([[[1, 1, 1],
[2, 2, 2],
[0, 0, 0]],
[[1, 1, 1],
[2, 2, 2],
[0, 0, 0]]])
If you compare with the pseudo code line above, you can see how every element of i represents the row of the original tensor x the operator will gather values from.
Applying the function gets us to the desired result:
x.gather(dim=1, index=index.T.expand_as(x))
tensor([[[ 4, 5, 6],
[ 7, 8, 9],
[ 1, 2, 3]],
[[13, 14, 15],
[16, 17, 18],
[10, 11, 12]]])

Can anyone write a prog that change the sum(1 to n) to n*(n+1)/2 automatic?

with the rec sum:
let rec sum a=if a==0 then 0 else a+sum(a-1)
if the compiler use the tail recursive optimization,it may create a variable "sum" to iteration(when I use the "ocamlc -dlambda",the recursive still there.when I use "ocamlc -dinstr" got the assemably code,I can't read it now)
but on the book《Design Concepts of programming languages》,page 287,it can change the function to this(the key line):n*(n+1)/2
"You should convince yourself that the least fixed point of this
function is the computation csum that returns a summation procedure that,returns n*(n+1)/2 if its argument is a nonnegative integer in"
I can't understand it,the prog not Gauss!I think it can't chang the "rec sum" to n*(n+1)/2 automatic!only man can do it,right?
So how this book write here means?Is anyone know?Thanks!
I believe your book is merely making a small point about equivalence of pure functions. Nevertheless, optimising away a loop that only contains affine operations is relatively easy.
Equivalence of pure functions
I haven't read that book, but from the paragraph you quote, I think the book merely makes a point about pure functions. Since sum is a pure function, i.e. a function without side-effect, then in a sense,
let rec sum n =
if n = 0 then 0
else n + sum (n - 1)
is equivalent to
let sum n =
n * (n + 1) / 2
But of course "equivalent" here ignores the time and space complexity, and unless the compiler has some sort of hardcoding for common functions to optimise, I'd be extremely surprised if it optimised sum like that.
Also note that the two above functions are only equivalent so far as they are only called on a nonnegative argument. The recursive version will loop infinitely (and provoke a stack overflow) if n is negative; the direct formula version will always return a result, although that result will be nonsensical if n is negative.
Optimising loops that only contain affine operations
Nevertheless, writing a compiler that would perform such optimisations is not complete science-fiction. At the end of this answer you will find links to two blogposts which you might be interested in. In this answer I will summarise how the method described in those blog posts can be applied to your problem.
First let's rewrite function sum as a loop in pseudo-code:
function sum(n):
s := 0
i := 1
repeat n:
s += i
i += 1
return s
This kind of rewriting is similar to what happens when sum is transformed into a tail-recursive function.
Now if you consider the vector v = [s, i, 1], then the affine operations s += i and i += 1 can be described as multiplying v by a matrix:
s += i
[[ 1, 0, 0 ], # matrix Msi
[ 1, 1, 0 ],
[ 0, 0, 1 ]]
i += 1
[[ 1, 0, 0 ], # matrix Mi1
[ 0, 1, 0 ],
[ 0, 1, 1 ]]
s += i, i += 1
[[ 1, 0, 0 ], # M = Msi * Mi1
[ 1, 1, 0 ],
[ 0, 1, 1 ]]
This affine operation is wrapped in a "repeat n" loop. So we have to multiply v by this matrix M, n times. But matrix multiplication is associative; so instead of doing n multiplications by matrix M, we can raise matrix M to its nth power, and then multiply v by the resulting matrix M**n.
As it turns out:
[[1, 0, 0], [[ 1, 0, 0],
[1, 1, 0], to the nth = [ n, 1, 0],
[0, 1, 1]] [n*(n - 1)/2, n, 1]]
which represents the affine operation:
s = s + n * i + n * (n - 1) / 2
i = i + n
Starting from s, i = 0, 1, this gives us s = n * (n+1) / 2 as expected.
More reading:
Using the Quick Raise of Matrices to a Power to Write a Very Fast Interpreter of a Simple Programming Language;
Automatic Algorithms Optimization via Fast Matrix Exponentiation.

Most common term in a vector - PARI/GP

I feel like I'm being really stupid here as I would have thought there's a simple command already in Pari, or it should be a simple thing to write up, but I simply cannot figure this out.
Given a vector, say V, which will have duplicate entries, how can one determine what the most common entry is?
For example, say we have:
V = [ 0, 1, 2, 2, 3, 4, 6, 8, 8, 8 ]
I want something which would return the value 8.
I'm aware of things like vecsearch, but I can't see how that can be tweaked to make this work?
Very closely related to this, I want this result to return the most common non-zero entry, and some vectors I look at will have 0 as the most common entry. Eg: V = [ 0, 0, 0, 0, 3, 3, 5 ]. So whatever I execute here I would like to return 3.
I tried writing up something which would remove all zero terms, but again struggled.
The thing I have tried in particular is:
rem( v ) = {
my( c );
while( c = vecsearch( v, 0 ); #c, v = vecextract( v, "^c" ) ); v
}
but vecextract doesn't seem to like this set up.
If you can ensure all the elements are within the some fixed range then it is enough just to do the counting sorting with PARI/GP code like this:
counts_for(v: t_VEC, lower: t_INT, upper: t_INT) = {
my(counts = vector(1+upper-lower));
for(i=1, #v, counts[1+v[i]-lower]++);
vector(#counts, i, [i-1, counts[i]])
};
V1 = [0, 1, 2, 2, 3, 4, 6, 8, 8, 8];
vecsort(counts_for(V1, 0, 8), [2], 4)[1][1]
> 8
V2 = [0, 0, 0, 0, 3, 3, 5];
vecsort(counts_for(V2, 0, 5), [2], 4)[1][1]
> 0
You also can implement the following short-cut for the sake of convenience:
counts_for1(v: t_VEC) = {
counts_for(v, vecmin(v), vecmax(v))
};
most_frequent(v: t_VEC) = {
my(counts=counts_for1(v));
vecsort(counts, [2], 4)[1][1]
};
most_frequent(V1)
> 8
most_frequent(V2)
> 0
The function matreduce provides this in a more general setting: applied to a vector of objects, it returns a 2-column matrix whose first column contains the distinct objects and the second their multiplicity in the vector. (The function has a more general form that takes the union of multisets.)
most_frequent(v) = my(M = matreduce(v), [n] = matsize(M)); M[n, 1];
most_frequent_non0(v) =
{ my(M = matreduce(v), [n] = matsize(M), x = M[n, 1]);
if (x == 0, M[n - 1, 1], x);
}
? most_frequent([ 0, 1, 2, 2, 3, 4, 6, 8, 8, 8 ])
%1 = 8
? most_frequent([x, x, Mod(1,3), [], [], []])
%2 = []
? most_frequent_non0([ 0, 0, 0, 0, 3, 3, 5 ])
%3 = 5
? most_frequent_non0([x, x, Mod(1,3), [], [], []])
%4 = x
The first function will error out if fed an empty vector, and the second one if there are no non-zero entries. The second function tests for "0" using the x == 0 test (and we famously have [] == 0 in GP); for a more rigorous semantic, use x === 0 in the function definition.

How can I get `True` from `[1, 1, 0, 0, 0] == [0, 0, 1, 1, 0]` in Python?

Example:
I have a solution list a:
a = [1, 1, 0, 0, 0]
and input lists bs:
b1 = [1, 1, 0, 0, 0]
b2 = [0, 1, 1, 0, 0]
b3 = [0, 0, 1, 1, 0]
...
bn = [1, 0, 0, 0, 1]
If I compare a to either b1, b2, ..., bn, I expected to get True value from the comparisons. For sure, this simple expression will not work:
if a == b:
...
because in Python only identical lists can be equal.
Is there any beautiful math that I can easily implement it in programming languages? Now I am thinking about building some hash function but I'm still not sure how?
Note 1) it can be easily implemented by just using for loop but I need some thing more robust. 2) this is maybe also related to problem of this post Cyclic group
A simple solution could be to adjust the a and b values:
a_original = [5, 2, 3, 1, 4]
a_formatted = sorted(a_original)
Then, you can just use the formatted variables. A simple "for" loop can be used to format all of your variables.
Hope this helps!

What do [0, rows) and [1, rows] mean? more specifically [ and )

I understand that it's from 0 to rows and from 1 to rows, but which is including and which is excluding?
[ - including,
( - excluding
Example: [10, 15)
10, 11, 12, 13, 14
Example: (2, 4)
3
The [ is inclusive, ) is exclusive. So everything from 0 to rows, including 0, but not rows.
Sometimes this syntax is used instead: [0, rows[
The [ denotes a closed interval (i.e. inclusive) and the ) denotes an open interval (i.e. exclusive). So [0, 10) means 0 through 10, excluding 10.

Resources