Pekka Väänänen's Avatar

Pekka Väänänen

@pekkavaa.bsky.social

Avid reader, computer graphics fan and atmospheric jungle beats enjoyer. Demoscene: cce/Peisik. Blog at https://30fps.net/

1,090 Followers  |  315 Following  |  176 Posts  |  Joined: 14.10.2024  |  1.9772

Latest posts by pekkavaa.bsky.social on Bluesky

I'm happy we took the time make a final version back then :)

10.08.2025 13:59 — 👍 1    🔁 0    💬 0    📌 0
A counter example where the PCA approximation fails.
The cross shape consists of 200 points with 100 uniformly distributed both on the X and Y axes. However, the points on the X axis have twice the range, hence more variation. PCA (red) interpretes the situation correctly but the approximation (orange) fails.

A counter example where the PCA approximation fails. The cross shape consists of 200 points with 100 uniformly distributed both on the X and Y axes. However, the points on the X axis have twice the range, hence more variation. PCA (red) interpretes the situation correctly but the approximation (orange) fails.

I wrote earlier of a PCA approximation which worked surprisingly well. After some discussions, I now finally have a clear counter example where it fails. If the channels are uncorrelated, the result is an average that's clearly wrong.

Updated the article: 30fps.net/pages/approx...

07.08.2025 11:52 — 👍 6    🔁 1    💬 0    📌 0

I've used their event tracing (ETW) compiler integration and it was indeed very useful! Managed to drop build times from 17 minutes to 6 in one project :) Now I run Linux, and when working on N64 homebrew I have to use gcc instead of clang so my options are more limited.

07.08.2025 10:02 — 👍 0    🔁 0    💬 0    📌 0
from dataclasses import dataclass

@dataclass
class Data:
    value: int
    other = 1  # conceptually not part of a dataclass

a = Data(0)
b = Data(0)
assert a == b, "values are equal"
b.other = 100
assert a == b, "values are still equal!"

print("a:", a)
print("b:", a) # even in printouts "other" is ignored
print("a == b:", a == b)

from dataclasses import dataclass @dataclass class Data: value: int other = 1 # conceptually not part of a dataclass a = Data(0) b = Data(0) assert a == b, "values are equal" b.other = 100 assert a == b, "values are still equal!" print("a:", a) print("b:", a) # even in printouts "other" is ignored print("a == b:", a == b)

Apparently in Python's dataclasses any member not set via the constructor gets ignored in equality checks. Both asserts pass in the code included code snippet. I put this example to a gist of it so I can reference it in the future: gist.github.com/pekkavaa/4a1...

05.08.2025 17:56 — 👍 3    🔁 0    💬 0    📌 0

So now I tried a different approach: use the full STL but compile as C++11. Result: Test file compile time dropped from 285 ms to 227 ms. So even a lean copy of the C++17 STL compiled slower than the real C++11 STL. I suppose the <memory> include I still have to use for placement new was slow in 17.

04.08.2025 11:52 — 👍 1    🔁 0    💬 1    📌 0

I'm trying to keep compile times under control in my N64 renderer, so I opted for writing a couple simple STL types (e.g. vector) myself. Since in C++17 the STL headers have gotten quite big, I had extracted only the necessary parts, such as std::forward, to make it work. But GLIBC is of course GPL!

04.08.2025 11:52 — 👍 4    🔁 1    💬 1    📌 0

