Sign in

SimonDev

@simondev.bsky.social
1K followers 28 following 168 posts
PostsRepliesMedia
SimonDev @simondev.bsky.social · 06/08/2026
Live demo: simondev.io/demos/gamede...
simondev.io
SimonDev.ioSimonDev.io
Learn real-time 3D from an industry veteran. From fundamentals to advanced GPU techniques, build projects that perform, scale, and stand out.
020
SimonDev @simondev.bsky.social · 06/08/2026
One last little trick: Normal fog blending kinda sucks, it basically just blends towards a colour. Instead, split that into 2 separate calculations, one for extinction and one for in-scattering. • extinction = darker silhouettes • in-scattering = brighter haze
120
SimonDev @simondev.bsky.social · 06/08/2026
This is a cheap height fog from Crytek's 2006 presentation. Instead of looking only at distance, it integrates an exponentially thinning fog layer along the camera ray. Valleys fill with dense fog, while the peaks stay relatively clear.
120
SimonDev @simondev.bsky.social · 06/08/2026
The terrain looks decent, but it's lacking a sense of depth. Basic distance fog is easy: blend toward a blurred sample of the sky as the terrain gets farther away. It works, but it doesn't look awesome.
120
SimonDev @simondev.bsky.social · 06/08/2026
Obviously, you can combine these however you want. Here we used low-frequency terraces to create the large shapes, then added the weathered fBM on top.
120
SimonDev @simondev.bsky.social · 06/08/2026
Terraces are another cheap shaping trick. Split the height range into steps, but keep a short ramp between each plateau. That gives us broad, flat areas without making every transition a vertical cliff.
130
SimonDev @simondev.bsky.social · 06/08/2026
A neat trick is to use noise derivatives, which tell you how steep the terrain is. Use that slope to reduce the smaller details in steep areas, and regular fBM starts to look weathered. Quick cheapie erosion effect.
120
SimonDev @simondev.bsky.social · 06/08/2026
The same trick works with Voronoi noise. Notice this gives a sort of "pockmarked" effect.
120
SimonDev @simondev.bsky.social · 06/08/2026
fBM isn’t a special kind of noise. It’s just a way of layering noise. Here, we're layering the "ridged" noise from earlier. Notice this gives a much rockier effect.
120
SimonDev @simondev.bsky.social · 06/08/2026
There are other types of noise. Voronoi noise scatters a point inside each grid cell, then calculates the distance to the nearest one. Checking the surrounding 3×3 cells gives us a completely different noise function.
120
SimonDev @simondev.bsky.social · 06/08/2026
A tiny change gives the noise a completely different character: ridge = 1.0 - abs(noise) This gives you sharp ridges.
120
SimonDev @simondev.bsky.social · 06/08/2026
One layer of noise is smooth, but you need more to get convincing terrain. You can use fBM (fractional brownian motion). Sample the noise repeatedly, at different scales, adding more detail iteratively. Extra: pow() reshapes the height range. I used "3" to flatten valleys and make peaks stand out.
130
SimonDev @simondev.bsky.social · 06/08/2026
An easy approach to procedural terrain: heightmaps Start with a tesselated plane, and calculate the height for each vertex. In our example, every vertex samples cheap value noise: height = noise(position.xy * frequency) * amplitude; Frequency -> xy scaling of noise Amplitude -> terrain height
120
SimonDev @simondev.bsky.social · 06/08/2026
If you wanna make better looking procedural terrain, this thread is full of simple tricks you can use straight away. #threejs #gamedev
1357
Reposted by SimonDev
Anderson Mancini @andersonmancini.bsky.social · 09/06/2026
New React Three Fiber Performance Monitor designed for WebGPU launching today: www.npmjs.com/package/r3f-... Please provide some feedback and give some start on GitHub if you think it can be useful to others ;)
161
SimonDev @simondev.bsky.social · 05/06/2026
Full video: youtu.be/sChQCdbLdHE Full course: simondev.io/lessons/game...
youtu.be
The ONE Texture Every Game NEEDS
YouTube video by SimonDev
030
SimonDev @simondev.bsky.social · 05/06/2026
For camera shake, use vector noise: offset = vectorNoise(time); This is just noise that gives you a 2D "random" value back. You can get the same thing by doing: offset.x = noise(time, seed1); offset.y = noise(time, seed2); This gets you smooth but unpredictable movement, with no jarring jumps.
150
SimonDev @simondev.bsky.social · 05/06/2026
Practical example: making a light flicker. Instead of random flicker, use 1D noise over time: • smooth variation • no harsh jumps • still feels organic light_intensity = noise(currentTime * frequency) * magnitude + bias; or light_intensity = fbmNoise(currentTime * frequency) * magnitude + bias;
140
SimonDev @simondev.bsky.social · 05/06/2026
fBM noise is an amazing starting point for procedural terrain. Treat the noise as a heightmap, where brightness = elevation. Here, we're simply taking a grid and using fBM to choose the height at any given point.
140
SimonDev @simondev.bsky.social · 05/06/2026
One powerful trick is called fBM, short fractional (or fractal) Brownian motion. Basically layering noise at increasing frequencies and decreasing amplitudes. You sample the noise, transform the coordinates, and sample again. Over and over, blending the results together.
161
SimonDev @simondev.bsky.social · 05/06/2026
There are lots of different flavours of "basic" noise. Value, Gradient, Perlin, Simplex. They all look a bit different, but the general idea is: use them as a source of controlled variation.
131
SimonDev @simondev.bsky.social · 05/06/2026
If you lay those random values out in space (a grid is the easiest) then you can interpolate between the points. This turns a bunch of random values into smooth "randomness", or noise.
151
SimonDev @simondev.bsky.social · 05/06/2026
Most noise starts with a super simple trick: Give it some input, like a position (x, y), and it returns a repeatable pseudo-random value. The hash function internally does a bunch of math to spit out something that "looks random enough".
151
SimonDev @simondev.bsky.social · 05/06/2026
Noise is used everywhere in games: terrain, animations, camera shake, and more. But it can still feel like magic. How do you turn greyscale blobs into real effects? Here are a few practical ways to use it. #threejs #shaders
15113
SimonDev @simondev.bsky.social · 27/04/2026
Hyped about AI? Hate AI? Have literally no opinion on AI? I made a video about AI & agentic coding, and what it might mean for you as a developer. youtu.be/2dTENijF30c
youtu.be
AI Coding Works. That’s the Problem
YouTube video by SimonDev
0130
Reposted by SimonDev
Xor @xordev.com · 10/02/2026
Here are some techniques I discovered through 14 years of shader programming:
422648
SimonDev @simondev.bsky.social · 04/02/2026
Full video: youtu.be/phbaxNPJxss Full course: simondev.io/lessons/game...
youtu.be
How to optimize (almost) anything
YouTube video by SimonDev
051
SimonDev @simondev.bsky.social · 04/02/2026
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.
120
SimonDev @simondev.bsky.social · 04/02/2026
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.
110
SimonDev @simondev.bsky.social · 04/02/2026
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.
110
SimonDev @simondev.bsky.social · 04/02/2026
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.
110
SimonDev @simondev.bsky.social · 04/02/2026
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.
110
SimonDev @simondev.bsky.social · 04/02/2026
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
110
SimonDev @simondev.bsky.social · 04/02/2026
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.
110
SimonDev @simondev.bsky.social · 04/02/2026
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)
110
SimonDev @simondev.bsky.social · 04/02/2026
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.
110
SimonDev @simondev.bsky.social · 04/02/2026
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.
110
SimonDev @simondev.bsky.social · 04/02/2026
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.
120
SimonDev @simondev.bsky.social · 04/02/2026
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
1242
SimonDev @simondev.bsky.social · 27/01/2026
Absolutely!
020
SimonDev @simondev.bsky.social · 27/01/2026
Full video: youtu.be/YJB1QnEmlTs Full course: simondev.io/lessons/math
youtu.be
An In-Depth look at Lerp, Smoothstep, and Shaping Functions
YouTube video by SimonDev
390
SimonDev @simondev.bsky.social · 27/01/2026
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)
170
SimonDev @simondev.bsky.social · 27/01/2026
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.
120
SimonDev @simondev.bsky.social · 27/01/2026
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.
130
SimonDev @simondev.bsky.social · 27/01/2026
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
140
SimonDev @simondev.bsky.social · 27/01/2026
And it's incredible the types of "shaping" (or "easing") functions that exist. Here's a little montage of a few fun ones.
130
SimonDev @simondev.bsky.social · 27/01/2026
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".
130
SimonDev @simondev.bsky.social · 27/01/2026
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.
150
SimonDev @simondev.bsky.social · 27/01/2026
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.
140
SimonDev @simondev.bsky.social · 27/01/2026
Basic uses are simple: lerp positions, colors, scales… anything that changes over time. It’s a quick, effective way to animate.
130