Skip to content

Commit

Permalink
many changes
Browse files Browse the repository at this point in the history
  • Loading branch information
stevenctl committed Oct 10, 2024
1 parent 3118b9a commit 50c3201
Show file tree
Hide file tree
Showing 225 changed files with 10,926 additions and 151 deletions.
Binary file added assets/train.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion config/_default/params.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ enableSearch=false
autoSwitchAppearance=false

[homepage]
layout="page"
layout="custom"
# layout="page"
homepageImage="train.png"

[article]
showDate=false
Expand Down
4 changes: 2 additions & 2 deletions content/_index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
---

{{< list limit=10 cardView=true title="Technical Posts" where="Type" value="tech">}}
{{< list limit=100 cardView=true title="Technical Posts" where="Type" value="tech">}}

{{< list limit=10 cardView=true title="Art" where="Type" value="art">}}
{{< list limit=100 cardView=true title="Art" where="Type" value="art">}}
70 changes: 21 additions & 49 deletions content/art/characters/index.md
Original file line number Diff line number Diff line change
@@ -1,70 +1,42 @@
---
title: Characters and Animation
title: My First Character Models
type: art
layout: "simple"
---

{{<video "showcase.webm">}}

I have yet to spend a _ton_ of time character modeling. These characters are the
best of what I've made so far.
These are a few of the first things I ever made in Blender. It turns out
characters are way harder than hard surface.

## Legfish
### Legfish

![legfish wireframe](legfish-wire.png)

The first character I made. Inspired by a figurine my wife made with some
leftover clay.

![skin workflow](skin.png)

