Dark Bit Factory & Gravity

PROGRAMMING => Freebasic => Topic started by: ninogenio on May 05, 2013

Title: bumpmapping
Post by: ninogenio on May 05, 2013
hey folks another boring sunday == some more code  ;D

ive coded some grayscale bumpmapping. now i remember seeing this done years ago but heres my take on it.. google was a poor source for info so i had too wing it.. im not 100% its correct so would be very greatfull if those with the knowledge could give some pointers.

cheers guys. 

very slow atm just want it too work properly first.

(-edit Removed exe too keep forum clean as they were rather large check further down )
Title: Re: bumpmapping
Post by: ninogenio on May 06, 2013
think ive got it  :)

i noticed while tinkering that rgb channels of the normalmap = x y z  direction vectors for light reflections i was thinking of them in color space not vector. so a dot product later and here we go. if you guys see anything wrong with it please let me know so i can mess around and learn. cheers..


(-Edit removed exe too keep forum clean as it was rather large check further down )
Title: Re: bumpmapping
Post by: hellfire on May 06, 2013
Nice one, Nino.
The pure 2d bump usually just offsets the light-texture coordinate according to the normal (that's how it's done here (http://www.youtube.com/watch?v=OEWZG7eYYMU&t=0m48s)).
The light-map was made big enough that you didn't have to care about clipping, so you don't have to recalculate it every frame.
Code: [Select]
for y= ...
  for x= ...
    col= colorMap[y][x]

    normal= normalMap[y][x]
    nx= normal shr 16 and 255
    ny= normal shr 8 and 255
           
    light= lightMap[y+ny+lightPosY][x+nx+lightPosX]

    dst[y][x]= blend(col, light)
If you really want to work in 3d, you can handle diffuse and reflection separately, though.
Title: Re: bumpmapping
Post by: ninogenio on May 06, 2013
what an elegant and cheep way of getting the effect. thanks very much hellfire..

im wanting to do specular and diffuse seprate though manly for learning. ive got the diffuse working properly it cycles through diffrent colors.. just wondering how specular works i would like it too look kind of wet like like so many modern games..

its manly the part where you calculate angle between eye and normal vector that i get lost.

ive got an idea but everytime i try and put it down it doesnt go well.

(-Edit removed exe too keep forum clean as it was rather large check further down )



Title: Re: bumpmapping
Post by: hellfire on May 06, 2013
im wanting to do specular and diffuse seprate though manly for learning.
its manly the part where you calculate angle between eye and normal vector that i get lost.
Let's say your bitmap is on a plane in 3d space, eg. from (-320,-240,0) - (320,240,0).
The camera is in the center, somewhere above, eg. at (0,0,50).
And there's a position of the light-source somewhere between the plane and the camera.
For each pixel you've got a normal vector (stored in the normal map), a view direction (vector from the camera to the pixel) and a light direction (vector from the light source to the pixel).

You've already figured out the diffuse lighting which is
  diffuse= dot3(lightdir, normal)

For the specular term you reflect (http://mathworld.wolfram.com/Reflection.html) the view direction at the normal:
   reflectdir= viewDir - normal * 2 * dot3(normal, viewDir);
(viewDir and normal must be normalized, so it might be cheaper to use blinn's half angle apprroximation instead)

Check if it's facing the light source and use some glossiness function:
   specular= dot3(reflectdir, lightdir)
   if (specular > 0.0) specular= pow(specular, 40.0)

The final color is something like
   textureColor * diffuse * lightColor + specular * specularColor
Title: Re: bumpmapping
Post by: ninogenio on May 06, 2013
wow thats awsome mate k++ i think ive cracked it and finally understand specular lighting!!

your explenation of how it all goes together into the final color helped hugely. does this look about right.


(-Edit removed exe too keep forum clean as it was rather large check further down )
Title: Re: bumpmapping
Post by: hellfire on May 06, 2013
Nice!
Looks like the pixels which are facing away from the light-source are now getting most of the specular term, though.
So I think your reflection vector is probably negated.
It's also useful to have another map (or abuse the alpha-channel of the color- or normal-map) to store a "specular level".
This way you can give less specular to the dirty parts between the stones.
Title: Re: bumpmapping
Post by: ninogenio on May 07, 2013
yeah your right Mate it wasnt right.

 i slowed down a little and really went over all you wrote and im 72.594% sure i finaly have it  :).

i changed the bump map image too clearly show the specular term working. im intrested in what you wrote about packing a specular level into the alpha channel too stop incorrect lighting going on would it be possible for you too elaborate a little further?

thanks for all the help so far. there's no way i would have gotten this far without.

(-Edit removed exe too keep forum clean as it was rather large check further down )
Title: Re: bumpmapping
Post by: Kirl on May 07, 2013
Looks great nino! Never coded bumps myself but use them extensively in my 3d work. Now I can't wait to give it a try as well, interesting thread. :)
Title: Re: bumpmapping
Post by: ninogenio on May 07, 2013
Thanks kirl!

yeah thats pretty much the same as me i use them a lot without even thinking how they work. my aim over this next while is too try and get behind lots of stuff i have seen and try and understand how they work.

glad you like the thread. maybe if you find anything on your bumpmapping quest that isnt covered you can share. that would be awsome.

in this version i have tried height mapping if im correct height mapping just makes a grey scale copy of the original image and each grey level represents a height vector. this height vector then gets normalised and mixed in with the z normals to extend each normal by heightmap factor. does that sound right i am getting a nice effect but maybe not to the extent i had hoped for.

(-Edit removed exe too keep forum clean as it was rather large check further down )
Title: Re: bumpmapping
Post by: hellfire on May 07, 2013
im 72.594% sure i finaly have it  :)
Looks pretty cool but this piece is certainly wrong because you're just using the normal vector to calculate the reflection:
Code: [Select]
SpecDot = ( Nx * -Nx + -Ny * Ny + Nz * -Nz )
Rx = (-Nx)-Nx*2.0*SpecDot
Ry = (-Ny)-Ny*2.0*SpecDot
Rz = (-Nz)-Nz*2.0*SpecDot
Title: Re: bumpmapping
Post by: ninogenio on May 07, 2013
woops..

it should be
specDot( pixellocation(-320 to 320)*Nx.... etc )
Rx = PixelLocation( -320 to 320 )-Nx*2.0*SpecDot
etc..

i was looking at some half angle stuff and got my wires extremely mixed up i hope ive got it close now. it looks much better at least.

(- Edit changed a few bits again added grey scale height mapp etc.modded exe attached )
Title: Re: bumpmapping
Post by: hellfire on May 07, 2013
im intrested in what you wrote about packing a specular level into the alpha channel too stop incorrect lighting going on would it be possible for you too elaborate a little further?
In Reply #4 I suggested a exponential function for the specular term:
Code: [Select]
if (specular > 0.0) specular= pow(specular, 40.0)The exponent is supposed to simulate the reflection of a light source.
A higher exponent creates a smaller highlight and makes the material appear more shiny (for example see here (http://udn.epicgames.com/Three/rsrc/Three/MaterialExamples/ex_shiny_specular.jpg)).
Now your surface might not be equally shiny everywhere, eg. imagine some rusty spots on a metal plane (like this (http://matrep.parastudios.de/mats/fullsize/463d74a6d4847.jpg)).

One way to do this is to have a separate (grey-scale) map to define the brightness of the specular term, like this:
Code: [Select]
if (specular > 0.0) specular= pow(specular, 40.0) * specularLevel[pixelPosition]
Another way is to store the specular exponent in the map:
Code: [Select]
if (specular > 0.0) specular= pow(specular, specularLevel[pixelPosition])
Since the exponential function is somewhat expensive, you'd probably use the first version in a software renderer and pick the pow-function with a constant exponent from a lookup table.

For the color- and normal-maps you're typically working with 32bit rgba-colors and the alpha-channel is often unused.
And as the specular-map just contains a single scalar value for each pixel, it can be stored in the alpha-component of one of the other maps to avoid another texture fetch.
Title: Re: bumpmapping
Post by: Raizor on May 07, 2013
Great stuff ninogenio :) And a ton of really insightful stuff from Hellfire too! Double win :)
K++
Title: Re: bumpmapping
Post by: ninogenio on May 08, 2013
cheers mate!  :)


@hellfire thanks for picking that up freebasic didnt have a pow func and i wasnt quite sure how that part should go ive replaced it with this SpecDot = (Exp(10*log(SpecDot)))*Height
ive added grey scale height mapping and also got rid of the lightmap as with the specular term there was no need so its running much faster ive stripped out a lot of the fudgy stuff as it was quite hard too read which resulted in me finding a few bugs.
Title: Re: bumpmapping
Post by: hellfire on May 08, 2013
i've replaced it with this Exp(10*log(SpecDot))
Since floating point numbers are represented as x*2^exp, pow2 and log2 can be approximated by fiddling with the exponent-bits within the float.
And because
Code: [Select]
pow(x, exp) = pow2(exp * log2(x))you can do a *very* rough approximation with:
Code: [Select]
   const float inv23= 1.0f / (1 << 23);
   const float bias= 126.94269504f;

   // approximate log2(x)
   int *ip= (int*)&x; // cast x to integer
   float y = ip[0];
   y= y * inv23 - bias;
   e*=y;

   // approximate 2.0^e
   int i= (1 << 23) * (e + bias);
   float *fp= (float*)&i; // cast i to float
   float result= fp[0];
Be aware that you can be off by a factor of 2 but for a shading function that usually doesn't really matter.
And if you're limiting this to positive integer exponents, it gets quite another bit tighter.
Title: Re: bumpmapping
Post by: ninogenio on May 08, 2013
thanks very much hellfire k++, some of this stuff you post is pure gold. using your pow function the specular light now bends round certain surfaces and does a bit of scattering. in my mind it behaves in a quite realistic manner now.
Title: Re: bumpmapping
Post by: ninogenio on May 08, 2013
hey again.

is this sort of specular term only possible when viewing from different angles and distances.

http://www.keithlantz.net/2011/10/tangent-space-normal-mapping-with-glsl/

the specular i have now is just a bit too overpowering but everything i try to get it more speckled and scattered just fades the whole specular term out. im even yousing the same texture and normals as the link posted.
Title: Re: bumpmapping
Post by: hellfire on May 08, 2013
If you're looking from (almost) the same angle as the light source hits the surface, diffuse and specular are on the same spot and hard to distinguish.
A typically specular-only setting is the sun going down over the ocean (example (http://briefhiatus.files.wordpress.com/2008/09/sun-over-the-ocean.jpg)) as the diffuse term gets almost zero but the light reflects towards the viewer.
Title: Re: bumpmapping
Post by: ninogenio on May 08, 2013
just had a big break through with this it just all come together! looks like glass now  :).

thanks hellfire mate!! might have a go at mapping this onto a cube and see what it looks like in 3d space with a dynamic camera proper light etc.
Title: Re: bumpmapping
Post by: Kirl on May 08, 2013
 :clap:
Title: Re: bumpmapping
Post by: ninogenio on May 09, 2013
cheers mate!  :)

right last version i promise lol.. ive made look up tables for almost everything and doing lots of precalcs also split the bump map function into two parts and running them on seperate threads. only down side is, it will run really really bad on single core cpus now.

the fps on this system shot up from around 15-20 to 62-66.. still to change all the array accesses too ptr addition and figure out how too pre calc pow. i should be able too hit the golden 70fps mark. Doing this much calcs, 307200 times a frame. im amazed at the speed so far. it just might be possible to make a little 3d demo with a few bump textures at 128*128 or something at reasonable speed.

could any one who tries this tell me there cpu and fps please.

ps to see fps run in windowed mode and look at the black box.
cheers.
Title: Re: bumpmapping
Post by: hellfire on May 09, 2013
33 fps on Intel Core2 Quad 2.8GHz.
Feels much faster than the previous versions.

And my magic crystal ball foretells twice the speed if you kick the floating point stuff out of the main loop and use only integers :)
Title: Re: bumpmapping
Post by: ninogenio on May 09, 2013
cheers helfire.

yep your right mate its bottle necked at the fpu atm.

ill confese fixed point scares me a bit. its been years since i did any. if im right you shift all ints and floats left by 8 bits, mulling the floats. add subtract multiply them together then shift right at the end again to clip off the fraction and bring everything back in range.
Title: Re: bumpmapping
Post by: hellfire on May 10, 2013
you shift all ints and floats left by 8 bits,
add subtract multiply them together
then shift right at the end again
Exactly.
Those values which contain integers anyway won't need any additional bits, though.
For the rest you usually have to check, how much precision is actually required.
The trick is to keep track of the number of fractional bits.
For example if you multiply to values with 8bits of fractional part, the result has 16bits fraction - so you need to shr8 to get back to 8bits of precision.

For the diffuse part I would quantize the vectors to 8bits (so a normalized vector ranges from -255..+255):
Code: [Select]
DiffDot = ( Nx * Lx + Ny * Ly + Nz * Lz ) shr 8;So you get an 8bit (0..255) value to shade your texture color, which fits nicely into mmx.

For the specular part you might need more bits because the exponent keeps only a small piece of the range (eg. 0.8 - 1.0), all the rest is black (below 1/255) anyway.
And the integer value makes it much easier to pick the pow-function from a table.

Another thing that makes your code slower at the moment is that you precalculated everything into double-arrays, which increases your memory bandwidth by a factor of 8.

I had a look at your code and noticed that this part:
Code: [Select]
            VdirX = (PixyX) * ReciOX
            VdirX = CamX-VdirX
            VdirY = (PixyY) * ReciOY
            VdirY = CamY-VdirY
            VdirZ = TextureZ - CamZ
           
            SpecDot = ( Nx*VDirX + Ny*VDirY + Nz*VDirZ )
           
            Rx = VDirX-Nx*2.0*SpecDot
            Ry = VDirY-Ny*2.0*SpecDot
            Rz = VDirZ-Nz*2.0*SpecDot
...is constant for each pixel and can be precalculated just as the normal map.
Title: Re: bumpmapping
Post by: ninogenio on May 10, 2013
cheers hellfire,

and thanks for looking at the code. excellent spot with the static veiwport and texture optimization, im wanting too try this dynamically though as it will be mapped onto a cube at some point so will be moving around.

well a fun night tonight i brushed off the fixed point cobwebs and after about 2 hours the whole thing is integerized with ptr addition included also.. the only part that i cant get my head around fixed pointing is the pow function. also there is loads of shifts in there now so that will slow things down a bit.

i had too use ten point shifts for precision as i was loosing too much specular term. even the diffuse part was suffering at eight.
Im Chuffed so far. at this point im up by about 5 fps at my end but im sure ive opened up loads of optimisation options now.

i just cant see them at this point too many shifts and a few rounding errors does that though  :)

oh and thanks for the quantize on the diffuse suggestion ill give that a bash next as i think it could get rid of a few calculations k+
Title: Re: bumpmapping
Post by: hellfire on May 10, 2013
This version runs at ~50fps on my machine, that's almost twice as fast as the previous one.

Quote
the only part that i cant get my head around fixed pointing is the pow function.
That's actually super easy:
If both vectors (Rx,Ry,Rz) and (Lx,Ly,Lz) are normalized to 10 bits, SpecDot is an integer in the range -1023..+1023.
Since you're only interested in positive values and (with an exponent of 40) all values <800 are zero anyway, you can look up the pow-function from a really small table.

But I noticed that your view direction vector (VDirX,VDirY,VDirZ) is not normalized (and I'm a bit surprised that it still works so well), so you have to be a bit careful with the actual numeric range of the dot-products.

And code like this:
Code: [Select]
Rx = VDirX-Nx*2*SpecDot
Ry = VDirY-Ny*2*SpecDot
Rz = VDirZ-Nz*2*SpecDot
...is predestinated for mmx, you just have to make sure that input and output fits into signed 16bit values.
If you extend your vectors to have a 4th coordinate (which just stays 0), it's much easier to load data into mmx registers.
Title: Re: bumpmapping
Post by: ninogenio on May 11, 2013
excellent thanks hellfire,

 im trying too pull specdot into the range of -1 1 for a precalced pow, normalizing Vdir and light  Vectors but there must be something off some where else because i always get around 1.4 -1.4. unless i normalize the reflection vector, light vector and Vdir then i get -1 1. just for clarity Can the light vector Be unormalized until after the diffuse angle is worked out?.

im surprised the extra sqrts dont hamper performance too much.

how would i go about making the reflection vector fit into a 16 bit number? that would mean only 4 bits of precision do you think this would be enough.

i was having a little look here..
http://www.dbfinteractive.com/forum/index.php?topic=1726.msg26106#msg26106

i see what you mean about padding 4Dvectors with w being 0, it seems like the lesser of 2 evils.
Title: Re: bumpmapping
Post by: hellfire on May 11, 2013
im trying too pull specdot into the range of -1 1 for a precalced pow, normalizing Vdir and light  Vectors but there must be something off some where else because i always get around 1.4 -1.4. unless i normalize the reflection vector, light vector and Vdir then i get -1 1.
the dot-product of two vectors v1 and v2 (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z) is in the range -1..+1 only if the length of both vectors is 1.0.
If that's not the case, the diffuse term is calculated by
Code: [Select]
dot= (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)
diffuse= dot / ( length(v1) * length(v2) )
To avoid the sqaure root associated with calculating the length, one tries to have both vectors *almost* normalized beforehand, so that length(v1) * length(v2) becomes ~1.0 and can be skipped.
Nobody will notice if the length of your vectors is off by a few percent, so a very rough approximation for 1/sqrt is totally sufficent (for floating point values the fast inverse square root (http://en.wikipedia.org/wiki/Fast_inverse_square_root) function is popular).
On the other hand, why bother with normalization if your shading function gives good results with unnormalized vectors?
All that happens is that your dot products deliver somewhat larger (or smaller) values. So if you want to use it for a lookup-table, your array must be somewhat larger...
Title: Re: bumpmapping
Post by: ninogenio on May 11, 2013
Quote
To avoid the sqaure root associated with calculating the length, one tries to have both vectors *almost* normalized beforehand, so that length(v1) * length(v2) becomes ~1.0 and can be skipped.
Nobody will notice if the length of your vectors is off by a few percent

im glad you said that as thats exactly what i was hoping for originally by reciprocally dividing each of the vectors elements by there know length. im still struggling with a lookup table for pow as it is my specdot is producing numbers in the range of 4 -4 so i thought great just multiply everything you wrote by a factor of 4 giving a lookup table of just over 800 elements. but i was wrong  :).

what i have noticed though is using the look up table even though the specular term isnt working correctly the fps has shot up too 110ish so this is going too be a big optimization i think.
Title: Re: bumpmapping
Post by: ninogenio on May 12, 2013
well after lots and lots of tinkering i think i have the whole thing completely working. on ints with pow arrayed im getting around 91 fps now. and its still decent quality.
Title: Re: bumpmapping
Post by: hellfire on May 12, 2013
Nice work, Nino!
Runs at about 85fps here.

This got me all a bit curious and I wanted to see how fast I could get it myself.
So I started from scratch with a glsl shader as it's much easier to figure out the math, check the required precisions and try different variations.
That code looks like this and runs at >5000 fps in 640x480 on my gtx560-ti:
Code: [Select]
uniform sampler2D colorTex;   // color texture
uniform sampler2D normalTex;  // normal texture
uniform vec3 lightPos;        // light position
uniform vec4 lightColor;      // light color (1.0, 0.4, 0.4, 1.0);
uniform vec4 specColor;       // specular color (1.0, 0.9, 0.9, 1.0)

varying vec2 fragPos;         // 2d pixel position (0..1, 0..1)
varying vec2 uv;              // 2d texture coordinate

void main()
{
   vec4 result= vec4(0.0, 0.0, 0.0, 0.0);

   // get color and normal from textures
   vec4 color= texture2D(colorTex, uv);
   vec3 normal= texture2D(normalTex, uv).xyz * 2.0 - 1.0;

   // make sure normal is actually normalized
   normal= normalize( normal );

   // get height from alpha channel of color map
   float height= 2.0 - color.a;

   // 3d fragment position
   vec3 pos= vec3(fragPos, height);

   // camera (0,0,0) to pixel
   vec3 viewDir= normalize(pos);

   // light to pixel
   vec3 lightDir= normalize(pos - lightPos);
   vec3 refl= normal*2.0*dot(normal, viewDir) - viewDir;

   // distance attenuation (1/dist^2) * 2.0 (disabled)
//   float dist= 2.0 / length(lightPos - pos);

   // diffuse term
   float diffuse= dot(normal, lightDir);
   if (diffuse > 0.0)
       result= color * lightColor * diffuse; // * dist;

   // specular term
   float specular= dot(refl, lightDir);
   if (specular > 0.8)
       result+= specColor * pow( specular, 20.0 );

   // store color
   gl_FragColor= result;
}

So I started to write this down in C, using 4d 16bit integer vectors to give the compiler a clue to use mmx and replaced the normalization- and pow-part with a lookup table.
That version ran at about 70fps using no multithreading. Looking at the disassembly it actually used mmx vectorization but wasn't very clever at it.
So I successively replaced all parts of the inner-loop with hand-crafted mmx blocks which got me at around 85fps and there's probably a good chance to make that a few percent faster.
Finally I added multi-core support to make it use all my cpu-cores and got at about >200fps (exe attached).
I must admit that it uses all my 4 cores to the max while your version only uses ~50%.

The c code I started from looks like this:
Code: [Select]
typedef struct
{
   short x,y,z,w;
} ShortVector4;

void drawBump2d_c(
      unsigned int *dst,          // destination buffer
      unsigned int *src,          // interleaved color/normal data per pixel
      int width,                  // width of buffer
      int height,                 // height of buffer
      ShortVector4 lightPos,      // light position
      ShortVector4 camera,        // camera position (w/2, h/2, 0, 0)
      ShortVector4 lightColor,    // light color
      ShortVector4 specColor      // specular color
)
{
   // run scanlines in parallel:
   #pragma omp parallel for
   for (int y=0; y<height; y++)
   {
      scanlineBump2d_c(
         dst + y * width,
         src + y * width * 2,
         width,
         y,
         lightPos,
         lightColor,
         specColor,
         camera
      );
   }
}


void scanlineBump2d_c(
   unsigned int* dst,
   unsigned int* src,
   int width,
   int y,
   ShortVector4 lightPos,
   ShortVector4 lightColor,
   ShortVector4 specColor,
   ShortVector4 camera)
{
   ShortVector4 pos;
   ShortVector4 norm;
   ShortVector4 viewdir;
   ShortVector4 refl;
   ShortVector4 col;
   ShortVector4 lightdir;

   pos.x= 0;
   pos.y= y;
   pos.z= 0;
   pos.w= 0;
   for (int x=0; x<width; x++)
   {
      // start with black color
      col.x= 0;
      col.y= 0;
      col.z= 0;
      col.w= 0;

      unsigned int pixelColor= src[0];
      pos.z= (pixelColor >> 24 & 255); // height stored in alpha
      // pos is the pixel's 3d coordinate

      // normal vector in 7bit fractional
      unsigned int nrm= src[1];
      norm.x= (nrm & 255) - 128;
      norm.y= (nrm >> 8 & 255) - 128;
      norm.z= (nrm >> 16 & 255) - 128;
      norm.w= (nrm >> 24 & 255) - 128;

      // light direction
      lightdir.x= pos.x - lightPos.x;
      lightdir.y= pos.y - lightPos.y;
      lightdir.z= pos.z - lightPos.z;
      lightdir.w= pos.w - lightPos.w;

      // normalize
      int t;
      short inv;
      t= lightdir.x*lightdir.x + lightdir.y*lightdir.y + lightdir.z*lightdir.z + lightdir.w*lightdir.w;
      inv= invSqrt[t>>10];

      // rescale this to end up with 16bit of fraction to match mmx' pmulhw
      lightdir.x= (lightdir.x<<3)*inv>>16;
      lightdir.y= (lightdir.y<<3)*inv>>16;
      lightdir.z= (lightdir.z<<3)*inv>>16;
      lightdir.w= (lightdir.w<<3)*inv>>16;

      // calculate diffuse term - result: -16383..+16383
      int diffuse= norm.x*lightdir.x + norm.y*lightdir.y + norm.z*lightdir.z + norm.w*lightdir.w;
      if (diffuse > 0)
      {
         diffuse= diffuse>>5;

         col.x+= (pixelColor >>  0 & 255) * lightColor.x * diffuse >> 16;
         col.y+= (pixelColor >>  8 & 255) * lightColor.y * diffuse >> 16;
         col.z+= (pixelColor >> 16 & 255) * lightColor.z * diffuse >> 16;
         col.w+= (pixelColor >> 24 & 255) * lightColor.w * diffuse >> 16;
      }


      // view direction vector: camera -> pixel
      viewdir.x= pos.x - camera.x;
      viewdir.y= pos.y - camera.y;
      viewdir.z= pos.z - camera.z;
      viewdir.w= pos.w - camera.w;

      // normalize
      t= viewdir.x*viewdir.x + viewdir.y*viewdir.y + viewdir.z*viewdir.z + viewdir.w*viewdir.w;
      inv= invSqrt[t>>10];

      viewdir.x= (viewdir.x<<3)*inv>>16;
      viewdir.y= (viewdir.y<<3)*inv>>16;
      viewdir.z= (viewdir.z<<3)*inv>>16;
      viewdir.w= (viewdir.w<<3)*inv>>16;

      // reflection vector
      t= (norm.x*viewdir.x + norm.y*viewdir.y + norm.z*viewdir.z + norm.w*viewdir.w);
      refl.x= ((norm.x<<3) * t >> 16) - viewdir.x;
      refl.y= ((norm.y<<3) * t >> 16) - viewdir.y;
      refl.z= ((norm.z<<3) * t >> 16) - viewdir.z;
      refl.w= ((norm.w<<3) * t >> 16) - viewdir.w;

      // specular term. result: -16383..+16383
      int specular= refl.x*lightdir.x + refl.y*lightdir.y + refl.z*lightdir.z + refl.w*lightdir.w;

      if (specular > 12288) // 16383 * 0.75 -> pow(0.75, 2.0) < 1/255
      {
         specular= specular >> 7;

         unsigned int s= powTable[specular] & 255;

         col.x+= (s * specColor.x >> 8);
         col.y+= (s * specColor.y >> 8);
         col.z+= (s * specColor.z >> 8);
         col.w+= (s * specColor.w >> 8);
      }

      // saturate
      if (col.x>255) col.x=255;
      if (col.y>255) col.y=255;
      if (col.z>255) col.z=255;
      if (col.w>255) col.w=255;

      *dst= (col.z<<16)|(col.y<<8)|col.x;

      dst++;
      src+=2;
      pos.x++;
   }
}

The two tables look like this:
Code: [Select]
   int invSqrt[65536];             // way too much
   unsigned int powTable[2048];

   for (int i=0; i<2048; i++)
   {
      double p= pow(i/128.0, 20.0)*2.0;
      if (p<0.0) p=0.0;
      if (p>1.0) p=1.0;
      int v= p*255.0;
      powTable[i]= (v<<24)|(v<<16)|(v<<8)|v;
   }

   for (int i=0; i<65536; i++)
   {
      double t= 32767.0 * 32.0 / sqrt(i*1024.0);
      if (t > 0x7fff) t= 0x7fff;
      invSqrt[i]= (int)t;
   }

As I don't want to kill the suspense I'm not going to add the mmx code for now ;)
Title: Re: bumpmapping
Post by: ninogenio on May 12, 2013
amazing :clap: best sunday morning ever  :D!!!

so you managed to get up too 85fps in your c code using only 1 core, then when you spread the work load across all cores you got >200, im getting 336fps at my end core i7 3.2  8).

i honestly never imagined this could be made that quick. and your specular and diffuse terms look lovely!!
what value do you hold in light w? is the norm.w Packed with your grey level height map, and integrated into the diffuse term dot product, as your diffuse term is much more prominent than mine.

just noticed you were able too make a look up table for normalization that is amazing. it was annoying me that mine wasn't correct but i couldn't afford the extra sqrt's. i would never have thought of your solution.

ill take a little while to digest all your code, already i can see lots of areas i can improve mine k++
Title: Re: bumpmapping
Post by: hellfire on May 12, 2013
Quote
what value do you hold in light w?
i'm on my mobile, so just a short reply:
all w components are zero and are just there to make the compiler use a single vector instruction on the whole 4 values.
otherwise it tries to mask the 4th component away and ends up slower...
i just use alpha of the colormap to store 255-grey.
i also renormalize the normalmap after loading because it didn't really fit.
have to look up the z components of the light and camera when i'm back home.
Title: Re: bumpmapping
Post by: ninogenio on May 12, 2013
thanks mate, no problem.

i just noticed my threading isnt working properly your example makes my cpu run at 100% full wack. i just tried to splice mine into 4 sections and 4 core it. my fps went too 136 but with only 36% cpu usage im guessing its because im not doing my multi threading on a scanline by scanline basis as you do, and as a result for whatever reason a lot of the time my code makes the cpu sit idle.
Title: Re: bumpmapping
Post by: hellfire on May 12, 2013
The rest of the parameters are:
Code: [Select]
w= width of bitmap (640)
h= height of bitmap (480)
time= time in seconds

lightPos.xyzw= ( (sin(time*1.6)+1)*w/2, (cos(time*1.8)+1)*h/2, 50, 0)
cameraPos.xyzw= (w/2, h/2, 0, 0)
lightColor.xyzw= (127, 127, 255, 0)  // blue, green, red, alpha
specColor.xyzw= (255, 192, 192, 0)   // blue, green, red, alpha

And the two textures are modified the following way:
Code: [Select]
// "height" into alpha channel:
color[i].w= 255 - ((color[i].x*30 + color[i].y*150 + color[i].z*76) >> 8);

// renormalize
int t= 32767 / sqrt( normal[i].x*normal[i].x + normal[i].y*normal[i].y + normal[i].z*normal[i].z);
normal[i].x= -(normal[i].x * t >> 8);  // negated!
normal[i].y=  (normal[i].y * t >> 8);
normal[i].z=  (normal[i].z * t >> 8);
the color/normal-buffers got interleaved into a separate buffer, so i can read both with a single movq and save an adress register.

As all vectors are normalized to signed 8bits (-128..+127) and the normalization-precision is very rough, you can see some quantization noise in the shading (which is actually good, if it wasn't there you'd see color banding) which could be removed by using more bits of the available range - but it's probably getting a bit trickier with mmx then...

Quote
im not doing my multi threading on a scanline by scanline basis as you do
open-mp doesn't schedule one job per scanline. instead it distributes the whole number of loop iterations (in this case 0..479) over the number of available cores.
So scanlines 0..119 are processed by core0, 120..239 by core1, 240..359 by core2, 360..479 by core3.
This gives minimal scheduling overhead but if one core gets interrupted by another task and thus finishes later, all other cores must wait until the last one finished.
Title: Re: bumpmapping
Post by: ninogenio on May 13, 2013
Excellent thanks mate ive taken a step back too the float version and redone all the base calcs watching there floor and roof values too make sure they stay in the correct ranges and it works nicely. the specular and diffuse terms look and behave the same as yours.

next step is too change the fixed point version too behave the same then ill change the size of my shifts too suit mmx.

its a pitty freebasic doesn't have a version of open mp. the standard freebasic threading commands don't seem too use more than two of my cores. so the cpu usage only ever gets as high as 36%. ill have too go a bit deeper into that.

Quote
As all vectors are normalized to signed 8bits (-128..+127) and the normalization-precision is very rough, you can see some quantization noise in the shading (which is actually good, if it wasn't there you'd see color banding) which could be removed by using more bits of the available range - but it's probably getting a bit trickier with mmx then...

glad you wrote this as i just couldnt produce the nice speckled effect ( in the float version ) around the shading and it was driving me nuts.  :)
Title: Re: bumpmapping
Post by: hellfire on May 13, 2013
ive taken a step back too the float version
next step is too change the fixed point version too behave the same
Another option is to keep everything in floating point and take the sse route...
Title: Re: bumpmapping
Post by: ninogenio on May 14, 2013
i think ill probably go the fixed point mmx way mostly for memory bandwidth.

