Chapter 3
Standard Operators and Functions

Operations are specified in Sh by applying operators and functions to instances of Sh types. Outside of a program definition, these operations execute immediately on values stored on the host. However, inside an ShProgram definition block enclosed by SH_BEGIN_PROGRAM and SH_END, the standard functions and operators instead build up a symbolic representation of the desired operations for later consideration by the compiler.

The built-in functions provided mirror and extend the support provided in other shading languages. Wherever possible we have tried to be consistent with the definitions of similar function libraries in other shading languages. Hopefully this will make it easier to port shaders back and forth to these languages.

The collection of functions described in this Chapter can be referred to as the “standard library”, but these functions are as much a part of the language as the operators are. In fact, the operators and the standard library functions are implemented in almost exactly the same way, except operators take advantage of the operator overloading functionality provided by C++.

You can define your own functions (and operators, and types) to extend the ones given here. However, whenever possible, use of the standard library functions is encouraged, since these functions have highly tuned implementations and have optimization support in the compiler.

For instance, repeated application of normalize is pointless, since normalization is idempotent. Therefore, if the compiler can prove that a tuple is already normalized, it can ignore any extra normalizations of that tuple. This particular optimization is very important, since normalization is expensive. You can take advantage of this optimization to improve the modularity and robustness of your code without sacrificing performance. In particular, you can always put in a normalization if you aren’t sure an input will be normalized. As long as the optimization works, the compiler will only actually perform the normalization if necessary.

All the functions and operators in the standard library can be compiled on hardware without branch support, unless specified otherwise. Certain functions, however, might use branching for some compilation targets if it is available and it is the best way to implement that function.

In general, the implementation of library functions and operators may vary by compilation target. An example is noise. The noise function might use a texture-based hash function on a fragment unit, a procedural hash on a vertex unit without texture support, and maybe a built-in instruction (hopefully) on future hardware. In fact, noise is special since it may not return the same value on different targets for the same inputs! But in most cases, for deterministic computations, we try to achieve numerical equivalence when using different implementations on different targets. The general goal of the compiler is to implement the desired computation in the most efficient and numerically stable way on each supported compilation target.

The rest of this chapter provides detailed descriptions of the library of operators and functions supported by Sh. The discussion is grouped into sections using a set of categories. Each section includes a table summarizing the operators and functions in alphabetical order. In these tables, we will often give mathematical definitions of the computation performed by a function. The mathematical definition refer to values labelled r, a, b, and c. The value r is the return value of the function being defined, whereas a, b and c refer to its arguments. In this chapter we give mathematical definitions, not implementations. The actual form of an implementation may differ significantly from its clearest mathematical definition to improve efficiency or numerical stability.

For each table entry the tuple size that may be used is indicated. Generally, each function returns the same type as was given to it. For example, adding two vectors results in another vector. Also, when a scalar (1-tuple) is used in an input that can also be used for an n-tuple, the scalar is usually promoted to a tuple by replication. For example, in s*a, with s a scalar and a an n-tuple, the value of s is replicated to form an n-tuple and then componentwise multiplication is performed, giving the expected meaning of scalar-vector multiplication. Scalar promotion is common but not universal: it depends on the particular operator or function involved. The tables indicate where scalar arguments are permitted.

Most functions work on all semantic types as noted, but some functions only work on particular semantic types, or have slightly different meanings on different semantic types. For instance, reflection of a vector by a normal is geometrically and mathematically different from reflection of a point by a plane. These execeptions are indicated in the respective sections.

Operators are also provided that act on ShProgram objects. These operators are used to manipulate ShProgram objects in various useful ways, and to apply programs to streams and tuples. These operators also permit the use of ShProgram objects as subroutines inside other ShProgram objects. However, since the implementation and use of these operators is substantially different from those of the basic operators on tuples and matrices, we defer their discussion to Chapter 5. Likewise, the access operators for textures will only be discussed in Chapter 4.


Operators










SymbolAssociativity ArityMeaning Reference





() postfix 1 swizzle/access/apply 3.1-48, 4-145, 5-153
[] postfix 1 access 3.1-48, 4-145





++ prefix/postfix1 increment 3.2-52
-- prefix/postfix1 decrement 3.2-52





~  prefix 1 complement 3.2-52
! prefix 1 clamped complement3.2-52





* LR 2 multiplication 3.2-52
/ LR 2 division 3.2-52
% LR 2 modulus 3.2-52





+ LR 2 addition 3.2-52
- LR 2 subtraction 3.2-52
- prefix 1 negation 3.2-52





<< LR 2 connect/apply 5-153
>> LR 2 extract 5-153





< LR 2 less than 3.9-121
<= LR 2 less than or equal 3.9-121
> LR 2 greater than 3.9-121
>= LR 2 greater than or equal3.9-121





== LR 2 equals 3.9-121
!= LR 2 not equals 3.9-121





& LR 2 combine 5-153





| LR 2 dot/matrix product 3.7-93, 3.8-107





^ LR 2 cross product 3.7-93





&& LR 2 and/min 3.9-121





|| LR 2 or/max 3.9-121






Table 3.1: Operator index. This table is arranged in order from highest to lowest precedence.

Since the parser for expressions in Sh is built upon C++ operator overloading, of course the available operators, their precedence, and their associativity is exactly the same as in C++. We do not describe these details here, only the new semantics of these operators that are in effect when at least one argument is a subclass of an Sh class type. However, for convenience we summarize the precedence and associativity of the operators used in Sh in Table 3.1.


Note: This manual is available as a bound book from AK Peters, including better formatting, in-depth examples, and about 200 pages not available on-line.