Open blockspacer opened 4 years ago
You cannot use COLOR
with the Transvoxel mesher at the moment, because it is used already to store transition mesh information.
Transvoxel polygonizes a block by taking corners of a cube (which is why it uses 8 voxels instead of one), which are interpolated to generate vertices. Then the process repeats for every voxel of the block ("marching" cubes). You are right about using a new channel for material IDs, but doing that in Transvoxel you need to make them part of the interpolation I guess. Also, the paper has a section dedicated to this subject, you may want to take a look: https://transvoxel.org/Lengyel-VoxelTerrain.pdf
@Zylann Thanks for response.
I'll try to use UV2 and pack multiple texture ids into one float like so https://stackoverflow.com/questions/32915724/pack-two-floats-within-range-into-one-float.
About COLOR
- are there any usage examples with shaders and Transvoxel? I changed triplanar shader to use get_transvoxel_position
from https://github.com/Zylann/godot_voxel/issues/2#issuecomment-570013045 and nothing changed.
This is the shader I use in my local demo:
shader_type spatial;
uniform sampler2D u_texture_top : hint_albedo;
uniform sampler2D u_texture_sides : hint_albedo;
uniform int u_transition_mask;
varying vec3 v_world_pos;
varying vec3 v_world_normal;
vec3 get_triplanar_blend(vec3 world_normal) {
vec3 blending = abs(world_normal);
blending = normalize(max(blending, vec3(0.00001))); // Force weights to sum to 1.0
float b = blending.x + blending.y + blending.z;
return blending / vec3(b, b, b);
}
vec4 texture_triplanar(sampler2D tex, vec3 world_pos, vec3 blend) {
vec4 xaxis = texture(tex, world_pos.yz);
vec4 yaxis = texture(tex, world_pos.xz);
vec4 zaxis = texture(tex, world_pos.xy);
// blend the results of the 3 planar projections.
return xaxis * blend.x + yaxis * blend.y + zaxis * blend.z;
}
vec3 get_transvoxel_position(vec3 vertex_pos, vec4 vertex_col) {
int border_mask = int(vertex_col.a);
int cell_border_mask = border_mask & 63; // Which sides the cell is touching
int vertex_border_mask = (border_mask >> 6) & 63; // Which sides the vertex is touching
// If the vertex is near a side where there is a low-resolution neighbor,
// move it to secondary position
int m = u_transition_mask & (cell_border_mask & 63);
float t = float(m != 0);
// If the vertex lies on one or more sides, and at least one side has no low-resolution neighbor,
// don't move the vertex.
t *= float((vertex_border_mask & ~u_transition_mask) == 0);
// Position to use when border mask matches
vec3 secondary_position = vertex_col.rgb;
return mix(vertex_pos, secondary_position, t);
}
void vertex() {
VERTEX = get_transvoxel_position(VERTEX, COLOR);
vec3 world_pos = (WORLD_MATRIX * vec4(VERTEX, 1.0)).xyz;
v_world_pos = world_pos;
v_world_normal = NORMAL;
}
void fragment() {
vec3 normal = v_world_normal;//normalize(v_world_normal);
vec3 wpos = v_world_pos * 0.2;
vec3 blending = get_triplanar_blend(normal);
vec3 top_col = texture_triplanar(u_texture_top, wpos, blending).rgb;
vec3 side_col = texture_triplanar(u_texture_sides, wpos, blending).rgb;
float r = top_col.r;
ALBEDO = mix(side_col, top_col, clamp(normal.y * 10.0 - 4.0 - 8.0*r, 0.0, 1.0));
}
@Zylann
I'm not sure that understood doing that in Transvoxel you need to make them part of the interpolation I guess
.
I hope that you said about VoxelMesherTransvoxel::build_internal
cause i tried to use cell_positions
like so https://github.com/blockspacer/godot_voxel/blob/indev/meshers/transvoxel/voxel_mesher_transvoxel.cpp#L873
const int cornerMaterials[8] =
{
fvoxels.get_voxel(cell_positions[0], VoxelBuffer::CHANNEL_DATA2),
fvoxels.get_voxel(cell_positions[1], VoxelBuffer::CHANNEL_DATA2),
fvoxels.get_voxel(cell_positions[2], VoxelBuffer::CHANNEL_DATA2),
fvoxels.get_voxel(cell_positions[3], VoxelBuffer::CHANNEL_DATA2),
fvoxels.get_voxel(cell_positions[4], VoxelBuffer::CHANNEL_DATA2),
fvoxels.get_voxel(cell_positions[5], VoxelBuffer::CHANNEL_DATA2),
fvoxels.get_voxel(cell_positions[6], VoxelBuffer::CHANNEL_DATA2),
fvoxels.get_voxel(cell_positions[7], VoxelBuffer::CHANNEL_DATA2),
};
dominant_texture_idx = (float)FindDominantMaterial(cornerMaterials);
I'm going to use function similar to FindDominantMaterial
from https://github.com/nickgildea/leven/blob/118bf1cacb75fdb3b306ccf27477037720af86e8/leven/cl/octree.cl#L96
Note that noise function must mark air voxels with empty texture (ID == 0
or - in my code draft - if(texture_idx < 0.9
), so i had to change voxel_stream_noise.cpp
like so https://github.com/blockspacer/godot_voxel/blob/indev/streams/voxel_stream_noise.cpp#L87
cause i don't know how determine air voxels from float d = (n + 2.0 * t - 1.0) * iso_scale;
BTW, is generation prioritized by distance to the camera?
Note that noise function must mark air voxels with empty texture (ID == 0 or - in my code draft - if(texture_idx < 0.9), so i had to change voxel_stream_noise.cpp like so https://github.com/blockspacer/godot_voxel/blob/indev/streams/voxel_stream_noise.cpp#L87 cause i don't know how determine air voxels from float d = (n + 2.0 t - 1.0) iso_scale;
You can tell if a voxel is air if its SDF value is above zero (though with Transvoxel it would be the opposite). Then a cell is all air if the 8 corner voxels are air. I can't really tell what to do for materials, all I can say is that I would follow the Transvoxel paper.
BTW, is generation prioritized by distance to the camera?
The order of blocks scheduled for processing is sorted by distance to viewer. However scheduling is controlled by octrees and regions. See #87
from https://transvoxel.org/Lengyel-VoxelTerrain.pdf If a vertex is reused from a preceding cell, and the preceding cell selects a different material, then the vertex must be duplicated so that the different texture map indexes can be assigned to it.
Any ideas how to do that will be useful
FYI I've been working in a branch with texturing support for a little while, in smooth_texturing
.
The way it works is as follows:
Two new channels were added, INDICES
and WEIGHTS
. They both default to 16-bit depth and should remain in that format in order to use the feature. Indices allow to tell which textures are present in a voxel, and weights tell how much of these textures there is. There can be just one (in which case only one weight will be maxed and others zero), or multiples.
Indices are encoded as four 4-bit values, and weights are also encoded as four 4-bit values. That means up to 16 different textures are supported, and weights can have up to 16 shades. This is very tight, but it should be enough for now.
The only mesher supporting this is VoxelMesherTransvoxel
. It has an option to turn texturing on (it has a performance cost). During meshing, for every voxel crossing the surface, indices and weights are looked up, and 4 textures with the highest amount are selected for generated vertices. If the set of textures changes between two cells, a seam is created to fix interpolation issues that could arise from vertex sharing.
Finally, the shader used to render the surface can read indices and weights in the UV
variable. It has nothing to do with uvs, it is only being repurposed. UV.x
contains 4 bytes for the 4 indices, and same for weights in UV.y
. After some decoding with floatBitsToUint
, we can now perform simple 4-way blending using a texture array.
I then implemented ways to paint textures with VoxelTool
. It's much more involved than sculpting, but it works too:
Allowing to generate textures in VoxelGeneratorGraph
is tricky as well, but I got something basic working. I had to implement new kinds of outputs (which is groundwork for custom outputs), so the density of each texture can be specified. However it involves math so that's another tricky part, as densities must add up to 1. Performance is not as good as I would like, but it works:
Now what's essentially left to do in the branch is to implement basic texture painting for remaining VoxelTools. Even though this took lots of work, it is still quite basic so it will probably be improved or changed over time.
The feature is now in master.
I`m trying to add support for multiple textures based on texarray and COLOR attributes of vertices.
Key idea is to texture differently shape created by
do_point
ordo_sphere
. For example, it may be useful in games where player can place voxel with chosen texture https://youtu.be/Xm1KVjMePBA?t=85I managed to pass texture ID via
_output_extra
fromvoxel_mesher_transvoxel.cpp
and addingCHANNEL_DATA2
tochannels_mask
like sounsigned int channels_mask = (1 << VoxelBuffer::CHANNEL_SDF) | (1 << VoxelBuffer::CHANNEL_DATA2);
But i don't know which element to choose from
corner_positions
to use inget_voxel
(voxel_mesher_transvoxel.cpp
)Now i'm using
corner_positions[0]
But
corner_positions[0]
seems related not to voxel, but to voxel nearby to it. That's why when i calldo_point
texture applies not to created voxel (but to part of mesh created bydo_point
).Thanks in advance for any help provided.