Repository: bitshifter/glam-rs
Pull request #528 (2024-06-20 02:53:32)

Added bvec constructors
I noticed that all `BVec*`s are missing the freestanding `fn *vec*()` constructors.

vallentin: The lint was triggered by a missing cfg attr for `scalar-math`. Fixed it and rebase.

Patch:
diff --git a/.tarpaulin.toml b/.tarpaulin.toml
index ec32887..804c6e2 100644
--- a/.tarpaulin.toml
+++ b/.tarpaulin.toml
@@ -1,11 +1,47 @@
 [sse2_math]
 features = "approx bytemuck mint rand rkyv serde debug-glam-assert"
-exclude-files = ["codegen/*", "src/wasm32.rs", "src/bool/wasm32/*", "src/f32/wasm32/*", "src/swizzles/wasm32/*", "benches/*", "tests/support.rs"]
+exclude-files = [
+    "codegen/*",
+    "src/neon.rs",
+    "src/bool/neon/*",
+    "src/f32/neon/*",
+    "src/swizzles/neon/*",
+    "src/wasm32.rs",
+    "src/bool/wasm32/*",
+    "src/f32/wasm32/*",
+    "src/swizzles/wasm32/*",
+    "benches/*",
+    "tests/support.rs"
+]
 
 [scalar_math]
 features = "scalar-math approx bytemuck mint rand rkyv serde debug-glam-assert"
-exclude-files = ["codegen/*", "src/wasm32.rs", "src/bool/wasm32/*", "src/f32/wasm32/*", "src/swizzles/wasm32/*", "benches/*", "tests/support.rs"]
+exclude-files = [
+    "codegen/*",
+    "src/neon.rs",
+    "src/bool/neon/*",
+    "src/f32/neon/*",
+    "src/swizzles/neon/*",
+    "src/wasm32.rs",
+    "src/bool/wasm32/*",
+    "src/f32/wasm32/*",
+    "src/swizzles/wasm32/*",
+    "benches/*",
+    "tests/support.rs"
+]
 
 [core_simd]
 features = "core-simd approx bytemuck mint rand rkyv serde debug-glam-assert"
-exclude-files = ["codegen/*", "src/wasm32.rs", "src/bool/wasm32/*", "src/f32/wasm32/*", "src/swizzles/wasm32/*", "benches/*", "tests/support.rs"]
+exclude-files = [
+    "codegen/*",
+    "src/neon.rs",
+    "src/bool/neon/*",
+    "src/f32/neon/*",
+    "src/swizzles/neon/*",
+    "src/wasm32.rs",
+    "src/bool/wasm32/*",
+    "src/f32/wasm32/*",
+    "src/swizzles/wasm32/*",
+    "benches/*",
+    "tests/support.rs"
+]
diff --git a/README.md b/README.md
index b212ac3..e93e1d1 100644
--- a/README.md
+++ b/README.md
@@ -60,6 +60,8 @@ SIMD is supported on `x86`, `x86_64` and `wasm32` targets.
 * `SSE2` is enabled by default on `x86_64` targets.
 * To enable `SSE2` on `x86` targets add `-C target-feature=+sse2` to
   `RUSTCFLAGS`.
+* `NEON` is enabled by default on `aarch64` targets.
+* To enable `NEON` on `aarch64` targets add `-C target-feature=+neon` to `RUSTFLAGS`.
 * To enable `simd128` on `wasm32` targets add `-C target-feature=+simd128` to
   `RUSTFLAGS`.
 * Experimental [portable simd] support can be enabled with the `core-simd`
diff --git a/codegen/templates/vec.rs.tera b/codegen/templates/vec.rs.tera
index 5c8c080..51026c7 100644
--- a/codegen/templates/vec.rs.tera
+++ b/codegen/templates/vec.rs.tera
@@ -1976,6 +1976,44 @@ impl {{ self_t }} {
         {% endif %}
     }
 
+    /// Returns the reflection vector for a given incident vector `self` and surface normal
+    /// `normal`.
+    ///
+    /// `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn reflect(self, normal: Self) -> Self {
+        glam_assert!(normal.is_normalized());
+        self - 2.0 * self.dot(normal) * normal
+    }
+
+    /// Returns the refraction direction for a given incident vector `self`, surface normal
+    /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs,
+    /// a zero vector will be returned.
+    ///
+    /// `self` and `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn refract(self, normal: Self, eta: {{ scalar_t }}) -> Self {
+        glam_assert!(self.is_normalized());
+        glam_assert!(normal.is_normalized());
+        let n_dot_i = normal.dot(self);
+        let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
+        if k >= 0.0 {
+            eta * self - (eta * n_dot_i + math::sqrt(k)) * normal
+        } else {
+            Self::ZERO
+        }
+    }
+
 {% if dim == 2 %}
     /// Creates a 2D vector containing `[angle.cos(), angle.sin()]`. This can be used in
     /// conjunction with the [`rotate()`][Self::rotate()] method, e.g.
diff --git a/codegen/templates/vec_mask.rs.tera b/codegen/templates/vec_mask.rs.tera
index 41e0e16..53f7135 100644
--- a/codegen/templates/vec_mask.rs.tera
+++ b/codegen/templates/vec_mask.rs.tera
@@ -51,7 +51,18 @@ union UnionCast {
 }
 {% endif %}
 
