Search across packages, versions, files, declarations, and comments. Use multiple terms to narrow results (AND), and add @vertex, @fragment, or @compute to filter by shader stage.
1import package::render::rgb9e5;
2import bevy_render::maths::{PI, PI_2, orthonormalize};
4/// Generates a random u32 in range [0, u32::MAX].
5///
6/// `state` is a mutable reference to a u32 used as the seed.
7///
8/// Values are generated via "white noise", with no correlation between values.
9/// In shaders, you often want spatial and/or temporal correlation. Use a different RNG method for these use cases.
10///
11/// https://www.pcg-random.org
12/// https://www.reedbeta.com/blog/hash-functions-for-gpu-rendering
13fn rand_u(state: ptr<function, u32>) -> u32 {
14 *state = *state * 747796405u + 2891336453u;
15 let word = ((*state >> ((*state >> 28u) + 4u)) ^ *state) * 277803737u;
16 return (word >> 22u) ^ word;
19/// Generates a random f32 in range [0, 1.0].
20fn rand_f(state: ptr<function, u32>) -> f32 {
21 *state = *state * 747796405u + 2891336453u;
22 let word = ((*state >> ((*state >> 28u) + 4u)) ^ *state) * 277803737u;
23 return f32((word >> 22u) ^ word) * bitcast<f32>(0x2f800004u);
26/// Generates a random vec2<f32> where each value is in range [0, 1.0].
27fn rand_vec2f(state: ptr<function, u32>) -> vec2<f32> {
28 return vec2(rand_f(state), rand_f(state));
31/// Generates a random u32 in range [0, n).
32fn rand_range_u(n: u32, state: ptr<function, u32>) -> u32 {
33 return rand_u(state) % n;
36/// returns the (0-1, 0-1) position within the given viewport for the current buffer coords .
37/// buffer coords can be obtained from `@builtin(position).xy`.
38/// the view uniform struct contains the current camera viewport in `view.viewport`.
39/// topleft = 0,0
40fn coords_to_viewport_uv(position: vec2<f32>, viewport: vec4<f32>) -> vec2<f32> {
41 return (position - viewport.xy) / viewport.zw;
44/// https://blog.demofox.org/2022/01/01/interleaved-gradient-noise-a-different-kind-of-low-discrepancy-sequence
45fn interleaved_gradient_noise(pixel_coordinates: vec2<f32>, frame: u32) -> f32 {
46 let xy = pixel_coordinates + 5.588238 * f32(frame % 64u);
47 return fract(52.9829189 * fract(0.06711056 * xy.x + 0.00583715 * xy.y));
50/// Hammersley sequence for quasi-random points
51fn hammersley_2d(i: u32, n: u32) -> vec2f {
52 let inv_n = 1.0 / f32(n);
53 let vdc = f32(reverseBits(i)) * 2.3283064365386963e-10; // 1/2^32
54 return vec2f(f32(i) * inv_n, vdc);
57/// https://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare (slides 120-135)
58/// TODO: Use an array here instead of a bunch of constants, once arrays work properly under DX12.
59/// NOTE: The names have a final underscore to avoid the following error:
60/// `Composable module identifiers must not require substitution according to naga writeback rules`
61const SPIRAL_OFFSET_0_ = vec2<f32>(-0.7071, 0.7071);
62const SPIRAL_OFFSET_1_ = vec2<f32>(-0.0000, -0.8750);
63const SPIRAL_OFFSET_2_ = vec2<f32>( 0.5303, 0.5303);
64const SPIRAL_OFFSET_3_ = vec2<f32>(-0.6250, -0.0000);
65const SPIRAL_OFFSET_4_ = vec2<f32>( 0.3536, -0.3536);
66const SPIRAL_OFFSET_5_ = vec2<f32>(-0.0000, 0.3750);
67const SPIRAL_OFFSET_6_ = vec2<f32>(-0.1768, -0.1768);
68const SPIRAL_OFFSET_7_ = vec2<f32>( 0.1250, 0.0000);
70/// https://www.realtimerendering.com/raytracinggems/unofficial_RayTracingGems_v1.9.pdf#0004286901.INDD%3ASec28%3A303
71fn sample_cosine_hemisphere(normal: vec3<f32>, rng: ptr<function, u32>) -> vec3<f32> {
72 let cos_theta = 1.0 - 2.0 * rand_f(rng);
73 let phi = PI_2 * rand_f(rng);
74 let sin_theta = sqrt(max(1.0 - cos_theta * cos_theta, 0.0));
75 let direction = normal + vec3(sin_theta * cos(phi), sin_theta * sin(phi), cos_theta);
76 let len_sq = dot(direction, direction);
77 if len_sq < 1e-8 { return normal; }
78 return direction * inverseSqrt(len_sq);
81/// https://www.pbr-book.org/3ed-2018/Monte_Carlo_Integration/2D_Sampling_with_Multidimensional_Transformations#UniformlySamplingaHemisphere
82fn sample_uniform_hemisphere(normal: vec3<f32>, rng: ptr<function, u32>) -> vec3<f32> {
83 let cos_theta = rand_f(rng);
84 let phi = PI_2 * rand_f(rng);
85 let sin_theta = sqrt(max(1.0 - cos_theta * cos_theta, 0.0));
86 let x = sin_theta * cos(phi);
87 let y = sin_theta * sin(phi);
88 let z = cos_theta;
89 return orthonormalize(normal) * vec3(x, y, z);
92fn uniform_hemisphere_inverse_pdf() -> f32 {
93 return PI_2;
96/// https://www.realtimerendering.com/raytracinggems/unofficial_RayTracingGems_v1.9.pdf#0004286901.INDD%3ASec19%3A294
97fn sample_disk(disk_radius: f32, rng: ptr<function, u32>) -> vec2<f32> {
98 let ab = 2.0 * rand_vec2f(rng) - 1.0;
99 let a = ab.x;
100 var b = ab.y;
101 if (b == 0.0) { b = 1.0; }
103 var phi: f32;
104 var r: f32;
105 if (a * a > b * b) {
106 r = disk_radius * a;
107 phi = (PI / 4.0) * (b / a);
108 } else {
109 r = disk_radius * b;
110 phi = (PI / 2.0) - (PI / 4.0) * (a / b);
111 }
113 let x = r * cos(phi);
114 let y = r * sin(phi);
115 return vec2(x, y);
118/// Convert UV and face index to direction vector
119fn sample_cube_dir(uv: vec2f, face: u32) -> vec3f {
120 // Convert from [0,1] to [-1,1]
121 let uvc = 2.0 * uv - 1.0;
123 // Generate direction based on the cube face
124 var dir: vec3f;
125 switch(face) {
126 case 0u: { dir = vec3f( 1.0, -uvc.y, -uvc.x); } // +X
127 case 1u: { dir = vec3f(-1.0, -uvc.y, uvc.x); } // -X
128 case 2u: { dir = vec3f( uvc.x, 1.0, uvc.y); } // +Y
129 case 3u: { dir = vec3f( uvc.x, -1.0, -uvc.y); } // -Y
130 case 4u: { dir = vec3f( uvc.x, -uvc.y, 1.0); } // +Z
131 case 5u: { dir = vec3f(-uvc.x, -uvc.y, -1.0); } // -Z
132 default: { dir = vec3f(0.0); }
133 }
134 return normalize(dir);
137// Convert direction vector to cube face UV
138struct CubeUV {
139 uv: vec2f,
140 face: u32,
142fn dir_to_cube_uv(dir: vec3f) -> CubeUV {
143 let abs_dir = abs(dir);
144 var face: u32 = 0u;
145 var uv: vec2f = vec2f(0.0);
147 // Find the dominant axis to determine face
148 if (abs_dir.x >= abs_dir.y && abs_dir.x >= abs_dir.z) {
149 // X axis is dominant
150 if (dir.x > 0.0) {
151 face = 0u; // +X
152 uv = vec2f(-dir.z, -dir.y) / dir.x;
153 } else {
154 face = 1u; // -X
155 uv = vec2f(dir.z, -dir.y) / abs_dir.x;
156 }
157 } else if (abs_dir.y >= abs_dir.x && abs_dir.y >= abs_dir.z) {
158 // Y axis is dominant
159 if (dir.y > 0.0) {
160 face = 2u; // +Y
161 uv = vec2f(dir.x, dir.z) / dir.y;
162 } else {
163 face = 3u; // -Y
164 uv = vec2f(dir.x, -dir.z) / abs_dir.y;
165 }
166 } else {
167 // Z axis is dominant
168 if (dir.z > 0.0) {
169 face = 4u; // +Z
170 uv = vec2f(dir.x, -dir.y) / dir.z;
171 } else {
172 face = 5u; // -Z
173 uv = vec2f(-dir.x, -dir.y) / abs_dir.z;
174 }
175 }
177 // Convert from [-1,1] to [0,1]
178 return CubeUV(uv * 0.5 + 0.5, face);
181/// The Porter-Duff OVER operator on RGBA, correctly computing alpha of the
182/// result.
183fn porter_duff_over(bg: vec4<f32>, fg: vec4<f32>) -> vec4<f32> {
184 return vec4<f32>(mix(bg.rgb * bg.a, fg.rgb, fg.a), bg.a + fg.a * (1.0 - bg.a));
Contents