For this, I used a [Skin
Literally the first completed model I ever made in Blender. For this, I used a [Skin
Modifier](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/skin.html)
and [Subdivision
Surface](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/subdivision_surface.html)
based workflow. Joey Carlino has some great
[tutorials](https://www.youtube.com/watch?v=DAAwy_l4jw4&t=2s) for this on
YouTube.

The significant advantage is that we only work with a skeleton instead of actual
geometry until the modifier is applied. This approach delays the need to think
about topology, which was very helpful as this project was one of my first ever
in Blender. The branch merging behavior confused me, so I avoided this workflow
to have 100% control of the topology. In the future, I want to revisit this
approach. Now, I am confident I can fix things by hand as needed.

## Cat Knight

{{<video "cat-run.webm">}}
YouTube. A nice starting point, but a bit finnicky.

My first run/walk cycle! I'm proud of it. At first, I was resigned to using
[Mixamo](https://www.mixamo.com/#/) because animation is too hard. Their library
wasn't compatible with my stylized characters' oversized heads. The animations
were too "real" as well. The model is cartoony, and the motion should be too.
### Cat Knight

I read the first few chapters of [The Animator's Survival
Kit](http://www.theanimatorssurvivalkit.com/) because winging it produced
unsatisfying results. While my focus will never be animation, learning the basic
concepts went a long way.
Kit](http://www.theanimatorssurvivalkit.com/) because Mixamo animations look
pretty bad on stylized characters, and winging it produced wasn't working
either. I'm never going to be a professional animator, but it's nice to be able
to throw things together for my own use. So this is my first run cycle.

![cat wireframe](cat-wire.png)

I box-modeled this mesh instead of generating it with the skin modifier. To make
things smoother, I used Catmull-Clark Subdivision and then used the
[Un-Subdivide](https://docs.blender.org/manual/en/latest/modeling/meshes/editing/edge/unsubdivide.html)
operator to keep the amount of geometry manageable while still getting a bit
more smoothness/roundness in the mesh. I got to try out some topology tricks at
the knees and elbows to help them deform properly.

I just copied the head and cut out some parts to make the helmet. Same with the
boots. I lifted the armor from the [Synty Fantasy
Hero](https://syntystore.com/products/polygon-modular-fantasy-hero-characters)
asset pack and played with the scale until it fit the character. I don't plan to
keep it long-term , but it looks good enough for now.

## Gobgob

![gobgob](gobgob.png)
### Gobgob

Because I already had a stylized biped base rigged, I reused that, modeled a new
head, and stitched that on. After extruding the ears, I learned a bit about
rerouting edge/face flow. I like him a lot.

{{< gallery >}}
<img src="legfish-wire.png" class="grid-w50">
<img src="skin.png" class="grid-w50">
<img src="cat-wire.png" class="grid-w50">
<video src="cat-run.webm" class="grid-w50" autoplay muted loop></video>
<img src="gobgob.png" class="grid-w50">
{{< /gallery >}}
Binary file added content/art/kopitiam/featured.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions content/art/kopitiam/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
title: Kopitiam
type: art
showTableOfContents: false
layout: "simple"
---

{{< gallery >}}

<img src="kopitiam_ext.png" class="rounded-2xl grid-w100" />
<img src="kopitiam_int.png" class="rounded-2xl grid-w100" />

{{< /gallery >}}
Binary file added content/art/kopitiam/kopitiam_ext.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added content/art/kopitiam/kopitiam_int.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
79 changes: 16 additions & 63 deletions content/art/train/index.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
---
title: Train Station
---
title: Train Station
type: art
showTableOfContents: false
layout: "simple"
---

I saw [this](https://joshuaautumn.artstation.com/projects/qAmeAz) picture on
Reddit or something, and I decided to recreate it. There were other decorations
I wanted to put in the train station, like a Goblin-themed vending machine and
better-looking torches. I needed to wrap it up and move on.
To build the environment, I re-used the tileset that I made for my Wave Function
Collapse level editor. Rather than copying the tiles into this blender file and
To build the environment, I re-used the tileset that I made for my [Wave Function
Collapse](/tech/wfc-02-advanced-wfc/) level editor. Rather than copying the tiles into this blender file and
arranging the tiles by hand, I was able to use my level editor in Godot, then
snapshot the scene using
[PackedScene](https://docs.godotengine.org/en/stable/classes/class_packedscene.html),
Expand All @@ -19,69 +21,20 @@ scene, the exported GLTF contains duplicated mesh data. I plan on looking into
whether this is something that can be optimized in Godot's exporter or is a
restriction in the GLTF format. The `blend` file should not be 90 megabytes.

---
I also played around with keying arbitrary properties and actually touched the
Non-Linear animation stuff for the first time.

### Animation

{{<video anim.webm >}}

This was my first time doing a couple of things in Blender.

1. Keying arbitrary properties. The torches have their "Power" animated to go up
and down. The phase offset on the wave texture is also keyed to increase with
time.

2. Using the Non-Linear Animation Tool. Before I would just duplicate the
keyframes for the duration of the clip. I wanted to repeat the idle animation
of my character.

### Projection

![orthographic](ortho-train.png)

I personally liked using an orthographic camera for rendering as an art piece.

![perspective](persp-train.png)

Perspective projection gives much more of an "in-game" feel. It would be cool to
make put this in a game someday. Maybe as a fast travel system like Hollow
Knight's stag.

### Wireframes

![wireframe](train-wire.png)

One lesson I learned during this project is that it's probably not worth cutting
holes in things that will never deform, and you can cover them using separate
objects. It was also the first time I'd used the bevel modifier, and I needed to
keep the topology clean enough for smooth shading.

![wireframe](train-wire-inside.png)

Although not strictly necessary, I did take the time to model the interior of
the train.

### Texturing

![pallete](pallete.png)

To color, I used a simple palette texture. Entire faces are each assigned a
color using this texture. Except for the "LCD" screen on the front of the
train. While I referenced a Japanese subway train, I can't speak or read
Japanese. I do like the style of Kanji here, and I a decent amount of Chinese,
so I just put "火车站" (huo che zhan) meaning train station.

![emissive check](emissive.png)
{{< gallery >}}

I wanted only the text on the screen to be emissive. It probably isn't the
simplest solution, but I first thought to check for specific colors and plug
that into the "emissive" socket of Blender's BSDF node. I ended up also using
that for the lights on the interior.

![levitation](levitation.png)
<!--<video src="anim.webm" class="grid-w50" />-->
<img src="train.png" class="grid-w50" />
<img src="ortho-train.png" class="grid-w50" />
<img src="persp-train.png" class="grid-w50" />
<img src="train-wire.png" class="grid-w50" />
<img src="train-wire-inside.png" class="grid-w50" />

I didn't feel like modeling anything else. Instead of building the mechanisms
beneath the train car, we will say, "It's magic." I decided to mess around with
some built-in noise textures, and the Wave Texture node is pretty cool. I made
parts of it transparent and most of it emissive. The result is decent. Plugging
it into the volume output seemed fine in the render, so I left it.
{{< /gallery >}}
Binary file added content/art/train/train.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added content/tech/buoyancy/shipship.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added content/tech/buoyancy/terrainedit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added content/tech/terrain/clipmap.webm
Binary file not shown.
Binary file added content/tech/terrain/featured.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
118 changes: 118 additions & 0 deletions content/tech/terrain/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
---
title: Non-Destructive Terrain Editor
type: tech
---

I am not a good artist. One of the reason 3D is a bit more
attractive to me is that I can usually build something rather
than draw it or sculpt it. Digital art gives you an "undo"
button, but being able to undo or redo things out of order makes
it even easier to experiment.

Most of the terrain tools out there have a destructive workflow. Using various
brushes, you write directly to a heightmap. Including the concept of "layers"
can help here, but I want something closer to modeling.

What I've built is a way to take `Node3D`s tagged as `ShapeInstance`s and
compose them onto the heightmap. Their `y` position is their height, and their
`y` scale is the steepness of the shape. Other transform properties work normally (except for rotation on `x` and `z`).

{{<video "terrain.webm">}}

A couple of custom properties are `roundness` so that rectancular shapes don't
have sharp corners, and a `shape` ID. The currently supported shapes are
`recatangle`, `circle` and `ramp`. In the future, I'd like to get rid of `ramp`
and instead support rotation on the horizontal axes for creating slopes.

## SDFs

Signed distance functions are an easy way to describe shapes using math. This
could be in either 2D or 3D. [Inigo
Quilez](https://iquilezles.org/articles/distfunctions/) has a nice library of
functions for different shapes on his website. They have all sorts of uses in
graphics; you can [render them directly](https://www.youtube.com/watch?v=BNZtUB7yhX4)
with a ray marcherwith them direclty, they can be used in [global
illumination](https://docs.godotengine.org/en/stable/tutorials/3d/global_illumination/using_sdfgi.html),
and they are probably in many other ways.

The SDFs of each shape are composed onto a single heightmap in a compute shader that looks roughly like this:

```glsl
layout(set = 0, binding = 0, std430) buffer ParamsBuffer {
int n_shapes;
int resolution;
float world_size;
vec2 world_offset;
}
params;
layout(set = 0, binding = 2, std430) buffer ShapesBuffer {
ShapeData data[MAX_SHAPES];
} shapes;
layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
void main() {
vec2 uv = gl_GlobalInvocationID.xy;
float height = -999999999.0;
for (uint i = 0; i < params.n_shapes; i++) {
ShapeData shape = shapes.data[i];
shape.position = world_to_shader(shape.position);
shape.size *= shader_scale();
shape.steepness *= shader_scale();
float shapeHeight = heightmapFromDistance(shape, uv, sdf(shape, uv));
if (shapeHeight >= height) {
height = shapeHeight;
maxInfluence = int(i);
}
}
int heightIdx = toIndex(uv);
heightmap.data[heightIdx] = max(height, 0.0) / MAX_HEIGHT;
}
```

## Clipmap

One difficult issue with large terrains is stitching the borders of chunks.
Instead of dealing with that, we can use a wandering
[clipmap](https://developer.nvidia.com/gpugems/gpugems2/part-i-geometric-complexity/chapter-2-terrain-rendering-using-gpu-based-geometry).
A massive plane that has more subdivisions towards the center, and fewer on the
edges gives us some basic LOD. As the player moves through the world, we
peridically recenter the clipmap at their position so the stuff they can see
closely has higher detail.

{{<video "clipmap.webm">}}

This technique works _especially_ well with the way we generate the heightmap.
We don't even need to create chunks of the heightmap, or be concerned with
artifacts due to sampling along the borders of two chunks. Instead, we can just
center the heightmap's world space offset along with the clipmap, and only
include the `Node3D` shapes that would be visible at this offset. To avoid
regenerating the heightmap everytime we move the clipmap, we can generate the
heightmap to be 2 times as big in world space. When we move outside some
margin, the heightmap gets recentered.


## Cursor Selection

In the editor, a `Node3D` with no collisions is a bit annoying to select in the
tree rather than visually. Writing the shape index that is actually influencing the
heightmap at some point lets us give the heighmap its own collider, and based on the `xz`
coordinate, we can sample that "influence map" to select the right shape.

![influence map](influence_map.png)


## Splat Mapping

It would be pretty boring to only have the grass, cliff and beach textures
based on normals and height. Paths, grass, plazas and other interesting details
should be available.

{{<video "splatmap.webm">}}

With a few modifications, we can use the very same code for terrain generation
for splatmap generation as well. Using these primitive shapes is pretty
unwieldy , so in the future I'd like to be able to use the `Path3D` node to
create paths and roads on the splatmap, as well as a free form drawing brush.
Maybe noise texture overlays as well with masking.
Binary file added content/tech/terrain/influence_map.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added content/tech/terrain/splatmap.webm
Binary file not shown.
Binary file added content/tech/terrain/terrain.webm
Binary file not shown.
Loading

0 comments on commit 50c3201

Please sign in to comment.