-{%- if is_scalar and is_bool %}
+/// Creates a {{ dim }}-dimensional `bool` vector mask.
+#[inline(always)]
+#[must_use]
+pub const fn {{ self_t | lower }}(
+    {% for c in components %}
+        {{ c }}: bool,
+    {% endfor %}
+) -> {{ self_t }} {
+    {{ self_t }}::new({{ components | join(sep=",") }})
+}
+
+{% if is_scalar and is_bool %}
 /// A {{ dim }}-dimensional `bool` vector mask.
 {%- elif is_scalar and is_u32 %}
 /// A {{ dim }}-dimensional `u32` vector mask.
diff --git a/src/bool.rs b/src/bool.rs
index 091cf0c..c920e0b 100644
--- a/src/bool.rs
+++ b/src/bool.rs
@@ -34,47 +34,47 @@ mod wasm32;
 ))]
 mod scalar;
 
-pub use bvec2::BVec2;
-pub use bvec3::BVec3;
-pub use bvec4::BVec4;
+pub use bvec2::{bvec2, BVec2};
+pub use bvec3::{bvec3, BVec3};
+pub use bvec4::{bvec4, BVec4};
 
 #[cfg(all(
     target_arch = "aarch64",
     not(any(feature = "core-simd", feature = "scalar-math"))
 ))]
-pub use neon::bvec3a::BVec3A;
+pub use neon::bvec3a::{bvec3a, BVec3A};
 #[cfg(all(
     target_arch = "aarch64",
     not(any(feature = "core-simd", feature = "scalar-math"))
 ))]
-pub use neon::bvec4a::BVec4A;
+pub use neon::bvec4a::{bvec4a, BVec4A};
 
 #[cfg(all(
     target_feature = "sse2",
     not(any(feature = "core-simd", feature = "scalar-math"))
 ))]
-pub use sse2::bvec3a::BVec3A;
+pub use sse2::bvec3a::{bvec3a, BVec3A};
 #[cfg(all(
     target_feature = "sse2",
     not(any(feature = "core-simd", feature = "scalar-math"))
 ))]
-pub use sse2::bvec4a::BVec4A;
+pub use sse2::bvec4a::{bvec4a, BVec4A};
 
 #[cfg(all(
     target_feature = "simd128",
     not(any(feature = "core-simd", feature = "scalar-math"))
 ))]
-pub use wasm32::bvec3a::BVec3A;
+pub use wasm32::bvec3a::{bvec3a, BVec3A};
 #[cfg(all(
     target_feature = "simd128",
     not(any(feature = "core-simd", feature = "scalar-math"))
 ))]
-pub use wasm32::bvec4a::BVec4A;
+pub use wasm32::bvec4a::{bvec4a, BVec4A};
 
 #[cfg(all(feature = "core-simd", not(feature = "scalar-math")))]
-pub use coresimd::bvec3a::BVec3A;
+pub use coresimd::bvec3a::{bvec3a, BVec3A};
 #[cfg(all(feature = "core-simd", not(feature = "scalar-math")))]
-pub use coresimd::bvec4a::BVec4A;
+pub use coresimd::bvec4a::{bvec4a, BVec4A};
 
 #[cfg(any(
     not(any(
@@ -85,16 +85,18 @@ pub use coresimd::bvec4a::BVec4A;
     )),
     feature = "scalar-math"
 ))]
-pub use scalar::bvec3a::BVec3A;
+pub use scalar::bvec3a::{bvec3a, BVec3A};
 
