Nathan Reed

Blog Stuff I’ve Made Talks About Me
Hash Functions for GPU RenderingTexture Gathers and Coordinate Precision

Slope Space in BRDF Theory

July 16, 2021 · Graphics, Math · 3 Comments

When you read BRDF theory papers, you’ll often see mention of slope space. Sometimes, components of the BRDF such as NDFs or masking-shadowing functions are defined in slope space, or operations are done in slope space before being converted back to ordinary vectors or polar coordinates. However, the meaning and intuition of slope space is rarely explained. Since it may not be obvious exactly what slope space is, why it is useful, or how to transform things to and from it, I thought I would write down a gentler introduction to it.

  • Slope Refresher
  • Normals and Slopes
  • Slope Space
  • Converting to Polar Coordinates
  • Properties of Slope Space
  • Distributions in Slope Space
    • The Jacobian
    • Some Common Distributions
  • Conclusion

Slope Refresher

First off, what even is this “slope” thing we’re talking about? If you think back to your high school algebra class, the slope of a line was defined as “rise over run”, or the ratio Δy/Δx\Delta y / \Delta xΔy/Δx between some two points on the line.

Slope of a line

The steeper the line, the larger the magnitude of its slope. The sign of the slope indicates which direction the line is sloping in. The slope is infinite if the line is vertical.

The concept of slope can readily be generalized to planes as well as lines. Planes have two slopes, one for Δz/Δx\Delta z / \Delta xΔz/Δx and one for Δz/Δy\Delta z / \Delta yΔz/Δy (using zzz-up coordinates, and assuming the surface is not vertical):

Slopes of a plane

These values describe how much the surface rises or falls in zzz if you take a step along either xxx or yyy. This completely specifies the orientation of a planar surface, as steps in any other direction can be derived from the xxx and yyy slopes.

In calculus, the slope of a line is generalized to the derivative or “instantaneous slope” of a curve, dy/dx\mathrm{d}y/\mathrm{d}xdy/dx. For curved surfaces, so long as they can be expressed as a heightfield (where zzz is a function of x,yx, yx,y), slopes become partial derivatives ∂z/∂x\partial z / \partial x∂z/∂x and ∂z/∂y\partial z / \partial y∂z/∂y.

It’s worth noting that slopes are completely coordinate-dependent quantities. If you transform to a different coordinate system, the slopes of zzz with respect to x,yx, yx,y will be totally different values, or even infinite (if the surface is not a heightfield anymore in the new coordinates).

Normals and Slopes

We usually describe surfaces in 3D by their normal vector rather than their slopes, as the normal is able to gracefully handle surfaces in any orientation without infinities, and is easier to transform into different coordinate systems. However, there is a simple relationship between a surface’s normal and its slopes, as this diagram should hopefully convince you:

Normal vector compared with slope

The two triangles with the dotted lines in the figure are congruent (same angles and sizes), but rotated by 90 degrees. As the normal is, by definition, perpendicular to the surface, the normal’s components have the same proportionality as coordinate deltas along the surface, just swapped around. This diagram shows the xzxzxz projection, but the same holds true of the yzyzyz components: ΔzΔx=−nxnzΔzΔy=−nynz \begin{aligned} \frac{\Delta z}{\Delta x} &= -\frac{\mathbf{n}_x}{\mathbf{n}_z} \\[1em] \frac{\Delta z}{\Delta y} &= -\frac{\mathbf{n}_y}{\mathbf{n}_z} \end{aligned} ΔxΔz​ΔyΔz​​=−nz​nx​​=−nz​ny​​​ The negative sign is because Δz\Delta zΔz is going down while nz\mathbf{n}_znz​ is going up (or vice versa, depending on the orientation).

Just for completeness, when you have a heightfield surface z(x,y)z(x, y)z(x,y), the partial derivatives are related to its normal at a point in the same way: ∂z∂x=−nxnz∂z∂y=−nynz \begin{aligned} \frac{\partial z}{\partial x} &= -\frac{\mathbf{n}_x}{\mathbf{n}_z} \\[1em] \frac{\partial z}{\partial y} &= -\frac{\mathbf{n}_y}{\mathbf{n}_z} \end{aligned} ∂x∂z​∂y∂z​​=−nz​nx​​=−nz​ny​​​

Slope Space