well i have too admit i didnt fully understand what was going on with your all your shifts etc so have spent the whole night tearing all my code down too a number by number basis and watching all the values in real time. i've learned stuff like fixed point reciprocal divides etc.. its really been a while  :).

ive fixed my range normalizing issues i had, tided it all up a bit and now its 4 core, i dont think it will use all the cpu though still haven't got round too getting into that. im still using 10 point shifts atm. my next job is too bring everthing into mmx range and try a bit of asm out.

150fps on my end at the moment.
Title: Re: bumpmapping
Post by: hellfire on May 14, 2013
well i have too admit i didnt fully understand what was going on with your all your shifts

Well, what's probably not so straight is the normalization part - which would usually look like this:
Code: [Select]
int t= lightdir.x*lightdir.x + lightdir.y*lightdir.y + lightdir.z*lightdir.z + lightdir.w*lightdir.w;

// factor to normalize to -128..+128 with 8bit of fractional part
int invSqrt= (128*256) / sqrt(t);

lightdir.x= lightdir.x * invSqrt >> 8;
lightdir.y= lightdir.y * invSqrt >> 8;
lightdir.z= lightdir.z * invSqrt >> 8;
lightdir.w= lightdir.w * invSqrt >> 8;

But when multiplying two 16bit values with mmx you can only keep the upper or the lower word, like this:
Code: [Select]
pmullw: lightdir= lightdir * invSqrt;
pmulhw: lightdir= lightdir * invSqrt >> 16;

