15.6. Normalized Texel Coordinate Operations

If the image sampler instruction provides normalized texel coordinates, some of the following operations are performed.

15.6.1. Projection Operation

For Proj image operations, the normalized texel coordinates $(s,t,r,q,a)$ and (if present) the $D_{ref}$ coordinate are transformed as follows:

\begin{align*} s & = \frac{s}{q}, & \textrm{for 1D, 2D, or 3D image} \\ \\ t & = \frac{t}{q}, & \textrm{for 2D or 3D image} \\ \\ r & = \frac{r}{q}, & \textrm{for 3D image} \\ \\ D_{ref} & = \frac{D_{ref}}{q}, & \textrm{if provided} \end{align*}

15.6.2. Derivative Image Operations

Derivatives are used for level-of-detail selection. These derivatives are either implicit (in an ImplicitLod image instruction in a fragment shader) or explicit (provided explicitly by shader to the image instruction in any shader).

For implicit derivatives image instructions, the derivatives of texel coordinates are calculated in the same manner as derivative operations above. That is:

\begin{align*} \partial{s}/\partial{x} & = dPdx(s), & \partial{s}/\partial{y} & = dPdy(s), & \textrm{for 1D, 2D, Cube, or 3D image} \\ \partial{t}/\partial{x} & = dPdx(t), & \partial{t}/\partial{y} & = dPdy(t), & \textrm{for 2D, Cube, or 3D image} \\ \partial{u}/\partial{x} & = dPdx(u), & \partial{u}/\partial{y} & = dPdy(u), & \textrm{for Cube or 3D image} \end{align*}

Partial derivatives not defined above for certain image dimensionalities are set to zero.

For explicit level-of-detail image instructions, if the optional SPIR-V operand $Grad$ is provided, then the operand values are used for the derivatives. The number of components present in each derivative for a given image dimensionality matches the number of partial derivatives computed above.

If the optional SPIR-V operand $Lod$ is provided, then derivatives are set to zero, the cube map derivative transformation is skipped, and the scale factor operation is skipped. Instead, the floating point scalar coordinate is directly assigned to $\lambda_{base}$ as described in Level-of-Detail Operation.

15.6.3. Cube Map Face Selection and Transformations

For cube map image instructions, the $(s,t,r)$ coordinates are treated as a direction vector $(r_{x},r_{y},r_{z})$ . The direction vector is used to select a cube map face. The direction vector is transformed to a per-face texel coordinate system $(s_{face},t_{face})$ . The direction vector is also used to transform the derivatives to per-face derivatives.

15.6.4. Cube Map Face Selection

The direction vector selects one of the cube map’s faces based on the largest magnitude coordinate direction (the major axis direction). Since two or more coordinates can have identical magnitude, the implementation must have rules to disambiguate this situation.

The rules should have as the first rule that $r_{z}$ wins over $r_{y}$ and $r_{x}$ , and the second rule that $r_{y}$ wins over $r_{x}$ . An implementation may choose other rules, but the rules must be deterministic and depend only on $(r_{x},r_{y},r_{z})$ .

The layer number (corresponding to a cube map face), the coordinate selections for $s_{c}$ , $t_{c}$ , $r_{c}$ , and the selection of derivatives, are determined by the major axis direction as specified in the following two tables.

Table 15.4. Cube map face and coordinate selection

Major Axis DirectionLayer NumberCube Map Face $s_{c}$ $t_{c}$ $r_{c}$

$+r_{x}$

$0$

$Positive X$

$-r_{z}$

$-r_{y}$

$r_{x}$

$-r_{x}$

$1$

$Negative X$

$+r_{z}$

$-r_{y}$

$r_{x}$

$+r_{y}$

$2$

$Positive Y$

$+r_{x}$

$+r_{z}$

$r_{y}$

$-r_{y}$

$3$

$Negative Y$

$+r_{x}$

$-r_{z}$

$r_{y}$

$+r_{z}$

$4$

$Positive Z$

$+r_{x}$

$-r_{y}$

$r_{z}$

$-r_{z}$

$5$

$Negative Z$

$-r_{x}$

$-r_{y}$

$r_{z}$


Table 15.5. Cube map derivative selection

Major Axis Direction $\partial{s_{c}}/\partial{x}$ $\partial{s_{c}}/\partial{y}$ $\partial{t_{c}}/\partial{x}$ $\partial{t_{c}}/\partial{y}$ $\partial{r_{c}}/\partial{x}$ $\partial{r_{c}}/\partial{y}$

