SimonDev's Avatar

SimonDev

@simondev.bsky.social

956 Followers  |  28 Following  |  144 Posts  |  Joined: 24.09.2024  |  2.0802

Latest posts by simondev.bsky.social on Bluesky

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 โ€” ๐Ÿ‘ 165    ๐Ÿ” 34    ๐Ÿ’ฌ 4    ๐Ÿ“Œ 1
How to optimize (almost) anything
YouTube video by SimonDev How to optimize (almost) anything

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

04.02.2026 14:57 โ€” ๐Ÿ‘ 4    ๐Ÿ” 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 โ€” ๐Ÿ‘ 1    ๐Ÿ” 0    ๐Ÿ’ฌ 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 โ€” ๐Ÿ‘ 20    ๐Ÿ” 1    ๐Ÿ’ฌ 1    ๐Ÿ“Œ 0

Absolutely!

27.01.2026 21:51 โ€” ๐Ÿ‘ 2    ๐Ÿ” 0    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0
An In-Depth look at Lerp, Smoothstep, and Shaping Functions
YouTube video by SimonDev An In-Depth look at Lerp, Smoothstep, and Shaping Functions

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 โ€” ๐Ÿ‘ 6    ๐Ÿ” 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 โ€” ๐Ÿ‘ 44    ๐Ÿ” 13    ๐Ÿ’ฌ 2    ๐Ÿ“Œ 0
What Kind of Math Should Game Developers Know?
YouTube video by SimonDev What Kind of Math Should Game Developers Know?

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

@simondev is following 17 prominent accounts