Spaces:
Runtime error
Runtime error
File size: 2,510 Bytes
c19ca42 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
from typing import Tuple
import numpy as np
XYZ = Tuple[np.ndarray, np.ndarray, np.ndarray]
"""
The normalized XYZ components of a normal map. Z is guaranteed to be >= 0.
"""
def normalize_normals(x: np.ndarray, y: np.ndarray) -> XYZ:
# The square of the length of X and Y
l_sq = np.square(x) + np.square(y)
# If the length of X and Y is >1, then make it 1
l = np.sqrt(np.maximum(l_sq, 1))
x /= l
y /= l
l_sq = np.minimum(l_sq, 1, out=l_sq)
# Compute Z
z = np.sqrt(1 - l_sq)
return x, y, z
def gr_to_xyz(n: np.ndarray) -> XYZ:
"""
Takes a BGR or BGRA image and converts it into XYZ normal components only by looking at the R and G channels.
"""
x = n[:, :, 2] * 2 - 1
y = n[:, :, 1] * 2 - 1
return normalize_normals(x, y)
def xyz_to_bgr(xyz: XYZ) -> np.ndarray:
"""
Converts the given XYZ components into an BGR image.
"""
x, y, z = xyz
r = (x + 1) * 0.5
g = (y + 1) * 0.5
b = z
return np.dstack((b, g, r))
def octahedral_gr_to_xyz(n: np.ndarray) -> XYZ:
"""
Takes a BGR or BGRA image of octahedral (RTX Remix) normals and converts it into XYZ normal components only by looking at the R and G channels.
"""
r = n[:, :, 2] * 2 - 1
g = n[:, :, 1] * 2 - 1
x: np.ndarray = (r + g) / 2
y: np.ndarray = r - x
z: np.ndarray = 1 - np.abs(x) - np.abs(y)
length = np.sqrt(np.square(x) + np.square(y) + np.square(z))
x /= length
y /= length
z /= length # type: ignore
return x, y, z
def xyz_to_octahedral_bgr(xyz: XYZ) -> np.ndarray:
"""
Converts the given XYZ components into an BGR image with normals using octahedral (RTX Remix) encoding.
For more information about octahedral normals, see:
https://knarkowicz.wordpress.com/2014/04/16/octahedron-normal-vector-encoding/
https://jcgt.org/published/0003/02/01/
"""
x, y, z = xyz
absolute = np.abs(x) + np.abs(y) + np.abs(z)
x /= absolute
y /= absolute
# This is a trick used in RTX Remix to more efficiently encode normals.
# Octahedral normals are defined for the whole range of values (so all possible unit vectors).
# However, we know that we are working with hemispheric normal maps (z>=0)
# and can use this knowledge to remap xy values to assume all possible values in [-1..1]x[-1..1].
r = x + y
g = x - y
r = (r + 1) * 0.5
g = (g + 1) * 0.5
b = np.zeros(x.shape, dtype=np.float32)
return np.dstack((b, g, r))
|