-#[cfg(not(any(
-    feature = "scalar-math",
-    feature = "core-simd",
-    target_arch = "aarch64",
-    target_feature = "sse2",
-    target_feature = "simd128"
-),))]
-pub use scalar::bvec4a::BVec4A;
+#[cfg(any(
+    not(any(
+        feature = "core-simd",
+        target_arch = "aarch64",
+        target_feature = "sse2",
+        target_feature = "simd128"
+    )),
+    feature = "scalar-math"
+))]
+pub use scalar::bvec4a::{bvec4a, BVec4A};
 
 mod const_test_bvec2 {
     const_assert_eq!(1, core::mem::align_of::<super::BVec2>());
diff --git a/src/bool/bvec2.rs b/src/bool/bvec2.rs
index 3fc13b1..5ac62cb 100644
--- a/src/bool/bvec2.rs
+++ b/src/bool/bvec2.rs
@@ -4,6 +4,13 @@
 use core::fmt;
 use core::ops::*;
 
+/// Creates a 2-dimensional `bool` vector mask.
+#[inline(always)]
+#[must_use]
+pub const fn bvec2(x: bool, y: bool) -> BVec2 {
+    BVec2::new(x, y)
+}
+
 /// A 2-dimensional `bool` vector mask.
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
 #[repr(C, align(1))]
diff --git a/src/bool/bvec3.rs b/src/bool/bvec3.rs
index 2f33cd1..94fd2b9 100644
--- a/src/bool/bvec3.rs
+++ b/src/bool/bvec3.rs
@@ -4,6 +4,13 @@
 use core::fmt;
 use core::ops::*;
 
+/// Creates a 3-dimensional `bool` vector mask.
+#[inline(always)]
+#[must_use]
+pub const fn bvec3(x: bool, y: bool, z: bool) -> BVec3 {
+    BVec3::new(x, y, z)
+}
+
 /// A 3-dimensional `bool` vector mask.
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
 #[repr(C, align(1))]
diff --git a/src/bool/bvec4.rs b/src/bool/bvec4.rs
index 2300a4a..c1f4931 100644
--- a/src/bool/bvec4.rs
+++ b/src/bool/bvec4.rs
@@ -4,6 +4,13 @@
 use core::fmt;
 use core::ops::*;
 
+/// Creates a 4-dimensional `bool` vector mask.
+#[inline(always)]
+#[must_use]
+pub const fn bvec4(x: bool, y: bool, z: bool, w: bool) -> BVec4 {
+    BVec4::new(x, y, z, w)
+}
+
 /// A 4-dimensional `bool` vector mask.
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
 #[repr(C, align(1))]
diff --git a/src/bool/coresimd/bvec3a.rs b/src/bool/coresimd/bvec3a.rs
index 080f3bd..4615fb4 100644
--- a/src/bool/coresimd/bvec3a.rs
+++ b/src/bool/coresimd/bvec3a.rs
@@ -12,6 +12,13 @@ union UnionCast {
     v: BVec3A,
 }
 
+/// Creates a 3-dimensional `bool` vector mask.
+#[inline(always)]
+#[must_use]
+pub const fn bvec3a(x: bool, y: bool, z: bool) -> BVec3A {
+    BVec3A::new(x, y, z)
+}
+
 /// A 3-dimensional SIMD vector mask.
 ///
 /// This type is 16 byte aligned.
diff --git a/src/bool/coresimd/bvec4a.rs b/src/bool/coresimd/bvec4a.rs
index a1213b0..dc65730 100644
--- a/src/bool/coresimd/bvec4a.rs
+++ b/src/bool/coresimd/bvec4a.rs
@@ -12,6 +12,13 @@ union UnionCast {
     v: BVec4A,
 }
 
+/// Creates a 4-dimensional `bool` vector mask.
+#[inline(always)]
+#[must_use]
+pub const fn bvec4a(x: bool, y: bool, z: bool, w: bool) -> BVec4A {
+    BVec4A::new(x, y, z, w)
+}
+
 /// A 4-dimensional SIMD vector mask.
 ///
 /// This type is 16 byte aligned.
diff --git a/src/bool/neon/bvec3a.rs b/src/bool/neon/bvec3a.rs
index 3e26c2c..7672a18 100644
--- a/src/bool/neon/bvec3a.rs
+++ b/src/bool/neon/bvec3a.rs
@@ -12,6 +12,13 @@ union UnionCast {
     v: BVec3A,
 }
 
+/// Creates a 3-dimensional `bool` vector mask.
+#[inline(always)]
+#[must_use]
+pub const fn bvec3a(x: bool, y: bool, z: bool) -> BVec3A {
+    BVec3A::new(x, y, z)
+}
+
 /// A 3-dimensional SIMD vector mask.
 ///
 /// This type is 16 byte aligned.
diff --git a/src/bool/neon/bvec4a.rs b/src/bool/neon/bvec4a.rs
index 27a188f..c8a1700 100644
--- a/src/bool/neon/bvec4a.rs
+++ b/src/bool/neon/bvec4a.rs
@@ -12,6 +12,13 @@ union UnionCast {
     v: BVec4A,
 }
 
+/// Creates a 4-dimensional `bool` vector mask.
+#[inline(always)]
+#[must_use]
+pub const fn bvec4a(x: bool, y: bool, z: bool, w: bool) -> BVec4A {
+    BVec4A::new(x, y, z, w)
+}
+
 /// A 4-dimensional SIMD vector mask.
 ///
 /// This type is 16 byte aligned.
diff --git a/src/bool/scalar/bvec3a.rs b/src/bool/scalar/bvec3a.rs
index 28c9f45..bd687af 100644
--- a/src/bool/scalar/bvec3a.rs
+++ b/src/bool/scalar/bvec3a.rs
@@ -4,6 +4,13 @@
 use core::fmt;
 use core::ops::*;
 
+/// Creates a 3-dimensional `bool` vector mask.
+#[inline(always)]
+#[must_use]
+pub const fn bvec3a(x: bool, y: bool, z: bool) -> BVec3A {
+    BVec3A::new(x, y, z)
+}
+
 /// A 3-dimensional `u32` vector mask.
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
 #[repr(C, align(16))]
diff --git a/src/bool/scalar/bvec4a.rs b/src/bool/scalar/bvec4a.rs
index eaf6572..82b4640 100644
--- a/src/bool/scalar/bvec4a.rs
+++ b/src/bool/scalar/bvec4a.rs
@@ -4,6 +4,13 @@
 use core::fmt;
 use core::ops::*;
 
+/// Creates a 4-dimensional `bool` vector mask.
+#[inline(always)]
+#[must_use]
+pub const fn bvec4a(x: bool, y: bool, z: bool, w: bool) -> BVec4A {
+    BVec4A::new(x, y, z, w)
+}
+
 /// A 4-dimensional `u32` vector mask.
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
 #[repr(C, align(16))]
diff --git a/src/bool/sse2/bvec3a.rs b/src/bool/sse2/bvec3a.rs
index 89b36c3..5c1943d 100644
--- a/src/bool/sse2/bvec3a.rs
+++ b/src/bool/sse2/bvec3a.rs
@@ -15,6 +15,13 @@ union UnionCast {
     v: BVec3A,
 }
 
+/// Creates a 3-dimensional `bool` vector mask.
+#[inline(always)]
+#[must_use]
+pub const fn bvec3a(x: bool, y: bool, z: bool) -> BVec3A {
+    BVec3A::new(x, y, z)
+}
+
 /// A 3-dimensional SIMD vector mask.
 ///
 /// This type is 16 byte aligned.
diff --git a/src/bool/sse2/bvec4a.rs b/src/bool/sse2/bvec4a.rs
index 344892d..9ea076d 100644
--- a/src/bool/sse2/bvec4a.rs
+++ b/src/bool/sse2/bvec4a.rs
@@ -15,6 +15,13 @@ union UnionCast {
     v: BVec4A,
 }
 
+/// Creates a 4-dimensional `bool` vector mask.
+#[inline(always)]
+#[must_use]
+pub const fn bvec4a(x: bool, y: bool, z: bool, w: bool) -> BVec4A {
+    BVec4A::new(x, y, z, w)
+}
+
 /// A 4-dimensional SIMD vector mask.
 ///
 /// This type is 16 byte aligned.
diff --git a/src/bool/wasm32/bvec3a.rs b/src/bool/wasm32/bvec3a.rs
index 1930290..5e889a2 100644
--- a/src/bool/wasm32/bvec3a.rs
+++ b/src/bool/wasm32/bvec3a.rs
@@ -6,6 +6,13 @@ use core::ops::*;
 
 use core::arch::wasm32::*;
 
+/// Creates a 3-dimensional `bool` vector mask.
+#[inline(always)]
+#[must_use]
+pub const fn bvec3a(x: bool, y: bool, z: bool) -> BVec3A {
+    BVec3A::new(x, y, z)
+}
+
 /// A 3-dimensional SIMD vector mask.
 ///
 /// This type is 16 byte aligned.
diff --git a/src/bool/wasm32/bvec4a.rs b/src/bool/wasm32/bvec4a.rs
index 4bb5afa..65438a1 100644
--- a/src/bool/wasm32/bvec4a.rs
+++ b/src/bool/wasm32/bvec4a.rs
@@ -6,6 +6,13 @@ use core::ops::*;
 
 use core::arch::wasm32::*;
 
+/// Creates a 4-dimensional `bool` vector mask.
+#[inline(always)]
+#[must_use]
+pub const fn bvec4a(x: bool, y: bool, z: bool, w: bool) -> BVec4A {
+    BVec4A::new(x, y, z, w)
+}
+
 /// A 4-dimensional SIMD vector mask.
 ///
 /// This type is 16 byte aligned.
diff --git a/src/f32/coresimd/vec3a.rs b/src/f32/coresimd/vec3a.rs
index ae76912..9e633ca 100644
--- a/src/f32/coresimd/vec3a.rs
+++ b/src/f32/coresimd/vec3a.rs
@@ -824,6 +824,44 @@ impl Vec3A {
         Self(self.0.mul_add(a.0, b.0))
     }
 
+    /// Returns the reflection vector for a given incident vector `self` and surface normal
+    /// `normal`.
+    ///
+    /// `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn reflect(self, normal: Self) -> Self {
+        glam_assert!(normal.is_normalized());
+        self - 2.0 * self.dot(normal) * normal
+    }
+
+    /// Returns the refraction direction for a given incident vector `self`, surface normal
+    /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs,
+    /// a zero vector will be returned.
+    ///
+    /// `self` and `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn refract(self, normal: Self, eta: f32) -> Self {
+        glam_assert!(self.is_normalized());
+        glam_assert!(normal.is_normalized());
+        let n_dot_i = normal.dot(self);
+        let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
+        if k >= 0.0 {
+            eta * self - (eta * n_dot_i + math::sqrt(k)) * normal
+        } else {
+            Self::ZERO
+        }
+    }
+
     /// Returns the angle (in radians) between two vectors in the range `[0, +π]`.
     ///
     /// The inputs do not need to be unit vectors however they must be non-zero.