With pmullw you get an overflow when the vector exceed 0..255 (which it does),
with pmulhw the result gets 256x smaller than it's supposed to.
So I distributed the factor of 256 to both values, *8 to the vector and *32 to the invSqrt, ending up at:
Code: [Select]
invSqrt= (128*256*32) / sqrt(t);
viewdir= (viewdir<<3) * invSqrt >> 16;
Now the invSqrt doesn't fit into 16bit anymore for small values of t.
But that's not a big problem because it's impossible to normalize very small vector anyway (because 1/sqrt(0) = infinity).
So I just clamp the table-values at 32767 and accept that vectors shorter than 0.25 (32 at 7bits fractional) will be too short.

I choose a factor of 32 because at that point the lookup-table for invSqrt was much larger and I was looking up invSqrt[t>>5], so only the value of invSqrt[0] got clamped (which must be clamped anyway).
Now that it's looking up invSqrt[t>>10], it makes more sense to put the whole *256 within the invSqrt-table and remove the shift...
Title: Re: bumpmapping
Post by: ninogenio on May 14, 2013
yeah i can see how they all work now, cheers Hellfire!

i just wanted too make sure i knew 100% what was going on first. now that everything is pretty much ready for mmx. Can mmx be used for almost everthing in the loop reflection vector invsqrt r,g,b saturation Etc. or is it better too use mmx for the color modulation part and saturation only?
Title: Re: bumpmapping
Post by: hellfire on May 14, 2013
Well, the main trick is to make your vectors fit into 4x short, so you can use mmx for vector and color-processing.
I'd suggest to convert your code to asm in very very small steps.
Write all intermediate values back into variables so you can check them.

