Advertisement · 728 × 90

Posts by SimonDev

Post image Post image Post image Post image

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

2 months ago 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...

2 months ago 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.

2 months ago 2 0 1 0
Video

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.

2 months ago 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.

2 months ago 1 0 1 0
Video

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.

2 months ago 1 0 1 0
Video

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.

2 months ago 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

2 months ago 1 0 1 0
Video

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.

2 months ago 1 0 1 0
Advertisement
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)

2 months ago 1 0 1 0
Video

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.

2 months ago 1 0 1 0
Video

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.

2 months ago 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.

2 months ago 2 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

2 months ago 23 2 1 0

Absolutely!

2 months ago 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

2 months ago 9 0 3 0
Video

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)

2 months ago 7 0 1 0
Video

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.

2 months ago 2 0 1 0
Video

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.

2 months ago 3 0 1 0
Advertisement
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

2 months ago 3 0 1 0
Video

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

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

2 months ago 3 0 1 0
Video

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".

2 months ago 3 0 1 0
Video

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.

2 months ago 5 0 1 0
Video

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.

2 months ago 4 0 1 0
Video

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

It’s a quick, effective way to animate.

2 months ago 3 0 1 0
Video

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

2 months ago 3 0 1 0
Advertisement
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.

2 months ago 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

3 months ago 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̂))

3 months ago 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̂)

3 months ago 4 0 1 0