diff --git a/src/f32/coresimd/vec4.rs b/src/f32/coresimd/vec4.rs
index f06bac8..62208e7 100644
--- a/src/f32/coresimd/vec4.rs
+++ b/src/f32/coresimd/vec4.rs
@@ -811,6 +811,44 @@ impl Vec4 {
         Self(self.0.mul_add(a.0, b.0))
     }
 
+    /// Returns the reflection vector for a given incident vector `self` and surface normal
+    /// `normal`.
+    ///
+    /// `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn reflect(self, normal: Self) -> Self {
+        glam_assert!(normal.is_normalized());
+        self - 2.0 * self.dot(normal) * normal
+    }
+
+    /// Returns the refraction direction for a given incident vector `self`, surface normal
+    /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs,
+    /// a zero vector will be returned.
+    ///
+    /// `self` and `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn refract(self, normal: Self, eta: f32) -> Self {
+        glam_assert!(self.is_normalized());
+        glam_assert!(normal.is_normalized());
+        let n_dot_i = normal.dot(self);
+        let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
+        if k >= 0.0 {
+            eta * self - (eta * n_dot_i + math::sqrt(k)) * normal
+        } else {
+            Self::ZERO
+        }
+    }
+
     /// Casts all elements of `self` to `f64`.
     #[inline]
     #[must_use]