Start with the simple stuff, for example:
Code: [Select]
ShortVector4 col;
/*
col.x= 0;
col.y= 0;
col.z= 0;
col.w= 0;
*/
_asm {
   pxor mm7,mm7
   movq [col], mm7
};

And
Code: [Select]
// calc light direction
/*
lightDir.x= pos.x - lightPos.x;
lightDir.y= pos.y - lightPos.y;
lightDir.z= pos.z - lightPos.z;
lightDir.w= pos.w - lightPos.w;
*/
_asm {
  movq      mm3, [pos]
  movq      mm4, [lightPos]
  psubw     mm3, mm4
  movq      [lightDir],mm3
};

Once you've converted the whole innerloop, you can remove most of the loading/storing from and to variables.
That's the point where your code suddenly gets much faster.
Title: Re: bumpmapping
Post by: ninogenio on May 14, 2013
cheers hellfire will do mate ive already started   ;).
ive 4dvectorized all my variables and shifted in the correct ranges also started converting all the simple parts too asm might take me a while thought..

when you say all the load and store instruction is where the code slows down you probably mean some thing like this i guess  :)

Code: [Select]
_BUMP1@4:
push ebp
mov ebp, esp
sub esp, 140
push ebx
.Lt_00DF:
mov dword ptr [ebp-4], 0
mov dword ptr [ebp-8], 0
mov dword ptr [ebp-12], 0
mov dword ptr [ebp-16], 0
mov dword ptr [ebp-20], 0
mov dword ptr [ebp-24], 0
mov dword ptr [ebp-28], 0
mov dword ptr [ebp-32], 0
mov dword ptr [ebp-36], 0
mov dword ptr [ebp-40], 0
mov dword ptr [ebp-44], 0
mov dword ptr [ebp-48], 0
mov dword ptr [ebp-52], 0
mov dword ptr [ebp-56], 0
mov dword ptr [ebp-60], 0
mov dword ptr [ebp-64], 0
mov dword ptr [ebp-68], 0
mov dword ptr [ebp-72], 0
mov dword ptr [ebp-76], 0
mov dword ptr [ebp-80], 0
mov dword ptr [ebp-84], 0
mov dword ptr [ebp-88], 0
mov dword ptr [ebp-92], 0
mov dword ptr [ebp-96], 0
mov dword ptr [ebp-100], 0
mov dword ptr [ebp-104], 0
mov dword ptr [ebp-108], 0
mov dword ptr [ebp-112], 0
mov dword ptr [ebp-116], 0
mov dword ptr [ebp-120], 0
mov dword ptr [ebp-124], 0
mov dword ptr [ebp-128], 0
mov dword ptr [ebp-132], 0
mov dword ptr [ebp-136], 0
fld qword ptr [_Lt_00D9]
fmul qword ptr [_DIFFUSER]
fistp dword ptr [ebp-96]
fld qword ptr [_Lt_00D9]
fmul qword ptr [_DIFFUSEG]
fistp dword ptr [ebp-100]
fld qword ptr [_Lt_00D9]
fmul qword ptr [_DIFFUSEB]
fistp dword ptr [ebp-104]
mov dword ptr [ebp-92], 0
mov eax, dword ptr [ebp-92]
lea ebx, [_BUFFER+eax*4]
mov dword ptr [ebp-4], ebx
mov ebx, dword ptr [ebp-92]
lea eax, [_NORMX+ebx*4]
mov dword ptr [ebp-32], eax
mov eax, dword ptr [ebp-92]
lea ebx, [_NORMY+eax*4]
mov dword ptr [ebp-36], ebx
mov ebx, dword ptr [ebp-92]
lea eax, [_NORMZ+ebx*4]
mov dword ptr [ebp-40], eax
mov eax, dword ptr [ebp-92]
lea ebx, [_BUMPR+eax*4]
mov dword ptr [ebp-8], ebx
mov ebx, dword ptr [ebp-92]
lea eax, [_BUMPG+ebx*4]
mov dword ptr [ebp-12], eax
mov eax, dword ptr [ebp-92]
lea ebx, [_BUMPB+eax*4]
mov dword ptr [ebp-16], ebx
mov ebx, dword ptr [ebp-92]
lea eax, [_RNORM+ebx*4]
mov dword ptr [ebp-20], eax
mov eax, dword ptr [ebp-92]
lea ebx, [_GNORM+eax*4]
mov dword ptr [ebp-24], ebx
mov ebx, dword ptr [ebp-92]
lea eax, [_BNORM+ebx*4]
mov dword ptr [ebp-28], eax
mov eax, dword ptr [ebp-92]
lea ebx, [_HEIGHTMAP+eax*4]
mov dword ptr [ebp-44], ebx
mov dword ptr [ebp-140], 0
mov dword ptr [ebp-52], 0
.Lt_00E4:
mov dword ptr [ebp-48], 0
.Lt_00E8:
mov ebx, dword ptr [ebp-32]
mov eax, dword ptr [ebx]
mov dword ptr [ebp-68], eax
mov eax, dword ptr [ebp-36]
mov ebx, dword ptr [eax]
mov dword ptr [ebp-72], ebx
mov ebx, dword ptr [ebp-40]
mov eax, dword ptr [ebx]
mov dword ptr [ebp-76], eax
mov eax, dword ptr [ebp-44]
mov ebx, dword ptr [eax]
add ebx, -20480
sar ebx, 10
mov dword ptr [ebp-120], ebx
fild dword ptr [ebp-48]
fsub qword ptr [_LIGHTX]
fistp dword ptr [ebp-112]
fild dword ptr [ebp-52]
fsub qword ptr [_LIGHTY]
fistp dword ptr [ebp-116]
mov ebx, dword ptr [ebp-112]
imul ebx, dword ptr [ebp-112]
mov eax, dword ptr [ebp-116]
imul eax, dword ptr [ebp-116]
add ebx, eax
mov eax, dword ptr [ebp-120]
imul eax, dword ptr [ebp-120]
add ebx, eax
mov dword ptr [ebp-140], ebx
mov ebx, dword ptr [ebp-140]
sar ebx, 10
mov eax, dword ptr [_INVSQRT+ebx*4]
mov dword ptr [ebp-140], eax