Now we’re finally ready to define slope space. Due to the relationship between slopes and normal vectors, slopes act as an alternate parameterization of unit vectors in the z>0z > 0z>0 hemisphere. Given any vector, we can treat it as a normal and find the slopes of a surface perpendicular to it. “Slope space” refers to this domain: the 2D space of all the possible slope values. As slopes can be any real numbers, slope space is just the real plane, R2\mathbb{R}^2R2, but with a special meaning.

A good way to visualize slope space is to identify it with the plane z=1z = 1z=1. Then, vectors at the origin can be converted to slope space by intersecting them with the plane:

Slope space as the z=1 plane

Here I’ve introduced the notation n~\tilde{\mathbf{n}}n~ for the 2D vector in slope space corresponding to the 3D vector n\mathbf{n}n. The tilde (∼\sim∼) notation for slope-space quantities is commonly used in the BRDF literature, and I’ll follow it here.

Intersecting a ray with the z=1z = 1z=1 plane is equivalent to rescaling the vector so that nz=1\mathbf{n}_z = 1nz​=1, and then the slopes can be read off as the negated x,yx, yx,y components of the rescaled vector. You can visualize the slope plane as having inverted x,yx, yx,y axes compared to the base coordinates to take care of this. (Note the xxx-axis on the slope plane, pointing to the left, in the diagram above.)

So, you can picture the hemisphere being blown up and stretched onto the plane, by projecting each point away from the origin until it hits the plane. This establishes a bijection (one-to-one mapping) between the unit vectors with z>0z > 0z>0 and points on the plane.

To make it official, the slope-space parameterization of an arbitrary vector v\mathbf{v}v with vz>0\mathbf{v}_z > 0vz​>0 is defined by: v~x=−vxvzv~y=−vyvz \begin{aligned} \tilde{\mathbf{v}}_x &= -\frac{\mathbf{v}_x}{\mathbf{v}_z} \\[1em] \tilde{\mathbf{v}}_y &= -\frac{\mathbf{v}_y}{\mathbf{v}_z} \end{aligned} v~x​v~y​​=−vz​vx​​=−vz​vy​​​ This assumes that the vector is upward-pointing, so that vz>0\mathbf{v}_z > 0vz​>0. Finite slopes cannot represent horizontal vectors (normal to vertical surfaces), and they cannot distinguish between upward- and downward-pointing vectors, as slopes have no sense of orientation—reverse the normal, and you still get the same slopes.

Converting back from slopes to an ordinary unit normal vector is also simple: v=normalize(−v~x,−v~y,1) \mathbf{v} = \text{normalize}(-\tilde{\mathbf{v}}_x, -\tilde{\mathbf{v}}_y, 1) v=normalize(−v~x​,−v~y​,1)

Converting to Polar Coordinates

Another common parameterization of unit vectors is the polar coordinates θ,ϕ\theta, \phiθ,ϕ. It’s straightforward to work out the direct conversion between slope space and polar coordinates.

Following common conventions, we define the polar coordinates so that θ\thetaθ measures downward from the +z+z+z axis, and ϕ\phiϕ measures counterclockwise from the +x+x+x axis. The conversion between polar and 3D unit vectors is: θ=acos(z)ϕ=atan2(y,x)x=sin⁡θcos⁡ϕy=sin⁡θsin⁡ϕz=cos⁡θ \begin{aligned} \theta &= \text{acos}(z) \\ \phi &= \text{atan2}(y, x) \end{aligned} \qquad \begin{aligned} x &= \sin\theta \cos\phi \\ y &= \sin\theta \sin\phi \\ z &= \cos\theta \end{aligned} θϕ​=acos(z)=atan2(y,x)​xyz​=sinθcosϕ=sinθsinϕ=cosθ​ and the conversion between polar and slope space is: θ=atan(x~2+y~2)ϕ=atan2(−y~,−x~)x~=− ⁣tan⁡θcos⁡ϕy~=− ⁣tan⁡θsin⁡ϕ \begin{aligned} \theta &= \text{atan}(\sqrt{\tilde x^2 + \tilde y^2}) \\ \phi &= \text{atan2}(-\tilde y, -\tilde x) \end{aligned} \qquad \begin{aligned} \tilde x &= -\!\tan\theta \cos\phi \\ \tilde y &= -\!\tan\theta \sin\phi \\ \end{aligned} θϕ​=atan(x~2+y~​2​)=atan2(−y~​,−x~)​x~y~​​=−tanθcosϕ=−tanθsinϕ​ This can be derived by setting x~=−x/z\tilde x = -x/zx~=−x/z and substituting the conversion from polar, then using the identity sin⁡/cos⁡=tan⁡\sin/\cos = \tansin/cos=tan.

