<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:base="https://lemoncosmos.com/blog/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>lemon cosmos 🍋🪐✨</title>
    <link>https://lemoncosmos.com/blog/</link>
    <atom:link href="https://lemoncosmos.com/blog/feed.xml" rel="self" type="application/rss+xml" />
    <description>welcome to the lemon cosmos, a queer indie dev&#39;s perspective on lots of stuff!</description>
    <language>en</language>
    <item>
      <title>Alpacas in Balaclavas</title>
      <link>https://lemoncosmos.com/blog/posts/2026/02/alpacas-in-balaclavas/</link>
      <description>&lt;p&gt;this year i co-hosted a &lt;a href=&quot;https://globalgamejam.org/jam-sites/2026/flensjam-ggj-flensburg-powered-ifgamesh-ev&quot;&gt;GGJ site&lt;/a&gt; in Flensburg again, and it was pretty fun!&lt;/p&gt;
&lt;p&gt;during the jam, I worked on a small typing/heist/dating game, where you mask or unmask your true feelings for your lifelong partner in crime &amp;lt;3&lt;/p&gt;
&lt;p&gt;before I get into the development, you can play the game over on &lt;a href=&quot;https://arcaelia.itch.io/alpacas-in-balaclavas&quot;&gt;itch.io&lt;/a&gt;!&lt;/p&gt;
&lt;h1&gt;ideation&lt;/h1&gt;
&lt;p&gt;to be fair, i&#39;d been thinking about working on a typing game recently. when during ideation to the theme &lt;em&gt;Mask&lt;/em&gt; someone pointed out my partner&#39;s alpaca shirt, and we started listing funny game titles, Alpacas With Balaclavas veeeery quickly stuck. it&#39;s just fun to say!&lt;/p&gt;
&lt;p&gt;silly names are for silly games, so we had a few ideas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it&#39;s a branching story game&lt;/li&gt;
&lt;li&gt;you will type anything you want to say, but
&lt;ul&gt;
&lt;li&gt;easy sentences progress a fairly normal story&lt;/li&gt;
&lt;li&gt;long, exhaustive answers let you seduce your partner&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;since it&#39;s mostly typing, make it as juicy as possible!&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;the script&lt;/h1&gt;
&lt;p&gt;one neat consequence of having to type our your answers is a deepened connection to the things your character says. It also meant we could get away with a shorter script length since progressing through the script takes a bit longer than just choosing answers by clicking. what we also found really charming was the possibility of making jokes through the mechanic: it&#39;s easy to call a cop a &lt;em&gt;pig&lt;/em&gt;, but being sarcastic and having to type &lt;em&gt;0h 1s Th4t 4 n1C3 pOl1c3 OfF1c3r?&lt;/em&gt; makes for good, practical jokes. There were a ton of variations on making answers harder to type which correspond to ways you could talk, so that was a really fun part of the script writing process.&lt;/p&gt;
&lt;h1&gt;typing gameplay&lt;/h1&gt;
&lt;p&gt;the neat thing about all of this? implementation took two minutes, i expected input for this to be way harder! check the code below, which already includes handling backspace:&lt;/p&gt;
&lt;pre class=&quot;language-gdscript&quot;&gt;&lt;code class=&quot;language-gdscript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_unhandled_key_input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InputEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; event &lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; InputEventKey&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;is_released&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
		
	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; keyEvent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InputEventKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; keyEvent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keycode &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEY_BACKSPACE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
		key_erased&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	
	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; keyTyped &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;PackedByteArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;keyEvent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;unicode&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_string_from_utf8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; keyTyped&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;is_empty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		
	key_typed&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;keyTyped&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;all that was left was to connect the signals to some entity which would select what text to type, or to select one of the options on screen. that meant we needed to be careful to have each option start with different letters, which is pretty easy but we still managed to sneak in at some point.&lt;/p&gt;
&lt;p&gt;the fun part for me was figuring out font styling. i wanted to try my hand at some shader effects for typing. but in the end (and even in the post-jam version) I think it&#39;s currently not really possible to differentiate individual letters in a shader. this might be a good opportunity to open up a feature request on the godot repo, as there is still &lt;em&gt;some&lt;/em&gt; functionality there. you are able to differentiate characters using INSTANCE_ID in a shader, but this quickly breaks down once you have different rich-text blocks. in our game, typing the words turns the letters bold, so the first letter of the bold segment shares an instance id with the first letter of the regular font. a bummer, but at least I managed to do some animation on the typables!&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2026/alpacas-in-balaclavas/anim_text.gif&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2026/alpacas-in-balaclavas/anim_text.gif&quot; alt=&quot;the text &amp;quot;alpacas in balaclavas&amp;quot; bobbing up and down in a wave pattern&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;notice how not the letters themselves are bobbing up and down, but their vertices instead&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;in the future i want to go back to this, and find a good solution for juicy text animations!&lt;/p&gt;
&lt;h1&gt;reception&lt;/h1&gt;
&lt;p&gt;learning from developing &lt;em&gt;Detective Bobert and the Hunt for Zweck The Ripper&lt;/em&gt;, our team shifted focus from trying to create a complex, mechanics-driven game to simpler, experience-driven stories. and I think that&#39;s a great approach for a jam! my favourite part of the jam must have been all of the other participants howling at our jokes during the final presentation. other than that i&#39;m really proud of having build a really stable game while making sure the GGJ site is running smoothly. i&#39;m excited for the next jam!&lt;/p&gt;
</description>
      <pubDate>Sat, 07 Feb 2026 01:00:00 GMT</pubDate>
      <dc:creator>lemon cosmos</dc:creator>
      <guid>https://lemoncosmos.com/blog/posts/2026/02/alpacas-in-balaclavas/</guid>
    </item>
    <item>
      <title>poster design: SPIEL&amp;SCHNACK</title>
      <link>https://lemoncosmos.com/blog/posts/2026/01/poster-design-spiel&amp;schnack/</link>
      <description>&lt;p&gt;during covid, over at &lt;a href=&quot;https://www.ifgamesh.de/&quot;&gt;IFgameSH&lt;/a&gt; we used to host online gamedev meetups to replace the in-person events. super fun! it was also a joy to welcome folks from outside the region to show projects and join the community.&lt;/p&gt;