and this was just a tiny snippet of the freebasic generated code from the bump function..

also do you think its possible i might have mutual exchange issues with my threading and that might make the cpu sit around a lot of the time each frame and do nothing. would passing the the same segment of memory too different threads even though they were working on different cells cause any binding issues. i have all my external arrays globally created atm and just let my bump1,2,3,4 pull them in and process different locations in them. there is actually a chance that my threads try and read variables such as lightx,y,z and address the same invsqrt and powtable indexes at the same time.
Title: Re: bumpmapping
Post by: hellfire on May 15, 2013
do you think its possible i might have mutual exchange issues with my threading and that might make the cpu sit around a lot of the time each frame and do nothing.
would passing the the same segment of memory too different threads even though they were working on different cells cause any binding issues.
I guess you're not using any kind of mutexing, so there's no reason why any of the threads should wait.
And as every thread works on his own block of data, the threads cannot interfere.
However, every thread needs his own set of temporary variables - when working on the same global variable from different threads, the result is of course totally unpredictable.

But there can still be really awkward situations like this one:
Code: [Select]
// once loaded from memory, both variables will be kept in the same cache line
int dataA;
int dataB;

core0:
dataA= 1234;    // write value back into cache

core1:
int value= dataB;
// cache line of dataB got invalided because core0 modified
// memory which refers to the same cache line!
// must request core0 to write its' cache line back into memory
// wait until memory is available
// read back whole cache line
// wait until data is available in the cache
Title: Re: bumpmapping
Post by: ninogenio on May 15, 2013
ahh i see,