I was planning to add a slider exactly like this to my tool but got sidetracked by all kinds of other stuff (wow it's a lot of work to make a GUI!)

03.08.2025 09:45 — 👍 2    🔁 0    💬 1    📌 0
Preview
GitHub - pekkavaa/gdbfile-n64: Emulated N64 file system access via GDB Emulated N64 file system access via GDB. Contribute to pekkavaa/gdbfile-n64 development by creating an account on GitHub.

I open sourced a little C library for GDB file access. It's meant for N64 homebrew running in an emulator: github.com/pekkavaa/gdb...

The main contribution here is perhaps the hooking Python script that works in old and partially-installed versions of GDB without its bundled Python helper modules.

30.07.2025 17:56 — 👍 10    🔁 3    💬 0    📌 0

I believe it was yet to come at the time. Perhaps in offline rendering?

27.07.2025 09:02 — 👍 0    🔁 0    💬 1    📌 0
A diagram from "The Irradiance Volume" by Gene Greger, Peter Shirley, Philip M. Hubbard, and Donald P. Greenberg. 1998.

A diagram from "The Irradiance Volume" by Gene Greger, Peter Shirley, Philip M. Hubbard, and Donald P. Greenberg. 1998.

A result render from "The Irradiance Volume" by Gene Greger, Peter Shirley, Philip M. Hubbard, and Donald P. Greenberg. 1998.

A result render from "The Irradiance Volume" by Gene Greger, Peter Shirley, Philip M. Hubbard, and Donald P. Greenberg. 1998.

Interesting to see how in the original "The Irradiance Volume" paper from 1998 they stored irradiance per discrete direction instead of smooth spherical harmonics. Makes sense of course, it's basically a cubemap.

22.07.2025 17:56 — 👍 10    🔁 0    💬 2    📌 0

By the way, I added a mention of that ShaderToy at the end of the article.

21.07.2025 08:54 — 👍 1    🔁 0    💬 0    📌 0

A neat 3D example of fitting an oriented bounding box using the approximate first principal component method I wrote about. Note that in the bottom left corner there's a toggle button for comparing between the exact PCA (red) its approximation (green).

20.07.2025 17:01 — 👍 6    🔁 1    💬 0    📌 0

I'm glad you find it useful. I still like to think the method should have a name somewhere in the statistics literature. "Directed mean absolute deviation" or something. A 3D vector sign flip to be consistent before averaging is kind of like taking the absolute value before summing some numbers.

20.07.2025 16:42 — 👍 2    🔁 0    💬 1    📌 0

Really cool :) Sometimes the PCA fit seems a tiny bit tighter than the approximation (more accurate 2nd and 3rd components?) but it's very close. Fun to see that just "some" orthogonal basis is enough for an OBB after you get some estimate of the first component, didn't think of that myself.

20.07.2025 15:51 — 👍 1    🔁 0    💬 1    📌 0
A plot comparing the original PCA and a simpler approximation. Accurate (red) and approximate (orange) first principal components are shown. The approximation seems close to the original based on this experiment. The shown directions are aggregated over five random trials.

A plot comparing the original PCA and a simpler approximation. Accurate (red) and approximate (orange) first principal components are shown. The approximation seems close to the original based on this experiment. The shown directions are aggregated over five random trials.

A new article on my site: Approximate first principal component

It describes simple trick to estimate the direction of most spread of a bunch of points without running a full PCA. Includes my Python re-implementation and plots of some quick tests.

30fps.net/pages/approx...

17.07.2025 17:56 — 👍 32    🔁 6    💬 1    📌 0

Lots of room for experimentation for sure :) I've yet to really dig into dithering myself.

17.07.2025 12:15 — 👍 0    🔁 0    💬 0    📌 0
A screenshot of an image editing tool with three horizontal panels. The first two show versions of the same image, with the one on the right slightly degraded. The third column includes a single horizontal slider labelled "Colors", set to 16, and a "Generate palette" button. Looks like a professional UNIX tool!

A screenshot of an image editing tool with three horizontal panels. The first two show versions of the same image, with the one on the right slightly degraded. The third column includes a single horizontal slider labelled "Colors", set to 16, and a "Generate palette" button. Looks like a professional UNIX tool!

I wanted to try using some experimental color quantization algorithms for a real project (on the N64) so I started working on a little GUI tool. Interactive parameter editing is already fun with that single color count slider😀 And tk's "Professional Motif Look" keeps expectations at the right level.

17.07.2025 11:52 — 👍 9    🔁 1    💬 0    📌 0
A four-pane comparison of the same image with different color palettes. From left to right, top to bottom: The original 128x128 image (has some saturation added), the baseline result in CAM16-UCS, the Oklab result, and the HyAB result. CAM16-UCS and Oklab use an Euclidean distance, HyAB a mixed absolute + Euclidean distance.

A four-pane comparison of the same image with different color palettes. From left to right, top to bottom: The original 128x128 image (has some saturation added), the baseline result in CAM16-UCS, the Oklab result, and the HyAB result. CAM16-UCS and Oklab use an Euclidean distance, HyAB a mixed absolute + Euclidean distance.

Oklab seems to work well for mapping pixels to palette colors. To make the task more fun (and difficult), I picked the PICO-8 color palette. "CAM16-UCS" is the very accurate, baseline perceptual color space, Oklab is an optimized version of it, and HyAB is L*a*b* with 2x absolute difference in L.

16.07.2025 11:52 — 👍 7    🔁 1    💬 1    📌 0
Hallucinations on the future of real-time rendering. Angelo Pesce's homepage & blog on computers, graphics and other things.

"Hallucinations on the future of real-time rendering", High Performance Graphics 2025 keynote: c0de517e.com/023_hpg.htm

13.07.2025 14:39 — 👍 19    🔁 7    💬 0    📌 0
Initialization and k-means refined results of palettes produced by two different color quantization methods (Wu91, VC) on two images (skybox, caps). The blue channel in the images was zeroed out. Colored points are image pixel colors and the X and V signs are Wu91 and VC methods' respective cluster centers. We can see that in the beginning the centers are spread out but after k-means they roughly line up. There are still many differences and the Wu91 seems to achieve lower sum of squared error ("inertia") in the end. The difference is less than 1% though.