diff --git a/src/f32/neon/vec3a.rs b/src/f32/neon/vec3a.rs
index 8cb986e..0440891 100644
--- a/src/f32/neon/vec3a.rs
+++ b/src/f32/neon/vec3a.rs
@@ -868,6 +868,44 @@ impl Vec3A {
         Self(unsafe { vfmaq_f32(b.0, self.0, a.0) })
     }
 
+    /// Returns the reflection vector for a given incident vector `self` and surface normal
+    /// `normal`.
+    ///
+    /// `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn reflect(self, normal: Self) -> Self {
+        glam_assert!(normal.is_normalized());
+        self - 2.0 * self.dot(normal) * normal
+    }
+
+    /// Returns the refraction direction for a given incident vector `self`, surface normal
+    /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs,
+    /// a zero vector will be returned.
+    ///
+    /// `self` and `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn refract(self, normal: Self, eta: f32) -> Self {
+        glam_assert!(self.is_normalized());
+        glam_assert!(normal.is_normalized());
+        let n_dot_i = normal.dot(self);
+        let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
+        if k >= 0.0 {
+            eta * self - (eta * n_dot_i + math::sqrt(k)) * normal
+        } else {
+            Self::ZERO
+        }
+    }
+
     /// Returns the angle (in radians) between two vectors in the range `[0, +π]`.
     ///
     /// The inputs do not need to be unit vectors however they must be non-zero.
diff --git a/src/f32/neon/vec4.rs b/src/f32/neon/vec4.rs
index be7b2e2..613841d 100644
--- a/src/f32/neon/vec4.rs
+++ b/src/f32/neon/vec4.rs
@@ -845,6 +845,44 @@ impl Vec4 {
         Self(unsafe { vfmaq_f32(b.0, self.0, a.0) })
     }
 
+    /// Returns the reflection vector for a given incident vector `self` and surface normal
+    /// `normal`.
+    ///
+    /// `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn reflect(self, normal: Self) -> Self {
+        glam_assert!(normal.is_normalized());
+        self - 2.0 * self.dot(normal) * normal
+    }
+
+    /// Returns the refraction direction for a given incident vector `self`, surface normal
+    /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs,
+    /// a zero vector will be returned.
+    ///
+    /// `self` and `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn refract(self, normal: Self, eta: f32) -> Self {
+        glam_assert!(self.is_normalized());
+        glam_assert!(normal.is_normalized());
+        let n_dot_i = normal.dot(self);
+        let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
+        if k >= 0.0 {
+            eta * self - (eta * n_dot_i + math::sqrt(k)) * normal
+        } else {
+            Self::ZERO
+        }
+    }
+
     /// Casts all elements of `self` to `f64`.
     #[inline]
     #[must_use]
diff --git a/src/f32/scalar/vec3a.rs b/src/f32/scalar/vec3a.rs
index 7186f9e..a55920f 100644
--- a/src/f32/scalar/vec3a.rs
+++ b/src/f32/scalar/vec3a.rs
@@ -871,6 +871,44 @@ impl Vec3A {
         )
     }
 
+    /// Returns the reflection vector for a given incident vector `self` and surface normal
+    /// `normal`.
+    ///
+    /// `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn reflect(self, normal: Self) -> Self {
+        glam_assert!(normal.is_normalized());
+        self - 2.0 * self.dot(normal) * normal
+    }
+
+    /// Returns the refraction direction for a given incident vector `self`, surface normal
+    /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs,
+    /// a zero vector will be returned.
+    ///
+    /// `self` and `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn refract(self, normal: Self, eta: f32) -> Self {
+        glam_assert!(self.is_normalized());
+        glam_assert!(normal.is_normalized());
+        let n_dot_i = normal.dot(self);
+        let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
+        if k >= 0.0 {
+            eta * self - (eta * n_dot_i + math::sqrt(k)) * normal
+        } else {
+            Self::ZERO
+        }
+    }
+
     /// Returns the angle (in radians) between two vectors in the range `[0, +π]`.
     ///
     /// The inputs do not need to be unit vectors however they must be non-zero.
diff --git a/src/f32/scalar/vec4.rs b/src/f32/scalar/vec4.rs
index 081cb3b..4fdba00 100644
--- a/src/f32/scalar/vec4.rs
+++ b/src/f32/scalar/vec4.rs
@@ -930,6 +930,44 @@ impl Vec4 {
         )
     }
 
+    /// Returns the reflection vector for a given incident vector `self` and surface normal
+    /// `normal`.
+    ///
+    /// `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn reflect(self, normal: Self) -> Self {
+        glam_assert!(normal.is_normalized());
+        self - 2.0 * self.dot(normal) * normal
+    }
+
+    /// Returns the refraction direction for a given incident vector `self`, surface normal
+    /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs,
+    /// a zero vector will be returned.
+    ///
+    /// `self` and `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn refract(self, normal: Self, eta: f32) -> Self {
+        glam_assert!(self.is_normalized());
+        glam_assert!(normal.is_normalized());
+        let n_dot_i = normal.dot(self);
+        let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
+        if k >= 0.0 {
+            eta * self - (eta * n_dot_i + math::sqrt(k)) * normal
+        } else {
+            Self::ZERO
+        }
+    }
+
     /// Casts all elements of `self` to `f64`.
     #[inline]
     #[must_use]
