SimonDev's Avatar

SimonDev

@simondev

963
Followers
28
Following
144
Posts
24.09.2024
Joined
Posts Following

Latest posts by SimonDev @simondev

Post image Post image Post image Post image

Here are some techniques I discovered through 14 years of shader programming:

10.02.2026 23:34 👍 166 🔁 34 💬 4 📌 1
How to optimize (almost) anything
How to optimize (almost) anything YouTube video by SimonDev

Full video: youtu.be/phbaxNPJxss
Full course: simondev.io/lessons/game...

04.02.2026 14:57 👍 5 🔁 1 💬 0 📌 0
Post image

Once you've made it through all these steps

• Reuse materials
• Batch/instance
• Optimize data
• Cull
• LOD/imposters

We're hitting 1 million+ trees, for very little CPU/GPU cost.

04.02.2026 14:57 👍 2 🔁 0 💬 1 📌 0
Video thumbnail

Octahedral imposters are a powerful technique, where we render the object from many angles into an atlas texture, then just show a billboard in the world.

Key detail: it responds to camera movement and lighting, but it's just smoke and mirrors.

TSL makes it easy to hook into the lighting system.

04.02.2026 14:57 👍 1 🔁 0 💬 1 📌 0
Post image

At this point, the last lever left is reducing quality.

LOD (level-of-detail) works by dropping detail with distance. Further away objects, you swap meshes (LOD0 -> LOD1 -> LOD2) and nobody notices (hopefully)

With instancing, you'll have to do this manually with an InstancedMesh for each level.

04.02.2026 14:57 👍 1 🔁 0 💬 1 📌 0
Video thumbnail

At some point it’s hard to “draw faster”. So stop drawing stuff you can’t see.

Frustum culling removes anything offscreen.

Not automatic with InstancedMesh, so you can either:
• Instance within a chunk, then cull by chunk.
• Cull manually per-instance

We’re at 250k+ trees now.

04.02.2026 14:57 👍 1 🔁 0 💬 1 📌 0
Video thumbnail

You can quantize way further than most people think.
It’s possible to squeeze a ~56B vertex down to ~16B with packing + quantization.

TSL makes unpacking clean (override attributes via node API).

Source
: x.com/SebAaltonen/...

This gets us to 50k+ trees.

04.02.2026 14:57 👍 1 🔁 0 💬 1 📌 0
Post image

Now take a look at your data.

You want GPU-friendly assets, not just smaller downloads.

Meshes: weld verts, simplify, quantize
Textures: Use GPU compressed formats (like ETC1S/ UASTC)

I use my in-browser GLB optimizer to do most of this: gltf-optimizer.simondev.io

04.02.2026 14:57 👍 1 🔁 0 💬 1 📌 0
Video thumbnail

Instancing allows us to tell the GPU in a single draw call: "hey, draw this thing a zillion times".

No need for the CPU to constantly submit draw commands, which alleviates the load on the CPU and shifts the bottleneck to the GPU.

We're hitting 30k+ trees now.

04.02.2026 14:57 👍 1 🔁 0 💬 1 📌 0
Post image

To draw a lot of stuff, you want to reduce materials. Collapse different materials into a single material by packing textures into atlases.

Then you can collapse draw calls with:
• InstancedMesh (same geo)
• BatchedMesh (different geo)

04.02.2026 14:57 👍 1 🔁 0 💬 1 📌 0
Video thumbnail

Low-hanging fruit: stop duplicating assets.
Share geometry & materials across instances and you'll see immediate improvements in the framerate.

This change alone gets us to ~700–800 trees at 60fps.

04.02.2026 14:57 👍 1 🔁 0 💬 1 📌 0
Video thumbnail

Draw calls are often the first hurdle.

In this example, as the trees stream in, the FPS drops, and we cap out around ~500 or so.

04.02.2026 14:57 👍 1 🔁 0 💬 1 📌 0
Post image

The first step is to make sure you’re measuring the right things. You need both CPU time and GPU time, to understand where the problems lie.

I use three-perf or the Three.js Inspector so I can see both numbers easily.

04.02.2026 14:57 👍 2 🔁 1 💬 1 📌 0
Post image

Optimization can be tricky.
Here’s how to go from drawing a few hundred trees to virtually unlimited in Three.js, step by step.

This will be high level, but not so much that you can’t fill in the details.

#threejs

04.02.2026 14:57 👍 23 🔁 2 💬 1 📌 0