A fact worth noting here is that the magnitude of a slope-space vector, ∣v~∣|\tilde{\mathbf{v}}|∣v~∣, is equal to tan⁡θv\tan\theta_\mathbf{v}tanθv​.

Properties of Slope Space

Now we’ve seen how to define slope space and convert back and forth from it. But why is it useful? Why would we want to represent vectors or functions in this way?

In microfacet BRDF theory, we usually assume the microsurface is a heightfield for simplicity (which is a pretty reasonable assumption for a lot of everyday materials). If the microsurface is a heightfield, then its normals are constrained to the upper hemisphere. Slope space, which parameterizes exactly the upper hemisphere, is a good match for this.

From a performance perspective, slope space is also much cheaper to transform to and from than polar coordinates, which makes it nicer to use in shaders. It requires only some divides or a normalize, as opposed to a bunch of forward or inverse trigonometric functions.

Slope space also has no boundaries, in contrast to other representations of unit vectors. The origin (0, 0) of the slope plane represents a flat surface normal, and the farther away you get, the more extreme the slope, but you can’t make the surface turn upside down or produce an invalid normal. So, you can freely do various manipulations on vectors in slope space without worrying about exceeding any bounds.

Another useful fact about slope space is that many linear transformations of a surface, such as scaling or shearing, map to transformations of its slope space in simple ways. For example, scaling a surface by a factor α\alphaα along its zzz-axis causes its normal vectors’ zzz-components to scale by 1/α1/\alpha1/α (due to normals taking the inverse transpose), but then since nz\mathbf{n}_znz​ is in the denominator in the definition of slope space, we have that the slopes of the surface are scaled by α\alphaα.

Here’s a table of how transformations of the microsurface map to transformations of slope space:

Surface Slope Space
Horizontal scale by (αx,αy)(\alpha_x, \alpha_y)(αx​,αy​) Scale by (1/αx,1/αy)(1/\alpha_x, 1/\alpha_y)(1/αx​,1/αy​)
Vertical scale by α\alphaα Scale by α\alphaα
Horizontal rotate (xyxyxy) by θ\thetaθ Rotate by θ\thetaθ
Vertical rotate (xz,yzxz, yzxz,yz) Projective transform
(not recommended)
Horizontal shear (xyxyxy) by [1k2k11]\begin{bmatrix} 1 & k_2 \\ k_1 & 1 \end{bmatrix}[1k1​​k2​1​] Shear by [1−k1−k21]\begin{bmatrix} 1 & -k_1 \\ -k_2 & 1 \end{bmatrix}[1−k2​​−k1​1​]
Vertical shear by [100010kxky1]\begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ k_x & k_y & 1 \end{bmatrix}⎣⎡​10kx​​01ky​​001​⎦⎤​ Translate by (kx,ky)(k_x, k_y)(kx​,ky​)
Vertical shear by [10kx01ky001]\begin{bmatrix} 1 & 0 & k_x \\ 0 & 1 & k_y \\ 0 & 0 & 1 \end{bmatrix}⎣⎡​100​010​kx​ky​1​⎦⎤​ Projective transform
(not recommended)

These transformations in slope space are often exploited by parameterized BRDF models; they can implement roughness, anisotropy, and such as transformations applied to a single canonical BRDF (see for example Heitz 2014, section 5).

Distributions in Slope Space

One of the key ingredients in a microfacet BRDF is its normal distribution function (NDF), and one of the key uses for slope space is defining NDFs. Because slope space is an unbounded 2D plane, we can import existing 1D or 2D distribution functions and manipulate them in various ways, just as we would in any 2D domain. As long as we end up with a valid, normalized probability distribution in the slope plane (sometimes called a slope distribution function, or a P22P^{22}P22 function—I’m not sure where the latter term comes from), we can transform it to a properly normalized NDF expressed in polar or vector form. Let’s see how to do that.

The Jacobian

When mapping distribution functions from one space to another, it’s important to remember that the values of these functions are not dimensionless numbers; they are densities with respect to the area or volume measure of the underlying space. Therefore, it’s not enough just to change variables to express the function in the new coordinates; you also have to correct for the way the mapping stretches or squeezes the volume, which can vary from place to place.