diff --git a/src/f32/sse2/vec3a.rs b/src/f32/sse2/vec3a.rs
index c48fdb4..44e1fca 100644
--- a/src/f32/sse2/vec3a.rs
+++ b/src/f32/sse2/vec3a.rs
@@ -876,6 +876,44 @@ impl Vec3A {
         )
     }
 
+    /// Returns the reflection vector for a given incident vector `self` and surface normal
+    /// `normal`.
+    ///
+    /// `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn reflect(self, normal: Self) -> Self {
+        glam_assert!(normal.is_normalized());
+        self - 2.0 * self.dot(normal) * normal
+    }
+
+    /// Returns the refraction direction for a given incident vector `self`, surface normal
+    /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs,
+    /// a zero vector will be returned.
+    ///
+    /// `self` and `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn refract(self, normal: Self, eta: f32) -> Self {
+        glam_assert!(self.is_normalized());
+        glam_assert!(normal.is_normalized());
+        let n_dot_i = normal.dot(self);
+        let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
+        if k >= 0.0 {
+            eta * self - (eta * n_dot_i + math::sqrt(k)) * normal
+        } else {
+            Self::ZERO
+        }
+    }
+
     /// Returns the angle (in radians) between two vectors in the range `[0, +π]`.
     ///
     /// The inputs do not need to be unit vectors however they must be non-zero.
diff --git a/src/f32/sse2/vec4.rs b/src/f32/sse2/vec4.rs
index c272e55..dabdb38 100644
--- a/src/f32/sse2/vec4.rs
+++ b/src/f32/sse2/vec4.rs
@@ -863,6 +863,44 @@ impl Vec4 {
         )
     }
 
+    /// Returns the reflection vector for a given incident vector `self` and surface normal
+    /// `normal`.
+    ///
+    /// `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn reflect(self, normal: Self) -> Self {
+        glam_assert!(normal.is_normalized());
+        self - 2.0 * self.dot(normal) * normal
+    }
+
+    /// Returns the refraction direction for a given incident vector `self`, surface normal
+    /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs,
+    /// a zero vector will be returned.
+    ///
+    /// `self` and `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn refract(self, normal: Self, eta: f32) -> Self {
+        glam_assert!(self.is_normalized());
+        glam_assert!(normal.is_normalized());
+        let n_dot_i = normal.dot(self);
+        let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
+        if k >= 0.0 {
+            eta * self - (eta * n_dot_i + math::sqrt(k)) * normal
+        } else {
+            Self::ZERO
+        }
+    }
+
     /// Casts all elements of `self` to `f64`.
     #[inline]
     #[must_use]
diff --git a/src/f32/vec2.rs b/src/f32/vec2.rs
index 6f52f8e..283948a 100644
--- a/src/f32/vec2.rs
+++ b/src/f32/vec2.rs
@@ -793,6 +793,44 @@ impl Vec2 {
         )
     }
 
+    /// Returns the reflection vector for a given incident vector `self` and surface normal
+    /// `normal`.
+    ///
+    /// `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn reflect(self, normal: Self) -> Self {
+        glam_assert!(normal.is_normalized());
+        self - 2.0 * self.dot(normal) * normal
+    }
+
+    /// Returns the refraction direction for a given incident vector `self`, surface normal
+    /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs,
+    /// a zero vector will be returned.
+    ///
+    /// `self` and `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn refract(self, normal: Self, eta: f32) -> Self {
+        glam_assert!(self.is_normalized());
+        glam_assert!(normal.is_normalized());
+        let n_dot_i = normal.dot(self);
+        let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
+        if k >= 0.0 {
+            eta * self - (eta * n_dot_i + math::sqrt(k)) * normal
+        } else {
+            Self::ZERO
+        }
+    }
+
     /// Creates a 2D vector containing `[angle.cos(), angle.sin()]`. This can be used in
     /// conjunction with the [`rotate()`][Self::rotate()] method, e.g.
     /// `Vec2::from_angle(PI).rotate(Vec2::Y)` will create the vector `[-1, 0]`
diff --git a/src/f32/vec3.rs b/src/f32/vec3.rs
index a8b5e68..aca3925 100644
--- a/src/f32/vec3.rs
+++ b/src/f32/vec3.rs
@@ -861,6 +861,44 @@ impl Vec3 {
         )
     }
 
+    /// Returns the reflection vector for a given incident vector `self` and surface normal
+    /// `normal`.
+    ///
+    /// `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn reflect(self, normal: Self) -> Self {
+        glam_assert!(normal.is_normalized());
+        self - 2.0 * self.dot(normal) * normal
+    }
+
+    /// Returns the refraction direction for a given incident vector `self`, surface normal
+    /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs,
+    /// a zero vector will be returned.
+    ///
+    /// `self` and `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn refract(self, normal: Self, eta: f32) -> Self {
+        glam_assert!(self.is_normalized());
+        glam_assert!(normal.is_normalized());
+        let n_dot_i = normal.dot(self);
+        let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
+        if k >= 0.0 {
+            eta * self - (eta * n_dot_i + math::sqrt(k)) * normal
+        } else {
+            Self::ZERO
+        }
+    }
+
     /// Returns the angle (in radians) between two vectors in the range `[0, +π]`.
     ///
     /// The inputs do not need to be unit vectors however they must be non-zero.
