Archive for project zombie

LINK to Tutorial on Discrete Ricci Flow for Global Parameterizations

Posted in programming, project zombie, thoughts with tags on October 16, 2008 by bey0ndy0nder

tutorial here.

Wow, it’s amazing how Ricci flow was used to prove Poincaré conjecture… And how the discrete ricci flow has many applications in computer graphics.

Maybe I will be able to use this in Project Zombie to parameterize my 3D objects, for usage in mapping my imposter views back into the geometry image.

(A side note: Do you think it is sort of weird that a ’stupid game’ can use such beautiful and powerful math? Do you think it’s sort of selfish..to use it purely for entertainment? No! I don’t think so.)

Some more on Geometry Image

Posted in programming, project zombie, thoughts with tags , on October 16, 2008 by bey0ndy0nder

Okay, so I’ve finished reading the Geometry Image paper. Links:

http://www.google.com/search?hl=en&fkt=505&fsdt=4659&q=geometry+image&btnG=Google+Search&aq=f&oq=

Hmm, now after reading, this is definitely something going on the back burner. I WILL eventually implement this, but just not now.

Another method of course, is to just use regular UVW mapping. It works the same way, I believe. For each imposter views I store the UV texture coords which maps into a texture map, plus a normal map. Of course, I need to think more about the details of how this is going to work exactly. But in general I think the method I outlined so far should work. Again, this is definitely something for the back burner.

What I’m focusing on now is to compress my imposter images. Currently, each of my imposter views is a 128 by 128 texture. This is wasteful, since the actual extent of the imposter object only takes up a small percentage of this texture. Thus, I need to come with a scheme so to minimize the area wasted. My thoughts as of now is to project the extent of each object, to figure out the extent, then from this extent get rid of all the wasted space. Another route is to use an optimization algorithm.

Once we get rid of the space, we still need a scale factor, in order to determine the extent of each individual imposter in the larger imposter texture (which contains all imposter views). In order to properly scale the billboard texture coordinates so I can access the correct imposter view, depending on the view direction. I can store this in an array of uniform shader variables, to be used in my fragment shader that renders the imposter.

But this means that the extent of each imposter view must be constant per a single phi value (the imposter views  are arranged vertically based on PHI). So the factor will be determined by the largest extent per row of phi value. Another route is storing the extent data in the first pixel of each imposter view. This is workable, and would allow arbitrary imposter extents, but at the cost of doing a texture read for this value…I’m probably going to implement it both ways to check it out.

Project Zombie screenshots…

Posted in project zombie with tags on October 16, 2008 by bey0ndy0nder

This is where I dump my screenshots, as a collection, so I can link to this post on my project zombie link text thingy on the right:

Project Zombie imposter shader effects

Posted in glsl, gpu, mathematics, programming, project zombie, source code, thoughts with tags , , , on October 15, 2008 by bey0ndy0nder

So I finally fixed some bugs with my imposter renderer. Now it works great. I also put in some simple Phong shading.

I have several options for doing lighting:

1.

Store normal data along with the imposter texture. This is workable, but not memory efficient. This option would look great though. (This may actual work in the end. I still have not thought much about compression schemes for imposter textures).

2.

Chi Ting Lighting ™. Yes, this is what I have implemented, and is probably what I will go with in the end, if option 3 does not work out. Basically, I’m using a sphere to approximate the shape of my imposter object. I sample this sphere based on the texture coordinate (i.e. theta and phi values). So that, every pixel is shaded based on this sphere. Sort of like hemispheric lighting… An improvement to the current method is to compute some factors, such that, using these factors I can get a better sample, e.g. finding bounds for each imposter view angle and map this bound to a best fit spherical theta and phi range. This brings me to option 3…

3.

Use fancy mathematics ™. That is, find some sort of mathematical function that approximates the shape our object, and then sample from this function. Of course, any storage used for this function should be less than required for the normal option. Think spherical harmonics type stuff… I am not saying spherical harmonics will be the solution here, but I take my ideas from that- some special function to represent data.

It’s ALIVE…(and some ramblings on imposters)

Posted in GPU Random Number, programming, project zombie, source code with tags on October 10, 2008 by bey0ndy0nder

