Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Getting Voxeldata to shader

Discussion in 'Shaders' started by b4nj0, Jan 11, 2015.

  1. b4nj0

    b4nj0

    Joined:
    Jan 11, 2015
    Posts:
    39
    Hey unity community.

    So im very new to shader programming and face a huge proplem at the moment.

    Im working on a Voxelengine (like so many others here) and i got a problem with texturing.

    Im using triplanar texturing to texture my Mesh (the mesh is generated by marching cubes)
    because i have no UV coordinates.

    So im able to use 3 textures for 1 chunk(1 for up + bottom , 1 for left+right, 1 for front+back, but i want to be able to texture atlas through different textures depending of which voxel type is set at this world position coordinat.

    i managed to implement the tiling texture atlas for my square textures inside my triplanar shader.

    But i have no idea how to get the actual voxel type into my shader, so i can switch texture for each of my squares.

    this is my shader so far(mostly based on a tutorial i found on the net):

    Code (CSharp):
    1. Shader "TriplanarTutorial/Triplanar_Final"
    2. {
    3.     Properties
    4.     {
    5.         _DiffuseMap ("Diffuse Map ", 2D)  = "white" {}
    6.         _DiffuseMap2 ("Diffuse Map ", 2D)  = "white" {}
    7.         _DiffuseMap3 ("Diffuse Map ", 2D)  = "white" {}
    8.         _TextureScale ("Texture Scale",float) = 1
    9.         _TextureOffset ("Texture Offset",float) = 1
    10.         _TriplanarBlendSharpness ("Blend Sharpness",float) = 1
    11.     }
    12.     SubShader
    13.     {
    14.         Tags { "RenderType"="Opaque" }
    15.         LOD 200
    16.  
    17.         CGPROGRAM
    18.         #pragma target 3.0
    19.         #pragma surface surf Lambert
    20.  
    21.         sampler2D _DiffuseMap;
    22.         sampler2D _DiffuseMap2;
    23.         sampler2D _DiffuseMap3;
    24.         float _TextureScale;
    25.         float _TriplanarBlendSharpness;
    26.         float _TextureOffset;
    27.        
    28.  
    29.         struct Input
    30.         {
    31.             float3 worldPos;
    32.             float3 worldNormal;
    33.  
    34.         };
    35.    
    36.  
    37.        
    38.         void surf (Input IN, inout SurfaceOutput o)    
    39.         {
    40.            
    41.             float2 offset1 = float2(0.0f,0.5f);
    42.             float2 offset2 = float2(0.5f,0.5f);
    43.             // Find our UVs for each axis based on world position of the fragment.
    44.             half3 VoxelPos = IN.worldPos;
    45.             half2 yUV = IN.worldPos.xz / _TextureScale;
    46.             half2 xUV = IN.worldPos.zy / _TextureScale;
    47.             half2 zUV = IN.worldPos.xy / _TextureScale;
    48.            
    49.             // Now do texture samples from our diffuse map with each of the 3 UV set's we've just made.
    50.             half3 yDiff1 = tex2D (_DiffuseMap, frac(yUV ) * 0.25f + offset1);
    51.             half3 xDiff = tex2D (_DiffuseMap2, frac(xUV) * 0.25f  + offset2);
    52.             half3 zDiff = tex2D (_DiffuseMap3,  frac(zUV) * 0.25f + offset2);
    53.             // Get the absolute value of the world normal.
    54.             // Put the blend weights to the power of BlendSharpness, the higher the value,
    55.             // the sharper the transition between the planar maps will be.
    56.             half3 blendWeights = pow (abs(IN.worldNormal), _TriplanarBlendSharpness);
    57.             // Divide our blend mask by the sum of it's components, this will make x+y+z=1
    58.             blendWeights = blendWeights / (blendWeights.x + blendWeights.y + blendWeights.z);
    59.             // Finally, blend together all three samples based on the blend mask.
    60.             o.Albedo = xDiff * blendWeights.x + yDiff * blendWeights.y + zDiff * blendWeights.z;
    61.  
    62.         }
    63.        
    64.         ENDCG
    65.     }
    66. }
    What i want:

    float2 offset1 = float2(0.0f,0.5f);
    float2 offset2 = float2(0.5f,0.5f);

    these hard coded offsetsts should be choosen by the actual Voxeltype at the actual World position

    What i tryed:

    - i tryed Using a 3d texture filled with colors representing the actual voxeltype for each position but when i try to acces this information i always end up with all pitch black (even if i try to just render the actual color in the 3D texture). I guess im just to stupid to use 3D textures and i cant find alot of ppl using it out there in the net aswell

    - i tryed writing the actual texture offsets to get the atlas texture for every verteci in to the uv coords, because i have no use for them inside my shader (but no mather what i wrote into the uv maps, i just got a (0,0) vector out of it)


    i hope someone here can help me figuring out how to get this working, or what would be the right way to get this working
     
  2. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    I'm assuming that the goal of this voxel engine is not to render life like images, but it has sort of one meter sized blocks as some game might have.

    I do wonder whether it is needed to switch things in the shader. When it comes to performance, the reduction of state switches is very important, which is why you want to reduce draw calls. On the other side, switching the shader is the most expensive state switch and you won't really need to do that. And Unity can do the batching for you.

    So, if we are talking about lets 100-300 different voxel textures, I don't think it's required to switch them in the shader. I would recommend not to use three textures for one voxel, but instead use a single one that is simply mapped using the UV coordinates. That will greatly simplify the shader. Then let Unity batch the voxels with the same texture. And in some way make sure you disable the voxels that are 100% blocked by surrounding (opaque) voxels. I think this last step is the key to proper performance.

    I see you are already using a marching cubes algorithm to generate just the visible part of the voxels. Then a 3D texture sounds logical. Or just a 2D texture with 3D information it. Lets say you use a 256x256 texture. Then you could store a 16 voxel wide, 16 voxel deep and 256 voxel high cluster. Just make sure you get your coordinates correct and disable filtering on the texture. The true coordinate of the voxel lies in the center of the voxel, not the outside. A combination of the uv coordinates and the normal could help you find the center of the voxel.

    Code (csharp):
    1.  
    2. float3 position = 0.5 + floor(pos_world - 0.5 * normalize(normal_world)); // Should be the position in the center of the voxel
    3. float3 position_uv = position / 256.0; // Divide to texture pixels
    4. position_uv.x = position_uv.x + 16.0 * position_uv.z; // Combine x and z into x
    5. float2 voxel_type = tex2D(type_sampler, position_uv).rg; // Make sure filtering is off
    6. voxel_type *= 255.0; // Scale from 0-1 to 0-255
    7. float2 offset = voxel_type / 8.0; // Assuming a 8 by 8 atlas
    8.  
     
  3. b4nj0

    b4nj0

    Joined:
    Jan 11, 2015
    Posts:
    39
    thanks for your reply.

    the problem with the marching cubes algorithm is that it doesnt give me UV data. thats why im using triplanar texturing.
    As far as i researched there is no good way of getting UV data from marching cubes algorithm.

    If i had UV coordinates i would just create one texture atlas with all my textures and all problems where gone :-/

    but i somehow solved the problem in a very hacky way by putting the voxel data for the neightboor voxels inside the uv1 uv2 and vertex color vectors, because my shader wont use them.

    its sad that there seems to be no way to add custom vertex attribute arrays to a mesh in unity


    Actualy my Voxel data set is already reduced to chunks in sice of 32x32x32 and every chunk builds a mesh. So a 3d texture would fit very good but i had big troubles getting the data out of my 3Dtexture (i tryed to fill a 3dtex 32x32x32 all with 0.5f colors and tryed to get this values inside my shader, but all i got was 0.0f colors...) maybe ill get it work with your cod example and can forget about my workaround(even if it works^^)
     
  4. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    I have little experience with 3D textures, but I think the reason to use them is to have filtering in all 3 directions. Since you don't want filtering anyway in this case, you might as well just pack things in a 2D texture. 32x32x32 can be projected to a 1024x32 2D texture in a similar way.

    Support for 3D textures has existed a while, but rendering to a 3D texture seems a bit more recent. Since you're not using 3D filtering, it's just safer to go for old school 2D textures for storage.
     
  5. b4nj0

    b4nj0

    Joined:
    Jan 11, 2015
    Posts:
    39
    yeah thats right. thanks alot your where a big help. ill try to get this working with 2D texture. ill just read the 3d voxel data line by line so theyre still somehow sorted.

    just to make sure: if my pixel has coords 512,512 in real image coords, then the 2dtex coords inside the shader will be
    (1.0f / atlas width) * 512 ?

    should i better do the maths and build vector/matrix calculations instead of using normal floats? as far as i know the gpu should be able to calculate vectors paralel instead of number by number. is that alot of speed boost or not realy worth it? easpecaly when i map 3D vecs to 2D vecs
     
  6. b4nj0

    b4nj0

    Joined:
    Jan 11, 2015
    Posts:
    39
    So i managed to get everything right in place. Thanks again for all your help and suggestions jvo3dc.

    Here is a small test screenshot to show the results



    in the voxel data set every voxel with y coords lower then 16 (32x32x32 voxel set) was set to gras
    So i just need to add some blending between the textures and add a little bit more tex tolerance in my atlas to get rid of the white seams at the texture borders and it should be fine for now ^^
    i realy love unity, so sad they limit all the real fun stuff in free version ^^
     
    bloomingdedalus and jason-fisher like this.
  7. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    That's quite a nice start. Well done.
     
  8. FuzzyQuills

    FuzzyQuills

    Joined:
    Jun 8, 2013
    Posts:
    2,871
  9. jason-fisher

    jason-fisher

    Joined:
    Mar 19, 2014
    Posts:
    133
    Any chance you could share your updated shader? Would love to play around with it..
     
  10. b4nj0

    b4nj0

    Joined:
    Jan 11, 2015
    Posts:
    39
    sadly this shader has changed alot now and is also a big mess, mainly because i noticed marchign cubes wont get me where i want to be and had to start from scretch using DC algorithm

    So the shader code i have here basicaly doesnt work anymore , not for the old code and not for the new...
    I'm happy to tell you any details if you have specific questions, and i can also post the shader code if you still want (i think the old code that works is somehow inside the comments, because i mostly commented it out instead of deleting)
    but i cant help you fixing that code again because i havent touched it along time and its such a mess
     
  11. bloomingdedalus

    bloomingdedalus

    Joined:
    Aug 13, 2012
    Posts:
    139
    I thought those seams were caused by UV bleeding as well - they're not: http://www.gamedev.net/topic/624663-unity-or-notproblem-with-repetition-in-texture-atlas/

    They're caused by fragment derivative discontinuity because of differing UV coords contained in a single fragment causing the fragment pass to think that it's rendering a really far away fragment and choosing a very small mipmap level: http://forum.unity3d.com/attachments/tex2d-_lod-jpg.38585/

    Here's an example of the same artifact from my 2d terrain - marching cubes is beyond my programming patience - kudos to you: http://i.imgur.com/DP5vraQ.jpg

    You're going to need to hand calculate the derivatives in each fragment pass and sample your textures via:

    tex2D(_Texture, i.uv.xy, derivativeX, derivativeY);
     
    jason-fisher likes this.
  12. b4nj0

    b4nj0

    Joined:
    Jan 11, 2015
    Posts:
    39

    I was not using mipmaps at this stage. The problem was realy that i used the nativ implementation and therefore i got floating point precission errors at the edges. I solved this by adding some repetetion borders around every texture in my atlas.
    i think its a very stupid misstake noone does outthere anymore, but i havent read alot about texture atlas and all this, i just implemented what came into my mind.
     
  13. bloomingdedalus

    bloomingdedalus

    Joined:
    Aug 13, 2012
    Posts:
    139
    You can turn on mipmaps and have borders and those artifacts are gone? If you turn them back on, they'll probably come back.
     
  14. b4nj0

    b4nj0

    Joined:
    Jan 11, 2015
    Posts:
    39
    i dont know, i dont even know where to turn mipmaps on or off. Im not that experienced in unity.
    i only know that i had this artifacts and that they where gone as soon as i added some tolerance border around every texture in the atlas ^^
     
  15. bloomingdedalus

    bloomingdedalus

    Joined:
    Aug 13, 2012
    Posts:
    139
    Huh... and they're not there really far away either? Those just didn't look like the bi-linear bleeding you get from UV bleed.
     
  16. b4nj0

    b4nj0

    Joined:
    Jan 11, 2015
    Posts:
    39
    the max dimension i could tested was about 2024x2024x2024, but my borders where 25% of the original tex size kinda waste of space