&lt;p&gt;buuuut...after covid the online meetups slowly faded out, folks wanted to meet in person again, which i thought to be reasonable, but i regretted not being able to have folks from further away joining. so, this year i&#39;m reviving the online meetup, and made this poster to go with it:&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2026/poster-design-spiel&amp;amp;schnack/poster_final.gif&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2026/poster-design-spiel&amp;amp;schnack/poster_final.gif&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;super happy, i cooked with this&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&amp;quot;SPIEL&amp;amp;SCHNACK&amp;quot; roughly translates to &amp;quot;play&amp;amp;talk&amp;quot;, using words specific to the region of northern germany.&lt;/p&gt;
&lt;p&gt;since i want to write about stuff i&#39;m doing (and folks asked about how i made it), here&#39;s a quick walkthrough!&lt;/p&gt;
&lt;h1&gt;concept&lt;/h1&gt;
&lt;p&gt;i knew the following things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;incorporate Schleswig-Holstein into the design&lt;/li&gt;
&lt;li&gt;emphasize long-distance connection&lt;/li&gt;
&lt;li&gt;PSX style&lt;/li&gt;
&lt;li&gt;some animated element&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;process&lt;/h1&gt;
&lt;p&gt;i started in blender, first modeling landscape, small blob creatures and some related objects. i threw them over into Godot, used some PSX shaders from &lt;a href=&quot;http://godotshaders.com&quot;&gt;godotshaders.com&lt;/a&gt; and arrived at this:&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2026/poster-design-spiel&amp;amp;schnack/preview_model.webp&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2026/poster-design-spiel&amp;amp;schnack/preview_model.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;First arrangement, ready to animate!&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;i simply animated the elements in godot to give them individual movement, making sure everything animates on a looping cycle. that way i&#39;d be able to perfectly loop the animation on the poster, perfect for a gif.&lt;/p&gt;
&lt;p&gt;next up, i threw down some face sprites in Aseprite to cycle between randomly. i iterated on these pretty little, since i wanted to have a rough look to the whole model.&lt;/p&gt;
&lt;video width=&quot;320&quot; height=&quot;240&quot; controls=&quot;&quot;&gt;
  &lt;source src=&quot;https://lemoncosmos.com/media/blog/2026/poster-design-spiel&amp;schnack/preview_faces.webm&quot; type=&quot;video/webm&quot;&gt;
Your browser does not support the video tag.
&lt;/video&gt; 
&lt;p&gt;after this, i added two dice to the model, careful not to overcrowd, and at the same time got started on the poster. the original poster design i lost the file of, but it featured a water background so the model would look like an island! i just couldn&#39;t get it to work in a way that appealed to me, so i tried my hand at something typography-heavy, which i always wanted to try my hand at.&lt;/p&gt;
&lt;p&gt;there&#39;s honestly nothing dramatic that happens here, just a lot of tweaking fonts &amp;amp; colors, with the colors roughly based on the original PSX logo.&lt;/p&gt;
&lt;p&gt;at the end, i exported the poster, threw it into godot, layered everything and recorded a gif from play mode. pretty simple, really!&lt;/p&gt;
&lt;p&gt;thanks for reading! just scroll back up to look at the finished poster again!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;cool side effect: i managed to create two cute emoji from this!&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2026/poster-design-spiel&amp;amp;schnack/emoji-spiel.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2026/poster-design-spiel&amp;amp;schnack/emoji-spiel.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;I&#39;m calling this one SPIEL...&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2026/poster-design-spiel&amp;amp;schnack/emoji-schnack.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2026/poster-design-spiel&amp;amp;schnack/emoji-schnack.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;...and this one SCHNACK&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;we love small pals&lt;/p&gt;
</description>
      <pubDate>Sun, 18 Jan 2026 01:00:00 GMT</pubDate>
      <dc:creator>lemon cosmos</dc:creator>
      <guid>https://lemoncosmos.com/blog/posts/2026/01/poster-design-spiel&amp;schnack/</guid>
    </item>
    <item>
      <title>setting up a blog!</title>
      <link>https://lemoncosmos.com/blog/posts/2026/01/setting-up-a-blog/</link>
      <description>&lt;p&gt;i&#39;m currently in the process of retiring my old wordpress site and setting up a smaller, leaner site. there&#39;s multiple reasons for this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it&#39;s easier to just write in a local document instead of going through a UI that screams at you to update something everytime you visit it&lt;/li&gt;
