Search Unity

After playing minecraft...

Discussion in 'General Discussion' started by jc_lvngstn, Oct 8, 2010.

Thread Status:
Not open for further replies.
  1. Cherno

    Cherno

    Joined:
    Apr 7, 2013
    Posts:
    515
    Care to elaborate a bit? :) How does the vertex light allocation work? (I assume it combines vertex lighting with normal lighting?)
     
  2. Captain Morgan

    Captain Morgan

    Joined:
    May 12, 2013
    Posts:
    144
    i sent you the code
     
  3. Cherno

    Cherno

    Joined:
    Apr 7, 2013
    Posts:
    515
    Thank you, I saw that. I was hoping that you could write about the inner workings here in the thread as I have no experience with shaders at all so I can't really comprehend shader code ;)
     
  4. Vanamerax

    Vanamerax

    Joined:
    Jan 12, 2012
    Posts:
    938
    Im interested in some explanation as well :)
     
  5. Captain Morgan

    Captain Morgan

    Joined:
    May 12, 2013
    Posts:
    144
    _Light - value between 0 and 1, to emulate time of day

    vert - vertex function to calculate vertex color. It use color (pre calculated vertex color) from mesh. rgb components used for colored light (pre calculated), alpha component - light amount
    mycolor - final color function. It multiplies color of the vertex (IN.color) with color from the texture (float4(o.Albedo, o.Alpha)) and with color from light sources (color)

    Code (CSharp):
    1. Shader "Custom/SurfaceVertColoredAlpha" {
    2.     Properties {
    3.         _MainTex ("Base (RGB)", 2D) = "white" {}
    4.         _Light ("Ligh", Range(0.0, 1.0)) = 1.0
    5.     }
    6.     SubShader {
    7.         Tags { "RenderType"="Opaque" }
    8.         LOD 200
    9.      
    10.         CGPROGRAM
    11.         #pragma surface surf Lambert vertex:vert finalcolor:mycolor addshadow
    12.         sampler2D _MainTex;
    13.         float _Light;
    14.         struct Input
    15.         {
    16.             float2 uv_MainTex;
    17.             float4 color : COLOR;
    18.         };
    19.         void mycolor (Input IN, SurfaceOutput o, inout fixed4 color)
    20.         {
    21.            color = IN.color * float4(o.Albedo, o.Alpha) * color;
    22.         }
    23.         void vert (inout appdata_full v, out Input o)
    24.         {
    25.             UNITY_INITIALIZE_OUTPUT(Input,o);
    26.             v.color = float4((_Light * v.color.a).xxxx) + v.color;
    27.             o.color = v.color;
    28.         }
    29.         void surf (Input IN, inout SurfaceOutput o)
    30.         {
    31.             half4 c = tex2D (_MainTex, IN.uv_MainTex);
    32.             o.Albedo = c.rgb;
    33.             o.Alpha = c.a;
    34.         }
    35.         ENDCG
    36.     }
    37.     Fallback "Diffuse"
    38. }
     
  6. Captain Morgan

    Captain Morgan

    Joined:
    May 12, 2013
    Posts:
    144
    Also, is there any way to implement shadow from unity object, such as three, without using light sources ?
    And is there any way to get bumped diffuse shader works without using light sources?
     
  7. stulleman

    stulleman

    Joined:
    Jun 5, 2013
    Posts:
    44
    @Captain Morgan Do you mind explaining how you use this shader? I'm looking for one that is capable of Day/Night Cycle. I Have pre calculated Vertex colors and I add them to the mesh. But it's not working. Do you mind helping me a bit out?
     
  8. Captain Morgan

    Captain Morgan

    Joined:
    May 12, 2013
    Posts:
    144
    I created SunController class wich calculate LighValue (0.0 - 1.0) from time of day. Then i pass it to my shader, to _Light properties.

    I will post SunController code later
     
  9. stulleman

    stulleman

    Joined:
    Jun 5, 2013
    Posts:
    44
    This is not my problem I think.
    How do you change your Vertex Colors? Do you pass a Color32[] to your mesh? And do you split it in to Sun and Torch light?
     
  10. Captain Morgan

    Captain Morgan

    Joined:
    May 12, 2013
    Posts:
    144
    Yes. alpha component of it is color amount, rgb used for colored lighting

    And yes. I have an array of LightData.

    struct LightData
    {
    byte a;
    byte r;
    byte g;
    byte b;
    }
     
  11. stulleman

    stulleman

    Joined:
    Jun 5, 2013
    Posts:
    44
    Okay since I still don't know really how to use this shader, I'll explain what I have.
    I have one Array with normal Light and one array with sunlight. Whenever a light is placed or something like this they get updated. When the light get's updated I also update my mesh, so it calculates new Vertex Colors.

    I don't know what and when I have to pass something to the shader. Maybe you could get into more detail there?

    Thanks so far!
     
  12. Captain Morgan

    Captain Morgan

    Joined:
    May 12, 2013
    Posts:
    144
    You need to put Color32 to your mesh. Alpha component - value from normal light, r,g,b components - sun lights
     
  13. stulleman

    stulleman

    Joined:
    Jun 5, 2013
    Posts:
    44
    Do I understand you right that I put my SunValues in r,g,b? How do you make colored lightning then?
    Sorry if it's really obvious but I simply do not understand how shader work really :p
     
  14. stulleman

    stulleman

    Joined:
    Jun 5, 2013
    Posts:
    44
    These are the both results I get. When i put the slider to the left (0.0f) it's not completely dark. And when it's day, it shouldn't look like this, should it?

    dark.png day.png
     
  15. Captain Morgan

    Captain Morgan

    Joined:
    May 12, 2013
    Posts:
    144
    At the screenshot above i use light sources from unity (directional light, point light and other). But my game also support minecraft-like colored lighting

    I calculate mincraft-like light value (1 byte) based on block. I put it in alpha component of Color32.
    3 other components (r,g,b) used for colored light.
    SunValue - 4 byte, float. Value between 0 - 1
    I pass it into the shader (_Light properties).
    0 - dark, 1 - normal light

    Then, final color will be: (_Light (SunLight) * vertex.Color.Alpha) + vertex.Color
     
  16. Captain Morgan

    Captain Morgan

    Joined:
    May 12, 2013
    Posts:
    144
    You just need to add float property to your shader. Then, in your shader, you need to multiply vertex color with this float property.
    If property is 0, then you will get float4(0,0,0,0)
     
  17. stulleman

    stulleman

    Joined:
    Jun 5, 2013
    Posts:
    44
    I used your shader. Do I have to change something there?
     
  18. Captain Morgan

    Captain Morgan

    Joined:
    May 12, 2013
    Posts:
    144
    Show me please how you generate mesh, color part please.

    Try to replace this

    Code (CSharp):
    1. v.color = float4((_Light * v.color.a).xxxx) + v.color;
    by this

    Code (CSharp):
    1. v.color = float4(_Light * v.color);
     
  19. stulleman

    stulleman

    Joined:
    Jun 5, 2013
    Posts:
    44
    vertexColors.Add(new Color32((byte)(17 * lightEmission), (byte)(17 * lightEmission), (byte)(17 * lightEmission), 255));
    vertexColors.Add(new Color32((byte)(17 * lightEmission), (byte)(17 * lightEmission), (byte)(17 * lightEmission), 255));
    vertexColors.Add(new Color32((byte)(17 * lightEmission), (byte)(17 * lightEmission), (byte)(17 * lightEmission), 255));
    vertexColors.Add(new Color32((byte)(17 * lightEmission), (byte)(17 * lightEmission), (byte)(17 * lightEmission), 255));

    lightEmission is a number between 0-15 and is the light of lightsources. I simulate sunlight with 255 as alpha.

    Is that how to use it? Because like this it isnt working for me

    Changing the line just changes that when i put the slider to the left everything gets black (also the lightsources)
     
  20. Captain Morgan

    Captain Morgan

    Joined:
    May 12, 2013
    Posts:
    144
    I fixes problem with lightsources gets black when you put the slider to the left

    Code (CSharp):
    1.     Shader "Custom/SurfaceVertColoredAlpha" {
    2.         Properties {
    3.             _MainTex ("Base (RGB)", 2D) = "white" {}
    4.             _Light ("Ligh", Range(-1.0, 1.0)) = 1.0
    5.         }
    6.         SubShader {
    7.             Tags { "RenderType"="Opaque" }
    8.             LOD 200
    9.        
    10.             CGPROGRAM
    11.             #pragma surface surf Lambert vertex:vert finalcolor:mycolor addshadow
    12.  
    13.             sampler2D _MainTex;
    14.             float _Light;
    15.  
    16.             struct Input
    17.             {
    18.                 float2 uv_MainTex;
    19.                 float4 color : COLOR;
    20.             };
    21.  
    22.             void mycolor (Input IN, SurfaceOutput o, inout fixed4 color)
    23.             {
    24.                color = IN.color * float4(o.Albedo, o.Alpha) * color;
    25.             }
    26.  
    27.             void vert (inout appdata_full v, out Input o)
    28.             {
    29.                 UNITY_INITIALIZE_OUTPUT(Input,o);
    30.                 v.color = float4(v.color.a.xxxx) + v.color;
    31.                 o.color = v.color;
    32.             }
    33.  
    34.             void surf (Input IN, inout SurfaceOutput o)
    35.             {
    36.                 half4 c = tex2D (_MainTex, IN.uv_MainTex);
    37.                 o.Albedo = c.rgb;
    38.                 o.Alpha = c.a;
    39.                 o.Emission = _Light.xxx;
    40.             }
    41.             ENDCG
    42.         }
    43.         Fallback "Diffuse"
    44.     }
    45.  
    46.  
     

    Attached Files:

  21. stulleman

    stulleman

    Joined:
    Jun 5, 2013
    Posts:
    44
    That's not fixing it I'm afraid. Maybe this isn't the way to do this in my game...
     
  22. Captain Morgan

    Captain Morgan

    Joined:
    May 12, 2013
    Posts:
    144
    You simulate sunlight with 255 as alpha component of vertex color.
    I simulate sunlight using slider in my shader (_Light variable)
     
  23. RuinsOfFeyrin

    RuinsOfFeyrin

    Joined:
    Feb 22, 2014
    Posts:
    785
    Hey Captain Morgan and Stulleman,

    I think I can help, a little, maybe

    Captain Morgan - How are your telling each block how much sunlight it is receiving? I know you are setting the Sunlight value on the shader itself, but the individual blocks how are you telling it how much sunlight its receiving? Or is that what you are saying you are using your alpha channel for?

    Cause the way I'm understanding it, or it sounds to me anyways is that you don't do a per-block sunlight value, but instead assume the sun affects all blocks equally. Maybe im misunderstanding, but then if i am i think stulleman is too.

    Stulleman - You posted two pictures one i assume was full night, the other full day. You said you didnt think full day should look like that. Im assuming you ment how the blocks around what i presume was a light looked over lit?

    The reason for this is mixing Light towards white (255,255,255) instead of the original color of the image (which is what it should look like when fully lit).

    If anyone would like I can explain the proper way to mix lights (Multi Colored Vertex Lit and Sunlight) to prevent this from happening.

    Hope that helps somewhat.
     
  24. Captain Morgan

    Captain Morgan

    Joined:
    May 12, 2013
    Posts:
    144
    I use alpha chanel for minecraft-like light - as everyone else do
     
  25. RuinsOfFeyrin

    RuinsOfFeyrin

    Joined:
    Feb 22, 2014
    Posts:
    785
    What do you use to say how much sunlight is affecting each block then?
     
  26. RuinsOfFeyrin

    RuinsOfFeyrin

    Joined:
    Feb 22, 2014
    Posts:
    785
    I think i kinda follow...maybe. Shaders are a bit of a new concept to me, and I've done nothing with surface shaders just vertex and fragment shaders and even that was torturous trial and error till things worked.

    Ok, where does the Multi-Color lighting support come in on the shader? Is the multi-color lighting only from unity lights?

    Here is how i have my shader and game setup. It supports multi colored lights (not unity lights) and sunlight.

    Shader Values
    SunLight - Float with a range of 0 - 1; 0 is no sun (black). 1 is Fully Sunlit (color depends on original block tile to prevent bleaching)

    Vertex Colors
    R,G,B - Used for lights that are not sunlight. Colors of all lights (except sun) applying to said vertex are mixed before going to the shader.

    A - Used to determine how much sunlight is affecting this vertex. This does not equate to full sunlight or no sunlight but instead is used as a percentage of the SunLight value that affects this block.

    So if the SunValue was .8, and the vertex had an Alpha value of .5 the amount of sun light reaching this block is actually .4

    You then have to properly mix the vertex RGB and the final Sunlight RGB which will then give me the final color.

    As far as I know this is the only way i could figure out to have both Multi Color Lights (not unity lights), and Sunlight and have them properly combined and have sunlight affect each block. Is this at all like what you are doing, or how do you have it setup differently.




     
  27. Deon-Cadme

    Deon-Cadme

    Joined:
    Sep 10, 2013
    Posts:
    288
    Work in progress:



    Rewriting all my code from scratch and changing the voxel system a lot... but having fun :) This test chunk only had 125 voxels and the point was to check that the shader could take over the graphics, the way I wanted it. A success so far (much thanks to Shader Forge). It should be able to mimic round shapes and the texture system will get more advanced.

    @RuinsOfFeyrin - My current project doesn't have demanding light needs because I am considering to move the graphics in a stylized toon-ish direction. White light should be enough (float 0-1), bleach texture color in bright areas and darken it in shaded areas. This can be stored in a vertex, will probably end up in the second UV array.

    I'm more concerned about collision stuff atm... still giving it a lot of thought, can I find a method to decrease the cost of changing a mesh? (need good, reliable info on what the collision system is doing) or should I cook up something on my own?
    I am currently wondering if Unity will accept that I only add a collision box around the chunk itself... that would make some stuff easier...
     
  28. Enoch

    Enoch

    Joined:
    Mar 19, 2013
    Posts:
    198
    I know there has been a huge focus in this thread on Collision, and while it is a concern. I really think this is one of those places that may not be as important to worry about. I can't speak at all for mobile, but I can say that for desktop dynamic creation of collision meshes isn't a huge problem for me (I am using a smooth mesh DC like algorithm for voxel rendering). I am straight up using a mesh collider based on the render mesh (no optimization). Unity seems to be handling both rendering and mesh creation just fine.

    Also I believe that when watching one of the Unity 5 updates about the physics engine they mentioned that static collision objects should be basically "free" to add/remove in the Update for Unity 5.

    Given that Unity 4.6 is damm fast enough and Unity 5 will probably be even faster in this area, I really think collision meshes maybe something we might be able to finally let go. Or at least focus optimization efforts in other more critical areas.
     
  29. RuinsOfFeyrin

    RuinsOfFeyrin

    Joined:
    Feb 22, 2014
    Posts:
    785
    @Deon Cadme
    I've has similiar results as Enoch when it comes to collision. Ive found that assigning a mesh collider is quick enough, at least for desktop games (have not tried mobile). The trick is do all the heavy work in a background thread and actually only build and assign the collider mesh on the main thread.

    Example:
    Building and assigning Mesh, and Collider - 1 chunks (16x16x128) of fairly complex terrain takes right around 5ms.
    Doing this without the collider is about 1ms.

    This is on my old as heck laptop (Athalon xII 2.0ghz) when running the game in the editor.
    In standalone mode Mesh + Collider is about 3ms, and just Mesh is < 1ms.

    It is important to note that both my rendered mesh, and my collision mesh are built up using a greedy algorithm so there
    are less vertices to handle which means its quicker then without such an algorithm.

    I personally don't find the time it takes to generate this collider to be a killer for me, though i did take steps to space out the rendering of chunks so I don't have 20 chunks trying to render at once.

    Its been said a lot on here, and every other programming forum in the world.
    Pre-mature optimization is the root of all evil.
    Or even better, if its not broke don't fix it. So if using mesh colliders works well enough for your case, dont worry about it.

    You also asked about finding a method to decrease the cost of modifying a mesh.
    The answer to that is yes and no. The less vertices a mesh has, the quicker it can be built, there is however no way to speed the process of building the mesh up. Make sense?

    With that in mind, as I said i use a greedy algorithm for generating my render and collision meshes. This is done in a background thread before everything else.

    Hope that helps some.
     
  30. Deon-Cadme

    Deon-Cadme

    Joined:
    Sep 10, 2013
    Posts:
    288
    @Enoch & @RuinsOfFeyrin - Just gonna cook both answers into one ;) I definitely would prefer to stick with using the existing collision system but it broke during past experiments. The collision system couldn't keep up with intensive editing of the mesh... maybe I did something wrong but it lag spikes always happened in Unitys collision system. Something was happening there... but what and why? Wish I had source code access, would have solved this at the blink of an eye then.

    The mesh isn't anything strange, cubes, shared vertices, smoothing, can do round surfaces by moving the vertices etc... think EQ Landmark, similar systems, at least in their core. I'm not sure if greedy will be added at all because these meshes can easily get too complex and then there is the shader that it will mess with, I need to look at future results and make a weighed decision.

    Don't optimize too early but stay ten steps ahead "on paper" and often test your code for bugs ;) Have been programming for a long time but my background is in C++ and Unreal Engine was the last engine I worked with (not modding or hobby tinkering). Recently moved to C# and Unity (1-2 years ago) because they allow me to prototype and develop faster... could maybe happen that I forgot to do some engine or language specific thing. Will figure it out eventually, need to clean up mesh generating code today because I was having fun and trying out fun ideas yesterday... always result in lots of comments and other junk in the files xD
     
  31. Cherno

    Cherno

    Joined:
    Apr 7, 2013
    Posts:
    515
    Just a question, does it make a noticeable difference if you generate greedy vs. non-greedy meshes for the MeshRenderer?
     
  32. Deon-Cadme

    Deon-Cadme

    Joined:
    Sep 10, 2013
    Posts:
    288
    @Cherno - Of course, greedy gives you less vertices if you can use it. Greedy is basically that you traverse the mesh and combine adjacent surfaces into a larger surface whenever it is possible. That gives the rendering pipeline less vertices to deal with or you can have bigger chunks if there is a Quad- or Octree in the background. The Quad- or Octree is necessary for cases when you get too many vertices and need to split the chunks into smaller pieces...

    Pro:s and Cons make greedy a choice for those that it fits but not something for everyone.
    Minecraft - almost certain that you can reap different advantages from using it.
    Everquest Landmark - not certain if it will benefit you, just the process might cost you more then you gain.

    Here is a video of greedy in action on a Minecraft-like voxel mesh:
     
  33. Cherno

    Cherno

    Joined:
    Apr 7, 2013
    Posts:
    515
    Yes, I understand the concept of greedy meshes, I am asking specifically for a noticeable difference between generating a greedy vs. non-greedy mesh for the MeshRenderer. Is it just a tiny fraction of a ms?
     
  34. Deon-Cadme

    Deon-Cadme

    Joined:
    Sep 10, 2013
    Posts:
    288
    I think you mean MeshFilter and the difference in time to generate a greedy mesh vs regular mesh depends on how you have set up your system in general. There are different ways to get a regular Minecraft-look-a-like mesh, some are easier to optimize with a greedy system, others take more time. There are also different ways to perform greedy operations as well and how complicated you make the pass(es) over the mesh... there simply isn't a straight answer to the question. Results also depend on how many materials you got in the voxel terrain and if you got shaders that render the mesh or manipulate UV coordinates... the list just get longer the more I think on it.

    Best case: Taking my current mesh system into account and force it to only generate even sized cubes as a terrain... *thinks* I should be able to perform a simple, single pass greedy operation by only adding 3 if-statements and 3 bools or something simple like that into my existing mesh generating function... so the added time cost is in this case minimal.
    If I use a material system like Minecraft... hmm... maybe it would be possible to reach an average 30%-50% vertex reduction across all the chunks if I use decent noice maps. Better results could be achieved with a bit of tinkering in the material system and noise maps in this case.

    Worse case: My current system without any fiddling would require a much more complicated greedy function... many more things to check for... my own material is more advanced then Minecraft... I would probably have to sit down with pen and paper to figure out how it affects greedy in the end... but the complex mesh would anyways have a huge impact that drastically decrease the efficiency of greedy to the point where I wonder if it is worth it from the start...

    Regarding the MeshRenderer, it handles many different things... but there are lots of places where I see potential to shave off a few milliseconds on different calculations if it operates on a greedy mesh ala Minecraft style... add a good shader and you could save a lot of fps in the end... that could be used to add new, more advanced weather effects instead as an example.
     
  35. RuinsOfFeyrin

    RuinsOfFeyrin

    Joined:
    Feb 22, 2014
    Posts:
    785
    @Cherno

    Using some sort of greedy algorithm for combining the mesh makes a HUGE difference for many reason, and no its not just a fraction of a millisecond. How much fast it gets depends on how much the mesh is reduced. An the more its reduced the faster it is.

    When you are assigning the verticies to the mesh you at some point are assigning a List of vertices to an array via ToArray. ToArray essentially loops through the list adding items to the array. Less vertices, less time it takes to get through making the array.

    Greedy has other benefits.
    • The main one is the meshes require less memory since they have less vertices.
    • It also has the benefit of speeding up your collider assignment if you are using the same mesh for your collider (Collider also uses less mesh do to less verticies, and it seeds up collision due to having to check less faces).

    A properly done greedy algorithm should not be much, if any slower than the standard way of building you mesh, just more complex. In my code most blocks are only looked at once, only if the block or row of blocks im checking does not fit in to the current section does it get looked at for a second time. Even this rare secondary look at a block im sure i could get rid of if i put a little more brain power in it but its working for now.

    If you are going to have Ambient Occlusion doing a greedy algorithm does become slightly more complicated, but still totally doable as I have both functioning in my code.

    If you do a greedy system you will need a shader that can use an AtlasMap (I have one I can post).

    I have a fairly complex Block/Material system much so more then minecrafts.
    • Different size/shape blocks
    • Blocks have several factors that determine what textures (yes more then one) are use to generate the block.
    • Blocks can move. So a block is not always at an integer position.
    • I have Faked Ambient Occlusion, so each vertex of a blocks face can potentially have a different light value.
    • I have blocks that support transparency.
    My system supports all of that and i still use a greedy mesh and find the gains from it more then worthwhile for the time i invested in getting it to work properly.

    And just to say it, though im sure its obvious from following this thread. EVERYTHING is done in a background thread with the exception of assigning values to the Mesh for the rendered and the collider.

    Hope that helps
     
  36. Cherno

    Cherno

    Joined:
    Apr 7, 2013
    Posts:
    515
    Yes, I meant the MeshFilter of course, thanks :)
     
  37. Shinyclef

    Shinyclef

    Joined:
    Nov 20, 2013
    Posts:
    505
    @RuinsOfFeyrin

    I would like to inquire as to how you achieved transparency. In particular, how you overcame the z-buffer issue.
    Here are a couple of pictures of my attempt at transparency. When looking at a chunk in a particular direction, it works fine, due to the draw order of the supplied triangles. However, when looking from the opposite direction, the triangle order causes issues.

    Looking at minecraft, I see that blocks such as glass and leaves use cutout shaders. But I can also see that water has partial transparency and basically seems to work. I had almost given up on partial transparency until I saw your comment about blocks with transparency. How did you achieve it?

    From one direction:


    From the opposite direction:
     
  38. RuinsOfFeyrin

    RuinsOfFeyrin

    Joined:
    Feb 22, 2014
    Posts:
    785
    @Shinyclef

    Hello,

    My use of transparency is not without z-buffer issues currently that I have not entirely worked out, mainly related to overlapping of semi-transparent objects not in the same mesh (like you I think). The issue is rare as i don't have lots of semi-transparent meshes that tend to overlap in a way that causes this problem.

    How does Minecraft handle this? As far as I can tell minecraft handles it in the same way as I am. Limited types of transparencies that can potentially overlap. As far as I know the only semi-transparent blocks that mine-craft supports are Water and Ice. This does not create an issue with overlapping as far as i know because all semi-transparent blocks of a chunk end up in the same mesh for a chunk thus get rendered properly against the scene.

    I know that is not the answer you were looking for, but its all i got. Hope it helps.

    (Unless you are talking about some other sort of issue with transparencies)
     
  39. Royall

    Royall

    Joined:
    Jun 15, 2013
    Posts:
    120
    Can anyone explain how I can fix this problem? I have lightflooding and ambient occlusion working for a single chunk. But I cant figure out how to do this with chunks like above without infinite regeneration loops.
     
  40. Enoch

    Enoch

    Joined:
    Mar 19, 2013
    Posts:
    198
    Well there doesn't seem to be a great simple and elegant solution for this type of problem that I could come up with. So this seems to be just one of those places where we have to deal with complexity. This is how I did it:

    I have two lightflood methods one that floods in local chunk space and one that floods in full world voxel space. My chunks are 16x16x128 so when in local chunk space my positions are limited to 0-15,0-127 and 0-15 respectively. I don't do a fully infinite world just a really big one so my world is 32768 *1* 32768 chunks, since we are 32 bit float bound in unity my world gets funky because of float precision errors long before anyone can reach its edge.

    Like I said two light flood functions. Lightflood Local floods the local chunk with light and if it reaches an edge it records that Lightflood point in a list to be executed in the world space light flood method. During chunk generation process after doing a local light flood a check is made to see if we have any world space floodpoints and we execute a world space light flood if we do. When flooding light in world space when we reach a chunk that isn't generated yet we need to associate those flood points with the un-generated chunk in some way. When we finally do generate that chunk we would have a combined list of points where the local light flood crossed into other chunks and where other chunks crossed into this one because it was un-generated at the time. Note that there is no infinite recursive flooding here. If Chunk A floods into Chunk B and Chunk B wasn't generated at the time, when chunk B does its own world space flooding it has no issue flooding back into A because Chunk A exists at that time.

    Note that flooding light local stops at the chunks edge while flooding in world space floods right across the chunk boundry. In the world flood version we need to check every flood point and make sure the chunk that it maps to actually exists (has been generated). I have found that flooding in world space requires a lot more boundary checking and space coordinate conversions so it is a heavier method in terms of processing. This is why I have two methods. You could reduce complexity by only having one light flood function that only deals in world voxel coordinates and floods happily across any chunk that currently exists, it just wouldn't be completely optimal in terms of processing speed.

    I hope I helped, this gets a little complicated so I doubt I did a great job of explaining it completely but I think I covered the basics of how to do it.
     
    Last edited: Feb 3, 2015
  41. RuinsOfFeyrin

    RuinsOfFeyrin

    Joined:
    Feb 22, 2014
    Posts:
    785
    @Royall and @Enoch

    The problem is not nearly as hard as it seems, and has been very well documented in this thread and several other places linked to from this thread.

    So your problem is you are thinking of lighting in terms of chunks. The problem you are finding with trying to handle thing on a "chunk" basis is edge cases (going in to a new chunk).

    You really should never process anything in terms of chunks other then loading the initial chunk data. After that everything should be taken in the scope of the entire loaded area (which is why you should use a single array for all loaded chunk data, and preferably a single flat array).

    Then when you process lights you are simply processing it in terms of a point, and the range it can affect and only have to check bounds for the whole map, not for each chunk, plus it speeds up accessing blocks that are in a neighbor chunk.

    If light goes in to a chunk that isnt loaded. Ignore it. When a new chunk is loaded you simply have to re-process light for the blocks in the chunk range, plus the distance light can go.


    Sorry this was quick, was on my way out but wanted to point you in the right direction. Any question ask and ill try to answer later, or peruse this thread it has the answers.
     
  42. Royall

    Royall

    Joined:
    Jun 15, 2013
    Posts:
    120
    So basically if you have an area of 8x8 chunks. You would first load and build up geometry of those 64 chunks. And after building you start the floodlight across the entire area with the use of one big lightdata array instead of lightdata arrays for each chunk?

    How can this be efficient when you have to recalculate the light of every chunk within viewing distance, each time a new chunk 'spawns', or when you for example remove or place a block?
     
    Last edited: Feb 3, 2015
  43. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    I'll just describe the system I finally came up with.

    Three basic rules:

    1. Chunk size must be greater or equal to the number of discrete light values, i.e. if chunk size is 16x16x16, then the light values must be between 0 and 15. It guarantees that any light source can affect only the chunk it is located in and its adjacent chunks. All the other chunks are not affected.

    2. You can generate chunk mesh only if all its neighbor chunks have their lighting calculated.

    3. You can calculate chunk lighting only if all its neighbor chunks are filled with blocks. The only exception is the vertical sun light because it can't cross chunk borders.

    PROS:
    - these rules guarantee that there will be no lighting or geometry artifacts,
    - also there will be no reasons to recalculate lighting or rebuild meshes other than player placing and removing blocks or light sources.

    CONS:
    - not all the chunks in memory are visible. The larger the chunks, the less is the visibility range and the more % of memory is "wasted".

    The initial world generation is done in the following order:
    1. Fill all the chunks with blocks.
    2. Fill all the chunks with vertical sun light.
    3. Calculate "recursive" light for those chunks that have all the neighbors.
    4. Build meshes for those chunks whose neighbors have recursive light calculated.

    This is how it should look like (gif-animation):
    lighting.gif

    What happens when the player crosses the chunk border:
    bbb.png

    Adding and removing light sources

    Adding light is trivial. There's no need to recalculate the whole chunk's lighting, just run the flood fill function and update the chunk meshes that have been affected.

    Removing light is a more tricky task. I've found the algorithm that reduces the amount of work needed for recalculations. See the attached pdf.
     

    Attached Files:

    Last edited: Feb 3, 2015
    goldbug likes this.
  44. Royall

    Royall

    Joined:
    Jun 15, 2013
    Posts:
    120
    @alexzzzz With this method, is it normal that I only see 1 chunk fully generated when I have 5x5 chunks, and for example 4x4 chunks when I have 8x8 chunks?
     
  45. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    Unfortunately, yes.

    If I keep an array of 1024x128x1024 blocks in memory and use 32x128x32 chunks, which gives 32x32 grid of chunks, the maximum visible area is 28x28 chunks or 896x896 blocks or 448 blocks in each direction instead of 512 blocks. This is 86% of potential maximum visibility range.

    If the array of blocks is 512x128x512, which is 16x16 grid of chunks, the visible area is 12x12 chunks or 384x384 blocks or 192 blocks in each direction instead of 256 blocks. This is only 75% of potential maximum visibility range.

    The smaller the map, the bigger the visibility range loss. However, the smaller the chunks, the lesser the loss. 16x128x16 chunks give 94% of potential maximum visibility range in case of 1024x128x1024 map, and 88% in case of 512x128x512 map.
     
  46. Royall

    Royall

    Joined:
    Jun 15, 2013
    Posts:
    120
    @alexzzzz Ok, I am working on it as im speaking..

    When do you do the checks to see if neighbours have filled with blocks before you calculate the light, and check if they calculated the light to generate the mesh?

    Right know I have:
    Code (csharp):
    1.  
    2. function checkGeneration() {
    3.     if(!lightCalculated) {
    4.         if(nNorth != null && nEast != null && nSouth != null & nWest != null && nNorthEast != null && nSouthEast != null && nSouthWest != null & nNorthWest != null) {
    5.             if(nNorth.blocksCalculated && nEast.blocksCalculated && nSouth.blocksCalculated && nWest.blocksCalculated && nNorthEast.blocksCalculated && nSouthEast.blocksCalculated && nSouthWest.blocksCalculated && nNorthWest.blocksCalculated) {
    6.                 calculateLight(); //If all the neighbours have calculated blocks, start calculating the light for this chunk
    7.             }
    8.         } else {
    9.             nNorth = chunkExists(transform.position.x/size,transform.position.z/size+1);
    10.             nNorthEast = chunkExists(transform.position.x/size+1,transform.position.z/size+1);
    11.             nEast = chunkExists(transform.position.x/size+1,transform.position.z/size);
    12.             nSouthEast = chunkExists(transform.position.x/size+1,transform.position.z/size-1);
    13.             nSouth = chunkExists(transform.position.x/size,transform.position.z/size-1);
    14.             nSouthWest = chunkExists(transform.position.x/size-1,transform.position.z/size-1);
    15.             nWest = chunkExists(transform.position.x/size-1,transform.position.z/size);
    16.             nNorthWest = chunkExists(transform.position.x/size-1,transform.position.z/size+1);
    17.         }
    18.     } else if(!meshCalculated) {
    19.         if(nNorth.lightCalculated && nEast.lightCalculated && nSouth.lightCalculated && nWest.lightCalculated && nNorthEast.lightCalculated && nSouthEast.lightCalculated && nSouthWest.lightCalculated && nNorthWest.lightCalculated) {
    20.             generateMesh(); //If all the neighbours have calculated light, start generating the mesh for this chunk
    21.         }
    22.     }
    23. }
    And this is runned for every chunk every time a new chunk spawns... Not very efficient yet..
    Start function with:
    Code (csharp):
    1.  
    2. var chunks =  GameObject.FindGameObjectsWithTag("Chunk");
    3.     for(var i = 0; i < chunks.length; i++) {
    4.         chunks[i].GetComponent(Chunk).checkGeneration();
    5.     }
    It also has a bug to not generate the mesh for the last chunk generated... It has something to do with the diagonal checks to neighbours (norteast/southeast/southwest/northwest). If I remove diagonal checking the last chunk will display, but the lighting is not correct on every chunk.

    It also takes about 14 seconds to generate 7x7 visible chunks...
     
  47. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
  48. Royall

    Royall

    Joined:
    Jun 15, 2013
    Posts:
    120
  49. Royall

    Royall

    Joined:
    Jun 15, 2013
    Posts:
    120
    Correction, with diagonal checking he doesnt generate the last two chunk meshes with the above code (instead of one).

    Screen:
     
  50. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    Only when it recieves a message that one of its neighbors has changed its state.

    After a new chunk has been created it immediately subscribes to its neighbors' StateChanged event and tells them to subscribe to its own StateChanged event. Then it adds a new job to the job queue that generates terrain data or loads it from disk and also calculates vertical sun light distribution. That's it. All the rest happens automatically.

    - When the job completes, the chunk's state changes from IsEmpty to HasTerrain.
    - The chunk fires its StateChanged event and checks whether it can upgrade the state even further. If it can, it adds the new job to the job queue. If it can't it does nothing.
    - At the same time all the neighbor chunks get the fired StateChanged message and check whether they need to upgrade their current states. If they need it and all the preconditions are met they add new corresponding jobs to the job queue. If they don't need or some preconditions are not yet met, they do nothing.
     
Thread Status:
Not open for further replies.