Initialization and k-means refined results of palettes produced by two different color quantization methods (Wu91, VC) on two images (skybox, caps). The blue channel in the images was zeroed out. Colored points are image pixel colors and the X and V signs are Wu91 and VC methods' respective cluster centers. We can see that in the beginning the centers are spread out but after k-means they roughly line up. There are still many differences and the Wu91 seems to achieve lower sum of squared error ("inertia") in the end. The difference is less than 1% though.

I read that Xiolin Wu's 1991 (Wu91) color quantization works as a k-means initializer. It's box splitting but with a split plane that minimizes predicted total squared error after the split. I compared it to Variance Cut (VC) that always splits at mean. They are close but there's a clear difference.

14.07.2025 17:56 — 👍 0    🔁 0    💬 0    📌 0
Preview
Nerdy Guide | HIDDEN_PALETTE A guide for PICO-8 about hidden colors, set palette, and more.

I love how the PICO-8 has a separate 16-color "hidden palette" you can access by poking some simulated memory location and setting the highest bit of 8-bit color index to 1😃

13.07.2025 11:52 — 👍 4    🔁 0    💬 0    📌 0

I'd expect the results to be pretty close to what happens in k-means.

Did it also support computing palettes in addition to VQ? I've seen a binary splitting VQ compressor (divide code with max error, nudge both children) used as is for it, but would be cool if palettes had their own algorithm.

11.07.2025 17:42 — 👍 0    🔁 0    💬 1    📌 0

Wow that's an incredible music video!

11.07.2025 17:34 — 👍 0    🔁 0    💬 0    📌 0
A 3D illustration of the L*a*b color space.

A 3D illustration of the L*a*b color space.

Now here's the fanciest illustration of the L*a*b* color space I've ever seen😀 Not sure if it's actually helpful but I love how the outlines make it pop. Found it from the technical brief "Understanding CIE *L*a*b Colour Space" by Kydex Thermoplastics.

11.07.2025 11:52 — 👍 15    🔁 1    💬 1    📌 0
ZeroRanger FM Arrangement Project
YouTube video by +TEK ZeroRanger FM Arrangement Project

I know the FM synth sound is, to put it politely, a bit of an acquired taste, but I literally listened through this today when working: www.youtube.com/watch?v=A6OG...

10.07.2025 18:36 — 👍 1    🔁 0    💬 0    📌 0

Angel's Egg has such an unique mood. I repurposed randomly shuffled clips of it in a "VJ set" (just clicked "Play" with a VLC playlist set to shuffle :) Looked really cool in the dark but the constantly moving panning shots projected behind the stage made some people feel sick!

10.07.2025 18:29 — 👍 1    🔁 0    💬 0    📌 0

I've been meaning to read up on XYB since I saw some great comparisons to JPEG's YCbCr in dark shades. Another option I learned about in HN comments is the "DeltaOK2" difference that's basically what I used but in OKLab and the 2x weight now going to chroma instead (heh): github.com/color-js/col...

10.07.2025 12:44 — 👍 1    🔁 0    💬 0    📌 0
Four crops of a photo showing a green baseball cap in two different color spaces. The caption says "Color quantization in CIELAB space, visualized. The input is converted to CIELAB space and a special “HyAB” distance formula is used when clustering. This in theory should result in better image quality."

Four crops of a photo showing a green baseball cap in two different color spaces. The caption says "Color quantization in CIELAB space, visualized. The input is converted to CIELAB space and a special “HyAB” distance formula is used when clustering. This in theory should result in better image quality."

New article on my site: HyAB k-means for color quantization

In which I try to improve color clustering with a different distance function. It's a simple technique in the end but it's pretty hard to evaluate if it's an improvement or not!

30fps.net/pages/hyab-k...
github.com/pekkavaa/HyA...

09.07.2025 17:56 — 👍 22    🔁 8    💬 3    📌 0

Yeah the skin looks much better. In perfect world you'd have some kind of priority map that would emphasize faces. Sometimes this effect (errors in visually salient areas) has fooled me to think some method is better than another, until I looked at more images and saw the patterns.

09.07.2025 13:31 — 👍 0    🔁 0    💬 0    📌 0

Hmm if the RGB->chroma/luma space conversion is a linear transform then it shouldn't make any difference, right? Perhaps having luminance separated could still be useful for some kind of weighting though.

09.07.2025 13:29 — 👍 0    🔁 0    💬 0    📌 0

@pekkavaa is following 20 prominent accounts