diff --git a/src/f32/wasm32/vec3a.rs b/src/f32/wasm32/vec3a.rs
index 74bb8f6..3cfec78 100644
--- a/src/f32/wasm32/vec3a.rs
+++ b/src/f32/wasm32/vec3a.rs
@@ -839,6 +839,44 @@ impl Vec3A {
         )
     }
 
+    /// Returns the reflection vector for a given incident vector `self` and surface normal
+    /// `normal`.
+    ///
+    /// `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn reflect(self, normal: Self) -> Self {
+        glam_assert!(normal.is_normalized());
+        self - 2.0 * self.dot(normal) * normal
+    }
+
+    /// Returns the refraction direction for a given incident vector `self`, surface normal
+    /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs,
+    /// a zero vector will be returned.
+    ///
+    /// `self` and `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn refract(self, normal: Self, eta: f32) -> Self {
+        glam_assert!(self.is_normalized());
+        glam_assert!(normal.is_normalized());
+        let n_dot_i = normal.dot(self);
+        let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
+        if k >= 0.0 {
+            eta * self - (eta * n_dot_i + math::sqrt(k)) * normal
+        } else {
+            Self::ZERO
+        }
+    }
+
     /// Returns the angle (in radians) between two vectors in the range `[0, +π]`.
     ///
     /// The inputs do not need to be unit vectors however they must be non-zero.
diff --git a/src/f32/wasm32/vec4.rs b/src/f32/wasm32/vec4.rs
index 5e28a49..603579f 100644
--- a/src/f32/wasm32/vec4.rs
+++ b/src/f32/wasm32/vec4.rs
@@ -833,6 +833,44 @@ impl Vec4 {
         )
     }
 
+    /// Returns the reflection vector for a given incident vector `self` and surface normal
+    /// `normal`.
+    ///
+    /// `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn reflect(self, normal: Self) -> Self {
+        glam_assert!(normal.is_normalized());
+        self - 2.0 * self.dot(normal) * normal
+    }
+
+    /// Returns the refraction direction for a given incident vector `self`, surface normal
+    /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs,
+    /// a zero vector will be returned.
+    ///
+    /// `self` and `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn refract(self, normal: Self, eta: f32) -> Self {
+        glam_assert!(self.is_normalized());
+        glam_assert!(normal.is_normalized());
+        let n_dot_i = normal.dot(self);
+        let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
+        if k >= 0.0 {
+            eta * self - (eta * n_dot_i + math::sqrt(k)) * normal
+        } else {
+            Self::ZERO
+        }
+    }
+
     /// Casts all elements of `self` to `f64`.
     #[inline]
     #[must_use]
diff --git a/src/f64/dvec2.rs b/src/f64/dvec2.rs
index c225b76..bdec24d 100644
--- a/src/f64/dvec2.rs
+++ b/src/f64/dvec2.rs
@@ -793,6 +793,44 @@ impl DVec2 {
         )
     }
 
+    /// Returns the reflection vector for a given incident vector `self` and surface normal
+    /// `normal`.
+    ///
+    /// `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn reflect(self, normal: Self) -> Self {
+        glam_assert!(normal.is_normalized());
+        self - 2.0 * self.dot(normal) * normal
+    }
+
+    /// Returns the refraction direction for a given incident vector `self`, surface normal
+    /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs,
+    /// a zero vector will be returned.
+    ///
+    /// `self` and `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn refract(self, normal: Self, eta: f64) -> Self {
+        glam_assert!(self.is_normalized());
+        glam_assert!(normal.is_normalized());
+        let n_dot_i = normal.dot(self);
+        let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
+        if k >= 0.0 {
+            eta * self - (eta * n_dot_i + math::sqrt(k)) * normal
+        } else {
+            Self::ZERO
+        }
+    }
+
     /// Creates a 2D vector containing `[angle.cos(), angle.sin()]`. This can be used in
     /// conjunction with the [`rotate()`][Self::rotate()] method, e.g.
     /// `DVec2::from_angle(PI).rotate(DVec2::Y)` will create the vector `[-1, 0]`
diff --git a/src/f64/dvec3.rs b/src/f64/dvec3.rs
index 9ad3da5..398490d 100644
--- a/src/f64/dvec3.rs
+++ b/src/f64/dvec3.rs
@@ -861,6 +861,44 @@ impl DVec3 {
         )
     }
 
+    /// Returns the reflection vector for a given incident vector `self` and surface normal
+    /// `normal`.
+    ///
+    /// `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn reflect(self, normal: Self) -> Self {
+        glam_assert!(normal.is_normalized());
+        self - 2.0 * self.dot(normal) * normal
+    }
+
+    /// Returns the refraction direction for a given incident vector `self`, surface normal
+    /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs,
+    /// a zero vector will be returned.
+    ///
+    /// `self` and `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn refract(self, normal: Self, eta: f64) -> Self {
+        glam_assert!(self.is_normalized());
+        glam_assert!(normal.is_normalized());
+        let n_dot_i = normal.dot(self);
+        let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
+        if k >= 0.0 {
+            eta * self - (eta * n_dot_i + math::sqrt(k)) * normal
+        } else {
+            Self::ZERO
+        }
+    }
+
     /// Returns the angle (in radians) between two vectors in the range `[0, +π]`.
     ///
     /// The inputs do not need to be unit vectors however they must be non-zero.