Symbolically, suppose we have a domain AAA with a probability density p(a)p(a)p(a) defined on it. We want to map this to a domain BBB parameterized by some new coordinates bbb. What we want is not just p(a)=p(b)p(a) = p(b)p(a)=p(b) when a↦ba \mapsto ba↦b under the mapping. Rather, we need to maintain: p(a) dA=p(b) dB p(a) \, \mathrm{d}A = p(b) \, \mathrm{d}B p(a)dA=p(b)dB where dA,dB\mathrm{d}A, \mathrm{d}BdA,dB are matching volume elements of the respective spaces, with dA↦dB\mathrm{d}A \mapsto \mathrm{d}BdA↦dB under the mapping we’re using. This says that the amount of probability (or whatever thing whose density we’re measuring) in the infinitesimal volume dA\mathrm{d}AdA is conserved under the mapping; the same amount of probability is present in dB\mathrm{d}BdB.

This equation can be rewritten: p(b)=p(a)dAdB p(b) = p(a) \frac{\mathrm{d}A}{\mathrm{d}B} p(b)=p(a)dBdA​ The factor dA/dB\mathrm{d}A / \mathrm{d}BdA/dB here is called the Jacobian, referring to the determinant of the Jacobian matrix which contains all the derivatives of the change of variables from aaa to bbb. Actually, this is the inverse Jacobian, as the forward Jacobian for A→BA \to BA→B would be dB/dA\mathrm{d}B / \mathrm{d}AdB/dA. The forward Jacobian is the factor by which the mapping stretches or squeezes volumes locally around a point. Because a probability density has volume in the denominator, it transforms using the inverse Jacobian.

So, when converting a slope-space distribution to an NDF, we have to multiply by the appropriate Jacobian. But how do we find out what that is? First off, we have to recall that NDFs are defined not as a density over solid angle in the hemisphere, but as a density over projected area on the xyxyxy plane. Thus, it’s not enough to just find the Jacobian from slope space to polar coordinates; we also need to find the Jacobian from polar coordinates to projected area.

To do this, I find it easiest to use the formalism of differential forms. Explaining how those work is out of the scope of this article, but here’s an exposition I found useful. They’re essentially fields of dual kkk-vectors.

