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 Dref 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 λ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 (rx,ry,rz). The direction vector is used to select a cube map face. The direction vector is transformed to a per-face texel coordinate system (sface,tface), 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 rz wins over ry and rx, and the second rule that ry wins over rx. An implementation may choose other rules, but the rules must be deterministic and depend only on (rx,ry,rz).

The layer number (corresponding to a cube map face), the coordinate selections for sc, tc, rc, 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 Direction Layer Number Cube Map Face sc tc rc

+rx

0

Positive X

-rz

-ry

rx

-rx

1

Negative X

+rz

-ry

rx

+ry

2

Positive Y

+rx

+rz

ry

-ry

3

Negative Y

+rx

-rz

ry

+rz

4

Positive Z

+rx

-ry

rz

-rz

5

Negative Z

-rx

-ry

rz


Table 15.5. Cube map derivative selection

Major Axis Direction ∂sc / ∂x ∂sc / ∂y ∂tc / ∂x ∂tc / ∂y ∂rc / ∂x ∂rc / ∂y

+rx

-∂rz / ∂x

-∂rz / ∂y

-∂ry / ∂x

-∂ry / ∂y

+∂rx / ∂x

+∂rx / ∂y

-rx

+∂rz / ∂x

+∂rz / ∂y

-∂ry / ∂x

-∂ry / ∂y

-∂rx / ∂x

-∂rx / ∂y

+ry

+∂rx / ∂x

+∂rx / ∂y

+∂rz / ∂x

+∂rz / ∂y

+∂ry / ∂x

+∂ry / ∂y

-ry

+∂rx / ∂x

+∂rx / ∂y

-∂rz / ∂x

-∂rz / ∂y

-∂ry / ∂x

-∂ry / ∂y

+rz

+∂rx / ∂x

+∂rx / ∂y

-∂ry / ∂x

-∂ry / ∂y

+∂rz / ∂x

+∂rz / ∂y

-rz

-∂rx / ∂x

-∂rx / ∂y

-∂ry / ∂x

-∂ry / ∂y

-∂rz / ∂x

-∂rz / ∂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 sface and tface 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:

mux = |∂s/∂x| × wbase
mvx = |∂t/∂x| × hbase
mwx = |∂r/∂x| × dbase
muy = |∂s/∂y| × wbase
mvy = |∂t/∂y| × hbase
mwy = |∂r/∂y| × dbase

where:

∂t/∂x = ∂t/∂y = 0 (for 1D images)
∂r/∂x = ∂r/∂y = 0 (for 1D, 2D or Cube images)

and

wbase = image.w
hbase = image.h
dbase = image.d

(for the baseMipLevel, from the image descriptor).

The scale factors x, ρ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 ρx and ρy may be approximated with functions fx and fy, 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*}
max(|mux|, |mvx|, |mwx|) ≤ fx ≤ |mux| + |mvx| + |mwx|
max(|muy|, |mvy|, |mwy|) ≤ fy ≤ |muy| + |mvy| + |mwy|
[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 is no reason to have conditional application of wbase, hbase, dbase for rectangle textures either, since they do not support implicit derivatives.

The minimum and maximum scale factors minmax) are determined by:

ρmax = max(ρx, ρy)
ρmin = min(ρx, ρy)

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:

sampler.maxAniso = maxAnisotropy (from sampler descriptor)
limits.maxAniso = maxSamplerAnisotropy (from physical device limits)
maxAniso = min(sampler.maxAniso, limits.maxAniso)

If ρmax = ρ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 ρmax ≠ 0 and ρ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 maxAniso. However, anytime the footprint is small in texel space the implementation may use a smaller value of N, even when ρ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 λ 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, dhi, and dlo 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

q = levelCount - 1

levelCount is taken 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*}
δ = frac(λ)

is the fractional value used for linear filtering between levels.

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 or dhi and dlo) 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.