Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

Teknologicus

122
Posts
3
Topics
19
Followers
25
Following
A member registered Apr 21, 2023 · View creator page →

Creator of

Recent community posts

(1 edit)

The level of detail equation in cone trace lighting (this is different than voxel level of detail) will be adjustable in game settings.  Default will be set to yield best performance to light quality ratio.

I'm still working on my C++ prototype cone trace progressive frustum app (for lack of a better description) to get algorithm and math right so I don't have to struggle so much when writing the shader code based on the app's code.

Thank you!

Very cool!

These are awesome!  I loaded each one up in my voxel engine and they look great!

Thank you!

Very cool!

Thank you!

I may be able to release one or more single-player mini-advanture/quest game versions with my voxel engine in the 4th quarter of this year.  Barring anything unexpected, I'll be releasing something next year if this year doesn't happen.

https://teknologicus.itch.io/hantverkn

Micro-voxel adventure & exploration game with an open "sandbox" world.  Game play will include removing and placing blocks (made of voxels or even individual voxels), ores, item crafting, monsters and varied terrains by biome.

P.S. Sunlight intensity and color also should change at sunset and sunrise.

Thank you!

Direct sunlight definitely needs more work...  I discovered direct sunlight isn't working correctly with levels of detail (mipmaps).  I would also like to ray march light from voxel faces instead of from voxel centers for more accuracy.  Furthermore, direct sunlight doesn't take into account lighting angle of incidence between voxel face directions (normal) and sunlight direction.

Once I've addressed what can be improved with direct sunlight, I will work on cone traced global illumination.  That will both propagate sunlight indirectly as well as handle per voxel emissive lighting (and propagation of emissive lighting).

(1 edit)

Hantverk'n

(2 edits)

The part that changed everything is about using atomic operations to query and/or update a hash map and add voxel hits to a list of voxel hits if not already present based on hash map query.  I've implemented it in my voxel engine's ray march code and it works well to build a unique list of voxel hits (voxel world space coordinates).  I also had to use an atomic operation to add to the list to avoid race conditions.

I will have dynamic direct sunlight working soon...

The code is too complicated for me to easily see where the calculations are incorrect.  Perhaps a test case in 2D would be easier to (visually) debug.

Google's AI describes it better than I can: "A projection matrix is a square matrix in linear algebra that, when multiplied with a vector, outputs the projection of that vector onto a specific subspace; essentially, it "flattens" the vector onto a lower-dimensional space while maintaining its relative position within that subspace."

I use perspective projection matrices for all things 3D in my voxel engine (calculations of ray origins and directions, rasterized lines and triangles).  Rasterizing pipelines in 3D graphics use the matrices to flatten projected 3D coordinates onto 2D screen space coordinates in order to rasterize in 2D.

(8 edits)

Stating perhaps the obvious:  It looks to me like the ray origins and directions are not being calculated correctly.

Here are code snippets from my voxel ray march compute shader that calculate ray origins and directions (format is a little "wonky" pasted here in a code block) :

vec3 invViewProjOrigin(mat4 matrix) {
  vec4 origin = matrix * vec4(0, 0, -1, 0);
  return origin.xyz / origin.w;
}
vec2 ndcFragCoord(ivec2 resolution, ivec2 fragCoord) {
  return vec2((((fragCoord.x + 0.5) / resolution.x) * 2) - 1, 1 - (((fragCoord.y + 0.5) / resolution.y) * 2));
}
vec3 fragCoordRayDirection(ivec2 fragCoord, ivec2 resolution, float near, float far, mat4 invViewProjMatrix) {
  return normalize((invViewProjMatrix * vec4(ndcFragCoord(resolution, fragCoord), 1 + (near / far), 1)).xyz);
}
void main() {
  uint index = gl_GlobalInvocationID.x;
  if (index >= (resolution.x * resolution.y))
    return;
  ivec2 fragCoord = ivec2(index % resolution.x, index / resolution.x);
  vec3  origin    = invViewProjOrigin(invViewProjMatrix),
        direction = fragCoordRayDirection(fragCoord, resolution, settings.near, settings.far, invViewProjMatrix);
  ...
}

P.S.  I have my Vulkan pipeline's coordinates system y-flipped so positive y is up in world space.  Hence the "1 - (((fragCoord.y + 0.5) / resolution.y) * 2)" in ndcFragCoord().

Maybe the second one would be the commander of the evil triangles that arrive from outer space in their giant triangle shaped spaceship.  ;)

Awesome!  Keep up the good work.

Thank you!

Thank you!

Hantverk'n


(1 edit)

My understanding is that it's due to incomplete Vulkan driver implementations.  I have an old laptop that has an Intel integrated (non-discrete) GPU and when my code requests the fragment shading rate extension with logical device creation, Vulkan reports that the extension is unsupported.

P.S.  Same laptop doesn't support GLSL uint64 nor float64.

Thank you!

(2 edits)

P.S.  Decreasing ray march compute shader workgroup size local_size_x has improved frame rates.

(3 edits)

P.S.  I've now switched to 15-bit RGB voxel color because I noticed some time ago in procedural block generation where a noise function it uses to modulate brightness of colors was slightly shifting voxel colors.  The noise function modulates the RGB channels equally but because green had more bits (6), red (5) and blue (5) didn't have an even distribution with respect to the green channel.

Thank you!

Thank you!

(2 edits)

These procedurally generated blocks support Hilbert Curve grooves which are scaled. In the above screen shot I'm using a scale of three, which means there are two voxels between each voxel groove. To calculate the the ideal "order" for the Hilbert Curve function based on the bounds of the block (or world selection bounds) I use the following math/code: 

int order = log2i(clampMin(pow2Ceil(divCeil(max(bounds.width(), bounds.height(), bounds.depth()), scale)), 2))

FYI, all of these nested function calls are of functions in my C++ (template) math library.

And the inverse Hilbert Curve function I wrote is based on code from here:
https://people.math.sc.edu/burkardt/cpp_src/hilbert_curve/hilbert_curve.html

(1 edit)

Hantverk'n


Excellent!  I've been looking into using a gbuffer with the ray marching version of my compute shader...  :)

Thank you

Hantverk'n

After overhauling host/device buffer use, voxel level ambient occlusion bits generation is now down to 0.62 seconds for test scene nuke.vox

Amazing!

Thanks!

Procedurally generated mushrooms caps are defined by generating voxels confined between two equations (see link).  The stem is obviously a cylinder.

Ambient occlusion is unnecessary with global illumination, but it does add extra definition to voxels when combined with GI.

(1 edit)

Hantverk'n