Absolutely!

27.01.2026 21:51 👍 2 🔁 0 💬 0 📌 0
An In-Depth look at Lerp, Smoothstep, and Shaping Functions
An In-Depth look at Lerp, Smoothstep, and Shaping Functions YouTube video by SimonDev

Full video: youtu.be/YJB1QnEmlTs
Full course: simondev.io/lessons/math

27.01.2026 21:36 👍 9 🔁 0 💬 3 📌 0
Video thumbnail

Another gotcha is damping (like camera smoothing)
Ex. if you do t = someConstant

Then: lerp(x, target, t) each frame, it’s frame-rate dependent.

People often try t = k * dt, which kinda works.

Instead compute t from deltaTime:
t = 1.0 - exp(-K * deltaTime)

or:
t = 1.0 - pow(someDecay, deltaTime)

27.01.2026 21:36 👍 7 🔁 0 💬 1 📌 0
Video thumbnail

Interpolating unit directions and rotations can be tricky.

Lerp directly interpolates between A and B, which leaves you with a non-normalized vector.

NLerp fixes this (normalize after lerp). It's fast and usually good enough for small angles.

Otherwise, slerp gives you constant angular speed.

27.01.2026 21:36 👍 2 🔁 0 💬 1 📌 0
Video thumbnail

If you need to lerp scale/zoom, then you'll want to use this transform trick.

In this case, transform to log space, lerp, and transform back.

You can see the animation on the left seems to accelerate/decelerate more abruptly than the one on the right.

27.01.2026 21:36 👍 3 🔁 0 💬 1 📌 0
Post image

Another powerful trick with lerp is to transform your inputs to another space, perform your lerp, and transform back.

This works especially well with colours:

So you've got:
• top: lerp of rgb values
• middle: lerp through HSV
• bottom: lerp through OKLAB

27.01.2026 21:36 👍 3 🔁 0 💬 1 📌 0
Video thumbnail

And it's incredible the types of "shaping" (or "easing") functions that exist.

Here's a little montage of a few fun ones.

27.01.2026 21:36 👍 3 🔁 0 💬 1 📌 0
Video thumbnail

Most of lerp's power comes from shaping the t parameter.

Here, we're doing a lerp on the position. Left uses t directly, while the right uses smoothstep(t)

One just looks "smoother".

27.01.2026 21:36 👍 3 🔁 0 💬 1 📌 0
Video thumbnail

If Bezier curves have ever scared you, you may be surprised to see that one way of doing them (de Casteljau) is just a big pile of lerps.

27.01.2026 21:36 👍 5 🔁 0 💬 1 📌 0
Video thumbnail

More sophisticated blending can be achieved just by using multiple lerps.

Bilinear filtering, the type used by your GPU, is just 3 lerps in a trenchcoat.

27.01.2026 21:36 👍 4 🔁 0 💬 1 📌 0
Video thumbnail

Basic uses are simple: lerp positions, colors, scales… anything that changes over time.

It’s a quick, effective way to animate.

27.01.2026 21:36 👍 3 🔁 0 💬 1 📌 0
Video thumbnail

Starting with the basic idea, lerp allows you to smoothly blend between 2 values. Typically, we use it with a "t" value between 0 and 1.

value = lerp(a, b, t);

So you get:
• a when t = 0
• b when t = 1

27.01.2026 21:36 👍 3 🔁 0 💬 1 📌 0
Post image

Lerp is used everywhere in games. It’s simple, but combined with a few small tricks it becomes incredibly powerful.

This thread is full of visual examples.

27.01.2026 21:36 👍 45 🔁 13 💬 2 📌 0
What Kind of Math Should Game Developers Know?
What Kind of Math Should Game Developers Know? YouTube video by SimonDev

Full video on gamedev math: youtu.be/eRVRioN4GwA

08.01.2026 15:02 👍 10 🔁 1 💬 0 📌 0
Post image

And of course, the dot product is the basis of diffuse lighting (Lambert).

With L̂ as the incoming light direction:

lambert = max(0, dot(N̂, -L̂))

08.01.2026 15:02 👍 2 🔁 0 💬 1 📌 0
Post image

Let’s say you’ve got a projectile, and you want to know how far it travelled along the ground this frame.

Compute the displacement vector a, then:

distanceAlongGround = dot(a, groundDir̂)

08.01.2026 15:02 👍 4 🔁 0 💬 1 📌 0