Git Product home page Git Product logo

bevy_smud's Introduction

bevy_smud

crates.io MIT/Apache 2.0 crates.io docs.rs

Signed_Distance_Field/Function (SDF) 2D shape rendering for Bevy.

screenshot of a bird drawn with bevy_smud

Bevy smud is a way to conveniently construct and render sdf shapes with Bevy.

Given a shape function/expression, and a fill type, it generates shaders at run-time.

If you keep the number of different sdf and fill combinations relatively low it's pretty performant. My machine easily handles 100k shapes at 60 fps, with 40 different shape/fill combinations in randomized order (see gallery example).

Usage

An SDF is a way to map points in space to distances to a surface. If a point maps to a positive value, it's outside the shape, if it's negative, it's inside the shape. These "mappings" can be described by functions, which takes a point as input and returns a distance to a surface. For instance, if you wanted to make a circle, it could be described as length(position - center) - radius. That way, all the points that are radius away from center would be 0 and would define the edge of the shape.

Many such functions describing geometric primitives are included in this library, they are imported automatically when using the single-expression or body shorthand for adding sdfs. For instance, the circle above could also be described as:

sd_circle(p - center, 50.)

Similarly there are a bunch of other shapes (sd_ellipse, sd_box, sd_rounded_box, sd_egg etc. etc.)

Most of the built-in shapes are direct ports of the ones on this page, which includes screenshots of the various shapes. So that page might act as a good reference. The ports here use snake_case instead of camelCase.

To put together a shape, you can do:

use bevy::prelude::*;
use bevy_smud::prelude::*;

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, SmudPlugin))
        .add_systems(Startup, setup)
        .run();
}

fn setup(
    mut commands: Commands,
    mut shaders: ResMut<Assets<Shader>>,
) {
    commands.spawn(Camera2dBundle::default());

    let circle = shaders.add_sdf_expr("sd_circle(p, 50.)");

    commands.spawn(ShapeBundle {
        shape: SmudShape {
            color: Color::TOMATO,
            sdf: circle,
            frame: Frame::Quad(55.),
            ..default()
        },
        ..default()
    });
}

Make sure you reuse the shaders, i.e. don't call add_sdf_expr every frame.

You can also define shapes in .wgsl files. Note that in order to use the built-in shapes, you have to import smud, and you must create a function named sdf that takes a vec2<f32> and returns f32.

Other than that, make sure you understand how to combine shapes, use symmetries and change domains. For instance, the bevy in the screenshot above is built up of several circles, ellipses, and a vesica for the beak.

Also, check out the examples. In particular, the basic example should be a good place to start.

Showcase

Send me a PR if you want your project featured here:

  • Dis order: dis order screenshot

Word of caution

This crate is still fairly experimental.

If you want something more finished, you should probably check out bevy_prototype_lyon.

Bevy version support

The main branch targets the latest bevy release.

bevy bevy_smud
0.12 0.7, main
0.11 0.6
0.10 0.5
0.9 0.4
0.8 0.3
0.7 0.2
0.6 0.1

Thanks!

Little of this crate is original work. It's mostly a mishmash of bevy_sprite and Inigo Quilez sdf rendering primitives ported to wgsl. I just put the two together in a way I found convenient.

bevy_smud's People

Contributors

hoijui avatar johanhelsing avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

bevy_smud's Issues

Add time uniform

I think using time to animate things in shaders is such a basic thing to want that it should probably just always be available. It allows implementing really cool stuff with very little effort.

Also, the performance overhead of needlessly adding the time uniform when not used is probably very low (but should double check this)

@omphalosprime did this in their fork: omphalosprime@72b2043

Also relevant: https://github.com/bevyengine/bevy/blob/main/examples/shader/animate_shader.rs

Shader compilation errors on "fwidth" on wasm

I think this is a bug in wasm/naga.

A workaround in the meantime would be to just disable the derivative-based anti-alias fills on wasm. Perhaps it could be done with shader defines.

Allow supplying a dynamic number of positions

It seems like currently only one dynamic position is supplied to the sdf function.
I would prefer if the user could define how many dynamic positions you supply to each shader.
At least up to 4 positions would be necessary for common bezier curves. Having dynamic curves is pretty important for my use cases.

Pass custom instance data to sdf shader function (to allow single-shader parametrized shapes)

For some shapes, for instance rectangles, circles, rounded rectangels, it makes a lot of sense to be able to send per instance data into the sdf function so you could change side length/radius without compiling a new shader.

I'm not really sure what the best way to implement this would be... One simple way could be to just have a couple of floats of "user data" that will just always be passed around. It may come with a performance cost, though.

