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

Voxel Texture array with shadows

Discussion in 'World Building' started by shatteredeyeinc, Jan 10, 2019.

  1. shatteredeyeinc

    shatteredeyeinc

    Joined:
    Jan 10, 2019
    Posts:
    5
    I spend way to long trying to figure this out below is how i got it to work

    when creating your mesh use
    Code (CSharp):
    1.  
    2.         mesh.SetUVs(0, Uvs );
    uvs are a vector 3 with the z axis set to the image index
    Code (CSharp):
    1.   this.GetComponent<MeshRenderer>().sharedMaterial.SetTexture("_Tex2DArray", texture);
    use this to set the texture array in the shader


    creating the texture map
    Code (CSharp):
    1.   var textureMap = new Texture2DArray(200, 200, textureMaps.Count, TextureFormat.RGB24, false, false);
    2.             textureMap.filterMode = FilterMode.Point;
    3.             textureMap.wrapMode = TextureWrapMode.Repeat;
    4.             for (int i = 0; i < textureMaps.Count; i++)
    5.             {
    6.                 textureMaps.ElementAt(i).Value.Texture2DLocation = i;
    7.  
    8.                 textureMap.SetPixels(textureMaps.ElementAt(i).Value.Texture2D.GetPixels(), i);
    9.  
    10.             }
    11.  
    12.             textureMap.Apply();
    13.             this.chunkTexure = textureMap;

    and finally the shader




    Code (CSharp):
    1. Shader "Lit/Diffuse With Shadows"
    2. {
    3.     Properties
    4.     {
    5.         _Tex2DArray("Tex2DArray (RGB)", 2DArray) = "white" {}
    6.     }
    7.         SubShader
    8.     {
    9.         Pass
    10.         {
    11.             Tags {"LightMode" = "ForwardBase"}
    12.             CGPROGRAM
    13.             #pragma vertex vert
    14.             #pragma fragment frag
    15.             #include "UnityCG.cginc"
    16.             #include "Lighting.cginc"
    17.  
    18.         // compile shader into multiple variants, with and without shadows
    19.         // (we don't care about any lightmaps yet, so skip these variants)
    20.         #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight
    21.         // shadow helper functions and macros
    22.         #include "AutoLight.cginc"
    23.  
    24.         struct v2f
    25.         {
    26.             float3 uv : TEXCOORD0;
    27.             SHADOW_COORDS(1) // put shadows data into TEXCOORD1
    28.             fixed3 diff : COLOR0;
    29.             fixed3 ambient : COLOR1;
    30.             float4 pos : SV_POSITION;
    31.         };
    32.         v2f vert(appdata_base v)
    33.         {
    34.             v2f o;
    35.             o.pos = UnityObjectToClipPos(v.vertex);
    36.             o.uv = v.texcoord;
    37.             half3 worldNormal = UnityObjectToWorldNormal(v.normal);
    38.             half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
    39.             o.diff = nl * _LightColor0.rgb;
    40.             o.ambient = ShadeSH9(half4(worldNormal,1));
    41.             // compute shadows data
    42.             TRANSFER_SHADOW(o)
    43.             return o;
    44.         }
    45.  
    46.         sampler2D _MainTex;
    47.         UNITY_DECLARE_TEX2DARRAY(_Tex2DArray);
    48.         fixed4 frag(v2f i) : SV_Target
    49.         {
    50.             fixed4 col = UNITY_SAMPLE_TEX2DARRAY(_Tex2DArray, i.uv);
    51.         // compute shadow attenuation (1.0 = fully lit, 0.0 = fully shadowed)
    52.         fixed shadow = SHADOW_ATTENUATION(i);
    53.         // darken light's illumination with shadow, keep ambient intact
    54.         fixed3 lighting = i.diff * shadow + i.ambient;
    55.         col.rgb *= lighting;
    56.         return col;
    57.     }
    58.     ENDCG
    59. }
    60.  
    61. // shadow casting support
    62. UsePass "Legacy Shaders/VertexLit/SHADOWCASTER"
    63.     }
    64. }



     
    PROE_ likes this.
  2. shatteredeyeinc

    shatteredeyeinc

    Joined:
    Jan 10, 2019
    Posts:
    5
  3. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,840
    You've done some really interesting things there! The shadows are one thing — and they do look nice — but I'm more intrigued by what you've done with the 3D UV's. I've made a voxel engine in Unity before, but I put all the voxel textures into one big texture map, and then used traditional 2D UVs to select the tiny part of that map for each square.

    Do I understand correctly that you're instead using a texture array, with a different element for each square texture? And you select these with the Z value of your 3D UV coordinates, so I guess the X and Y values are always in the range 0-1?
     
  4. shatteredeyeinc

    shatteredeyeinc

    Joined:
    Jan 10, 2019
    Posts:
    5
    yes exactly 00 01 10 11 for the uvs texture atlas has some drawbacks on the size of the atlas and the filtering mode with this even a small texture of 30px by 30px can have better filtering
     
  5. shatteredeyeinc

    shatteredeyeinc

    Joined:
    Jan 10, 2019
    Posts:
    5
    yes this is the code for the texture loader
    Code (CSharp):
    1.   public void GenerateChunkTextureMap()
    2.         {
    3.             List<TextureMap> maps = new List<TextureMap>();
    4.            foreach(var item in _directories)
    5.             {
    6.                 foreach (var image in System.IO.Directory.GetFiles(item.DirectoryName).Where(o => o.EndsWith(".png")))
    7.                 {
    8.                     var data = System.IO.File.ReadAllBytes(image);
    9.                     var texture = new Texture2D(0, 0);
    10.                     texture.LoadImage(data);
    11.                     if (texture.width != 32 || texture.height != 32)
    12.                         continue;
    13.  
    14.                     var map = new TextureMap();
    15.                     map.Prefix = item.Prefix;
    16.                     map.Texture = texture;
    17.                     map.FileLocation = image;
    18.                     map.Name = Regex.Match(image, @"(?<=\\).+(?=(\.png))").Value;
    19.                     maps.Add(map);
    20.  
    21.                 }
    22.             }
    23.        
    24.             var sq = (int) Math.Ceiling(Math.Sqrt(maps.Count));
    25.        
    26.             var textureMap = new Texture2D(sq*32,sq*32);
    27.             // System.IO.File.WriteAllBytes("test.png", textureMap.EncodeToPNG());
    28.             var index = 0;
    29.  
    30.             for (int x= 0;x<sq;x++)
    31.                 for(int y= 0; y < sq; y++)
    32.                 {if (index>maps.Count-1)
    33.                         continue;
    34.                     var image = maps[index];
    35.                     image.Xpos = x;
    36.                     image.Ypos = y;
    37.  
    38.                  
    39.                     textureMap.SetPixels(x  *32, y  * 32, 32, 32, image.Texture.GetPixels());
    40.  
    41.                  
    42.                     index++;
    43.  
    44.                 }
    45.             this._textureMaps = new Dictionary<string, TextureMap>();
    46.             foreach(var item in maps)
    47.             {
    48.                 this._textureMaps.Add($"{item.Prefix}_{item.Name}", item);
    49.             }
    50.             this.chunkTexture = textureMap;
    51.         }
    52.  
    53.     }

    and the code for the entire chunk class

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using V3 = UnityEngine.Vector3;
    5. using V2 = UnityEngine.Vector2;
    6. using UnityEngine;
    7.  
    8. public class Chunk
    9. {
    10.     private static readonly int chunkWidth = 12;
    11.     private static readonly int chunkHeight = 12;
    12.     private Mesh mesh { get; set; }
    13.     public byte[,,] voxels = new byte[chunkWidth, chunkHeight, chunkWidth];
    14.     public Mesh GetMesh() => mesh;
    15.     private static System.Random random = new System.Random();
    16.     public void GenerateChunk()
    17.     {
    18.         for(int x=0;x<chunkWidth;x++)
    19.             for (int y = 0; y < chunkHeight; y++)
    20.                 for (int z = 0; z < chunkWidth; z++)
    21.                 {
    22.                    
    23.                     voxels[x, y, z] = random.Next(2)==1? (byte)1 : (byte)0;
    24.                 }
    25.     }
    26.     public void RedrawChunk()
    27.     {
    28.         var mesh = new MultiVoxMesh();
    29.  
    30.         for (int x = 0; x < chunkWidth; x++)
    31.             for (int y = 0; y < chunkHeight; y++)
    32.                 for (int z = 0; z < chunkWidth; z++)
    33.                 {
    34.                     if (voxels[x, y, z] == 0)
    35.                         continue;
    36.                  
    37.  
    38.                     if (x == 0 || voxels[x - 1, y, z] == 0)
    39.                         mesh.Inject(FaceDefinitions.LeftFace, x, y, z);
    40.  
    41.                     if (y == 0 || voxels[x, y - 1, z] == 0)
    42.                         mesh.Inject(FaceDefinitions.BottomFace, x, y, z);
    43.  
    44.                     if (z == 0 || voxels[x, y, z - 1] == 0)
    45.                         mesh.Inject(FaceDefinitions.FrontFace, x, y, z);
    46.  
    47.  
    48.                     if(y==chunkHeight-1||voxels[x,y+1,z]==0)
    49.                          mesh.Inject(FaceDefinitions.TopFace, x, y, z);
    50.  
    51.                     if (x == chunkWidth - 1 || voxels[x + 1, y, z] == 0)
    52.                         mesh.Inject(FaceDefinitions.RightFace, x, y, z);
    53.  
    54.                     if (z == chunkWidth - 1 || voxels[x, y , z + 1] == 0)
    55.                         mesh.Inject(FaceDefinitions.BackFace, x, y, z);
    56.  
    57.  
    58.  
    59.                
    60.                  
    61.  
    62.  
    63.                 }
    64.         this.mesh = mesh.ToMesh();
    65.   }
    66. }
    67. public class CubeFace
    68. {
    69.     public V3[] Verticies = { };
    70.     public int[] Triangles = { };
    71.     public V2[] Uvs = { };
    72. }
    73. public class FaceDefinitions
    74. {
    75.     private static readonly int[] normal = { 1, 3, 0, 2, 0, 3 };
    76.     private static readonly int[] inverted = {1,0,3,2,3,0 };
    77.     public static CubeFace BottomFace = new CubeFace()
    78.     {
    79.         Verticies = new V3[]{
    80.             new V3(0,0,0),//0
    81.             new V3(1,0,0),//1
    82.             new V3(0,0,1),//2
    83.             new V3(1,0,1)//3
    84.  
    85.         },
    86.         Triangles = normal,
    87.         Uvs = new V2[] {
    88.             new V2(0,0),
    89.             new V2(0,1),
    90.             new V2(1,0),
    91.             new V2(1,1)
    92.  
    93.         }
    94.  
    95.  
    96.  
    97.     };
    98.  
    99.     public static CubeFace TopFace = new CubeFace()
    100.     {
    101.         Verticies = new V3[]{
    102.             new V3(0,1,0),//0
    103.             new V3(1,1,0),//1
    104.             new V3(0,1,1),//2
    105.             new V3(1,1,1)//3
    106.  
    107.         },
    108.         Triangles = inverted,
    109.         Uvs = new V2[] {
    110.             new V2(0,0),
    111.             new V2(0,1),
    112.             new V2(1,0),
    113.             new V2(1,1)
    114.  
    115.         }
    116.  
    117.  
    118.  
    119.     };
    120.     public static CubeFace BackFace = new CubeFace()
    121.     {
    122.         Verticies = new V3[]{
    123.             new V3(0,0,1),//0
    124.             new V3(1,0,1),//1
    125.              new V3(0,1,1),//0
    126.             new V3(1,1,1)
    127.          
    128.  
    129.         },
    130.         Triangles = normal,
    131.         Uvs = new V2[] {
    132.             new V2(0,0),
    133.             new V2(0,1),
    134.             new V2(1,0),
    135.             new V2(1,1)
    136.  
    137.         }
    138.  
    139.  
    140.  
    141.     };
    142.     public static CubeFace FrontFace = new CubeFace()
    143.     {
    144.         Verticies = new V3[]{
    145.             new V3(0,0,0),//0
    146.             new V3(1,0,0),//1
    147.              new V3(0,1,0),//0
    148.             new V3(1,1,0)
    149.  
    150.  
    151.         },
    152.         Triangles = inverted,
    153.         Uvs = new V2[] {
    154.             new V2(0,0),
    155.             new V2(0,1),
    156.             new V2(1,0),
    157.             new V2(1,1)
    158.  
    159.         }
    160.  
    161.  
    162.  
    163.     };
    164.     public static CubeFace RightFace = new CubeFace()
    165.     {
    166.         Verticies = new V3[]{
    167.             new V3(1,0,0),
    168.              new V3(1,1,0),
    169.               new V3(1,0,1),
    170.              new V3(1,1,1)
    171.         },
    172.         Triangles = normal,
    173.         Uvs = new V2[] {//11 01 10 00
    174.             new V2(1,1),
    175.             new V2(0,1),
    176. new V2(1,0),
    177.  
    178. new V2(0,0)
    179.  
    180.  
    181.         }
    182.     };
    183.  
    184.     public static CubeFace LeftFace = new CubeFace()
    185.     {
    186.         Verticies = new V3[]{
    187.             new V3(0,0,0),
    188.              new V3(0,1,0),
    189.               new V3(0,0,1),
    190.              new V3(0,1,1)
    191.         },
    192.         Triangles = inverted,
    193.         Uvs = new V2[] {//11 01 10 00
    194.             new V2(1,1),
    195.             new V2(0,1),
    196. new V2(1,0),
    197.  
    198. new V2(0,0)
    199.  
    200.  
    201.  
    202.         }
    203.     };
    204. }
    205.  
    206. public class MultiVoxMesh
    207. {
    208.     private List<V3> verticies { get; set; }
    209.     private List<int> triangles { get; set; }
    210.     private List<V2> uvs { get; set; }
    211.     public MultiVoxMesh()
    212.     {
    213.         verticies = new List<V3>();
    214.         triangles = new List<int>();
    215.         uvs = new List<V2>();
    216.  
    217.     }
    218.     public void InjectTriangles(int[] triangles)
    219.     {
    220.         int currentIndex = verticies.Count;
    221.  
    222.         this.triangles.AddRange(triangles.Select(o => o + currentIndex));
    223.      
    224.     }
    225.     public void InjectUvs(V2[] uvs) => this.uvs.AddRange(uvs);
    226.     public void InjectVectors(V3[] face,int x , int y, int z)=>this.verticies.AddRange(face.Select(o => new V3( o.x + x - 0.5f, o.y + y - 0.5f,o.z + z - 0.5f)  ));
    227.     public void Inject(CubeFace face, int x, int y, int z)
    228.     {
    229.         this.InjectTriangles(face.Triangles);
    230.         this.InjectVectors(face.Verticies,x,y,z);
    231.         this.InjectUvs(face.Uvs);
    232.  
    233.     }
    234.     public Mesh ToMesh()
    235.     {
    236.         Mesh mesh = new Mesh();
    237.         mesh.vertices = this.verticies.ToArray();
    238.         mesh.triangles = this.triangles.ToArray();
    239.         mesh.uv = this.uvs.ToArray();
    240.         return mesh;
    241.     }
    242.  
    243. }
    244.  
     
    PROE_ likes this.
  6. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,840
    That's really neat.

    As far as I know, there are currently no good, well-maintained voxel assets in the Asset Store. You should consider cleaning & documenting this and making it available there!
     
  7. shatteredeyeinc

    shatteredeyeinc

    Joined:
    Jan 10, 2019
    Posts:
    5
    yeah thats not a bad idea
     
    JoeStrout likes this.
  8. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,840
    Please notify me if you do. Even though I have my own voxel code sitting around somewhere, I like your approach better!

    I'm currently working on a Scratch-like environment in VR. One of the features I'm thinking about adding someday is a voxel chunk. You'd define a bunch of block types in terms of the 6 face textures they show, and then have script pieces to set/get the block type at any xyz position in the chunk. Pretty straightforward, but I think scripters would really get into it.