and does your code wait on the threads finishing before updating the frame or does it just unlock them and let them run, the way mines is if i remove the threadwaits i get similar behavior too yours, 100% usage with about 280 fps, yours gets 340fps but that would be down too mmx.

sorry for all the questions im new too all this kind of stuff and its great too be able too ask people with as much knowledge as your self questions about it.
Title: Re: bumpmapping
Post by: hellfire on May 15, 2013
does your code wait on the threads finishing before updating the frame or does it just unlock them and let them run
I don't really know how to determine that a frame has finished without waiting on the threads to finish.
At the moment I don't really do anything at all - all the multi-threading is handled by the open-mp macro automatically.
It behaves just like running without multiple threads, so the loop finishes as soon as all threads are done.
As open-mp provides a thread-id for every iteration of the parallel loop, I figured it processes continuous blocks with each thread.
That's probably not the best possible solution for all scenarios but good enough to not think about a better way for now.
If each block requires very different processing time, it probably makes sense to work on a smaller granularity to minimize thread sync time.
I haven't checked the processing time for each thread yet, but I guess it should be quite constant as the amount of source data is equal and accessed strictly linear.
Title: Re: bumpmapping
Post by: ninogenio on May 15, 2013
i think i might be onto something.. ive noticed that the call too ptcupdate( @Buffer(0)) drops my cpu usage by about 50% across all cores. if i comment it out i obviously get no final render but the cpu usage jumps too about 93% with all cores almost maxed out evenly. im going too give setting up a bit of gdi blitting a go rather than ptc, too see if that helps if not ill just ditch freebasic and jump back into my visual studio and use open mp as i cant afford for my cpu to be so heavily underutilized.
Title: Re: bumpmapping
Post by: ninogenio on May 16, 2013
i've fixed the problem!!  :)