$+r_{x}$

$-\partial{r_{z}}/\partial{x}$

$-\partial{r_{z}}/\partial{y}$

$-\partial{r_{y}}/\partial{x}$

$-\partial{r_{y}}/\partial{y}$

$+\partial{r_{x}}/\partial{x}$

$+\partial{r_{x}}/\partial{y}$

$-r_{x}$

$+\partial{r_{z}}/\partial{x}$

$+\partial{r_{z}}/\partial{y}$

$-\partial{r_{y}}/\partial{x}$

$-\partial{r_{y}}/\partial{y}$

$-\partial{r_{x}}/\partial{x}$

$-\partial{r_{x}}/\partial{y}$

$+r_{y}$

$+\partial{r_{x}}/\partial{x}$

$+\partial{r_{x}}/\partial{y}$

$+\partial{r_{z}}/\partial{x}$

$+\partial{r_{z}}/\partial{y}$

$+\partial{r_{y}}/\partial{x}$

$+\partial{r_{y}}/\partial{y}$

$-r_{y}$

$+\partial{r_{x}}/\partial{x}$

$+\partial{r_{x}}/\partial{y}$

$-\partial{r_{z}}/\partial{x}$

$-\partial{r_{z}}/\partial{y}$

$-\partial{r_{y}}/\partial{x}$

$-\partial{r_{y}}/\partial{y}$

$+r_{z}$

$+\partial{r_{x}}/\partial{x}$

$+\partial{r_{x}}/\partial{y}$

$-\partial{r_{y}}/\partial{x}$

$-\partial{r_{y}}/\partial{y}$

$+\partial{r_{z}}/\partial{x}$

$+\partial{r_{z}}/\partial{y}$

$-r_{z}$

$-\partial{r_{x}}/\partial{x}$

$-\partial{r_{x}}/\partial{y}$

$-\partial{r_{y}}/\partial{x}$

$-\partial{r_{y}}/\partial{y}$

$-\partial{r_{z}}/\partial{x}$

$-\partial{r_{z}}/\partial{y}$


15.6.5. Cube Map Coordinate Transformation

\begin{align*} s_{face} & = \frac{1}{2} \times \frac{s_c}{|r_c|} + \frac{1}{2} \\ t_{face} & = \frac{1}{2} \times \frac{t_c}{|r_c|} + \frac{1}{2} \\ \end{align*}

15.6.6. Cube Map Derivative Transformation

\begin{align*} \frac{\partial{s_{face}}}{\partial{x}} &= \frac{\partial}{\partial{x}} \left ( \frac{1}{2} \times \frac{s_{c}}{|r_{c}|} + \frac{1}{2}\right ) \\ \frac{\partial{s_{face}}}{\partial{x}} &= \frac{1}{2} \times \frac{\partial}{\partial{x}} \left ( \frac{s_{c}}{|r_{c}|} \right ) \\ \frac{\partial{s_{face}}}{\partial{x}} &= \frac{1}{2} \times \left ( \frac{ |r_{c}| \times \partial{s_c}/\partial{x} -s_c \times {\partial{r_{c}}}/{\partial{x}}} {\left ( r_{c} \right )^2} \right ) \end{align*}
\begin{align*} \frac{\partial{s_{face}}}{\partial{y}} &= \frac{1}{2} \times \left ( \frac{ |r_{c}| \times \partial{s_c}/\partial{y} -s_c \times {\partial{r_{c}}}/{\partial{y}}} {\left ( r_{c} \right )^2} \right )\\ \frac{\partial{t_{face}}}{\partial{x}} &= \frac{1}{2} \times \left ( \frac{ |r_{c}| \times \partial{t_c}/\partial{x} -t_c \times {\partial{r_{c}}}/{\partial{x}}} {\left ( r_{c} \right )^2} \right ) \\ \frac{\partial{t_{face}}}{\partial{y}} &= \frac{1}{2} \times \left ( \frac{ |r_{c}| \times \partial{t_c}/\partial{y} -t_c \times {\partial{r_{c}}}/{\partial{y}}} {\left ( r_{c} \right )^2} \right ) \end{align*}
[Note]editing-note

(Bill) Note that we never revisited ARB_texture_cubemap after we introduced dependent texture fetches (ARB_fragment_program and ARB_fragment_shader).

The derivatives of $s_{face}$ and $t_{face}$ are only valid for non-dependent texture fetches (pre OpenGL 2.0).

15.6.7. Scale Factor Operation, Level-of-Detail Operation and Image Level(s) Selection