I finished preliminary implementation of GPU based zombie movement. Right now it’s very simple, but I’m working on it. it’s coming together nicely. My next step is to finish the Imposter renderer. After that, I will on interaction between the world and the zombies. The game-play would suck if all the zombies just wandered around like … well.. zombie. I will blog about game-play ideas in an upcoming post.

Here is the ping pong source code to update the zombies (onUpdate function):

The Code

Notice that I’m using a naked pointer. I know it’s bad, but my idea is that this is the controller, and thus is not responsible for the state (the model). I think I should change it from pointer to reference to make that “borrowing” notion more clear. I think it should be a priority that I change my habit to use references more often.

And here are the two shaders:

positional update

directional update

Finished Imposter implementation:

The imposter renderer is not yet finished. All the imposter “views” are facing the camera; it does not correspond to the actual direction of the zombie. So to get the proper “view” of the imposter, we need to take into account the eye direction with respect to direction of the zombie.

Our imposters are stored in a single texture, and mapped according to two keys that is based on sphereical coordinate parameter of phi and theta, corresponding to the viewing direction. Thus, to properly calculate which imposter “view” texture to render we must map the current view direction into this singlular imposter storage texture. Therefore, the proper solution is to compute phi and theta from the current viewing vector.

We know that (let’s assume r = 1):

phi = arccos(z)

theta = atan2(y/x). (remembering to add 2pi for negative values).

But we know that actan2 is undefined when y = 0 and x = 0. So we need to carefully examine this case: When this happens, we are looking down at the object in question. But we can still derive theta, by looking at the camera’s orthonormal basis. Thus, when y=0 and x=0, we use the camera’s local axes. However, we need to transform the camera’s local axes into the object space. This can be done by noting the following:

For our purposes, we only require one degree of freedom, namely yaw, for the zombie. So the zombie’s directional vector will always be in the XY plane. So again, using atan2, we can compute it’s angle from the object’s direction in object space. So, we can then construct a rotation matrix, or quaternion, or whatever, which corresponds to the transformation of the object from object space into world space. Thus, the inverse of this transformation will transform the camera’s orthonormal basis in world space into object space. From here, we can then use it to compute theta by following the scheme noted above.

Zombie “smooth wandering.” (randomly)

Posted in GPU Noise, GPU Random Number, programming, project zombie, thoughts with tags , , on October 7, 2008 by bey0ndy0nder

Right, I mentioned in an earlier post that I need my Zombies to wander randomly. However, if I simply randomly change the orientation the behavior would be similar to random walk. I.E. changing of orientation would not be “smooth.” So we need some sort of interpolation. First, I only need one degree of freedom for this sort of orientation, e.g. yaw. Secondly, I don’t need much precision, since the plan is to smoothly interpolate between orientations. So, some preliminary brainstorm suggests that I can pack a 2d vector (on the xy plane) into a 32bit float. That leaves me with 3 free floats left, which I can use to store interpolation offset. This leaves me with the question of “uniform smoothness” during orientation interpolation. I do not want to use quaternions since I only require a single degree of freedom. Perhaps I can store a single float “theta factor,” and interpolate this theta (i.e. X = cos(t)i + sin(t)j).

Edited:
Maybe I need to give some more thoughts into this. Smoothly interpolating orientation is fine, but we also need to think about the rate of this rotation. THis probably will be a randomized factor also. I don’t EVERYTHING to have smooth rotation, some need to arbitrarily change directions. Yeah, I think randomness will be fine. Just have to have some sort interpolation tho, so you won’t have TOO abrupt of a change.

MD5GPU reloaded (and debugged):

Posted in GPU Noise, GPU Random Number, glsl, gpu, mathematics, programming, project zombie, source code, thoughts with tags , , , , on October 5, 2008 by bey0ndy0nder

It’s working now. I haven’t tested it with DIEHARDER yet, I may do it later, when I have time. But if it looks like white noise, walks likes whitenoise…

BTW, the author’s (of the paper) optimization works fine. Realy, think about it, why wouldn’t it work? It’s still rotating, that’s all matters really.

I’m going to start working on the agent simulation part of PZ.