&lt;li&gt;i want to make an effort to write more, and lowering the barrier to do so is a win!&lt;/li&gt;
&lt;li&gt;i want to be in control of my website, not wrangle with a CMS to do what i could do myself faster&lt;/li&gt;
&lt;li&gt;building a website is fun!?&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;tech&lt;/h1&gt;
&lt;p&gt;the site is using eleventy, which is pretty fun to use! glad to see learning nunjucks in university was not a waste after all! so now I just build the site locally, then push my changes to my git repo, the web server gets notified and pulls the updated files. cool!
actually i&#39;m writing this paragraph to test if the auto-deploy works, so wish me luck!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;i&#39;m waiting for you to wish me luck&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;now i&#39;m deploying&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;it worked, hell yeah!&lt;/p&gt;
&lt;p&gt;things left to-do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[x] add favicon&lt;/li&gt;
&lt;li&gt;[ ] migrate portfolio
&lt;ul&gt;
&lt;li&gt;[x] text&lt;/li&gt;
&lt;li&gt;[x] portfolio images&lt;/li&gt;
&lt;li&gt;[x] new titles&lt;/li&gt;
&lt;li&gt;[x] CSS&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;[x] setup RSS feed (lives &lt;a href=&quot;https://lemoncosmos.com/blog/feed.xml&quot;&gt;here&lt;/a&gt;!)&lt;/li&gt;
&lt;li&gt;[ ] retire old website&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Mon, 12 Jan 2026 01:00:00 GMT</pubDate>
      <dc:creator>lemon cosmos</dc:creator>
      <guid>https://lemoncosmos.com/blog/posts/2026/01/setting-up-a-blog/</guid>
    </item>
    <item>
      <title>My 2021 in Review</title>
      <link>https://lemoncosmos.com/blog/posts/2021/my-2021-in-review/</link>
      <description>&lt;p&gt;This year’s been weird. Weird for you, weird for me, weird for the whole planet. I’d lie if I said I hadn’t hoped we’d be back to some kind of normal, and looking back at my new year’s resolutions makes that really evident to me. One of my goals this year was “release 3 games”. When I was writing this goal, I thought I was being modest. In 2020, I had released 6 games in total, so halving that amount seemed pretty generous. Well, I did not account for how much my life would change this year, and what that’d mean for the way I work on projects (and myself). So this year I’d like to look back at what happened, what it means for my games, and just take stock of what’s going on. This will feel very self-indulgent, I’m sure.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Before we hop in: Are you alright, friend? Do you need a small stretch? Or a sip of water? Go ahead, I’ll wait :)