my initial hunch was correct there is something in ptc ext++ that doesn't behave properly in multi core systems, it acts a bit like a sleep command so no matter how much or little threads you create cpu usage doesnt go very high.

ive coded two versions in this zip using gdi. the thread stuff works great now. one of the versions is an eight thread as my i7 has 8 effective cores ( 4 hardware 4 virtual ) this version gets 270 280fps with 90%-93% usage. and a 4 core one which gets 200 210fps with 50%  -  60%.

if anyone tries these could they please tell me usage and fps please it would come in most handy too know that it works as hoped.

-Removed too keep Forum tidy see below-
Title: Re: bumpmapping
Post by: hellfire on May 16, 2013
Since I don't have "virtual cores" it doesn't seem to make much of a difference:
BumpMap4: around 125fps and 75-85% cpu usage.
BumpMap8: around 130fps and 70-90% cpu usage.

I think there's simply no thread running while ptc transfers the framebuffer over to the window (and it's probably not very fast).
How about running them in parralel with a double buffer?
Code: [Select]
Start threads rendering to buffer0
display buffer1
wait for threads
swap pointers of buffer0 / buffer1
Title: Re: bumpmapping
Post by: ninogenio on May 16, 2013
thanks very much hellfire.

great little offscreen rendering idea. i tried it with ptc but it doesnt make a difference. i think there is a little more too the ptc issue though as when i start task manager too check usage with the ptc version the fps jumps a lot like 70fps and stays there. so i think maybe one of the windows calls inside ptc are causing me issues. it might even be a problem that only affects my setup.

