Home Artists Posts Import Register

Content

We are releasing one Bach Invention portrait every day for 15 days, with Chris (Gruber_music) who remade the musics in Pico-8. (I can't prove it yet, but I'm fairly certain he used some form of black magic)


Hi everyone!

Obviously we're talking about yesterday's portrait's led screen effect!

As you may or may not know, each color in the Pico-8 palette has both a darker and a lighter counterpart in the Pico-8 palette. The hue changes, for some more than for others, but that only makes it look even better in my opinion.

We can use this to create a lighting effect. For example, we might take an orange square, make its left and up borders yellow and its right and low borders brown, and that will give us an impression of relief on our orange tile.

If we go even smaller, with 2x2 pixels, we can have the lighter color on the upper-left pixel, the darker one on the lower-right pixel and the actual color on the two other pixels. And that will give us one led!

Now, we can literally do this again and again, covering the whole screen, using pget(x,y) to get our base color, and two arrays to keep our references to the lighter and darker counterparts. That will work, but it will also use way too much CPU.

Another idea might be to draw all the color leds on sprites and then cover the screen with those sprites. That turns out to be a terrible idea. It works, yes, but also the CPU goes through the roof, to an astonishing 250% at 30fps.


So what would take less CPU? Well, there is this one function that is used a lot to render triangles and other funky things, and it is rectfill. We're going to use rectfill.

So, in our screen-covering-double-'for'-loop, (for y=0,127,2 do for x=0,127,2 do [...] end end) the first thing we'll do is get the original pixel color from the screen, with pget(x,y).

Then, we'll use fill patterns! Fill patterns let you either draw two different colors at the same time, or draw just one color but only a defined pattern. We're using that first propriety to draw the lighter and darker parts of each led with one 2x2 rectfill and then we're using the second propriety to draw the actual color on the other two pixels with a second 2x2 rectfill.

Here are the two patterns we're using:

<code>
0 0 0 0   \   1 0 1 0
1 1 1 1   \   0 1 0 1
0 0 0 0   \   1 0 1 0
1 1 1 1   \   0 1 0 1
</code>

To use the first propriety of fillp, we have to use 'color0+color1*16' as color parameter on our draw calls. To make things efficient, we're using an array to store the corresponding color code (with the lighter and the darker shade) of every color.

To use the second propriety of fillp, we only have to add '0b.1' (or just '0.5') to the pattern when calling fillp(). That way it will only draw the one color you give to your draw calls.

And that works! It still takes a lot of CPU but at least we can make it work at 30 fps. But can we make it even faster?


We can't make the effect itself much faster, but maybe we can optimize the whole cart.

Our effect only processes 1 pixel in 4 when it reads the screen. So we really only have to draw these 1-in-4 pixels. How do we do that though? Well, we could just bring all the pixels that matter into one corner of the screen.

Instead of having the rest of our cart draw all over the screen, we will have everything drawn smaller and into the first 64x64 pixels. If you draw on less pixels, you will use less CPU.

All you have to do then is adapt the code for our effect. Instead of 'pget(x,y)', we'll use 'pget(x/2,y/2)', and more importantly we will reverse the way our loops go. We don't want to cover pixels of the important corner of the screen before we process them, so instead we must start with the lower-right corner of the screen. Did you know we can use a negative number as the third parameter of a 'for' loop?

Before I show you the final result, I need to mention one more optimization I did. 'pget' actually uses a lot of CPU and it's best if we can avoid using it. So instead I'm using 'peek()' to directly read the screen memory. And since every byte of the screen memory codes two pixels at a time, I went ahead and made it draw two leds at a time. Ok, here's what the final iteration of the code looks like:

<code>
local aaa={[0]=1,13,30,27,41,29,215,103,46,74,151,55,23,22,39,231}
local a=0x6000+63*64+31
for y=126,0,-2 do
for x=124,0,-4 do
 local v=peek(a)

 local c=band(v,0xf)
 fillp(0b0000111100001111)
 rectfill(x,y,x+1,y+1,aaa[c])
 fillp(0b1010010110100101.1)
 rectfill(x,y,x+1,y+1,c)

 local c=band(shr(v,4),0xf)
 fillp(0b0000111100001111)
 rectfill(x+2,y,x+3,y+1,aaa[c])
 fillp(0b1010010110100101.1)
 rectfill(x+2,y,x+3,y+1,c)

 a-=1
end
a-=32
end
</code>

Try just slapping this at the end of a '_draw()' function and send me a gif or a screenshot on Twitter @TRASEVOL_DOG, I'd be really curious to see the result!


That's it for today! This one was a little more thorough than the others! Do feel free to ask any questions you might have!

I'm going to start gathering up these posts and bringing them to the blog. At first I wanted to do one big blog entry with all the posts in it, but it turns out I'm making these posts a lot longer than I intended. So instead there will be one blog entry per 5 posts. Watch out for those! (it'd be cool if you shared them around! :X)

As usual, the downloads for this portrait are available to my 5$+ supporters over there!

Take care and have a nice week-end!

TRASEVOL_DOG

Files

Invention No. 10 in G Major, BWV 781 (Pico-8 Edition)

Invention No. 10 in G Major, BWV 781 composed by J.S. Bach (1685-1750). Music arrangement by Gruber (@gruber_music) Artwork/Animation/Code by Trasevol_Dog (@TRASEVOL_DOG) Support us on Patreon! https://www.patreon.com/Gruber99 https://www.patreon.com/trasevol_dog Arranged and animated in Pico-8. Pico-8 is a fantasy console for making, sharing and playing tiny games and other computer programs. https://www.lexaloffle.com/pico-8.php

Comments

No comments found for this post.