Search Unity

Trying to dynamically generate a normal map

Discussion in 'Shaders' started by moosefetcher, Nov 25, 2016.

  1. moosefetcher

    moosefetcher

    Joined:
    Sep 23, 2015
    Posts:
    74
    In a space exploration game I'm developing, I'm combining several textures in my planet shader, with different UV offsets, to generate 'randomised' planet surfaces.
    Now I'd like to add a normal map but, given that the surface is a result of shader calculations, I can't use pregenerated normal maps.
    My theory was, in C#, to combine the source Texture2Ds into a new Texture2D, then test each pixel to calculate the normal at that pixel depending on the brightness of its neighbours and store these values in another new Texture2D and use THAT as the normal map.
    But I wasn't even able to get that far. As soon as I include the line...
    private Texture2D _combinedTexture = new Texture2D(2048, 2048);
    ... and test it, Unity will crash saying it has run out of memory.
    So what am I doing wrong? Is a 2048 square Texture2D too big? The textures I'm loading from Assets/Resources are all 2048x2048. Am I not instantiating the Texture2D correctly? Or do I need to allocate more memory somewhere?
    Any help, much appreciated.
     
  2. McDev02

    McDev02

    Joined:
    Nov 22, 2010
    Posts:
    664
    How many textures did you generate? You don't calculate the normal maps in Update(), or?
    Without seeing code it is hard to tell where the issue lies.

    In fact doing it with C# is possible but this will take a lot of time. You should use shaders to calculate that texture using Graphics.Blit(). You will need two passes I assume. One which creates the height map and one to generate the normal map.
     
  3. moosefetcher

    moosefetcher

    Joined:
    Sep 23, 2015
    Posts:
    74
    The would-be normal-map calculation is in a custom initialisation function. The process happens just once per planet, when the StarSystem is first initialised. I added the _combinedTexture Texture2D instance to my custom StartSystem class, so that the process would use the same Texture2D instance each time, and overwrite the pixels of the previous bumpmap. (I was hoping that wouldn't affect the bumpmaps already passed to previous planets, but haven't yet been able to check, because of this running-out-of-memory problem).
    But 'Graphics.Blit' looks promising. I'll look into that, thanks.
     
  4. moosefetcher

    moosefetcher

    Joined:
    Sep 23, 2015
    Posts:
    74
    Just a couple more questions (I haven't been able to start experimenting with Graphics.Blit yet):
    Do you know if it's possible to send more than 1 source Texture to the shader in Graphics.Blit? I'm trying to generate a composite of 3 textures to get the height map, which would then need to be turned into a normal map.
    Also, since Graphics.Blit can render to texture, is it only available in the pro edition? I'm using just the personal (free) edition.
    Thanks.
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Render textures (and Graphics.Blit) are not limited to a specific edition with Unity 5; personal, plus, pro, and enterprise all have access to all features of the engine. It was limited to Pro with Unity 4.

    To pass multiple textures you need to set them on the material used by the blit prior to calling blit.
     
  6. jbooth

    jbooth

    Joined:
    Jan 6, 2014
    Posts:
    5,461
    Here's how I do it:



    Also, if you want to generate good looking normals from a height image, the process your describing is insufficient. It will create very granular normals that stairstep.
     
  7. moosefetcher

    moosefetcher

    Joined:
    Sep 23, 2015
    Posts:
    74
    Crazy! Apart from including the normal map in the main texture, I'm pretty much doing everything he describes (at around 19 minutes) including using 2 randomly chosen atmosphere textures moving at different speeds and multiplied! Oh and I'm just about to add cloud shadows.
    I was wondering if by-pixel calculated normals might look odd, but maybe I can do a third pass and average them by their neighbours. If I'm going to stick with generating a normal map dynamically, do you have any suggestions about how to do that more satisfactorily?
     
  8. jbooth

    jbooth

    Joined:
    Jan 6, 2014
    Posts:
    5,461
    Best method in realtime is using a sobel filter, but that's still pretty crappy. Offline, you can do a multi-pass analysis where you perform the same technique at multiple filter sizes and blend them together; that's basically what crazy bump, etc, do..
     
  9. moosefetcher

    moosefetcher

    Joined:
    Sep 23, 2015
    Posts:
    74
    Thanks. I don't need to do it in realtime - I only need to do it once, at initialisation. But, because each planet surface is made (effectively) randomly, I can't know what the normal maps should look like before initialisation. And thanks again for that Unite Boston vid link. Both very informative and reassuring that I'm on the right track.