diff --git a/content/Resources/crepuscular_rays_fake.png b/content/Resources/god-rays/crepuscular_rays_fake.png similarity index 100% rename from content/Resources/crepuscular_rays_fake.png rename to content/Resources/god-rays/crepuscular_rays_fake.png diff --git a/content/Resources/crepuscular_rays_real.png b/content/Resources/god-rays/crepuscular_rays_real.png similarity index 100% rename from content/Resources/crepuscular_rays_real.png rename to content/Resources/god-rays/crepuscular_rays_real.png diff --git a/content/Resources/god-rays/god-rays-base-scene.png b/content/Resources/god-rays/god-rays-base-scene.png new file mode 100644 index 000000000..5dc1698a5 Binary files /dev/null and b/content/Resources/god-rays/god-rays-base-scene.png differ diff --git a/content/Resources/god-rays/god-rays-beams.mp4 b/content/Resources/god-rays/god-rays-beams.mp4 new file mode 100644 index 000000000..9d16d2190 Binary files /dev/null and b/content/Resources/god-rays/god-rays-beams.mp4 differ diff --git a/content/Resources/god-rays/god-rays-billboard.mp4 b/content/Resources/god-rays/god-rays-billboard.mp4 new file mode 100644 index 000000000..1792982f6 Binary files /dev/null and b/content/Resources/god-rays/god-rays-billboard.mp4 differ diff --git a/content/Resources/god-rays-default-scene.png b/content/Resources/god-rays/god-rays-default-scene.png similarity index 100% rename from content/Resources/god-rays-default-scene.png rename to content/Resources/god-rays/god-rays-default-scene.png diff --git a/content/Resources/god-rays/god-rays-depth_modulated.png b/content/Resources/god-rays/god-rays-depth_modulated.png new file mode 100644 index 000000000..102486ade Binary files /dev/null and b/content/Resources/god-rays/god-rays-depth_modulated.png differ diff --git a/content/Resources/god-rays-final.png b/content/Resources/god-rays/god-rays-final.png similarity index 100% rename from content/Resources/god-rays-final.png rename to content/Resources/god-rays/god-rays-final.png diff --git a/content/Resources/god-rays-occlusion-map.png b/content/Resources/god-rays/god-rays-occlusion-map.png similarity index 100% rename from content/Resources/god-rays-occlusion-map.png rename to content/Resources/god-rays/god-rays-occlusion-map.png diff --git a/content/Resources/god-rays-quads.png b/content/Resources/god-rays/god-rays-quads.png similarity index 100% rename from content/Resources/god-rays-quads.png rename to content/Resources/god-rays/god-rays-quads.png diff --git a/content/Resources/god-rays-radial.png b/content/Resources/god-rays/god-rays-radial.png similarity index 100% rename from content/Resources/god-rays-radial.png rename to content/Resources/god-rays/god-rays-radial.png diff --git a/content/Resources/god-rays-scatter-function.png b/content/Resources/god-rays/god-rays-scatter-function.png similarity index 100% rename from content/Resources/god-rays-scatter-function.png rename to content/Resources/god-rays/god-rays-scatter-function.png diff --git a/content/Resources/god-rays/god-rays-some_quads.png b/content/Resources/god-rays/god-rays-some_quads.png new file mode 100644 index 000000000..e46acbfeb Binary files /dev/null and b/content/Resources/god-rays/god-rays-some_quads.png differ diff --git a/content/blogs/concepts/coordinate-spaces.md b/content/blogs/concepts/coordinate-spaces.md index 9ca7bfbd5..e6d93adb8 100644 --- a/content/blogs/concepts/coordinate-spaces.md +++ b/content/blogs/concepts/coordinate-spaces.md @@ -1,4 +1,12 @@ --- title: Coordinate Spaces --- -I have yet to write an article on this topic. \ No newline at end of file +I have yet to write an article on this topic. + +## Model Space + +## World Space + +## View Space + +## Screen Space \ No newline at end of file diff --git a/content/blogs/concepts/depth-buffer.md b/content/blogs/concepts/depth-buffer.md new file mode 100644 index 000000000..a60a0e1bc --- /dev/null +++ b/content/blogs/concepts/depth-buffer.md @@ -0,0 +1,4 @@ +--- +title: Depth Buffer +--- +I have yet to write an article on this topic. \ No newline at end of file diff --git a/content/blogs/graphics/god-rays-01.md b/content/blogs/graphics/god-rays-01.md index 7e5c10095..5d174318e 100644 --- a/content/blogs/graphics/god-rays-01.md +++ b/content/blogs/graphics/god-rays-01.md @@ -7,7 +7,7 @@ created: 2024-06-12 date: 2024-06-13 aliases: - God Rays -description: In this post we go over what are god rays and a couple different high-level approaches to them followed by my personal implementation. +description: In this post we go over what are god rays and a couple different high-level approaches to them. previewImg: ./Resources/crepuscular_rays_fake.png --- @@ -52,7 +52,7 @@ The approach seen [here](https://github.com/math-araujo/screen-space-godrays) by > ![[god-rays-occlusion-map.png]] > Occlusion Map [(Source)](https://raw.githubusercontent.com/math-araujo/screen-space-godrays/master/docs/images/first_pass.png) -The occlusion map is a grayscale version of colored image where whiter colors show the sources of reflected light that reach the camera and the blacker colors represent anything that does not reflect light to the camera. This tells us which pixels on the screen has light coming towards our camera. +The occlusion map is an image that tells us which pixels are being **directly lit** and which pixels are being **indirectly lit**. The lighter regions of the screen show us where light sources are directly pointed at our camera and the dark regions show us where light is being unobserved or blocked. #### Default Scene > [!caption|center] @@ -111,7 +111,7 @@ I will quickly go over the process here as this is likely the approach I will go #### Quads Okay so what's a quad? Well it's just a rectangle but in three-dimensional space but with some finagling it can become a ray from the heavens. -The first step is to place our quads in our scene where we would like the rays to be and set them act as **billboards**. This means the quad will always face the camera regardless of how the camera moves. Then we use a [[shaders|vertex shader]] to stretch the upper parts of the quad towards our light source. This creates the effect of a quadrilateral shape stretching from its base towards the sun. +The first step is to place our quads in our scene where we would like the rays to be and set them to act as **billboards**. This means the quad will always face the camera regardless of how the camera moves. Then we use a [[shaders|vertex shader]] to stretch the upper parts of the quad towards our light source. This creates the effect of a quadrilateral shape stretching from its base towards the sun. Then it's just a matter of shading the rays the color you want them, applying some noise and modulating the opacity of the rays based off of several factors including: - **How far the quads are from the camera**. We want rays close to the camera to fade away as to not interfere with our view of the scene. diff --git a/content/blogs/graphics/god-rays-02.md b/content/blogs/graphics/god-rays-02.md new file mode 100644 index 000000000..f29b8817e --- /dev/null +++ b/content/blogs/graphics/god-rays-02.md @@ -0,0 +1,144 @@ +--- +draft: true +title: God Rays - Part 2 +tags: + - graphics +created: 2024-06-14 +date: 2024-06-13 +aliases: +description: In this post I document my progress as I attempt my own implementation of god rays. +previewImg: ./Resources/crepuscular_rays_fake.png +--- + +[[god-rays-01|Last time]] we talked about what god rays are and some ways that video games try to emulate them. Today I'll be documenting my progress as I attempt to implement a version of the approach detailed [[god-rays-01#Quad-based Approximation|here]]. Giant shout out to [Cyanilux's blog](https://www.cyanilux.com/tutorials/god-rays-shader-breakdown/) and [t3ssel8r](https://www.youtube.com/watch?v=fSNdZ82I-eQ)'s video on the topic for the inspiration. + + +> [!important] For Developers +> I will be creating this effect in my game [[01.far-reaches|The Far Reaches]] using [4.3 Beta 1](https://godotengine.org/article/dev-snapshot-godot-4-3-beta-1/) build of the Godot open source engine. This leverages some of the features strictly available to that version so if you're following along as a developer please bear that in mind. + +## Implementation + +To kick things off, I quickly threw together a basic scene to work with and add our god rays to. We may need to fiddle around with this scene and add objects and geometry to experiment with later. + +> [!caption|center] +> ![[god-rays-base-scene.png]] +> Basic Scene + +### Adding Quads + +To quickly reiterate from last time: +> The first step is to place our quads in our scene where we would like the rays to be and set them to act as **billboards**. This means the quad will always face the camera regardless of how the camera moves. + +Let's place a few quads throughout the our scene and uhhh... + +> [!caption|center] +> ![[god-rays-some_quads.png]] +> Some Quads + +Those quads certainly don't look like they're facing the camera. Let's try to remedy that with a [[shaders|shader]] that transforms our quads such that the quads don't rotate. + +> [!math]- Here There Be Math! +> When we are transforming our quads from [[coordinate-spaces#Model Space|model space]] to [[coordinate-spaces#View Space|view space]] we want to translate without rotating our quads. Typically our vertex transformation would look something like this: +> ```glsl +> vec4 world_pos = MODEL_MATRIX * vec4(VERTEX, 1.0); +> vec4 view_pos =VIEW_MATRIX * world_pos; +> vec4 clip_pos = PROJECTION_MATRIX * view_pos; +> ``` +> However, we don't want to use the built-in `VIEW_MATRIX` as that contain a rotation component. Instead we effectively just want `view_pos` to be `world_pos + `. We can figure out this translation by transforming the origin or `(0, 0, 0)` to world space and view space and finding the difference between them. +> +> Taking this approach yields us: +> ```glsl +> void vertex() { +> vec4 origin = vec4(0.0f, 0.0f, 0.0f, 1.0f); +> vec4 world_origin = MODEL_MATRIX * origin; +> vec4 view_origin = VIEW_MATRIX * world_origin; +> vec4 world_to_view_translation = view_origin - world_origin; +> +> vec4 world_pos = MODEL_MATRIX * vec4(VERTEX, 1.0); +> vec4 view_pos = world_pos + world_to_view_translation; +> vec4 clip_pos = PROJECTION_MATRIX * view_pos; +> +> POSITION = clip_pos; +> } +> ``` +> While this approach is quite readable I will likely elect to go for an approach with less matrix multiplications for the long run. There is an approach from the [Godot forums](https://forum.godotengine.org/t/how-to-do-i-make-a-shader-a-billboard-face-the-player/1980/2) that surgically manipulates the view matrix using its inverse to achieve the same effect. + +Applying the shader seems to have given us the billboard effect we we're looking for! + +> [!caption|center] +> ![[god-rays-billboard.mp4]] +> Billboarding Effect + +### Stretching Towards the Light +Now we have a bunch of squares but they're not really looking like heavenly rays yet. Let's jump into this next little bit that I mentioned last time: +> Then we use a [[shaders|vertex shader]] to stretch the upper parts of the quad towards our light source. This creates the effect of a quadrilateral shape stretching from its base towards the sun. + +The trick here is that we need to only have the upper parts of the quad stretching. This way the base of the quads remain firmly planted where we placed them in our scene. We need two things to do this: The **direction** towards our sun or light source and an **amount** to stretch by. + +Then we can simply move the vertices of along that direction by that amount. + +> [!math]- Here There Be Math! +> Jumping back to our vertex shader from before, we first add two new uniforms. A global uniform for the **sun's direction** as this should be available across multiple shader in the game and a regular uniform **stretch amount** so we can control how much each beam stretches. +> ```glsl +> global uniform vec3 sun_direction; +> uniform float stretch_amount; +>``` +>Then to get our displacement we can just multiple the inverse sun direction by our stretch amount and add it to our view position. +>```glsl +> global uniform vec3 sun_direction; +> uniform float stretch_amount; +> +> void vertex() { +> ... +> // Stretch towards the light +> float uv_mask = 1.0 - UV.y; +> vec3 stretch_vector = uv_mask * stretch_amount * -sun_direction; +> view_pos += vec4(stretch_vector, 0.0f); +> ... +> } +>``` +> Note that we use the UV coordinate's to mask the `stretch_vector`. This causes the only the top vertices to be displaced as the **y** component of top vertices' is 0 while the bottoms' are 1. + +With just a couple lines of code, our little quads are starting to look a bit more beam-like! + +> [!caption|center] +> ![[god-rays-beams.mp4]] +> Stretched Quads + +### Modulating the Alpha +"Modulating the Alpha" sounds like a bit of Magic: The Gathering card text but the reality is much more mundane. What we mean here is that we want to change (*modulate*) the transparency (*the alpha component of our color*) of our rays based on several factors. Those factors include: +- [[god-rays-02#Scene Depth]] + +#### Scene Depth +The closer a ray is to an object, the less we want the ray to obscure that object. This can accomplished using what's called a [[depth-buffer|depth buffer]]. Depth buffers are just images that store how far away each pixel is from the camera. + +We can take a look at how far away our ray is from the screen and then compare it against how far away the rest of the scene is. The **closer they are** together, the **smaller the alpha value** we want to for our ray so long as the ray is **in front of the scene.** + +> [!math]- Here There Be Math! +> We can acquire the scene and ray (referred to as fragment) depth in our fragment shader for our god rays. +> +> We get the **fragment depth** out of `FRAGCOORD` as described in the [Godot docs](https://docs.godotengine.org/en/stable/tutorials/shaders/shader_reference/spatial_shader.html#fragment-built-ins). The **scene depth** we can get from reading the depth texture for the scene. +> ```glsl +> float fragment_depth = abs(FRAGCOORD.z); +> float scene_depth = texture(depth_texture, SCREEN_UV).r; +> ``` +> Then we need only subtract the two and clamp the resulting value between 0 and 1. We can also multiple the difference by a strength factor to be able to control how much scene depth affects the alpha. +> +> When it's all said and done it looks a little something like this: +> ```glsl +> float alpha = 1.0f; +> +> float fragment_depth = abs(FRAGCOORD.z); +> float scene_depth = texture(depth_texture, SCREEN_UV).r; +> +> alpha *= clamp((fragment_depth - scene_depth) * god_ray_depth_strength, 0.0f, 1.0f); +> +> ALPHA = alpha; +> ``` + +I took this as an opportunity to widen the quads slightly and the results are a huge step in the right direction: + +> [!caption|center] +> ![[god-rays-depth_modulated.png]] +> Alpha Modulation - Scene Depth + diff --git a/quartz/components/RecentBlog.tsx b/quartz/components/RecentBlog.tsx index 1cb6fd6ae..e66df3c50 100644 --- a/quartz/components/RecentBlog.tsx +++ b/quartz/components/RecentBlog.tsx @@ -1,7 +1,8 @@ import style from "./styles/recentblog.scss" import { i18n } from "../i18n" -import { resolveRelative } from "../util/path" +import { FullSlug, resolveRelative } from "../util/path" import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types" +import { Data } from "vfile" export default (() => { @@ -12,9 +13,13 @@ export default (() => { }: QuartzComponentProps) => { const page = allFiles.reduce((prev, curr) => { - if (!curr.filePath?.startsWith("content/blogs/graphics/")) { + const isValid = (p: Data) => { + return p.filePath?.startsWith("content/blogs/graphics/") && !p.frontmatter?.draft + } + + if (!isValid(curr)) { return prev - } else if (!prev.filePath?.startsWith("content/blogs/graphics/")) { + } else if (!isValid(prev)) { return curr } @@ -27,6 +32,7 @@ export default (() => { }, allFiles[0]) const title = page.frontmatter?.title ?? i18n(cfg.locale).propertyDefaults.title + const tags = page.frontmatter?.tags ?? [] const parseDate = (date: Date) => { return date.toLocaleDateString('en-US') //`${date.getFullYear()}/${date.getMonth()}/${date.getDay()}` } @@ -52,6 +58,18 @@ export default (() => { {parseDate(page.dates?.published!)} +

