Jump to content
  • Advertisement
Sign in to follow this  
RobMaddison

Compressed Normals into Vertex Shader

This topic is 3569 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all In order to save memory, I am compressing my terrain normals into two seperate bytes. I lose a bit of accuracy doing it this way, but it should be okay. My question, is how do I get those two bytes into my vertex shader? I can't see any way of importing BYTEs into the vertex shader, is the lowest common denominator a FLOAT type? If so, that surely means that the lowest type you can have in a vertex buffer is a FLOAT which kind of defeats the object of my compression slightly. Is there a way to import BYTEs into a vertex shader, or if not, is there a way to import a 4 byte float and then carry out bitwise ops on it? Or, can anyone think of a better way to represent normals for lighting a 4096x4096 terrain in the shader? (without using lightmaps). Thanks in advance

Share this post


Link to post
Share on other sites
Advertisement
(I'm not a D3D expert, so treat the following as tips to start with.)

Looking into the DeclarationType (of D3D9), I see
Ubyte4 -> [v0,v1,v2,v3]
which costs 4 bytes of storage, and expands its (4) values as they are. Since you need only 2 values, you may use
Ushort2 -> [v0,v1,0,1]
instead, which costs the same but gives you a higher precision.

Or you go with one of the normalized types, namely
Short2N -> [v0/32767,v1/32767,0,1]
or even the designated compressed 3D vector type
Dec3N -> [v0/511,v1/511,v2/511,1]

All of them cost 4 bytes per normal, but are presumbly better suited than a Float.

Share this post


Link to post
Share on other sites
It does get a little fiddly when dealing with D3D9 vertex shaders as, from memory, the ubyte4 and possibly short/half precisions aren't universally supported by all hardware. Check the cardcaps.xls file for more details and, obviously, check in your code [smile]

Compression systems don't really become 'natural' until you're at D3D10 and above due to the lack of bitwise ops in D3D8 and D3D9 so doing thing like shortening down to a XY and Sign at half precision can allow you to reconstruct the full normal later on...


hth
Jack

Share this post


Link to post
Share on other sites
Thanks for the replies.

I was hoping to avoid DX10 at the moment as my ultimate goal is for this to run on the XBox360.

I'll take a closer look at the declaration types.

Cheers

Share this post


Link to post
Share on other sites
If you want extra XY precision and will just recompute Z in the shader, then a standard short2 should work The un-normalized short type was a required type since D3D8 and shader 1.1 (They're not listed in the decltypes caps bits, so they are required types).

Share this post


Link to post
Share on other sites
I have to admit to finding it hard to visualise how you can "recompute the z component". How can you know what the z is without any clue as to what it was pre-compression?

Apologies for my ignorance, this is my first intro into geometry compression techniques.

Share this post


Link to post
Share on other sites
The cross product of two axis will give the third - so normalize(cross(nx,ny)) = nz with one huge caveat - you don't know what sign the resultant axis is. I forget the finer mathematical details, but you have to be careful of this... It's a neat trick for tangent space normals as you can know that its +Z, but for regular normal vectors it could be + or - so sometimes storing extra information elsewhere (e.g. in an unused diffuse channel) can help...

hth
Jack

Share this post


Link to post
Share on other sites
Thanks, Jack... I was just reading another site and how it works suddenly dawned on me! I raced to post my new understanding but you'd posted first :)

My compression is on tangent normals, so I don't really need to worry about the sign of the z (or in my case, y).

I've also been reading about DXT5_NM compression which looks promising.

Rob

Share this post


Link to post
Share on other sites
For your normal, you know the length should be 1. Tell the shader that normal is a float3. Normal.z will be 0, as short2 is expanded to x,y,0,1. The 0 makes it a float3 which we can use for dot products (I don't think there is a 2D dot product... maybe there is in later shader models).

Since the input is XY, and you probably want it in XZ, I swizzle the input during the scaling phase.

float3 xyzscaled = IN.normal.xzy / 32767.0f;
float lensq = dot(xyzscaled, xyzscaled);
xyzscaled.y = sqrt(1.0f - lensq);

While it's true that we don't know the sign of the computed axis (Y in this case). But as you said it's for terrain, we can probably safely assume the land is facing UP.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!