Level-of-detail selection can be either explicit (provided explicitly by the image instruction) or implicit (determined from a scale factor calculated from the derivatives).

Scale Factor Operation

The magnitude of the derivatives are calculated by:

\begin{align*} m_{ux} & = \left | \partial s / \partial x \right | \times w_{base} \\ m_{vx} & = \left | \partial t / \partial x \right | \times h_{base} \\ m_{wx} & = \left | \partial r / \partial x \right | \times d_{base} \\ \\ m_{uy} & = \left | \partial s / \partial y \right | \times w_{base} \\ m_{vy} & = \left | \partial t / \partial y \right | \times h_{base} \\ m_{wy} & = \left | \partial r / \partial y \right | \times d_{base} \end{align*}

where:

\begin{align*} \partial t / \partial x & = \partial t / \partial y = 0 & \textrm{(for 1D image)} \\ \partial r / \partial x & = \partial r / \partial y = 0 & \textrm{(for 1D, 2D or Cube image)} \\ \\ w_{base} & = image.w \\ h_{base} & = image.h \\ d_{base} & = image.d \\ & \textrm{of the } baseMipLevel & \textrm{(from image descriptor)} \end{align*}

The scale factors $(\rho_{x}, \rho{y})$ should be calculated by:

\begin{align*} \rho_{x} & = \sqrt{ m_{ux} ^{2} + m_{vx} ^{2} + m_{wx} ^{2} } \\ \rho_{y} & = \sqrt{ m_{uy} ^{2} + m_{vy} ^{2} + m_{wy} ^{2} } \end{align*}

The ideal functions $\rho_{x}$ and $\rho_{y}$ may be approximated with functions $f_x$ and $f_y$ , subject to the following constraints:

\begin{align*} & f_x \textrm{ is continuous and monotonically increasing in each of } m_{ux}, m_{vx}, \textrm{ and } m_{wx} \\ & f_y \textrm{ is continuous and monotonically increasing in each of } m_{uy}, m_{vy}, \textrm{ and } m_{wy} \end{align*} \begin{align*} \max \left ( \left | m_{ux} \right | , \left | m_{vx} \right | , \left | m_{wx} \right | \right ) \leq & f_x \leq \left | m_{ux} \right | + \left | m_{vx} \right | + \left | m_{wx} \right | \\ \max \left ( \left | m_{uy} \right | , \left | m_{vy} \right | , \left | m_{wy} \right | \right ) \leq & f_y \leq \left | m_{uy} \right | + \left | m_{vy} \right | + \left | m_{wy} \right | \end{align*}
[Note]editing-note

(Bill) For reviewers only - anticipating questions.

We only support implicit derivatives for normalized texel coordinates.

So we are documenting the derivatives in s,t,r (normalized texel coordinates) rather than u,v,w (unnormalized texel coordinates) as in OpenGL and OpenGL ES specifications. (I know, u,v,w is the way it has been documented since OpenGL V1.0.)

Also there’s no reason to have conditional application of $w_{base} , h_{base} , d_{base}$ for rectangle textures either, since they don’t support implicit derivatives.

The minimum and maximum scale factors $(\rho_{min},\rho_{max})$ are determined by:

\begin{align*} \rho_{max} & = \max( \rho_{x} , \rho_{y} ) \\ \rho_{min} & = \min( \rho_{x} , \rho_{y} ) \end{align*}

The sampling rate is determined by:

\begin{align*} N & = \min \left (\left \lceil \frac{\rho_{max}}{\rho_{min}} \right \rceil ,max_{Aniso} \right ) \end{align*}

where:

\begin{align*} sampler.max_{Aniso} & = maxAnisotropy & \textrm{(from sampler descriptor)} \\ limits.max_{Aniso} & = maxSamplerAnisotropy & \textrm{(from physical device limits)} \\ max_{Aniso} & = \min \left ( sampler.max_{Aniso}, limits.max_{Aniso} \right ) \end{align*}

If $\rho_{max} = \rho_{min} = 0$ , then all the partial derivatives are zero, the fragment’s footprint in texel space is a point, and $N$ should be treated as 1. If $\rho_{max} \neq 0 \textrm{ and } \rho_{min} = 0$ then all partial derivatives along one axis are zero, the fragment’s footprint in texel space is a line segment, and $N$ should be treated as $max_{Aniso}$ . However, anytime the footprint is small in texel space the implementation may use a smaller value of $N$ , even when $\rho_{min}$ is zero or close to zero.

An implementation may round $N$ up to the nearest supported sampling rate.