All set? Great, let’s jump into it!&lt;/em&gt;&lt;/p&gt;
&lt;h1&gt;Shutter Stroll&lt;/h1&gt;
&lt;p&gt;Ripping the band-aid off right away: the Steam release will not be happening this year. You probably guessed that one already, as there are not too many days left in this year, but I should probably state it explicitly! I’ve been struggling with deciding on when to put out the game, but there is still a lot of stuff I’d like to see make the cut. I still think the version hitting Steam should be generator-complete; I don’t want to change the generator post-launch, save for fixes if possible.&lt;/p&gt;
&lt;p&gt;Now that I’m working in a game studio full-time, my work process on Shutter Stroll has definitely shifted. Not only do I have a lot less time to work on the game; work on it mostly focuses on features I want to develop at that moment, not the ones I think are strictly critical for the Steam release. I know that’s not optimal, and the game already being released in some way undoubtedly minimizes the pressure that’d push me to complete the critical features.
I experimented with objects that interact with light in interesting ways!&lt;/p&gt;
&lt;p&gt;But as this year nears its end, I keep thinking about what it really is I want this game to be. And you know what? I think it is good as it is. This simple sentence actually took me a lot of time to realize. I’m realizing that the generator does not need the absolutely huge overhaul I had planned initially. Many things I would like to implement are definitely novel, but do not add to the experience as much as I like to think. After all, Shutter Stroll is a minimal game. No goals, no time limits, just you and whatever you want to do on these islands. And I think I should make sure I do not lose that through implementing (super cool) features, but actually just polish up what is there and provide more content, not change up the one that is already in the game. There’s always another game on the horizon, no need to put all of the things into this one. After all, that’s why I already made one spin-off, and I’m certain there will be more.&lt;/p&gt;
&lt;p&gt;So in short: it will be done when it’s done, and that done does not have to be world-changing ✨ it’s nice if it’s nice.&lt;/p&gt;
&lt;p&gt;I want to take the time to spotlight a few creations inspired by Shutter Stroll that made me so very happy to see! So here are a few goodies for y’all!&lt;/p&gt;
&lt;p&gt;First off: A short, relaxing track by &lt;a href=&quot;https://mastodon.social/@NylePudding&quot;&gt;@NylePudding&lt;/a&gt;, made while visiting a daily island!&lt;/p&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;300&quot; scrolling=&quot;no&quot; frameborder=&quot;no&quot; allow=&quot;autoplay&quot; src=&quot;https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/soundcloud%253Atracks%253A1044054898&amp;color=%23ff5500&amp;auto_play=false&amp;hide_related=false&amp;show_comments=true&amp;show_user=true&amp;show_reposts=false&amp;show_teaser=true&amp;visual=true&quot;&gt;&lt;/iframe&gt;&lt;div style=&quot;font-size: 10px; color: #cccccc;line-break: anywhere;word-break: normal;overflow: hidden;white-space: nowrap;text-overflow: ellipsis; font-family: Interstate,Lucida Grande,Lucida Sans Unicode,Lucida Sans,Garuda,Verdana,Tahoma,sans-serif;font-weight: 100;&quot;&gt;&lt;a href=&quot;https://soundcloud.com/user-154035141&quot; title=&quot;Ethereal Disco&quot; target=&quot;_blank&quot; style=&quot;color: #cccccc; text-decoration: none;&quot;&gt;Ethereal Disco&lt;/a&gt; · &lt;a href=&quot;https://soundcloud.com/user-154035141/little-journey&quot; title=&quot;Little Journey&quot; target=&quot;_blank&quot; style=&quot;color: #cccccc; text-decoration: none;&quot;&gt;Little Journey&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This photo series blew my mind, courtesy of @SpiffSnaps (some of the photos are black on purpose, including the first, but keep clicking!)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://flic.kr/s/aHsmWGUkd9&quot;&gt;https://flic.kr/s/aHsmWGUkd9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Another enthralling, mysterious track by @adibabidan! I highly recommend loading up the island and walking around to the music!&lt;/p&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;300&quot; scrolling=&quot;no&quot; frameborder=&quot;no&quot; allow=&quot;autoplay&quot; src=&quot;https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/soundcloud%253Atracks%253A1358860132&amp;color=%23ff5500&amp;auto_play=false&amp;hide_related=false&amp;show_comments=true&amp;show_user=true&amp;show_reposts=false&amp;show_teaser=true&amp;visual=true&quot;&gt;&lt;/iframe&gt;&lt;div style=&quot;font-size: 10px; color: #cccccc;line-break: anywhere;word-break: normal;overflow: hidden;white-space: nowrap;text-overflow: ellipsis; font-family: Interstate,Lucida Grande,Lucida Sans Unicode,Lucida Sans,Garuda,Verdana,Tahoma,sans-serif;font-weight: 100;&quot;&gt;&lt;a href=&quot;https://soundcloud.com/adibabidan&quot; title=&quot;aidan Online&quot; target=&quot;_blank&quot; style=&quot;color: #cccccc; text-decoration: none;&quot;&gt;aidan Online&lt;/a&gt; · &lt;a href=&quot;https://soundcloud.com/adibabidan/6998n-7883e-starside-remastered&quot; title=&quot;69°98&amp;#x27;N 78°83&amp;#x27;E - Starside (Remastered)&quot; target=&quot;_blank&quot; style=&quot;color: #cccccc; text-decoration: none;&quot;&gt;69°98&amp;#x27;N 78°83&amp;#x27;E - Starside (Remastered)&lt;/a&gt;&lt;/div&gt;
&lt;h1&gt;New Shores&lt;/h1&gt;
&lt;p&gt;One of the biggest reason for the delay on any Shutter Stroll updates is my new job! I’ve joined Off The Beaten Track as a game developer full-time, which obviously cuts my time for personal game projects down to a small fraction of before. I’ve been able to work to my strengths as a generalist, and jumping between departments and team members is a lot of fun! That also means I’m definitely still in the process of adjusting and balancing work-time and free-time dev, which has been very tricky!&lt;/p&gt;
&lt;h1&gt;Where the mind wanders…&lt;/h1&gt;
&lt;p&gt;Being busy doesn’t mean I don’t have ideas, though! I’ve been journaling all of the game concepts my brain has been cooking up all year long, just to make sure I don’t forget any gold. And a few months ago, after playing some very inspiring games (more on that below) I had a game idea that kept me awake all night. So in the span of a few days I started jotting down notes, planning out systems and visuals! And while there is not much to show publically yet, maybe I can show you at least one small thing related to the next game (?) set in the Shutter Stroll universe.&lt;/p&gt;
&lt;h1&gt;fun and games (but mostly games)&lt;/h1&gt;
&lt;p&gt;So here’s a heads-up: Most of these games didn’t come out in 2021. Heck, s0me of them are a few years old already, but these games meant a lot to me this year, and I’d like to at least tell you to play them!&lt;/p&gt;
&lt;h2&gt;A Catfiend‘s Impending Relapse&lt;/h2&gt;
&lt;p&gt;I first played A Catfiend’s Impending Relapse at the end of one of our horror game streams, as an encore so to speak. We thought, “Hey, let’s give the game a go, should be fairly short!”. What followed were a good two hours of me trying to clear devilishly clever freestyle platforming with input lag due to playing remote, but still having a great time! It’s a game thick with atmosphere, a Sisyphean task which keeps you guessing: “Can I make that jump? If I can’t, I’ll have to start all over…”. The sheer thrill and stakes of the game kept me awake until deep in the night, and even after finishing it on stream and replaying it the morning after, I am still thinking about it regularly. It’s been a huge inspiration in how I think about games.&lt;/p&gt;
&lt;h2&gt;Celeste&lt;/h2&gt;
&lt;p&gt;I played Celeste back when it came out and loved it. I bought it again on Switch, where I completed it to 100% out of sheer love for the world, the mechanics, and the characters. Days were spent in wonder, playing Farewell and having my mind blown with every new challenge. When Maddy Thorson wrote about how Celeste was a trans story she did not know she was writing, it just further solidified my belief that Celeste in one of the most impactful games I have played in my life. I keep bothering friends to play it, and I’m glad they’re listening, haha. This year, I started missing playing it so much, I finally jumped in and tried something I was interested in for a long time now: speedrunning. I started doing speedrunning attempts on stream, and it is super fun; I love hanging out with you all while I try not to choke on summit. As of writing this, my personal best for Any% is 53:45, which you can watch below!&lt;/p&gt;
&lt;h2&gt;The MISSING: J.J. Macfield and the Island of Memories&lt;/h2&gt;
&lt;p&gt;At the moment of writing this, my playthrough of The MISSING has only been a few days ago. But I’ll be honest: As soon as the credits of the game rolled, no, even before, I was certain this game will stay with me for years to come. It is a story I do not wish to spoil too much; I think the way the game slowly pulls back the curtain on the actual events of the game is something you should experience yourself, but I can say it is a magnificent story about the pain of trying to be who you truly are. The story resonated with me so much, and there has not been a day when I have not tried to get friends to play it. Trust me on this one.&lt;/p&gt;
&lt;h2&gt;Breakfast on Trappist-1&lt;/h2&gt;
&lt;p&gt;When we started to stream Adventure Jam games on the Off The Beaten Track Twitch channel, I had not expected to stumble upon such a gem. The setting of BoT1 tingled my brain in the best way: set on a planet with perpetual daylight, you’re trying to help your partner overcome her trauma of her home planet being destroyed. The multicultural setting is unlike anything I had ever experienced before, and the unique mixture of medially underrepresented cultures and the ramifications of a planet that has no night are felt in every fiber of this story. I was and am still incredibly impressed with the richness of the game, and thinking about this being developed in a game jam has my jaw on the floor. I’m looking forward to the future games of the developers, and anything I’ve heard so far suggests that it will be just as good, if not even better.&lt;/p&gt;
&lt;h2&gt;Paradise Killer&lt;/h2&gt;
&lt;p&gt;I love information games. Games that make you scrounge the world for hints, clues, and have you interrogating everyone around to piece together the happenings of the past. Paradise Killer is a murder mystery set on a weird vaporwave island. The setting is positively insane, dripping with lore, and I was utterly lost when the game started. You arrive the night after a murder happened, and it is your job to find out what happened, and execute justice. What surprised me, was that you are not able to access the actual scene of the crime when your investigation starts! It kept subverting my expectations as I tried to put together a timeline of events, collecting hundreds of crystals in the weirdest places, and blasting some funky disco bangers wherever I went. The game drips with style, the murder case is interesting and obscure, and after I had already said to myself “this game is amazing”, the game surprised me with one of the coolest mechanics I ever saw in a detective game. Again, I won’t spoil it, but you need to play it. The journey is incredible, and the end is nothing short of that.&lt;/p&gt;
&lt;h2&gt;Honorable Mentions&lt;/h2&gt;
&lt;p&gt;Some games that I still enjoyed a lot this year are: Doom Eternal (+ DLC), Boomerang X, Disc Room, Spelunky 2, ULTRAKILL, Warframe, Inscryption, Outer Wilds (+ DLC), Going Under and Ghostrunner!
Are we on the internet?&lt;/p&gt;
&lt;p&gt;One of my goals I actually did manage to fulfill this year was to pick up streaming! There were times where I was streaming very regularly, and some times where I did not for a few weeks, but I never truly stopped! It’s been so much fun, honestly. I love every minute I get to hang out with you all who decide to come by and chat a bit, or laugh at me losing it at the word “Boner”. A special thank you to my friend Sol who decided to share his brain cell with me as we tried not to cry playing horror games (will be back soon!) and Fiore for playing some really rad and queer games with me!&lt;/p&gt;
&lt;h1&gt;What else?&lt;/h1&gt;
&lt;p&gt;Even though I missed the mark on the goals I set myself for this year, a bunch of cool stuff happened! I want to list a few cool things that happened, that I am incredibly excited about, so future Jannik can look at it and remember that the goals we set are not the goals we might accomplish:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;released a successful game as part of my job&lt;/li&gt;
&lt;li&gt;organized game jams and focused on helping people make games&lt;/li&gt;
&lt;li&gt;started the first steps to make our local-community more inclusive&lt;/li&gt;
&lt;li&gt;talked about queer communities at the Pride Game Jam Helsinki&lt;/li&gt;
&lt;li&gt;gave my first talk at a university&lt;/li&gt;
&lt;li&gt;joined fundraising streams (and participated in multiple charity events)&lt;/li&gt;
&lt;li&gt;got interviewed for TV! (WHAT)&lt;/li&gt;
&lt;li&gt;learned so much about myself and my identity, and who I want to be&lt;/li&gt;
&lt;li&gt;met so many incredible peers &amp;amp; friends&lt;/li&gt;
&lt;li&gt;celebrated others’ games&lt;/li&gt;
&lt;li&gt;received the opportunity to ██████████ ███████ ███ ██ ██ █████&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, that’s it! I’m ready to be surprised by what the next year is holding for us (please not another pandemic though), and I hope to see you on the other side with me! Thank you for reading this, I wish you all the best, and I’ll see you soon 💖&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Maybe I should do a “best looks of the year” post next…&lt;/em&gt;&lt;/p&gt;
</description>
      <pubDate>Sun, 26 Dec 2021 01:00:00 GMT</pubDate>
      <dc:creator>lemon cosmos</dc:creator>
      <guid>https://lemoncosmos.com/blog/posts/2021/my-2021-in-review/</guid>
    </item>
    <item>
      <title>Reaching for the Stars ✨</title>
      <link>https://lemoncosmos.com/blog/posts/2020/reaching-for-the-stars/</link>
      <description>&lt;p&gt;Whenever I show off the game I am currently working on, the conversation usually lands on the skybox I am using. I am pretty proud of it, and I have gotten multiple questions on it already, so I decided it was time to try and write about it! This shader utilizes Unity’s Shader Graph, and I will be working on version 2019.3.&lt;/p&gt;
