Paper Goomba
Alright, alright. I know this isn’t rendering text to the grid. I know this isn’t “according to plan”! But… We need to walk with sprites in a 3d world before we can run with beautiful ASCII art in a 3d world. It turns out that Bevy does not make it easy to render text or sprites using a 3d camera. So we’ll need to get that working, and we’ll add a little extra embellishment as well. At the end of this post, we’ll be rendering some paper goombas that look like this. We are going to start with rendering basic images because ASCII text can be rendered to a texture and used in the same way as a sprite sheet. It turns out that rendering text is really hard though! So we’ll take the smaller step of rendering sprites in our 3d world first.
Code Refactor
I refactored the code a bit between the last post and this one. Normally you can follow along by making edits to the code with the post, but in this case I thought it would be easier to refactor the code and just include the important snippets in the post. If you want to follow along and try things out, get the code for this post.
2D Mesh for a 3D World
Our world is 3D, its glorious grid lines zooming off into the distance. But we want to render 2D characters to represent NPCs and players in our world. To do this, we will need to represent our characters using 3D meshes. In 2D graphics programming, we would use sprites and blit them to the screen. When rendering with a GPU, using a quad (2 triangles that form a rectangle) with a texture is a great way to accomplish the same thing as blitting. So, let’s create a mesh that will let us render our sprites in a 3D world. The mesh is just two quads back to back. One quad for the front one quad for the back. We’ll want to integrate our mesh and material into the physically-based rendering (PBR) pipeline, and so we will call our module pbr_sprite
. We’ll create a struct that we use to build our mesh, let’s call it QuadSprite
. We can base this code on the code for bevy::shape::Quad
.
pbr_sprite.rs
|
|
Rendering a Sprite in 3D
We’ll use this little goomba sprite that I hacked together in Aseprite.
main.rs
|
|
Running our application with cargo run
will produce a result that looks like this.
If we rotate to the back of the goomba, it looks like this.
Consistent Lighting
This is certainly an aesthetic. But it’s not one that I think works very well with the paper look. The goomba should be in a consistent light regardless of if we are looking at the front or back face. In order to accomplish this, we need to modify the normals of our mesh. Let’s point all of the normals in the up direction, on both faces. This will produce nice, consistent lighting. Se let’s create a new Mesh
from our QuadSprite
, we’ll call this new shape a PaperSprite
.
pbr_sprite.rs
|
|
main.rs
|
|
Rerun the scene with cargo run
and see the rewards that we have reaped.
Paper Material
This is cool, we’ve got a 2D goomba that is rendering in 3D space, and it’s well-lit! Let’s make it even more 2D, more papery, more better by adding an outline to the sprite. This will give it kind of a paper cutout look to it. We’ll start by adding a new PBR material extension called PbrPaperMaterial
.
See the shader code this is based on at: https://github.com/cukiakimani/shaders-yo/blob/master/Assets/Sprite%20Outline/Shaders/SpriteOutline.shader.
pbr_sprite.rs
|
|
pbr_paper.wgsl
|
|
We’ll use this new material in our scene to render the goomba with an outline.
main.rs
|
|
We should now see our paperized goomba when we run our application with cargo run
.
Next Up…
Alright, we are rendering a paperized goomba into our scene and it looks pretty good. We’ll want to start rendering our ASCII characters next. In order to do that we’ll have to make textures from our character glyphs and use them to render our paper material.
Fine the full code for this post here: TextGem Post 6.