First, we can write down the xyxyxy projected area element, dx∧dy\mathrm{d}x \wedge \mathrm{d}ydx∧dy, in terms of polar coordinates by differentiating the mapping from polar to Cartesian, which I’ll repeat here for convenience: {x=sin⁡θcos⁡ϕy=sin⁡θsin⁡ϕz=cos⁡θdx∧dy=(cos⁡θcos⁡ϕ dθ−sin⁡θsin⁡ϕ dϕ) ∧(cos⁡θsin⁡ϕ dθ+sin⁡θcos⁡ϕ dϕ)=cos⁡θsin⁡θcos⁡2ϕ (dθ∧dϕ) −cos⁡θsin⁡θsin⁡2ϕ (dϕ∧dθ)=cos⁡θsin⁡θ (dθ∧dϕ) \begin{gathered} \left\{ \begin{aligned} x &= \sin\theta \cos\phi \\ y &= \sin\theta \sin\phi \\ z &= \cos\theta \end{aligned} \right. \\[2em] \begin{aligned} \mathrm{d}x \wedge \mathrm{d}y &= (\cos\theta\cos\phi\,\mathrm{d}\theta - \sin\theta\sin\phi\,\mathrm{d}\phi) \ \wedge \\ &\qquad (\cos\theta\sin\phi\,\mathrm{d}\theta + \sin\theta\cos\phi\,\mathrm{d}\phi) \\[0.5em] &= \cos\theta\sin\theta\cos^2\phi\,(\mathrm{d}\theta \wedge \mathrm{d}\phi) \ - \\ &\qquad \cos\theta\sin\theta\sin^2\phi\,(\mathrm{d}\phi \wedge \mathrm{d}\theta) \\[0.5em] &= \cos\theta\sin\theta\,(\mathrm{d}\theta \wedge \mathrm{d}\phi) \end{aligned} \end{gathered} ⎩⎨⎧​xyz​=sinθcosϕ=sinθsinϕ=cosθ​dx∧dy​=(cosθcosϕdθ−sinθsinϕdϕ) ∧(cosθsinϕdθ+sinθcosϕdϕ)=cosθsinθcos2ϕ(dθ∧dϕ) −cosθsinθsin2ϕ(dϕ∧dθ)=cosθsinθ(dθ∧dϕ)​​ Then, we can do the same thing with the slope-space area element: {x~=− ⁣tan⁡θcos⁡ϕy~=− ⁣tan⁡θsin⁡ϕdx~∧dy~=−(cos⁡−2θcos⁡ϕ dθ−tan⁡θsin⁡ϕ dϕ) ∧−(cos⁡−2θsin⁡ϕ dθ+tan⁡θcos⁡ϕ dϕ)=tan⁡θcos⁡−2θcos⁡2ϕ (dθ∧dϕ) −tan⁡θcos⁡−2θsin⁡2ϕ (dϕ∧dθ)=tan⁡θcos⁡2θ (dθ∧dϕ) \begin{gathered} \left\{ \begin{aligned} \tilde x &= -\!\tan\theta \cos\phi \\ \tilde y &= -\!\tan\theta \sin\phi \\ \end{aligned} \right. \\[1.5em] \begin{aligned} \mathrm{d}\tilde x \wedge \mathrm{d} \tilde y &= -(\cos^{-2}\theta\cos\phi\,\mathrm{d}\theta - \tan\theta\sin\phi\,\mathrm{d}\phi) \ \wedge \\ &\qquad -(\cos^{-2}\theta\sin\phi\,\mathrm{d}\theta + \tan\theta\cos\phi\,\mathrm{d}\phi) \\[0.5em] &= \tan\theta\cos^{-2}\theta\cos^2\phi\,(\mathrm{d}\theta \wedge \mathrm{d}\phi) \ - \\ &\qquad \tan\theta\cos^{-2}\theta\sin^2\phi\,(\mathrm{d}\phi \wedge \mathrm{d}\theta) \\[0.5em] &= \frac{\tan\theta}{\cos^2\theta} \, (\mathrm{d}\theta \wedge \mathrm{d}\phi) \end{aligned} \end{gathered} {x~y~​​=−tanθcosϕ=−tanθsinϕ​dx~∧dy~​​=−(cos−2θcosϕdθ−tanθsinϕdϕ) ∧−(cos−2θsinϕdθ+tanθcosϕdϕ)=tanθcos−2θcos2ϕ(dθ∧dϕ) −tanθcos−2θsin2ϕ(dϕ∧dθ)=cos2θtanθ​(dθ∧dϕ)​​ Now, all we have to do is divide: dx~∧dy~dx∧dy=tan⁡θcos⁡2θ1cos⁡θsin⁡θ=1cos⁡4θ \begin{aligned} \frac{\mathrm{d}\tilde x \wedge \mathrm{d} \tilde y}{\mathrm{d}x \wedge \mathrm{d}y} &= \frac{\tan\theta}{\cos^2\theta} \frac{1}{\cos\theta\sin\theta} \\[1em] &= \frac{1}{\cos^4\theta} \end{aligned} dx∧dydx~∧dy~​​​=cos2θtanθ​cosθsinθ1​=cos4θ1​​ Et voilà! The Jacobian for converting densities from slope space to NDF form is 1/cos⁡4θ1/\cos^4\theta1/cos4θ. We’ll have to multiply by this factor in addition to changing variables.

Some Common Distributions

As an example of the conversion from slope space to NDF, let’s take the standard (bivariate) Gaussian distribution defined on slope space: D(m~,σ)=12πσ2exp⁡(−∣m~∣22σ2) D(\tilde{\mathbf{m}}, \sigma) = \frac{1}{2\pi\sigma^2} \exp\left(-\frac{|\tilde{\mathbf{m}}|^2}{2\sigma^2}\right) D(m~,σ)=2πσ21​exp(−2σ2∣m~∣2​) To turn this into an NDF, we need to change variables from m~\tilde{\mathbf{m}}m~ to (θm,ϕm)(\theta_\mathbf{m}, \phi_\mathbf{m})(θm​,ϕm​), and also multiply by the Jacobian 1/cos⁡4θm1/\cos^4\theta_\mathbf{m}1/cos4θm​. Recalling that ∣m~∣=tan⁡θm|\tilde{\mathbf{m}}| = \tan\theta_\mathbf{m}∣m~∣=tanθm​, this becomes: D(m,σ)=12πσ2cos⁡4θmexp⁡(−tan⁡2θm2σ2) D(\mathbf{m}, \sigma) = \frac{1}{2\pi\sigma^2\cos^4\theta_\mathbf{m}} \exp\left(-\frac{\tan^2\theta_\mathbf{m}}{2\sigma^2}\right) D(m,σ)=2πσ2cos4θm​1​exp(−2σ2tan2θm​​) Hey, that looks familiar—it’s the Beckmann NDF! (Although it’s more usually seen with the roughness parameter α=2σ\alpha = \sqrt{2}\sigmaα=2​σ.) The Beckmann distribution is a Gaussian in slope space.

The isotropic GGX NDF (Walter et al 2007) looks like this: D(m,α)=α2πcos⁡4θm(α2+tan⁡2θm)2 D(\mathbf{m}, \alpha) = \frac{\alpha^2}{\pi \cos^4\theta_\mathbf{m} \bigl(\alpha^2 + \tan^2\theta_\mathbf{m} \bigr)^2 } D(m,α)=πcos4θm​(α2+tan2θm​)2α2​ You might now recognize those familiar-looking cos⁡4θm\cos^4\theta_\mathbf{m}cos4θm​ and tan⁡θm\tan\theta_\mathbf{m}tanθm​ factors. Yep, this NDF is also a convert from slope space! Working backwards, we can see that it was originally: D(m~,α)=α2π(α2+∣m~∣2)2 D(\tilde{\mathbf{m}}, \alpha) = \frac{\alpha^2}{\pi \bigl(\alpha^2 + |\tilde{\mathbf{m}}|^2 \bigr)^2 } D(m~,α)=π(α2+∣m~∣2)2α2​ Although this formula is probably less familiar, it matches the pdf of the bivariate Student’s ttt-distribution with the “normality” parameter ν\nuν set to 2, and scaled by α/2\alpha/\sqrt{2}α/2​. (You can also create a family of NDFs that interpolate between GGX and Beckmann, by exposing a user parameter that controls ν\nuν; see Ribardière et al 2017.)

(Incidentally, the GGX NDF is often seen written in this alternate form: D(m,α)=α2π((α2−1)cos⁡2θm+1)2 D(\mathbf{m}, \alpha) = \frac{\alpha^2}{\pi \bigl( (\alpha^2 - 1)\cos^2\theta_\mathbf{m} + 1 \bigr)^2 } D(m,α)=π((α2−1)cos2θm​+1)2α2​ This is the same function as the form above (which is from the original GGX paper), but rearranged to make it cheaper to evaluate, as it eliminates the tan⁡2\tan^2tan2 using the identity tan⁡2=(1−cos⁡2)/cos⁡2\tan^2 = (1 - \cos^2)/\cos^2tan2=(1−cos2)/cos2. However, this form also introduces numerical precision problems, and Filament has a numerically stable form: D(m,α)=α2π(α2cos⁡2θm+sin⁡2θm)2 D(\mathbf{m}, \alpha) = \frac{\alpha^2}{\pi \bigl(\alpha^2 \cos^2\theta_\mathbf{m} + \sin^2\theta_\mathbf{m} \bigr)^2 } D(m,α)=π(α2cos2θm​+sin2θm​)2α2​ which is again the same function, rearranged some more; you’re meant to calculate sin⁡2θm\sin^2\theta_\mathbf{m}sin2θm​ as the squared magnitude of the cross product ∣n×m∣2|\mathbf{n} \times \mathbf{m}|^2∣n×m∣2. This has nothing to do with slope space; I just thought it was neat and worth knowing.)

Conclusion

To recap, the most important thing to take away about slope space is that it provides an alternate representation for unit vectors in the upper hemisphere, by projecting them out onto an infinite plane. This enables us to work with distributions in plain old 2D space, and then map them back into functions on the hemisphere. Slope space also provides convenient mappings from some linear transformations of the microsurface to linear or affine transformations in the slope plane.

I hope this has demystified the concept of slope space a little bit, and now you won’t be confused by it anymore when reading BRDF papers! 😄

Hash Functions for GPU RenderingTexture Gathers and Coordinate Precision

3 Comments on “Slope Space in BRDF Theory”

Subscribe

  • RSS RSS

Recent Posts

  • Reading Veach’s Thesis, Part 2
  • Reading Veach’s Thesis
  • Texture Gathers and Coordinate Precision
  • git-partial-submodule
  • Slope Space in BRDF Theory
  • Hash Functions for GPU Rendering
  • All Posts

Categories

  • Graphics(32)
  • Coding(23)
  • Math(21)
  • GPU(15)
  • Physics(6)
  • Eye Candy(4)
© 2007–2024 by Nathan Reed. Licensed CC-BY-4.0.