Perhaps some way to toggle it with shader defs would be good, and then have it be an optional component SdfParameters(Vec4) so we don't pay the price for it when not used.

Gray borders around shapes

Basically the title i'm not sure if it's due to my intel gpu or what. My console spits out some things but I'm pretty sure it's unrelated in case though here:

2022-11-05T00:18:39.528209Z  INFO winit::platform_impl::platform::x11::window: Guessed window scale factor: 1.5
2022-11-05T00:18:39.548085Z  INFO bevy_render::renderer: AdapterInfo { name: "Intel(R) UHD Graphics (CML GT2)", vendor: 32902, device: 39745, device_type: IntegratedGpu, backend: Vulkan }
2022-11-05T00:18:39.548672Z ERROR wgpu_hal::vulkan::instance: VALIDATION [VUID-VkDeviceCreateInfo-pNext-02830 (0x211e533b)]
        Validation Error: [ VUID-VkDeviceCreateInfo-pNext-02830 ] Object 0: handle = 0x55d6e0ac6500, type = VK_OBJECT_TYPE_INSTANCE; | MessageID = 0x211e533b | If the pNext chain includes a VkPhysicalDeviceVulkan12Features structure, then it must not include a VkPhysicalDevice8BitStorageFeatures, VkPhysicalDeviceShaderAtomicInt64Features, VkPhysicalDeviceShaderFloat16Int8Features, VkPhysicalDeviceDescriptorIndexingFeatures, VkPhysicalDeviceScalarBlockLayoutFeatures, VkPhysicalDeviceImagelessFramebufferFeatures, VkPhysicalDeviceUniformBufferStandardLayoutFeatures, VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures, VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures, VkPhysicalDeviceHostQueryResetFeatures, VkPhysicalDeviceTimelineSemaphoreFeatures, VkPhysicalDeviceBufferDeviceAddressFeatures, or VkPhysicalDeviceVulkanMemoryModelFeatures structure The Vulkan spec states: If the pNext chain includes a VkPhysicalDeviceVulkan12Features structure, then it must not include a VkPhysicalDevice8BitStorageFeatures, VkPhysicalDeviceShaderAtomicInt64Features, VkPhysicalDeviceShaderFloat16Int8Features, VkPhysicalDeviceDescriptorIndexingFeatures, VkPhysicalDeviceScalarBlockLayoutFeatures, VkPhysicalDeviceImagelessFramebufferFeatures, VkPhysicalDeviceUniformBufferStandardLayoutFeatures, VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures, VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures, VkPhysicalDeviceHostQueryResetFeatures, VkPhysicalDeviceTimelineSemaphoreFeatures, VkPhysicalDeviceBufferDeviceAddressFeatures, or VkPhysicalDeviceVulkanMemoryModelFeatures structure (https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-VkDeviceCreateInfo-pNext-02830)
2022-11-05T00:18:39.548723Z ERROR wgpu_hal::vulkan::instance:   objects: (type: INSTANCE, hndl: 0x55d6e0ac6500, name: ?)
2022-11-05T00:18:39.598406Z ERROR wgpu_hal::vulkan::instance: VALIDATION [VUID-VkDescriptorSetLayoutCreateInfo-descriptorType-03001 (0x8ac432cd)]
        Validation Error: [ VUID-VkDescriptorSetLayoutCreateInfo-descriptorType-03001 ] Object 0: handle = 0x55d6e13a95a0, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0x8ac432cd | vkCreateDescriptorSetLayout(): binding (8) has VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT flag, but binding (1) has descriptor type VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC. The Vulkan spec states: If any binding has the VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT bit set, then all bindings must not have descriptorType of VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC or VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC (https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-VkDescriptorSetLayoutCreateInfo-descriptorType-03001)
2022-11-05T00:18:39.598511Z ERROR wgpu_hal::vulkan::instance

and heres a screenshot:
20221104_19h17m54s_grim

Error on entity despawn

Hello I encountered the error on SmudShape entity despawn:

ERROR bevy_render::render_resource::pipeline_cache: failed to process shader: Unresolved import: Custom("bevy_smud::generated::cec721b4-2ec6-478e-85ab-afaec5a87178")

or on bevy 0.11

│ var<uniform> globals: _naga_oil_mod_MJSXM6K7OJSW4ZDFOI5DUZ3MN5RGC3DT_memberGlobals;
  │                                ^
  │
  = missing import 'bevy_smud::sdf'

To reproduce the issue just call commands.entity(smud_entity).despawn_recursive(); and check logs.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.