Search Unity

  1. We've closed the job boards. If you're looking for work, or looking to hire check out Unity Connect. You can see more information here.
    Dismiss Notice
  2. Unity 2017.3 has arrived! Read about it here.
    Dismiss Notice
  3. Unity’s Demo Team is excited to share their upcoming interactive demo, Book of the Dead. See more information here.
    Dismiss Notice
  4. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice

bumpmap (grayscale) to normalmap (rgb)

Discussion in 'Scripting' started by metervara, Jul 23, 2007.

  1. metervara

    metervara

    Joined:
    Jun 15, 2006
    Posts:
    203
    I'm generating some grayscale textures with code and need to convert them to normal maps (in a script) for use with bumped shaders, has anyone tried that? I'd like to do it in the Update function so it needs to be pretty fast. The grayscale step does not need to be stored as a texture if that's slower than built in arrays i guess.

    /P
     
  2. Omar Rojo

    Omar Rojo

    Joined:
    Jan 4, 2007
    Posts:
    494
    MMmmm depending on the texture resolution, i dont know, i want some thoughts about this too..

    .ORG
     
  3. Aras

    Aras

    Graphics Plumber Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,526
    A good normal map filter is called "Sobel filter", it basically takes derivatives in both directions with a 3x3 filter and you get the normal out of that.

    A simple way is: for each pixel, take difference in heights horizontally and vertically. Imagine them as vectors, i.e. horizontal to next pixel is (1,0,deltaHeightHorizontal) and vertical to next pixel is (0,1,deltaHeightVertical). These vectors lie on the "plane of the surface" at that pixel. Now doing a cross product of those will give the normal.

    Finally, encode the normal so that each component -1.0 = 0, 0.0 = 128, +1.0 = 255.

    All this can be done on the GPU of course, by sampling the heightmap and outputting the normal map into the render texture.
     
    AshfahanKhan likes this.
  4. metervara

    metervara

    Joined:
    Jun 15, 2006
    Posts:
    203
    Thanks.

    I'll see what i can dig up on "Sobel Filter".

    My first attemt was kind of successful. Just checking grayscale difference of neighboring pixels and generating red+green values for the color from that. Problem is the 'bumpiness' isn't very high so the effect is not very visible. (Left: generated, Right: Converted by Unity)

    [​IMG]

    Code (csharp):
    1.  
    2. for(int y=1;y<_source.width-1;y++){
    3.    for(int x=1;x<_source.height-1;x++){
    4.       float xLeft = _source.GetPixel(x-1,y).grayscale;
    5.       float xRight = _source.GetPixel(x+1,y).grayscale;
    6.       float yUp = _source.GetPixel(x,y-1).grayscale;
    7.       float yDown = _source.GetPixel(x,y+1).grayscale;
    8.       float xDelta = ((xLeft-xRight)+1)*.5f;
    9.       float yDelta = ((yUp-yDown)+1)*.5f;
    10.       _destination.SetPixel(x,y,new Color(xDelta,yDelta,1f,1f));
    11.    }
    12. }
    13.  
    I'll treat them as vectors instead like you propose and see if I can't do it on the GPU instead :)

    /P
     
  5. metervara

    metervara

    Joined:
    Jun 15, 2006
    Posts:
    203
    Trying to get this to work in a fragment program.

    At the moment I just want to output the normal color to see how it works, but all I get is the "normal map blue" (0.5,0.5,1). I have a alpha heightmap in _MainTex, 128x128px, and the fragment program looks like this:

    Code (csharp):
    1.  
    2. half4 frag (v2f i) : COLOR {
    3.  
    4.    float val = tex2D(_MainTex, i.uv).a;
    5.    float valX = tex2D(_MainTex, i.uv + float2(1 / 128, 0)).a;
    6.    float valY = tex2D(_MainTex, i.uv + float2(0, 1 / 128)).a;
    7.    float3 h = float3(1,0,val - valX);
    8.    float3 v = float3(0,1,val - valY);
    9.    float3 norm = cross(h,v);
    10.            
    11.    return half4(norm.x*0.5+0.5,norm.y*0.5+0.5,norm.z*0.5+0.5,1);
    12. }
    I'm not sure what goes on with the cross product so maybe that's where it goes wrong?

    /P
     
  6. Aras

    Aras

    Graphics Plumber Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,526
    First guess: 1/128 is integer division, so the result is zero. Try 1.0/128.0 perhaps?
     
  7. AaronC

    AaronC

    Joined:
    Mar 6, 2006
    Posts:
    3,494
    Hi, //digs up old thread

    Metervara did you figure out why the normal map generated in your first example was so subtle? I've experimented with a javascript version here and is working well, but as you posted with your example above its a very subtle effect.

    Did you find a way to amplify the effect at all? I hope you dont mind me sampling a bit of your code.

    Cheers
    AaronC

    Code (csharp):
    1. var bumpTexture : Texture2D;
    2. var bumpSource : Texture2D;
    3. var x: float;
    4. var y: float;
    5. var xLeft: float;
    6. var xRight: float;
    7. var yUp: float;
    8. var yDown: float;
    9. var yDelta: float;
    10. var xDelta: float;
    11. function Start () {
    12.    
    13.     bumpSource=gameObject.renderer.material.GetTexture("_BumpMap");
    14.     bumpTexture = new Texture2D (bumpSource.width, bumpSource.height, TextureFormat.ARGB32, false);
    15.    
    16.    
    17.     for (y=0; y < bumpTexture.height; y++) {
    18.     for (x=0; x < bumpTexture.width; x++) {
    19.    
    20.              xLeft = bumpSource.GetPixel(x-1,y).grayscale;
    21.              xRight = bumpSource.GetPixel(x+1,y).grayscale;
    22.              yUp = bumpSource.GetPixel(x,y-1).grayscale;
    23.              yDown = bumpSource.GetPixel(x,y+1).grayscale;
    24.              xDelta = ((xLeft-xRight)+1)*0.5;
    25.              yDelta = ((yUp-yDown)+1)*0.5;
    26.              
    27.         bumpTexture.SetPixel(x,y,new Color(xDelta,yDelta,1.0,1.0));
    28.          
    29.     }
    30.    
    31.    
    32.    }
    33.    
    34.   bumpTexture.Apply();
    35.   gameObject.renderer.material.SetTexture("_BumpMap",bumpTexture);
    36.  
    37. }
     

    Attached Files:

  8. b4cksp4ce

    b4cksp4ce

    Joined:
    Apr 13, 2011
    Posts:
    113
    Sorry to bump old post but I found a solution to enhance this effect and get the result of the "create normal from grayscale" in UNITY importer.

    First I added an S-curve on the detals to control more the sharpness of the normal map created in the function. I find it good when the distortion varies between 10 and 20 !

    Code (CSharp):
    1. n.SetPixel(x,y,new Color( Mathf.Clamp01(sCurve(xDelta, distortion)) , Mathf.Clamp01(sCurve(yDelta, distortion)),1f,1.0f));
    Code (csharp):
    1.  
    2. public static float sCurve(float x, float distortion){
    3.         return 1f / ( 1f + Mathf.Exp(-Mathf.Lerp(5f,15f,distortion)*(x-0.5f)));
    4.     }
    5.  
    And then I pass the resulting texture in this function :

    Code (csharp):
    1.  
    2.  
    3. public static Texture2D toNormalMap(Texture2D t){
    4.  
    5.         Texture2D n = new Texture2D(t.width,t.height,TextureFormat.ARGB32,true);
    6.         Color oldColor = new Color();
    7.         Color newColor = new Color();
    8.      
    9.         for (int x=0; x<t.width; x++){
    10.             for (int y=0; y<t.height; y++){
    11.              
    12.                 oldColor = t.GetPixel(x,y);
    13.                 newColor.r = oldColor.g;
    14.                 newColor.b = oldColor.g;
    15.                 newColor.g = oldColor.g;
    16.                 newColor.a = oldColor.r;
    17.                 n.SetPixel(x,y, newColor);
    18.             }
    19.         }
    20.      
    21.         n.Apply();
    22.      
    23.         return n;
    24.     }
    25.  
    26.