Implemented CRT shader
This commit is contained in:
parent
d70d183861
commit
8ace32102b
5
game/assets/configs/final_shader.json
Normal file
5
game/assets/configs/final_shader.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"pixel_scale": 1.0,
|
||||||
|
"warp_factor": 0.35,
|
||||||
|
"scanline_darkness": 0.15
|
||||||
|
}
|
@ -1,3 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* This shader is the last piece of the graphics pipeline. EVERYTHING is passed through it.
|
||||||
|
*/
|
||||||
|
|
||||||
#version 330
|
#version 330
|
||||||
|
|
||||||
// Fragment texture UV coordinate
|
// Fragment texture UV coordinate
|
||||||
@ -13,22 +17,41 @@ uniform vec2 viewport;
|
|||||||
out vec4 finalColor;
|
out vec4 finalColor;
|
||||||
|
|
||||||
// Pixel scaling factor (pixels per pixel)
|
// Pixel scaling factor (pixels per pixel)
|
||||||
const vec2 pixelScale = vec2(1.0, 1.0);
|
uniform vec2 pixelScale;
|
||||||
|
|
||||||
|
// CRT effect parameters
|
||||||
|
uniform float warpFactor;
|
||||||
|
uniform float scanlineDarkness;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// Calculate the distance to merge pixels
|
// Calculate the distance to merge pixels
|
||||||
float dx = pixelScale.x * (1.0 / viewport.x);
|
float dx = pixelScale.x * (1.0 / viewport.x);
|
||||||
float dy = pixelScale.y * (1.0 / viewport.y);
|
float dy = pixelScale.y * (1.0 / viewport.y);
|
||||||
|
|
||||||
|
// Calculate the squared distance from the center
|
||||||
|
vec2 dist_center_sq = abs(0.5 - fragTexCoord);
|
||||||
|
dist_center_sq *= dist_center_sq;
|
||||||
|
|
||||||
// Get the base UV coordinate of the pixel
|
// Get the base UV coordinate of the pixel
|
||||||
vec2 baseUV = fragTexCoord;
|
vec2 baseUV = fragTexCoord;
|
||||||
|
|
||||||
// Calculate a UV for this new blocky pixel
|
// Calculate a UV for this new blocky pixel
|
||||||
vec2 pixelatedUV = vec2(dx * floor(baseUV.x / dx), dy * floor(baseUV.y / dy));
|
vec2 pixelatedUV = vec2(dx * floor(baseUV.x / dx), dy * floor(baseUV.y / dy));
|
||||||
|
|
||||||
// Rebuild the texture with the new UVs
|
// Warp the UVs of the pixelated texture
|
||||||
vec3 tc = texture(texture0, pixelatedUV).rgb;
|
vec2 warpedUV = pixelatedUV;
|
||||||
|
warpedUV.x -= 0.5; warpedUV.x *= 1.0+(dist_center_sq.y * (0.3 * warpFactor)); warpedUV.x += 0.5;
|
||||||
|
warpedUV.y -= 0.5; warpedUV.y *= 1.0+(dist_center_sq.x * (0.4 * warpFactor)); warpedUV.y += 0.5;
|
||||||
|
|
||||||
|
// If the UV is outside the texture, return black
|
||||||
|
if (warpedUV.x < 0.0 || warpedUV.x > 1.0 || warpedUV.y < 0.0 || warpedUV.y > 1.0) {
|
||||||
|
finalColor = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine factor of if we are rendering on a scanline
|
||||||
|
float scanlineFactor = abs(sin(fragTexCoord.y * viewport.y) * 0.5 * scanlineDarkness);
|
||||||
|
|
||||||
// Build the final pixel
|
// Build the final pixel
|
||||||
finalColor = vec4(tc, 1.0);
|
finalColor = vec4(mix(texture(texture0, warpedUV).rgb, vec3(0.0), scanlineFactor), 1.0);
|
||||||
}
|
}
|
||||||
|
@ -80,9 +80,12 @@ use crate::{
|
|||||||
context::GameContext,
|
context::GameContext,
|
||||||
discord_rpc::{maybe_set_discord_presence, try_connect_to_local_discord},
|
discord_rpc::{maybe_set_discord_presence, try_connect_to_local_discord},
|
||||||
scenes::{build_screen_state_machine, Scenes},
|
scenes::{build_screen_state_machine, Scenes},
|
||||||
utilities::shaders::{
|
utilities::{
|
||||||
shader::ShaderWrapper,
|
game_config::FinalShaderConfig,
|
||||||
util::{dynamic_screen_texture::DynScreenTexture, render_texture::render_to_texture},
|
shaders::{
|
||||||
|
shader::ShaderWrapper,
|
||||||
|
util::{dynamic_screen_texture::DynScreenTexture, render_texture::render_to_texture},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -166,16 +169,20 @@ pub async fn game_begin(game_config: &GameConfig) -> Result<(), Box<dyn std::err
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Create a dynamic texture to draw to for processing by shaders
|
// Create a dynamic texture to draw to for processing by shaders
|
||||||
info!("Allocating a SNOWZ7Zresizable texture for the screen");
|
info!("Allocating a resizable texture for the screen");
|
||||||
let mut dynamic_texture =
|
let mut dynamic_texture =
|
||||||
DynScreenTexture::new(&mut context.renderer.borrow_mut(), &raylib_thread)?;
|
DynScreenTexture::new(&mut context.renderer.borrow_mut(), &raylib_thread)?;
|
||||||
|
|
||||||
// Load the pixel art shader
|
// Load the pixel art shader
|
||||||
info!("Loading the pixel art shader");
|
info!("Loading the pixel art shader");
|
||||||
|
let pixel_shader_config = FinalShaderConfig::load(
|
||||||
|
StaticGameData::get("configs/final_shader.json").expect("Failed to load final_shader.json"),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let mut pixel_shader = ShaderWrapper::new(
|
let mut pixel_shader = ShaderWrapper::new(
|
||||||
None,
|
None,
|
||||||
Some(StaticGameData::get("shaders/pixelart.fs")).expect("Failed to load pixelart.fs"),
|
Some(StaticGameData::get("shaders/pixelart.fs")).expect("Failed to load pixelart.fs"),
|
||||||
vec!["viewport"],
|
vec!["viewport", "pixelScale", "warpFactor", "scanlineDarkness"],
|
||||||
&mut context.renderer.borrow_mut(),
|
&mut context.renderer.borrow_mut(),
|
||||||
&raylib_thread,
|
&raylib_thread,
|
||||||
)?;
|
)?;
|
||||||
@ -202,6 +209,15 @@ pub async fn game_begin(game_config: &GameConfig) -> Result<(), Box<dyn std::err
|
|||||||
|
|
||||||
// Update the pixel shader to correctly handle the screen size
|
// Update the pixel shader to correctly handle the screen size
|
||||||
pixel_shader.set_variable("viewport", screen_size)?;
|
pixel_shader.set_variable("viewport", screen_size)?;
|
||||||
|
pixel_shader.set_variable(
|
||||||
|
"pixelScale",
|
||||||
|
Vector2::new(
|
||||||
|
pixel_shader_config.pixel_scale,
|
||||||
|
pixel_shader_config.pixel_scale,
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
pixel_shader.set_variable("warpFactor", pixel_shader_config.warp_factor)?;
|
||||||
|
pixel_shader.set_variable("scanlineDarkness", pixel_shader_config.scanline_darkness)?;
|
||||||
|
|
||||||
// Render the game via the pixel shader
|
// Render the game via the pixel shader
|
||||||
render_to_texture(&mut dynamic_texture, || {
|
render_to_texture(&mut dynamic_texture, || {
|
||||||
|
@ -8,7 +8,7 @@ use rust_embed::EmbeddedFile;
|
|||||||
pub struct Author {
|
pub struct Author {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
pub roles: Vec<String>
|
pub roles: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
@ -16,7 +16,7 @@ pub struct GameConfig {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
// pub authors: Vec<Author>,
|
// pub authors: Vec<Author>,
|
||||||
pub base_window_size: (i32, i32),
|
pub base_window_size: (i32, i32),
|
||||||
pub sentry_dsn: String
|
pub sentry_dsn: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameConfig {
|
impl GameConfig {
|
||||||
@ -25,3 +25,17 @@ impl GameConfig {
|
|||||||
serde_json::from_slice(&file.data)
|
serde_json::from_slice(&file.data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct FinalShaderConfig {
|
||||||
|
pub pixel_scale: f32,
|
||||||
|
pub warp_factor: f32,
|
||||||
|
pub scanline_darkness: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FinalShaderConfig {
|
||||||
|
/// Load from a file
|
||||||
|
pub fn load(file: EmbeddedFile) -> Result<Self, serde_json::Error> {
|
||||||
|
serde_json::from_slice(&file.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user