From 3f114ebe5c4d3618504b0e623b52c22e262de854 Mon Sep 17 00:00:00 2001
From: soaos today i wanted to take a bit of time to write about a shader i implemented for my in-progress game project (more
- on that soon™) i wanted to create a "blacklight" effect, where specific lights could reveal part of the base texture. this
- shader works with spot lights only, but could be extended to work with point lights i wrote this shader in wgsl for a bevy engine project, but
- it should translate easily to other shading languages the finished shader can be found as part of this repo
- for this shader, i wanted the following features:
- Creating a Blacklight Shader
-
- NOTE: THIS POST WAS TRANSFERRED FROM MARKDOWN BY HAND SO I MIGHT HAVE MISSED SOME STUFF SORRY
-
-
- shader inputs
-
-
-
-
- for this to work i need the following information about each light:
-
-
-
- i also need some info from the vertex shader:
-
-
-
bevy's default pbr vertex shader provides this information, but as long as you can get this info into your - fragment - shader you should be good to go
- -lastly i'll take a base color texture and a sampler
- -- with all of that, i can start off the shader by setting up the inputs and fragment entry point: - -
- #import bevy_pbr::forward_io::VertexOutput;
-
- struct BlackLight {
- position: vec3<f32>,
- direction: vec3<f32>,
- range: f32,
- inner_angle: f32,
- outer_angle: f32,
- }
-
- @group(2) @binding(0) var<storage> lights: array<BlackLight>;
- @group(2) @binding(1) var base_texture: texture_2d<f32>;
- @group(2) @binding(2) var base_sampler: sampler;
-
- @fragment
- fn fragment(
- in: VertexOutput,
- ) -> @location(0) vec4<f32> {
- }
-
- (bevy uses group 2 for custom shader bindings)
-
-
- - since the number of lights is dynamic, i use a storage buffer to store - that information -
- -the first thing we'll need to know is how close to looking at the fragment the light source - is
- -- we can get this information using some interesting math: - -
- let light = lights[0]; - let light_to_fragment_direction = normalize(in.world_position.xyz - light.position); - let light_to_fragment_angle = acos(dot(light.direction, light_to_fragment_direction)); -- - the first step of this is taking the dot product of light direction and the direction from - the light to the fragment - - -
since both direction vectors are normalized, the dot product will be between -1.0 and 1.0
- -- the dot product of two unit vectors is the cosine of the angle between them (proof - here) -
- -- therefore, we take the arccosine of that dot product to get the angle between the light and - the fragment -
- -- once we have this angle we can plug it in to a falloff based on the angle properties of the - light: - -
- let angle_inner_factor = light.inner_angle/light.outer_angle; - let angle_factor = linear_falloff_radius(light_to_fragment_angle / light.outer_angle, angle_inner_factor); --
- fn linear_falloff_radius(factor: f32, radius: f32) -> f32 {
- if factor < radius { return 1.0; } else {
- return 1.0 - (factor - radius) / (1.0 - radius);
- }
- }
-
-
- - next, we need to make sure the effect falls off properly over distance we can do this by getting the distance - from the light to - the fragment and normalizing it with the range of the light before plugging that into an inverse square falloff - we'll use squared distance to avoid expensive and unnecessary square root operations: -
- let light_distance_squared=distance_squared(in.world_position.xyz, light.position); - let distance_factor=inverse_falloff_radius(saturate(light_distance_squared / (light.range * light.range)), 0.5); --
- fn distance_squared(a: vec3f, b: vec3f) -> f32 {
- let vec = a - b;
- return dot(vec, vec);
- }
-
- fn inverse_falloff(factor: f32) -> f32 {
- return pow(1.0 - factor, 2.0);
- }
-
- fn inverse_falloff_radius(factor: f32, radius: f32) -> f32 {
- if factor < radius { return 1.0; } else {
- return inverse_falloff((factor - radius) / (1.0 - radius));
- }
- }
-
-
- - now we'll have a float multiplier between 0.0 and 1.0 for our angle and distance to the light we can get the - resulting color by multiplying these with the base color texture: -
- let base_color = textureSample(base_texture, base_sampler, in.uv); - let final_color=base_color * angle_factor * distance_factor; -- this works for one light, but we need to refactor it to loop over all the provided blacklights: -
-
- @fragment fn fragment( in: VertexOutput ) -> @location(0) vec4<f32> {
- let base_color = textureSample(base_texture, base_sampler, in.uv);
- var final_color = vec4f(0.0, 0.0, 0.0, 0.0);
- for (var i = u32(0); i < arrayLength(&lights); i = i+1) {
- let light=lights[i];
- let light_to_fragment_direction = normalize(in.world_position.xyz - light.position);
- let light_to_fragment_angle = acos(dot(light.direction, light_to_fragment_direction));
- let angle_inner_factor = light.inner_angle / light.outer_angle;
- let angle_factor = linear_falloff_radius(light_to_fragment_angle / light.outer_angle, angle_inner_factor);
- let light_distance_squared = distance_squared(in.world_position.xyz, light.position);
- let distance_factor = inverse_falloff_radius(saturate(light_distance_squared / (light.range * light.range)), 0.5);
- final_color = saturate(final_color + base_color * angle_factor * distance_factor);
- }
- return final_color;
- }
-
- and with that, the shader is pretty much complete you can view the full completed shader code here
-
- have fun!
- - - \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index eed9644..0000000 Binary files a/public/favicon.ico and /dev/null differ diff --git a/public/index.html b/public/index.html deleted file mode 100644 index 725c752..0000000 --- a/public/index.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - -Hey, welcome to my site, or what's left of it, LMAO.
- -- I'm Silas Bartha, an "artist" and professional software developer. - I do full stack for my work and in my free time I mostly work on eccentric software projects which you can read - about here (soon). -
- -- Recently I've been trying to branch out a bit. - I have a few skills I'd like to pick up over the course of the next few years, - which will help me better execute future projects. - It's hard because I hate being awful at things haha. -
- -This is a project I've been working on since April 2024. It's probably the longest-running personal project I've ever done and has been a monumental undertaking so far.
-I'm unsure how much I want to reveal about this project while I'm developing it, I want the experience to be as novel as possible once it's out. I think I'll probably stick to posting about it here on my site and the occasional YouTube video until it's closer to ready.
-NIX AVREA is the codename for my first game project. The game is highly experimental and features mechanics that (as far as I know) have never been attempted. The game is centered around dynamic content, using steganographic techniques to embed binary payloads inside of asset files in order to construct the game world from a directory on the player's filesystem.
-There is a ton of stuff that's already implemented for this project and I'll gradually add more to the following directories explaining in-depth some of the components:
--+-- mechanics/ -+-- design/ -+-- narrative/ -- - - \ No newline at end of file diff --git a/public/projects/piss_daemon/index.html b/public/projects/piss_daemon/index.html deleted file mode 100644 index 5cb9481..0000000 --- a/public/projects/piss_daemon/index.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - -
This is a D-Bus daemon (pissd) and client (piss-level) that monitor the international
- space station's urine tank level.
I have it integrated into my status bar (X version):
-
-
- #...
-
- function piss {
- PISS_LEVEL="$(piss-level)";
- if [ -n "$PISS_LEVEL" ]; then
- echo " ${PISS_LEVEL}%";
- fi;
- }
-
- #...
-
- while true; do
- xsetroot -name "$(piss)$(batt)$(datetime)";
- sleep 1;
- done;
-
- I made this pretty much entirely so that I could call a program "piss daemon".
-I might organize this better at some point, but for now there's no particular order to these. I just want to - share - my awful taste with everyone LMAO.
-I only included one album per artist in order to avoid making this list infinitely long LOL. It was difficult for - some of these bands...
- -
-
-
-
-
-
-
-
-
-