#extension GL_EXT_gpu_shader4 : enable
//This function initializes the 512bit data according to the MD5 spec.
//Such that, the first 128 bit is the input;
//we also xor these 128 bits with the key, which can act like a seed value.
//And the rest up of the 12 32bit data blocks are filled
//according to the md5 spec, in order to pad our data to 512 bits.
//block 0-3: input xor with key
//block 4: 0x80000000. This correponds to append 1 bit to block 0-4.
//block 5-13: 0. This corresponds to appending zeros up to 448 bit.
//block 14-15: 0x0000000000000080. This correspond to the bit length of the input (128 bit), as a 64bit
//litten endian.
void setupInput(in uvec4 input, in unsigned int key, inout unsigned int data[16])
{
	data[0] = input.x^key; data[1] = input.y^key; data[2] = input.z^key; data[3] = input.w^key; //xor base with key
	data[4] = 0x80000000u;
	data[5] = 0u; data[6] = 0u; data[7] = 0u; data[8] = 0u;
	data[9]=0u; data[10]=0u; data[11]=0u; data[12]=0u; data[13]=0u;
	data[14] = 0x00000000u; data[15]=0x00000080u;
}
//initialize to the 4 hexes.
uvec4 initDigest()
{
	return uvec4(0x01234567u,0x89ABCDEFu,0xFEDCBA98u,0x76543210u);
}
//F compression functions
//(b & c) | ((not b) & d)
unsigned int F0_15(in uvec3 tD)
{
	return (tD.x & tD.y) | ((~tD.x) & tD.z);
}
//(d & b) | ((not d) & c)
unsigned int F16_31(in uvec3 tD)
{
	return (tD.z & tD.x) | ((~tD.z) & tD.y);
}
//b ^ c ^ d
unsigned int F32_47(in uvec3 tD)
{
	return tD.x ^ tD.y ^ tD.z;
}
//c ^ (b | (~d))
unsigned int F48_63(in uvec3 tD)
{
	return tD.y ^ (tD.x | (~tD.z));
}

//return input/(2^32) //2^32 - 1.0 + 1.0
vec4 convertToR0_R1(in uvec4 input)
{

	return output;
}

uvec4 whiteNoise(in uvec4 input,in unsigned int key)
{
	unsigned int data[16];
	setupInput(input,key,data);
	uvec4 rot0_15 = uvec4(7u,12u,17u,22u);
	uvec4 rot16_31 = uvec4(5u,9u,14u,20u);
	uvec4 rot32_47 = uvec4(4u,11u,16u,23u);
	uvec4 rot48_63 = uvec4(6u,10u,15u,21u);

	uvec4 digest = initDigest();
	uvec4 tD;
	uvec4 fTmp;
	unsigned int i = 0u;
	unsigned int idx;
	unsigned int r;
	unsigned int trig; const unsigned int MAXFT = 4294967295; //2^32-1
	//What follows is the unrolled loop from 0 through 63
	//0
	tD = digest;
	unsigned int temp;
	for(;i<16u;i++)
	{
		fTmp = F0_15(tD.yzw);
		idx = i;
		r = rot0_15.x;
		rot0_15 = rot0_15.yzwx;
		trig = truncate(abs(sin(float(i+1)))*float(MAXFT));
		tD.x = tD.y + ((tD.x+fTmp+data[int(idx)]+trig) << r);
		tD = tD.yzwx;

		digest +=tD;
	}
	for(;i<32u;i++)
	{
		fTmp = F16_31(tD.yzw);
		idx = (5u*i + 1u) % 16u;
		r = rot16_31.x;
		rot16_31 = rot16_31.yzwx;
		trig = truncate(abs(sin(float(i+1)))*float(MAXFT));
		tD.x = tD.y + ((tD.x+fTmp+data[int(idx)]+trig) << r);
		tD = tD.yzwx;
		digest +=tD;
	}
	for(;i<48u;i++)
	{
		fTmp = F32_47(tD.yzw);
		idx = (3u*i + 5u) % 16u;
		r = rot32_47.x;
		rot32_47 = rot32_47.yzwx;
		trig = truncate(abs(sin(float(i+1)))*float(MAXFT));
		tD.x = tD.y + ((tD.x+fTmp+data[int(idx)]+trig) << r);
		tD = tD.yzwx;
		digest +=tD;
	}
	for(;i<64u;i++)
	{
		fTmp = F48_63(tD.yzw);
		idx = (7u*i) % 16u;
		r = rot48_63.x;
		rot48_63 = rot48_63.yzwx;
		trig = truncate(abs(sin(float(i+1)))*float(MAXFT));
		tD.x = tD.y + ((tD.x+fTmp+data[int(idx)]+trig) << r);
		tD = tD.yzwx;
		digest +=tD;
	}

	return digest;
}

