CoolDotty / Shader-Stacker

The GPU accelerated version of Sprite Stacking. A top-down 2.5D trick for the Godot Engine.
https://dotty.cool/Shader-Stacker/
Mozilla Public License 2.0
54 stars 7 forks source link

feat: Go 3D! #10

Closed CoolDotty closed 2 years ago

Sousio commented 2 years ago

Hey Karl, happy to see you progress with the project. I just don't understand something: sprite stacking is used to fake 3D in 2D. But if you move to 3D, basically there is no need to sprite stacking anymore, as a simple mesh works best, with to extra overhead. So is it your artistic choice to continue stacking in 3D?

CoolDotty commented 2 years ago

To me, there's 2 compelling things about sprite stacking.

The first is the moddability. My theory is if you made a compelling enough sandbox on top of sprite stacking, you could potentially release a game with really sweet modding support. Most games require modders to know existing 3D tooling (blender, 3dsmax, or worse, something proprietary). On top of that, there's the question of rigging and skeletons, it's all a big barrier for people to get into. With sprite stacking, you could potentially make a game where this barrier is much lower. An aspiring modder just needs to know what sprite stacking is and how to use an image editor.

The second is how interesting it looks. The edges are oddly jagged, gaps appear and disappear between "lines" as the model rotates. Models are constantly in a state of discomfort over what pixel they should exist in. And it's all wrapped up into a ~30 degree perspective. Its an art style that has "rules" much like how an NES looking game would limit itself to the original pallet size, or a PS looking game would have limit on polygons per model. It feels nostalgic because of it, but at the same time, fresh because of how there never was a "sprite stacking" generation of graphics. It's really unique.

jaggy

That being said, the reason for 3D is to address 3 problems. Which, the word problem is used subjectively, because they're behaviors of the original effect which I personally have decided are undesirable.

1

Adding a performant way to lower the camera angle (which is crucial to achieving the Nium art style). Once the camera is set to orthogonal, the image and sprites are being transformed the same. The difference now being unintuitive values aren't baked into the engine frontend. Every billboard needed it's height doubled. All stacks needed to render each layer twice, making code confusing. All just to result in all the data being dropped at runtime in a nearest neighbor compression squishing the 8:9 (16:18) resolution into a 16:9 frame. And don't get me started on how this resulted in needing to fight the automatic offscreen culling in the 2D engine.

Now with the new code, the values in the inspector all make sense and no unnecessary calculations are made. The look is also the same due to being able to adjust camera3D from perspective into orthogonal rendering image

6

Fixing unexpected z sorting due to only the center point being considered. Gif is of the bug. Due to Real 3D™ magic, this is fixed in the new implementation zfighting

7

Related to #1, now the camera can support all 3D operations, or hook seamlessly into any existing camera 3D libraries.

The thing in common with all the above problems, is there are no tricks. The only way to solve them is to write the existing code in the 3D engine, but slower due to godot lang overhead. The goal isn't to make feature complete 3D, but is to make something performant that looks and acts like sprite stacking, and hooks into as many existing godot systems as possible to keep the developer experience in the editor enjoyable.

That being said, there's still some work to be done to hone in the effect. "Subpixels" are visible due to the "pixel size" between billboards and the layers are different when they should be the same. But this is something not inheretly wrong with a 3D approach, it just requires tuning of the viewport size and the px value in SpriteStack.tscn.

I'll be revisiting existing sprite stacking projects and look for other effects that are missing.

image

Sousio commented 2 years ago

Thank you for detailed answer. It got me thinking into them... I found the moddability idea very interesting. While modding can be much easier in 2D games overall, and pixel editing software are easier to start with in comparison to 3D ones, speaking of the sprite stacking, the users still may need a way to visualize the stacking result, which is not always very straightforward. And rigging/ skeletons only makes sense when animating models, which is not the case with the most of sprite stacking projects I've seen so far. I personally use 3D pipeline to better visualize the animated models before converting them to 2D sprite sheets, as it's so harder to work only with 2D sheets alone.

Models are constantly in a state of discomfort over what pixel they should exist in

Yeah exactly, and this is what makes it not pixel perfect, which may also contribute to causing some consequences like the "Subpixels" effect you've mentioned later as an issue. I firstly started to work on my project as a pixel art game, but then realized it has no way to remain pixel-perfect by sprite stacking. And then I decided that I like it and it's better to let it go. However if that style is the case, the same effect can be achieved in 3D by some pixelation shaders like this one by Lukenomics. But unlike you, I just avoid the extreme angles so not to show the stripes! This way it's not obvious for the gamer how the models are made, or whether it's 3D or 2D.

I totally agree with the issue #1 about the performance and and #7 about the 3d features. But I've had never experienced the issue #6 of YSorting, as I found it easy to Y-sort the objects based on the lowest coordination of the models, e.g. the shoes for a human. To avoid any interposition with the stacked-sprite's center coordination, I just offset them as a child of a 2D node parent.

Anyway thank you for the elaborated description, wish you best with your new journey.

CoolDotty commented 2 years ago

@Sousio Thanks for the reply!

I'm deep in the weeds working on another patch, but here's a quick update.

I understand your concerns about pixel perfect-ness. After seeing whats out there, it seems like most people consider "Jaggedness" in 3D pixel art to be undesirable, and instead, pixels should be aliased rotated squares.

Jagged vs Smoothed Source: SmoothPixelFiltering

This is incorrect for sprite stacking, as jaggedness super important for causing staircasing. Staircasing is an effect visible as the camera's rotation and position change. Because 2D engines enforce integer positioning of sprite pixels, subtle changes to the camera cause ripples in the sprite stack.

For example, if you focus on the striped rooftop of this hut, the very slow passive rotation of the camera causes the pattern to flow from right to left. Then when the character moves, you can also see this effect accelerated as the camera tweens to the new player position. staircasing

Another bug in the current build is randomized sprite rending. Identical sprites should look the same, regardless of where they are on the screen. This is because traditional sprite stacking picks a position and renders the sprite pixel perfectly the same in every location. Right now in 3D, sprite rendering has jaggies, but they're placed randomly by rounding errors when projecting the texture onto the mesh.

Notice how the left most column of pixels on the left sprite stack is not the same as on the right sprite stack. Despite being the camera position, rotation, and sprite stack being the same. The only difference was the position on screen. image

I'm hoping I can modify the shader such that it hooks into 3D object culling, depth sorting, and other optimizations, but then I take over and render the sprite in a deterministic way such that the same sprites all look and shimmer the same regardless of position. So my current goals are

And as an aside, I dug up the reason I didn't use lowest point depth sorting. image image You could solve this in the game design phase by not having very rectangular stacks, or by having extra large collision boxes on rectangular stacks. But I'm stubborn and convinced I can get authentic sprite stacking in a 3D 😜