Zylann / godot_voxel

Voxel module for Godot Engine
MIT License
2.62k stars 246 forks source link

Is it possible to have large texture per multiple blocks? #270

Open Favkis opened 3 years ago

Favkis commented 3 years ago

Is it possible that for example I have 512x512 texture and each block gets only 128x128, and depending on where block is in the world, it'll choose what part of texture to apply? So if you want to see all 512x texture you'll have to place 4 blocks near each other.

Zylann commented 3 years ago

Assuming you want to use blocky voxels, this is how they are intented to use. You may create each Voxel in VoxelLibrary as a mesh that has UVs from inside an atlas. This is most effectively done in Blender (or another modeler) where you can have full control over which faces get which part of the texture. I think there is also a CUBE mode shortcut but it might be limited. If your need is simpler than that you could also create a shader that uses world coordinates as UVs, in which case you would not need plenty of "parts".

Favkis commented 3 years ago

@Zylann So I need to make 4^3 cubes and set up voxel coords for them and then calculate position from coords using remainder of the division?

Zylann commented 3 years ago

You can do it like that, or you could use a shader that uses world coordinates / 4 in place of UVs (procedural UVs, sort of, like triplanar mapping). You may look into shaders to better understand how to do this

Favkis commented 3 years ago

You can do it like that, or you could use a shader that uses world coordinates / 4 in place of UVs (procedural UVs, sort of, like triplanar mapping). You may look into shaders to better understand how to do this

oh that sounds much easier thank you

Favkis commented 3 years ago

How to pass voxel coords to shader? I need to use voxel coords, not vertex coords because if I apply one part of atlas to 4x4 square, I will have incorrect UV on edges of that square.

image

I want single block to have 16 of these variations, so depending on their position in world, block gets square from D1, if I do it per vertex then 4th block would have one edge of rightest quare and another edge of leftest.

Favkis commented 3 years ago

Where can I find code in your model that generates UV coords?

Zylann commented 3 years ago

How to pass voxel coords to shader?

You may use vertex coordinates multiplied by the world matrix to get world coordinates, then use multiplier to adjust the size. I'm not sure why the rectange would be incorrect if the proper values are used? If you mean filter leaking or mipmap issues, then you would either have to implement filter manually with texelFetch, add padding in the atlas, or use a texture array so each layer solely has the relevant texture in it.

For a moment I was thinking we could add a different value to UVs if triplanar mapping is used, which would simply be the index of the texture in a texture array/atlas, so all there would be left to do is to use the interpolated world coordinate like usual. But I realized you could still achieve this without extra info: You can UV-map your blocks using the top-left cell of its corresponding patch in the atlas. Then in the vertex shader, you can get the coordinate of the voxel by getting world coordinate (world matrix * VERTEX), which you can floor or round so the coordinates will be the same for every vertex (careful about max edges tho). Then you pass it to the fragment shader with a flat varying (not interpolated then), so that in the fragment shader you can offset the cell of UV based on it. A float modulo would be used so that it wraps through the 4x4 area instead of being offset further in the texture (unless you use texture arrays). It would work because you know that every block would start from the top-left corner, and that it can always extend up to 4 cells right and down.

Interestingly I have the same kind of issue to solve in a side project and I think that might just do^^ I'll experiment that later.

Where can I find code in your model that generates UV coords?

I don't think you need to change that, but if you really want to then it's here https://github.com/Zylann/godot_voxel/blob/fa2f93c099e5b1db33b06059b5b7285b7cc658cc/meshers/blocky/voxel_mesher_blocky.cpp#L227 And here https://github.com/Zylann/godot_voxel/blob/fa2f93c099e5b1db33b06059b5b7285b7cc658cc/meshers/blocky/voxel_mesher_blocky.cpp#L326

Favkis commented 3 years ago

I can't tell voxel coords from vertex coords because any vertex can be a member of 8 different voxels. While it's okay for some tiles, some vertex will have to have UV of 0th tile OR 4th tile depending on what voxel coords this vertex corespond to. This is why vertex shader needs to know voxel coords, not just vertex coords to do this correctly. If I do this in C++ code instead, I need voxel coords too. I need to add same vector2 to UVs of entire triangle/quad and it depends on voxel coords.

Zylann commented 3 years ago

What I was thinking first was something like this (simplified, this is not all the code):

uniform sampler2DArray u_texture_array;

varying vec3 v_voxel_pos;

void vertex() {
    v_voxel_pos = WORLD_MATRIX * VERTEX;
}

void fragment() {
    texture_index = UV.x; // ?
    vec3 col = texture_triplanar(u_texture_array, v_voxel_pos * scale, NORMAL, texture_index);
}

When doing like this, there is no need to know voxel coordinates, and no need to know in which quad you are in the atlas, since there is no atlas. The only missing part is the index within the texture array. Since this shader does not use UV, then you can repurpose it to store just that, without having to modify the module. As a bonus you can then also choose a different texture on different parts of the voxel, as long as they tile. You can do the same thing with an atlas but it's a bit more finnicky.

For the second approach, I understand that the vertex shader cannot know if a vertex is on the left or right side of the voxel. It would cause floor to return the position of the next voxel if the vertex is on a positive side. I thought there was a way to workaround this but it sounds more complicated. So if you can't afford doing the first approach then you'd have to slightly modify the mesher I guess.

Favkis commented 3 years ago

image

I managed to calculate voxel coords for every vertex from their NORMAL and UV. But NORMAL is weird in inner corners, so it gives inacurate results...

Zylann commented 3 years ago

I wonder why would NORMAL be weird on any side, it's really just cubes isnt it 🤔 (unless they share vertices, which they should not) If you think they are not correct you could output them as albedo

Favkis commented 3 years ago

I forgot that I tried to use color channel to pick type of voxels(R and G were added to resulting UV). I removed that line and artifacts are gone. It was actually color messing it up, not normal. Thanks! I figured it out by putting NORMAL and UV into ALBEDO and saw that they were exactly as expected in every corner.

Favkis commented 3 years ago

image