&lt;h1&gt;Why do I need a procedural skybox?&lt;/h1&gt;
&lt;p&gt;You might not need one! But it is really fun to work on one. My currently untitled game (EDIT: it’s called &lt;a href=&quot;https://store.steampowered.com/app/1513480/Shutter_Stroll/&quot;&gt;Shutter Stroll&lt;/a&gt; and is out already!) is an exploration game with procedurally generated islands. So whenever you would visit another island, I wanted the place to feel different in every way, so I opted for a custom skybox shader, which can be adjusted in many ways to evoke different atmospheres. Overcast with a dark sky? A big, pale moon among thousands of twinkling stars? A clear day with only a few clouds and the sun up high? Suddenly I can generate skies in addition to my islands, and it improves the game by a lot. I have been posting development over on Twitter, and reception has been great!&lt;/p&gt;
&lt;p&gt;I want to break down the general structure of my shader, so you can build your own skyboxes on top of it! Keep in mind that this is not exactly the shader I am using since I like to tweak it a lot, but the features are the same, and you will be able to easily extend it. Let’s jump into it!&lt;/p&gt;
&lt;h1&gt;Colouring the sky 🌈&lt;/h1&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/sky_example.jpg&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/sky_example.jpg&quot; alt=&quot;Photo by Luc Dobigeon on Unsplash&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;Photo by Luc Dobigeon on Unsplash&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Okay, let’s begin with the thing that will cover the area on our skybox: the sky. Sure, you could just pick a color and be done with it, but it might look a bit bland. Look at the picture on the left: the sky is not colored uniformly, there is a gradient originating from the horizon upwards. I won’t go into scientific details here, because we do not aim to be particularly realistic, it just so happens that nature is really pretty in this regard! So let’s pick two colors, and interpolate them based on height. So…how do I determine the height?!&lt;/p&gt;
&lt;h1&gt;Unwrapping the skybox&lt;/h1&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_uv_control.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_uv_control.png&quot; alt=&quot;a shader graph node setup, sampling a texture&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;To properly judge whether our calculated coordinates are right, I used a UV test texture by Thomas Schall for the visualization. You can find the texture over &lt;a href=&quot;https://www.oxpal.com/uv-checker-texture.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So let’s just plug a Sample Texture 2D node into the Color input of our master node and see what happens!&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/uv_stretch_1.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/uv_stretch_1.png&quot; alt=&quot;stretched UV texture&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/uv_stretch_2.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/uv_stretch_2.png&quot; alt=&quot;stretched UV texture&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;Yeah, that’s not optimal. The texture is getting stretched everywhere!&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;It’s clear to see that our UV coordinates are all over the place! It might not matter too much for the vertical gradient (still stretched near the top!) but it will matter on the horizontal axis! So let us fix that!&lt;/p&gt;
&lt;p&gt;We will be doing the following things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get the World Position and Normalize it. That way we can make sure we are working on a sphere, which is the shape we’d like our skybox to have.&lt;/li&gt;
&lt;li&gt;Take the Arcsine of the Y component. This will eliminate the stretch near the top. We still need to divide the value by Pi/2, since Arcsin(1) = Pi/2.&lt;/li&gt;
&lt;li&gt;Calculate the Arctangent2 of the X and Z component. This will return the angle on the circumference of our skybox. Divide it by Tau (You can use the Constant node to get the value of Tau).&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Tau is Pi times 2. Most of the time, you will double Pi anyway, so just use Tau. Tau is the cooler Pi.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Lastly, recombine the (now two) components into one Vector2. These are our UV coordinates!&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_skybox_uv.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_skybox_uv.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;The complete node setup to calculate our UVs.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;We can convert this into a subgraph, so we don’t have to look at this anymore! Plug it into the UV input on the Sample Texture 2D node and you should see the following:&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/uv_unstretched.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/uv_unstretched.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;The texture now wraps around once, and the stretch along the top is way less noticeable.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Now let us finally put some colors into this thing! I’ll create two properties and call them HorizonColor and SkyColor. Then we will interpolate between them with a Lerp node, based on the V component of our new UV node.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_skybox_gradient.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_skybox_gradient.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;Lerping Color&lt;/figcaption&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Pro Tip: Instead of interpolating the color values in RGB space, you can lerp in HSV space instead! Interpolating the Hue, Saturation and Value components separately can lead to prettier results! But this breakdown is already long as is, so I will be skipping over that.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can play around with the colors to find a combination you like. There is one small problem though: without other objects in the scene, it is hard to judge in which direction you are looking currently. Let’s change that by adding stars! ✨&lt;/p&gt;
&lt;h1&gt;Adding sparkles: Stars ✨&lt;/h1&gt;
&lt;p&gt;The base for our starfield will be the Voronoi node. Voronoi patterns are really handy for a variety of shaders! This pattern is constructed by placing semi-randomly distributed points, and then every pixel gets a value depending on its distance to the nearest point. This does not look like stars at all yet, but we can take advantage of the generated points to draw stars! So here’s what we will do:&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_node_voronoi.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_node_voronoi.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;The Voronoi node.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;We’ll Saturate the output of the Voronoi node. Since some pixels have a distance greater than 1 to some of the points, this would leave us with some artifacts on our sky. (the Saturate node clamps values between 0 and 1)&lt;/li&gt;
&lt;li&gt;Using the One Minus node we invert the values, leaving us with something that looks like bubbles.&lt;/li&gt;
&lt;li&gt;Now we could step the result by some high value (0.95-.99), and that would leave us with nice circles. But I will use a Power node with a high power value (~100) instead because I think it looks nicer!&lt;/li&gt;
&lt;li&gt;Lastly, we plug our Skybox UV into a Tiling and Offset node, set the tiling to [8,2] (to correct the horizontal stretch and get a smaller scale) and route that into the UV input of the Voronoi.&lt;/li&gt;
&lt;li&gt;Add the result onto our gradient sky and plug the result into the Color of the master node.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_stars.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_stars.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;This is where we are at now!&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Notice I created a property to control the star density, which I’ve set at 10 for now. I also increased the Voronoi angle offset, otherwise our stars will look like we placed them on a grid.&lt;/p&gt;
&lt;p&gt;Now might be a good time to create a material, assign the shader and set it as our skybox material.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can change the current skybox material under Window &amp;gt; Rendering &amp;gt; Lighting Settings. It’s right up top. This is scene-specific; so you will need to assign it in every scene you need the skybox. You will see a warning that the material does not support skybox rendering, but it will work just fine!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/preview_sky_1.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/preview_sky_1.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;That’s a pretty sky!&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h1&gt;The big pale moon (or sun?) 🌙&lt;/h1&gt;
&lt;p&gt;Right now we can’t see where the light in our scene is coming from, so let’s add a sun (or moon)! We could set the sun’s position in the sky by hand, but I would like to have it follow our main directional light! The fastest way is to add a script to your light with the following line of code in its Start() function (or Update(), if you move your light during gameplay):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Shader.SetGlobalVector(“_SunDirection”, transform.forward);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can access the light direction in our shader! We just need to create a Vector3 property, set its reference to “_SunDirection” and untick the Exposed checkbox. This tells our property that we don’t want to set it by hand in the material inspector later, but that it should read global shader variables, like the one we just set in our script.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/property_sun.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/property_sun.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;The proper (ha!) property setup.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;We will be using the dot product of our sun direction and the view direction of each fragment to see if the directions align. The View Direction is a vector spanning from the position of the fragment directly to the camera! The Dot Product node will return 1 if the vectors align and -1 when they are exact opposites. If they are perfectly perpendicular, we will see a value of 0. In other words: a value of 1 means we are staring directly into the sun, so we’ll put on sunglasses and do the following things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We’ll Normalize the output of a View Direction node (set to world space). Since the view direction points from the fragment position to the camera, it’s a pretty long vector! Save yourself some trouble by normalizing it right at the start.&lt;/li&gt;
&lt;li&gt;Calculate the Dot Product of the light direction and the normalized view direction!&lt;/li&gt;
&lt;li&gt;(Optional, but recommended) We can run the result through the Arcosine node to change the rate the value changes over the surface of our sphere. This will help us change our sun size later.&lt;/li&gt;
&lt;li&gt;Step the result with a value between 0 and 1. We can expose this as a property that we will call Sun Size.&lt;/li&gt;
&lt;li&gt;Invert the value range by running it through the One Minus node.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/preview_sun.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/preview_sun.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;Let us add this effect on top of the sky and the stars, and see how it looks! You might need to tweak the sun size first, I recommend starting at a value of ~0.1, which is still pretty big for most cases. Our sun has a pretty sharp edge though, how can we change that? In most cases, I would swap the Step node with a Smoothstep one. In this case, I will use bloom in my post-processing, so that will soften the edge instead.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pro tip: Since most of the desired values for the sun size are near 0, I like to multiply the sun size by itself, to get a tiny bit more granularity in those lower values.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_sun.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_sun.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;The node setup for the sun!&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Now, we could stop here. But we are already able to draw one circle, so maybe we can draw another one and subtract it from the first one in order to get a nice moon shape?&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/preview_moon_cutout.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/preview_moon_cutout.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;Just like this 👆&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;This is actually pretty easy! We will duplicate the node setup we just built, but this time we will add a small offset to the light direction. That way the center of our second sun is shifted slightly. Then we subtract the result from our first sun, saturate that value and we are done with our moon shape! We could even split the two sun sizes into their own properties, so we can tweak them independently.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/preview_moon_animated.gif&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/preview_moon_animated.gif&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;Tweaking the values. Reducing the mask size to 0 will leave you with a full moon/sun.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Let us take a look at the final graph for the moon:&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_moon_final.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_moon_final.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;Clouds 🌥️&lt;/h1&gt;
&lt;p&gt;Right now our horizon is a bit empty, and I would like to see some clouds back there, so let us try making some!&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/noise_perlin.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/noise_perlin.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;Just save this texture!&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;We will be using a seamless noise texture for the clouds, just like the one on the left. You can use your own texture, just make sure that it is tileable! The type of texture you choose will drastically change the look of the clouds, so experiment with them to find a look you like.
The graph for the clouds will be a bit larger since we need to adjust values at every corner, so I will show you the first part of the graph beforehand; that might make it easier to follow along! You can expose many of the values used here as properties, but I don’t want to have a ton of properties in my inspector that I will rarely use. With that said, let’s get into it!&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_clouds.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_clouds.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;Man, that’s a big graph!&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Let’s walk through this step by step:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We’ll create a float property called Wind Speed. We will multiply this by the linear time value of the Time node to calculate the speed the clouds will move at. (I multiply it by 0.01 before that, so I have more control in the inspector)&lt;/li&gt;
&lt;li&gt;We feed the resulting speed into the offset input of a Tiling and Offset node, which also receives our Skybox UV. This will move our UV over time. I also turn up the tiling, to make the texture repeat over the whole sphere (use integer values here)&lt;/li&gt;
&lt;li&gt;We use a Sample Texture 2D node to with our new UV and our texture as an input to get access to the color values in the texture. We will use the Red channel output moving forward since a greyscale image should have equal values across all channels. Otherwise, we would need to work with a Vector4 here, which just complicates things.&lt;/li&gt;
&lt;li&gt;We Saturate the result just in case, then we copy what we have done and repeat it a second time below, this time changing the offset (use the opposite sign so the two textures scale in opposite directions) and using higher tiling values.&lt;/li&gt;
&lt;li&gt;Now we can multiply both values, Saturate them again (probably unnecessary, but I like to be safe), and Posterize it to stylize it a bit.&lt;/li&gt;
&lt;li&gt;At the end we Remap the result because we don’t need the values between 0 and 0.5, they are simply too dark for clouds!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I’ll plug the result straight into our Color input of the Master node, this is how it looks:&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/preview_clouds_full.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/preview_clouds_full.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;But now there are clouds everywhere!&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Of course, you can multiply the result with any color you’d like, but for simplicity’s sake, I’ll be skipping over that for now. We still need to mask our cloud effect, and it would be great if we could control how high the clouds appear along the horizon!&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_clouds_mask.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_clouds_mask.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;Extending the setup with a mask.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;Let us isolate the V component of our Skybox UV and feed it into a Smoothstep node. The lower Edge will be where we want our clouds to start (between 0 and 1), and we add another value on top of that for the second edge. That way we can control how far the clouds should fade out.&lt;/li&gt;
&lt;li&gt;We invert the value range with the One Minus node (so 1 is near the horizon, and 0 high up in the sky) and multiply it with our combined cloud texture. I will remap the cloud values to go from 1 to 2 so that it won’t erase completely white parts of the gradient.&lt;/li&gt;
&lt;li&gt;We step the result again at a high value to get a clean edge, and now our mask is done!&lt;/li&gt;
&lt;li&gt;To put it into effect, we can create a Lerp node, assign our mask to the T value and plug in our existing sky color and our clouds! Route the result into our master node yet again and we should see something like this:&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/preview_clouds_animated.gif&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/preview_clouds_animated.gif&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;Adjusting the cloud height and edge.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;We could stop here, as we are already at it for quite some time now, but I don’t like the harsh intersection between the sky and the ocean below it. You might notice that We do have fog in our scene, but under normal circumstances, skyboxes don’t get affected by fog! So let us try to blend that edge, and we will be done!&lt;/p&gt;
&lt;h1&gt;Hiding the horizon: Fog! 😶‍🌫️&lt;/h1&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_fog.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_fog.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;This is a small one, phew!&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;Let’s isolate the V component of the Skybox UV again, invert the value range as we have done multiple times already and saturate the result.&lt;/li&gt;
&lt;li&gt;Use the Power node to change the value ramp, I think a power value of 25 is a good start, but we can expose it as a property because this might change often.&lt;/li&gt;
&lt;li&gt;Lastly, use the Fog node to get the scene fog color, and lerp it using our value. And with that, we are done!&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/preview_fog.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/preview_fog.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;figcaption&gt;See? No more hard edges!&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;And with this, we are done! You can take a look at the finished graph, it is not that big!&lt;/p&gt;
&lt;figure&gt;&lt;a href=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_final.png&quot;&gt;&lt;img src=&quot;https://lemoncosmos.com/media/blog/2020/reaching-for-the-stars/graph_final.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;You can explore the graph in its full glory over &lt;a href=&quot;https://shadergraph.stelabouras.com/library/53baa7512b7c4b88da670bd7ecc3345f/&quot;&gt;here&lt;/a&gt;! You’ll also be able to download it there, but I can only recommend building it yourself and experimenting every step of the way!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Thank you for reading my shader breakdown! I hope this serves as a good inspiration for creating your own sky shaders. If you end up doing something with it, I would love to see it! Just mention me on &lt;a href=&quot;https://peoplemaking.games/@sparkles&quot;&gt;Mastodon&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;If you’d like to play Shutter Stroll, the game that I created a more complex version of this shader for, you can wishlist the game on Steam!&lt;/p&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;200px&quot; src=&quot;https://store.steampowered.com/widget/1513480&quot;&gt;&lt;/iframe&gt;</description>
      <pubDate>Wed, 12 Feb 2020 01:00:00 GMT</pubDate>
      <dc:creator>lemon cosmos</dc:creator>
      <guid>https://lemoncosmos.com/blog/posts/2020/reaching-for-the-stars/</guid>
    </item>
  </channel>
</rss>