{page.description}

diff --git a/quartz/components/styles/recentblog.scss b/quartz/components/styles/recentblog.scss index 1c44beb87..54e372cae 100644 --- a/quartz/components/styles/recentblog.scss +++ b/quartz/components/styles/recentblog.scss @@ -26,6 +26,19 @@ & > div a { font-size: 20; + color: rgb(152, 173, 104); + background-color: rgba(157, 169, 143, 0.062); + } + } + + & > .preview-tags { + margin: 0px 0px; + margin-top: 16px; + + & > li a { + padding: 2px 8px; + color: rgb(104, 173, 162); + background-color: rgba(143, 169, 160, 0.062); } } @@ -38,6 +51,12 @@ @media (max-width: 767px) { .preview { + flex-direction: column; + + & > .preview-image { + display: none; + } + & > .preview-content { & > .preview-title { flex-direction: column; diff --git a/quartz/styles/callouts.scss b/quartz/styles/callouts.scss index b1fd180ce..f0ac6a559 100644 --- a/quartz/styles/callouts.scss +++ b/quartz/styles/callouts.scss @@ -28,6 +28,7 @@ --callout-icon-example: url('data:image/svg+xml; utf8, '); --callout-icon-quote: url('data:image/svg+xml; utf8, '); --callout-icon-fold: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"%3E%3Cpolyline points="6 9 12 15 18 9"%3E%3C/polyline%3E%3C/svg%3E'); + --callout-icon-sigma: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLXNpZ21hIj48cGF0aCBkPSJNMTggN1Y1YTEgMSAwIDAgMC0xLTFINi41YS41LjUgMCAwIDAtLjQuOGw0LjUgNmEyIDIgMCAwIDEgMCAyLjRsLTQuNSA2YS41LjUgMCAwIDAgLjQuOEgxN2ExIDEgMCAwIDAgMS0xdi0yIi8+PC9zdmc+'); &[data-callout] { --color: #448aff; @@ -43,6 +44,13 @@ --callout-icon: var(--callout-icon-abstract); } + &[data-callout="math"] { + --color: #dba642; + --border: #dba64244; + --bg: #dba64210; + --callout-icon: var(--callout-icon-sigma); + } + &[data-callout="info"], &[data-callout="todo"] { --color: #00b8d4;