My MD5GPU problems/bugs, oh noes…

Posted in programming, project zombie, thoughts with tags , on October 5, 2008 by bey0ndy0nder

Okay, i need to actually run my code first before pasting it all over the intertubes…

I found some problem with it. First, the way the pseudo code in the paper is doing the rotating a,b,c,d values does not reflect the MD5 spec as shown on the WIKI page. Perhaps I’m wrong, but I tried to work this out on paper… The paper has something like this:

x.a = x.b + (a + blah < < blah)
x = x.bcda

whereas in the wiki it goes like this:

temp = d.
d = c
c = b
b = b + (a + blah << blah)
a = temp

Which would give you different values… Unless the paper and the wiki code are equivalent, and I’m not seeing it…

Anyway, I will try to rework this in and see what happens.

Also I was setting the digest wrong at each clunk of the loop.

I feel stupid. I broke my own rules, never to unroll a loop until I made sure the damn loop worked first.

ARGHGHH!!!

0000
I’m not so sure, the dude did say it was an optimization… And when you think about sure, why would it matter?

What I was able to get is a what appears to be white noise. It’s just that when I’m trying to convert it to float, using bit packing, it screws up.

More on the whys of md5

Posted in programming, project zombie, thoughts with tags , , on October 4, 2008 by bey0ndy0nder

Edited: I do not claim the stuff below is right. It’s just my thoughts, and in no way shape or form is attempting to provide a concrete claim of understanding .

Yeah, if you think about it. The compression functions of md5 scrambles the bit in an independent manner. Of course, the probability of a bit changing (through bitwise ops) is not dependent on any other bits, thus it is independent. So given a sample space S, where A and B comes from, P(A | B) = P(A), so P(AB) = P(A)P(B). Of course! They are binary bits! This makes sense. So with the way it is rotating it creates an ‘avalanche effect,’ so to minimized the chance for collision of similar digests (specially with this P(A)P(B) combinatoric thing).

After rotating through this space, the probability of collisions between two similar digests is very small. Just examining the output, since we know it’s can have 2^128 events in this sample space S, and since they are independent, the P(A) where A is in S = {2^128 elements} is very very very small.

But we also know now that MD5 has collision attack problems. Why? Just glancing at the wiki page it has to do with local collisions.. Hmm interesting.

This works in mind mind. I don’t have to convince you!!!

MD5GPU algorithm implemented (source code)

Posted in glsl, gpu, programming, project zombie, source code, thoughts with tags , , , , , on October 3, 2008 by bey0ndy0nder

So I started implementing the MD5GPU algorithm. It’s pretty straightforward. Matter of fact, I will be brief. (Note, I have not tested the code below)

From the original MD5 algo., we need to break the input into 512 bit chunks. So, we need to first pad the input to a length a, and then add 64 bit to it in order to be able to break our input into 512 bit chunks, such that:

a congurent to 448 mod 512. So we keep on appending 0 to our digest (after we appended a 1 first) so that (a – 448) mod 512 = 0. Since, (note ~ is the equivalence relation)

a ~ b mod n => (a-b) = cn, => (a-b)/n = c, where c is in Z (integers), iff (a-b) mod n = 0. (Maybe in a future post I will talk about how this relates to group theory.)

But for our problem, since the message length is a constant 128 bit, then we know from the get-go that a is 448.

Okay, that’s not all that interesting, since I’m just describing the algorithm. I may post some more on this tomorrow. I’m heading out for the night.

The interesting part is WHY does this thing work? Why does it produce results that passes all the DIEHARD tests? My intitutive understanding of this (but I’m not sure. Never studied crypto) this is due to the combinatoric explosion nature of working with 512 bit chunks. The compression functions are such that a change in ONE single input bit results in change of each output bit with a probability of 1/2. So, it’s like we are ’scrambling’ the input in this 512 bit combinatoric ’space’, which is a huge space… (I’m really sorry if the above bit totally pisses you off due to all the hand waving due to ignorant understanding)

Not sure tho. That’s just my intuition…

What do you think?