If $N=1$ , sampling is isotropic. If $N>1$ , sampling is anisotropic.

Level-of-Detail Operation

The level-of-detail parameter $\lambda$ is computed as follows:

\begin{align*} \lambda_{base}(x,y) & = \begin{cases} shaderOp.Lod & \textrm{(from optional SPIR-V operand)} \\ \log_2 \left ( \frac{\rho_{max}}{N} \right ) & \textrm{otherwise} \end{cases} \\ \lambda'(x,y) & = \lambda_{base} + \operatorname{clamp}(sampler.bias + shaderOp.bias,-maxSamplerLodBias,maxSamplerLodBias) \\ \lambda & = \begin{cases} lod_{max}, & \lambda' > lod_{max} \\ \lambda', & lod_{min} \leq \lambda' \leq lod_{max} \\ lod_{min}, & \lambda' < lod_{min} \\ undefined, & lod_{min} > lod_{max} \\ \end{cases} \end{align*}

where:

\begin{align*} sampler.bias & = mipLodBias & \textrm{(from sampler descriptor)} \\ shaderOp.bias & = \begin{cases} Bias & \textrm{(from optional SPIR-V operand)} \\ 0 & \textrm{otherwise} \end{cases} \\ sampler.lod_{min} & = minLod & \textrm{(from sampler descriptor)} \\ shaderOp.lod_{min} & = \begin{cases} MinLod & \textrm{(from optional SPIR-V operand)} \\ 0 & \textrm{otherwise} \end{cases} \\ \\ lod_{min} & = \max(sampler.lod_{min}, shaderOp.lod_{min}) \\ lod_{max} & = maxLod & \textrm{(from sampler descriptor)} \end{align*}

and $maxSamplerLodBias$ is the value of the VkPhysicalDeviceLimits feature maxSamplerLodBias.

Image Level(s) Selection

The image level(s) $d, d_{hi},\textrm{ and }d_{lo}$ which texels are read from are selected based on the level-of-detail parameter, as follows. If the sampler’s mipmapMode is VK_SAMPLER_MIPMAP_MODE_NEAREST, then level d is used:

\begin{align*} d = \begin{cases} level_{base}, & \lambda \leq \frac{1}{2} \\ nearest(\lambda), & \lambda > \frac{1}{2}, level_{base} + \lambda \leq q + \frac{1}{2} \\ q, & \lambda > \frac{1}{2}, level_{base} + \lambda > q + \frac{1}{2} \end{cases} \end{align*}

where:

\begin{align*} nearest(\lambda) & = \begin{cases} \left \lceil level_{base}+\lambda + \frac{1}{2}\right \rceil - 1, & \textrm{preferred} \\ \left \lfloor level_{base}+\lambda + \frac{1}{2}\right \rfloor, & \textrm{alternative} \end{cases} \end{align*}

and where q is the levelCount from the subresourceRange of the image view.

If the sampler’s mipmapMode is VK_SAMPLER_MIPMAP_MODE_LINEAR, two neighboring levels are selected:

\begin{align*} d_{hi} & = \begin{cases} q, & level_{base} + \lambda \geq q \\ \left \lfloor level_{base}+\lambda \right \rfloor, & \textrm{otherwise} \end{cases} \\ d_{lo} & = \begin{cases} q, & level_{base} + \lambda \geq q \\ d_{hi}+1, & \textrm{otherwise} \end{cases} \end{align*}

$\delta$ is the fractional value used for linear filtering between levels.

\begin{align*} \delta & = \operatorname{frac}(\lambda) \end{align*}

15.6.8. (s,t,r,q,a) to (u,v,w,a) Transformation

The normalized texel coordinates are scaled by the image level dimensions and the array layer is selected. This transformation is performed once for each level ( $d\textrm{ or }d_{hi}\textrm{ and }d_{lo}$ ) used in filtering.

\begin{align*} u(x,y) & = s(x,y) \times width_{level} \\ v(x,y) & = \begin{cases} 0 & \textrm{for 1D images} \\ t(x,y) \times height_{level} & \textrm{otherwise} \end{cases} \\ w(x,y) & = \begin{cases} 0 & \textrm{for 2D or Cube images} \\ r(x,y) \times depth_{level} & \textrm{otherwise} \end{cases} \\ \\ a(x,y) & = \begin{cases} a(x,y) & \textrm{for array images} \\ 0 & \textrm{otherwise} \end{cases} \end{align*}

Operations then proceed to Unnormalized Texel Coordinate Operations.