diff --git a/src/f64/dvec4.rs b/src/f64/dvec4.rs
index ec4bd01..b09f95d 100644
--- a/src/f64/dvec4.rs
+++ b/src/f64/dvec4.rs
@@ -919,6 +919,44 @@ impl DVec4 {
         )
     }
 
+    /// Returns the reflection vector for a given incident vector `self` and surface normal
+    /// `normal`.
+    ///
+    /// `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn reflect(self, normal: Self) -> Self {
+        glam_assert!(normal.is_normalized());
+        self - 2.0 * self.dot(normal) * normal
+    }
+
+    /// Returns the refraction direction for a given incident vector `self`, surface normal
+    /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs,
+    /// a zero vector will be returned.
+    ///
+    /// `self` and `normal` must be normalized.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled.
+    #[inline]
+    #[must_use]
+    pub fn refract(self, normal: Self, eta: f64) -> Self {
+        glam_assert!(self.is_normalized());
+        glam_assert!(normal.is_normalized());
+        let n_dot_i = normal.dot(self);
+        let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
+        if k >= 0.0 {
+            eta * self - (eta * n_dot_i + math::sqrt(k)) * normal
+        } else {
+            Self::ZERO
+        }
+    }
+
     /// Casts all elements of `self` to `f32`.
     #[inline]
     #[must_use]
diff --git a/src/lib.rs b/src/lib.rs
index 86914f9..25c2d88 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -46,7 +46,7 @@ padding so that object sizes and layouts will not change between architectures.
 math fallback implementations exist when SIMD is not available. It is intended to add support for
 other SIMD architectures once they appear in stable Rust.
 
-Currently only SSE2 on x86/x86_64 is supported as this is what stable Rust supports.
+Currently only SSE2 on x86/x86_64, NEON on Aarch64, and simd128 on WASM are supported.
 
 ## Vec3A and Mat3A
 


Test patch:
diff --git a/tests/vec2.rs b/tests/vec2.rs
index c63d5c4..a8b4688 100644
--- a/tests/vec2.rs
+++ b/tests/vec2.rs
@@ -1001,6 +1001,22 @@ macro_rules! impl_vec2_float_tests {
             assert_approx_eq!(vec, $vec2::new(0.0, -1.0));
             assert_approx_eq!(vec.to_angle(), angle);
         });
+
+        glam_test!(test_reflect, {
+            let incident = $vec2::new(1.0, -1.0);
+            let normal = $vec2::Y;
+            assert_approx_eq!(incident.reflect(normal), $vec2::ONE);
+        });
+
+        glam_test!(test_refract, {
+            let incident = $vec2::NEG_ONE.normalize();
+            let normal = $vec2::ONE.normalize();
+            assert_approx_eq!(incident.refract(normal, 0.5), incident);
+
+            let incident = $vec2::new(1.0, -1.0).normalize();
+            let normal = $vec2::Y;
+            assert_approx_eq!(incident.refract(normal, 1.5), $vec2::ZERO);
+        });
     };
 }
 
diff --git a/tests/vec3.rs b/tests/vec3.rs
index cf3ac92..b5e62d3 100644
--- a/tests/vec3.rs
+++ b/tests/vec3.rs
@@ -1098,6 +1098,22 @@ macro_rules! impl_vec3_float_tests {
             let a = $vec3::new(1.0, 2.0, 3.0);
             assert_eq!(format!("{:.2}", a), "[1.00, 2.00, 3.00]");
         });
+
+        glam_test!(test_reflect, {
+            let incident = $vec3::new(1.0, -1.0, 1.0);
+            let normal = $vec3::Y;
+            assert_approx_eq!(incident.reflect(normal), $vec3::ONE);
+        });
+
+        glam_test!(test_refract, {
+            let incident = $vec3::NEG_ONE.normalize();
+            let normal = $vec3::ONE.normalize();
+            assert_approx_eq!(incident.refract(normal, 0.5), incident);
+
+            let incident = $vec3::new(1.0, -1.0, 0.0).normalize();
+            let normal = $vec3::Y;
+            assert_approx_eq!(incident.refract(normal, 1.5), $vec3::ZERO);
+        });
     };
 }
 
diff --git a/tests/vec4.rs b/tests/vec4.rs
index 74154b1..b0b0990 100644
--- a/tests/vec4.rs
+++ b/tests/vec4.rs
@@ -1196,6 +1196,22 @@ macro_rules! impl_vec4_float_tests {
             let a = $vec4::new(1.0, 2.0, 3.0, 4.0);
             assert_eq!(format!("{:.2}", a), "[1.00, 2.00, 3.00, 4.00]");
         });
+
+        glam_test!(test_reflect, {
+            let incident = $vec4::new(1.0, -1.0, 1.0, 1.0);
+            let normal = $vec4::Y;
+            assert_approx_eq!(incident.reflect(normal), $vec4::ONE);
+        });
+
+        glam_test!(test_refract, {
+            let incident = $vec4::NEG_ONE.normalize();
+            let normal = $vec4::ONE.normalize();
+            assert_approx_eq!(incident.refract(normal, 0.5), incident);
+
+            let incident = $vec4::new(1.0, -1.0, 0.0, 0.0).normalize();
+            let normal = $vec4::Y;
+            assert_approx_eq!(incident.refract(normal, 1.5), $vec4::ZERO);
+        });
     };
 }