im fine using gdi. i was just being lazy using ptc. your little double buffer idea gave a nice little step up with it. about 5% usage so im now sitting at roughly 95% which is fine for me i wouldnt like too run 100% all the time anyway.

i tried too multi thread portions of the screen with setdibitstodevice but windows didnt like that at all :).. the principal works fine a can render the full screen with 4 segment calls but the minute threading gets involved the app instantly gives up..
Title: Re: bumpmapping
Post by: ninogenio on May 16, 2013
for any one that might find it usefull ive coded a little sort of engine for this tonight it dynamically allocates and deallocates cores you can see the number of threads in use in the text box too reduce cores press Z and add cores X key. ive also made the bump maps hold all there own data so they can now easily be loaded and handed all the 3d info for use too texture map objects.. thats my next thing.

this now does 2 bump map images. that can be fliped back and forth with the 1 and 2 keys.

so Z and X allocates deallocates threads And..
1 and 2 flips back and forth through textures..

thanks for all your help hellfire its been a really great little project ive enjoyed it loads!! and im going too keep chipping away at the mmx stuff in my free time.
Title: Re: bumpmapping
Post by: ninogenio on December 15, 2013
Quote
Nice one, Nino.
The pure 2d bump usually just offsets the light-texture coordinate according to the normal (that's how it's done here).
The light-map was made big enough that you didn't have to care about clipping, so you don't have to recalculate it every frame.
Code: [Select]

for y= ...
  for x= ...
    col= colorMap[y]


    normal= normalMap[y]

    nx= normal shr 16 and 255
    ny= normal shr 8 and 255
           
    light= lightMap[y+ny+lightPosY][x+nx+lightPosX]

    dst[y]
  • = blend(col, light)


If you really want to work in 3d, you can handle diffuse and reflection separately, though.

^ on first page...

i was just doing my end of year back ups and organizing all my things, while doing so i was just randomly running things then while backing up this i remembered your comment hellfire about how simply this can be done.

so after a quick half hour i codded this up and it works and looks pretty neat much better than expected actually. it is so cheap i got away with 4 lights white blue red and green and still blazes along on my machine... well impressed cheers mate  ;)
Title: Re: bumpmapping
Post by: Hotshot on December 17, 2013
That is coolest Bump mapping I have seen :)

Title: Re: bumpmapping
Post by: ninogenio on December 18, 2013
thanks hotshot, this hole topic was an excellent learning project that i hope others can enjoy as much as i have.  :cheers: