Skip to content

Commit

Permalink
Render in-game SDF sprites with a black outline
Browse files Browse the repository at this point in the history
SDF sprites are created with a pxrange of 8 (4 inside, 4 outside pixel range) and a size of 64x64. SDF are also now always grayscale and are mainly colored using the instruction color. For this we extended the texture loader to also handle grayscale images properly. Only PNG and TGA provide proper grayscale images.
  • Loading branch information
hasenbanck committed Dec 18, 2024
1 parent 1077bc5 commit 17feab8
Show file tree
Hide file tree
Showing 24 changed files with 218 additions and 59 deletions.
Binary file modified korangar/archive/data/texture/collapsed_arrow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified korangar/archive/data/texture/expanded_arrow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified korangar/archive/data/texture/filled_box.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified korangar/archive/data/texture/marker_effect.png
100755 → 100644
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified korangar/archive/data/texture/marker_entity.png
100755 → 100644
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified korangar/archive/data/texture/marker_light.png
100755 → 100644
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified korangar/archive/data/texture/marker_object.png
100755 → 100644
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified korangar/archive/data/texture/marker_particle.png
100755 → 100644
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified korangar/archive/data/texture/marker_shadow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified korangar/archive/data/texture/marker_sound.png
100755 → 100644
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified korangar/archive/data/texture/unfilled_box.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions korangar/src/graphics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub use self::texture::*;
pub use self::vertices::*;
use crate::graphics::sampler::{create_new_sampler, SamplerType};
use crate::interface::layout::ScreenSize;
use crate::loaders::TextureLoader;
use crate::loaders::{ImageType, TextureLoader};
use crate::NUMBER_OF_POINT_LIGHTS_WITH_SHADOWS;

/// The size of a tile in pixel of the tile based light culling.
Expand Down Expand Up @@ -377,7 +377,7 @@ impl GlobalContext {
RgbaImage::from_raw(1, 1, vec![255, 255, 255, 255]).unwrap().as_raw(),
false,
));
let walk_indicator_texture = texture_loader.get("grid.tga").unwrap();
let walk_indicator_texture = texture_loader.get("grid.tga", ImageType::Color).unwrap();
let forward_textures = Self::create_forward_textures(device, forward_size, msaa);
let picker_textures = Self::create_picker_textures(device, screen_size);
let directional_shadow_map_texture = Self::create_directional_shadow_texture(device, directional_shadow_size);
Expand Down
4 changes: 2 additions & 2 deletions korangar/src/graphics/passes/interface/shader/rectangle.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
switch (instance.rectangle_type) {
case 1u: {
// SDF
let pixel = textureSample(texture, linear_sampler, input.texture_coordinates);
color *= vec4(pixel.rgb, saturate((pixel.a - 0.5) * 2.0 / fwidth(pixel.a)));
let distance = textureSample(texture, linear_sampler, input.texture_coordinates).r;
color *= vec4(saturate((distance - 0.5) * 2.0 / fwidth(distance)));
}
case 2u: {
// Sprite (linear filtering)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
switch (instance.rectangle_type) {
case 1u: {
// SDF
let pixel = textureSample(textures[instance.texture_index], linear_sampler, input.texture_coordinates);
color *= vec4(pixel.rgb, saturate((pixel.a - 0.5) * 2.0 / fwidth(pixel.a)));
let distance = textureSample(textures[instance.texture_index], linear_sampler, input.texture_coordinates).r;
color *= vec4(saturate((distance - 0.5) * 2.0 / fwidth(distance)));
}
case 2u: {
// Sprite (linear filtering)
Expand Down
48 changes: 40 additions & 8 deletions korangar/src/graphics/passes/postprocessing/shader/rectangle.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,61 @@ fn vs_main(
return output;
}

/// The range of the SDF border defines the outline of an SDF. msdfgen calls this pxrange.
/// We normaly use pxrange of 8 px when creating 64x64 SDFs, which results in an out border of 4 px in texture space.
const BORDER_WIDTH: f32 = 0.5;
const EDGE_VALUE: f32 = 0.5;

@fragment
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
let instance = instance_data[input.instance_index];

var color: vec4<f32> = instance.color;

switch (instance.rectangle_type) {
case 1u: {
// SDF
let pixel = textureSample(texture, linear_sampler, input.texture_coordinates);
return vec4(pixel.rgb, saturate((pixel.a - 0.5) * 2.0 / fwidth(pixel.a))) * instance.color;
let distance = textureSample(texture, linear_sampler, input.texture_coordinates).r;
let aa_width = fwidth(distance);

color *= vec4(step(EDGE_VALUE, distance));

// Outside outline
if (distance > EDGE_VALUE - BORDER_WIDTH && distance < EDGE_VALUE) {
let bias = 0.1;
let border_max = (EDGE_VALUE - BORDER_WIDTH) + bias;

// Transition from transparent to black outline
let outer_alpha = smoothstep(
border_max,
border_max + aa_width,
distance
);
color = vec4<f32>(0.0, 0.0, 0.0, outer_alpha);
}
// Inside outline
else if (distance >= EDGE_VALUE && distance < EDGE_VALUE + aa_width) {
// Transition from black outline to fill color
let inner_blend = smoothstep(
EDGE_VALUE,
EDGE_VALUE + aa_width,
distance
);
color = mix(vec4<f32>(0.0, 0.0, 0.0, 1.0), instance.color, inner_blend);
}
}
case 2u: {
// Sprite (linear filtering)
return textureSample(texture, linear_sampler, input.texture_coordinates) * instance.color;
color *= textureSample(texture, linear_sampler, input.texture_coordinates);
}
case 3u: {
// Sprite (nearest filtering)
return textureSample(texture, nearest_sampler, input.texture_coordinates) * instance.color;
}
default: {
// Solid
return instance.color;
color *= textureSample(texture, nearest_sampler, input.texture_coordinates);
}
default: {}
}

return color;
}

// Optimized version of the following truth table:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,61 @@ fn vs_main(
return output;
}

/// The range of the SDF border defines the outline of an SDF. msdfgen calls this pxrange.
/// We normaly use pxrange of 8 px when creating 64x64 SDFs, which results in an out border of 4 px in texture space.
const BORDER_WIDTH: f32 = 0.5;
const EDGE_VALUE: f32 = 0.5;

@fragment
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
let instance = instance_data[input.instance_index];

var color: vec4<f32> = instance.color;

switch (instance.rectangle_type) {
case 1u: {
// SDF
let pixel = textureSample(textures[instance.texture_index], linear_sampler, input.texture_coordinates);
return vec4(pixel.rgb, saturate((pixel.a - 0.5) * 2.0 / fwidth(pixel.a))) * instance.color;
let distance = textureSample(textures[instance.texture_index], linear_sampler, input.texture_coordinates).r;
let aa_width = fwidth(distance);

color *= vec4(step(EDGE_VALUE, distance));

// Outside outline
if (distance > EDGE_VALUE - BORDER_WIDTH && distance < EDGE_VALUE) {
let bias = 0.1;
let border_max = (EDGE_VALUE - BORDER_WIDTH) + bias;

// Transition from transparent to black outline
let outer_alpha = smoothstep(
border_max,
border_max + aa_width,
distance
);
color = vec4<f32>(0.0, 0.0, 0.0, outer_alpha);
}
// Inside outline
else if (distance >= EDGE_VALUE && distance < EDGE_VALUE + aa_width) {
// Transition from black outline to fill color
let inner_blend = smoothstep(
EDGE_VALUE,
EDGE_VALUE + aa_width,
distance
);
color = mix(vec4<f32>(0.0, 0.0, 0.0, 1.0), instance.color, inner_blend);
}
}
case 2u: {
// Sprite (linear filtering)
return textureSample(textures[instance.texture_index], linear_sampler, input.texture_coordinates) * instance.color;
color *= textureSample(textures[instance.texture_index], linear_sampler, input.texture_coordinates);
}
case 3u: {
// Sprite (nearest filtering)
return textureSample(textures[instance.texture_index], nearest_sampler, input.texture_coordinates) * instance.color;
}
default: {
// Solid
return instance.color;
color *= textureSample(textures[instance.texture_index], nearest_sampler, input.texture_coordinates);
}
default: {}
}

return color;
}

// Optimized version of the following truth table:
Expand Down
4 changes: 2 additions & 2 deletions korangar/src/loaders/effect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use ragnarok_formats::version::InternalVersion;
use wgpu::BlendFactor;

use super::error::LoadError;
use super::TextureLoader;
use super::{ImageType, TextureLoader};
use crate::graphics::Color;
use crate::loaders::GameFileLoader;
use crate::world::{AnimationType, Effect, Frame, FrameType, Layer, MultiTexturePresent};
Expand Down Expand Up @@ -70,7 +70,7 @@ impl EffectLoader {
.into_iter()
.map(|name| {
let path = format!("effect\\{}{}", prefix, name.name);
texture_loader.get(&path).unwrap()
texture_loader.get(&path, ImageType::Color).unwrap()
})
.collect(),
{
Expand Down
4 changes: 2 additions & 2 deletions korangar/src/loaders/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub use self::vertices::MAP_TILE_SIZE;
use self::vertices::{generate_tile_vertices, ground_vertices};
use super::error::LoadError;
use crate::graphics::{Buffer, ModelVertex, NativeModelVertex, Texture};
use crate::loaders::{GameFileLoader, ModelLoader, TextureAtlasFactory, TextureLoader};
use crate::loaders::{GameFileLoader, ImageType, ModelLoader, TextureAtlasFactory, TextureLoader};
use crate::world::{LightSourceKey, Model};
use crate::{EffectSourceExt, LightSourceExt, Map, Object, ObjectKey, SoundSourceExt};

Expand Down Expand Up @@ -121,7 +121,7 @@ impl MapLoader {
let water_paths = get_water_texture_paths(water_type);
water_paths
.iter()
.map(|path| texture_loader.get(path).expect("Can't load water texture"))
.map(|path| texture_loader.get(path, ImageType::Color).expect("Can't load water texture"))
.collect()
});

Expand Down
2 changes: 1 addition & 1 deletion korangar/src/loaders/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub use self::model::*;
pub use self::script::{ResourceMetadata, ScriptLoader};
pub use self::server::{load_client_info, ClientInfo, ServiceId};
pub use self::sprite::*;
pub use self::texture::{TextureAtlasFactory, TextureLoader};
pub use self::texture::{ImageType, TextureAtlasFactory, TextureLoader};

pub const FALLBACK_BMP_FILE: &str = "missing.bmp";
pub const FALLBACK_JPEG_FILE: &str = "missing.jpg";
Expand Down
6 changes: 3 additions & 3 deletions korangar/src/loaders/script/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use korangar_util::FileLoader;
use mlua::Lua;
use ragnarok_packets::ItemId;

use super::TextureLoader;
use super::{ImageType, TextureLoader};
use crate::graphics::Texture;
use crate::loaders::GameFileLoader;

Expand Down Expand Up @@ -152,7 +152,7 @@ end

let resource_name = self.get_item_resource_from_id(item.item_id, is_identified);
let full_path = format!("À¯ÀúÀÎÅÍÆäÀ̽º\\item\\{resource_name}.bmp");
let texture = texture_loader.get(&full_path).unwrap();
let texture = texture_loader.get(&full_path, ImageType::Color).unwrap();
let name = self.get_item_name_from_id(item.item_id, is_identified);

let metadata = ResourceMetadata { texture, name };
Expand All @@ -163,7 +163,7 @@ end
pub fn load_market_item_metadata(&self, texture_loader: &TextureLoader, item: ShopItem<NoMetadata>) -> ShopItem<ResourceMetadata> {
let resource_name = self.get_item_resource_from_id(item.item_id, true);
let full_path = format!("À¯ÀúÀÎÅÍÆäÀ̽º\\item\\{resource_name}.bmp");
let texture = texture_loader.get(&full_path).unwrap();
let texture = texture_loader.get(&full_path, ImageType::Color).unwrap();
let name = self.get_item_name_from_id(item.item_id, true);

let metadata = ResourceMetadata { texture, name };
Expand Down
Loading

0 comments on commit 17feab8

Please sign in to comment.