Merge 7996d5126e
into 877d278785
This commit is contained in:
commit
99ddf518d8
11
Cargo.toml
11
Cargo.toml
@ -2865,6 +2865,17 @@ description = "A custom shader that builds on the standard material"
|
||||
category = "Shaders"
|
||||
wasm = true
|
||||
|
||||
[[example]]
|
||||
name = "stochastic_sampling"
|
||||
path = "examples/shader/stochastic_sampling.rs"
|
||||
doc-scrape-examples = true
|
||||
|
||||
[package.metadata.example.stochastic_sampling]
|
||||
name = "Stochastic Sampling"
|
||||
description = "A custom shader that avoids repetition artifacts when tiling a texture"
|
||||
category = "Shaders"
|
||||
wasm = true
|
||||
|
||||
[[example]]
|
||||
name = "shader_prepass"
|
||||
path = "examples/shader/shader_prepass.rs"
|
||||
|
87
assets/shaders/stochastic_sampling.wgsl
Normal file
87
assets/shaders/stochastic_sampling.wgsl
Normal file
@ -0,0 +1,87 @@
|
||||
#import bevy_sprite::mesh2d_vertex_output::VertexOutput
|
||||
#import bevy_sprite::mesh2d_view_bindings::globals
|
||||
#import bevy_sprite::mesh2d_functions
|
||||
|
||||
fn permute_3_(x: vec3<f32>) -> vec3<f32> {
|
||||
return (((x * 34.) + 1.) * x) % vec3(289.);
|
||||
}
|
||||
|
||||
// Noise implementation from https://github.com/johanhelsing/noisy_bevy/blob/v0.8.0/assets/noisy_bevy.wgsl
|
||||
fn simplex_noise_2d(v: vec2<f32>) -> f32 {
|
||||
let C = vec4(
|
||||
0.211324865405187, // (3.0 - sqrt(3.0)) / 6.0
|
||||
0.366025403784439, // 0.5 * (sqrt(3.0) - 1.0)
|
||||
-0.577350269189626, // -1.0 + 2.0 * C.x
|
||||
0.024390243902439 // 1.0 / 41.0
|
||||
);
|
||||
|
||||
// first corner
|
||||
var i = floor(v + dot(v, C.yy));
|
||||
let x0 = v - i + dot(i, C.xx);
|
||||
|
||||
// other corners
|
||||
var i1 = select(vec2(0., 1.), vec2(1., 0.), x0.x > x0.y);
|
||||
var x12 = x0.xyxy + C.xxzz - vec4(i1, 0., 0.);
|
||||
|
||||
// permutations
|
||||
i = i % vec2(289.);
|
||||
|
||||
let p = permute_3_(permute_3_(i.y + vec3(0., i1.y, 1.)) + i.x + vec3(0., i1.x, 1.));
|
||||
var m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), vec3(0.));
|
||||
m *= m;
|
||||
m *= m;
|
||||
|
||||
// gradients: 41 points uniformly over a line, mapped onto a diamond
|
||||
// the ring size, 17*17 = 289, is close to a multiple of 41 (41*7 = 287)
|
||||
let x = 2. * fract(p * C.www) - 1.;
|
||||
let h = abs(x) - 0.5;
|
||||
let ox = floor(x + 0.5);
|
||||
let a0 = x - ox;
|
||||
|
||||
// normalize gradients implicitly by scaling m
|
||||
// approximation of: m *= inversesqrt(a0 * a0 + h * h);
|
||||
m = m * (1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h));
|
||||
|
||||
// compute final noise value at P
|
||||
let g = vec3(a0.x * x0.x + h.x * x0.y, a0.yz * x12.xz + h.yz * x12.yw);
|
||||
return 130. * dot(m, g);
|
||||
}
|
||||
|
||||
fn sum(v: vec4<f32>) -> f32 {
|
||||
return v.x+v.y+v.z;
|
||||
}
|
||||
|
||||
// Stochastic sampling method from https://iquilezles.org/articles/texturerepetition/
|
||||
fn stochastic_sampling(uv: vec2<f32>, dx: vec2<f32>, dy: vec2<f32>, s: f32) -> vec4<f32> {
|
||||
|
||||
// sample variation pattern
|
||||
let frequency_scale = 5.0;
|
||||
let amplitude_scale = 0.3;
|
||||
let k = simplex_noise_2d(uv.xy / frequency_scale) * amplitude_scale;
|
||||
|
||||
// compute index from 0-7
|
||||
let index = k * 8.0;
|
||||
let i = floor(index);
|
||||
let f = fract(index);
|
||||
|
||||
// offsets for the different virtual patterns from 0 to 7
|
||||
let offa = sin(vec2<f32>(3.0,7.0)*(i+0.0)); // can replace with any other hash
|
||||
let offb = sin(vec2<f32>(3.0,7.0)*(i+1.0)); // can replace with any other hash
|
||||
|
||||
// sample the two closest virtual patterns
|
||||
let cola = textureSampleGrad(texture, texture_sampler, uv + s * offa, dx, dy);
|
||||
let colb = textureSampleGrad(texture, texture_sampler, uv + s * offb, dx, dy);
|
||||
|
||||
// interpolate between the two virtual patterns
|
||||
return mix(cola, colb, smoothstep(0.2,0.8,f - 0.1*sum(cola-colb)) );
|
||||
}
|
||||
|
||||
@group(2) @binding(1) var texture: texture_2d<f32>;
|
||||
@group(2) @binding(2) var texture_sampler: sampler;
|
||||
|
||||
@fragment
|
||||
fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
let speed = 0.5;
|
||||
let s = smoothstep(0.4, 0.6, sin(globals.time * speed));
|
||||
return stochastic_sampling(in.uv, dpdx(in.uv), dpdy(in.uv), s);
|
||||
}
|
BIN
assets/textures/rocks.png
Normal file
BIN
assets/textures/rocks.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 99 KiB |
@ -476,6 +476,7 @@ Example | Description
|
||||
[Post Processing - Custom Render Pass](../examples/shader/custom_post_processing.rs) | A custom post processing effect, using a custom render pass that runs after the main pass
|
||||
[Shader Defs](../examples/shader/shader_defs.rs) | A shader that uses "shaders defs" (a bevy tool to selectively toggle parts of a shader)
|
||||
[Specialized Mesh Pipeline](../examples/shader/specialized_mesh_pipeline.rs) | Demonstrates how to write a specialized mesh pipeline
|
||||
[Stochastic Sampling](../examples/shader/stochastic_sampling.rs) | A custom shader that avoids repetition artifacts when tiling a texture
|
||||
[Storage Buffer](../examples/shader/storage_buffer.rs) | A shader that shows how to bind a storage buffer using a custom material.
|
||||
[Texture Binding Array (Bindless Textures)](../examples/shader/texture_binding_array.rs) | A shader that shows how to bind and sample multiple textures as a binding array (a.k.a. bindless textures).
|
||||
|
||||
|
75
examples/shader/stochastic_sampling.rs
Normal file
75
examples/shader/stochastic_sampling.rs
Normal file
@ -0,0 +1,75 @@
|
||||
//! Demonstrates using a custom extension to the `StandardMaterial` to create a repeating texture that avoids seams
|
||||
//! by using stochastic sampling. This example uses a custom shader to achieve the effect.
|
||||
use bevy::image::{ImageAddressMode, ImageSamplerDescriptor};
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::mesh::VertexAttributeValues;
|
||||
use bevy::render::render_resource::{AsBindGroup, ShaderRef};
|
||||
use bevy::sprite::{Material2d, Material2dPlugin};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins.set(ImagePlugin {
|
||||
default_sampler: ImageSamplerDescriptor {
|
||||
address_mode_u: ImageAddressMode::Repeat,
|
||||
address_mode_v: ImageAddressMode::Repeat,
|
||||
address_mode_w: ImageAddressMode::Repeat,
|
||||
..Default::default()
|
||||
},
|
||||
}))
|
||||
.add_plugins(Material2dPlugin::<CustomMaterial>::default())
|
||||
.add_systems(Startup, setup)
|
||||
.run();
|
||||
}
|
||||
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
asset_server: Res<AssetServer>,
|
||||
mut materials: ResMut<Assets<CustomMaterial>>,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
) {
|
||||
commands.spawn(Camera2d);
|
||||
let texture = asset_server.load("textures/rocks.png");
|
||||
commands.spawn((
|
||||
Mesh2d(meshes.add(repeating_quad(10.0))),
|
||||
MeshMaterial2d(materials.add(CustomMaterial {
|
||||
texture: Some(texture),
|
||||
})),
|
||||
Transform::default(),
|
||||
));
|
||||
}
|
||||
|
||||
// This struct defines the data that will be passed to your shader
|
||||
#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
|
||||
struct CustomMaterial {
|
||||
#[texture(1)]
|
||||
#[sampler(2)]
|
||||
texture: Option<Handle<Image>>,
|
||||
}
|
||||
|
||||
/// This example uses a shader source file from the assets subdirectory
|
||||
const SHADER_ASSET_PATH: &str = "shaders/stochastic_sampling.wgsl";
|
||||
|
||||
/// The Material trait is very configurable, but comes with sensible defaults for all methods.
|
||||
/// You only need to implement functions for features that need non-default behavior. See the Material api docs for details!
|
||||
impl Material2d for CustomMaterial {
|
||||
fn fragment_shader() -> ShaderRef {
|
||||
SHADER_ASSET_PATH.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a quad where the texture repeats n times in both directions.
|
||||
fn repeating_quad(n: f32) -> Mesh {
|
||||
let mut mesh: Mesh = Rectangle::from_length(1000.0).into();
|
||||
let uv_attribute = mesh.attribute_mut(Mesh::ATTRIBUTE_UV_0).unwrap();
|
||||
// The format of the UV coordinates should be Float32x2.
|
||||
let VertexAttributeValues::Float32x2(uv_attribute) = uv_attribute else {
|
||||
panic!("Unexpected vertex format, expected Float32x2.");
|
||||
};
|
||||
// The default `Rectangle`'s texture coordinates are in the range of `0..=1`. Values outside
|
||||
// this range cause the texture to repeat.
|
||||
for uv_coord in uv_attribute.iter_mut() {
|
||||
uv_coord[0] *= n;
|
||||
uv_coord[1] *= n;
|
||||
}
|
||||
mesh
|
||||
}
|
Loading…
Reference in New Issue
Block a user