SL 1.0 language and VM spec

SL is probably best describe'd as a combination of Renderman's globally-aware shading language, and the easily hardware-implementable DirectX 8 pixel shading language. SL shaders are run inside of a virtual machine, and with the exception of two global-illumination knowledgeable operations, there is little to distinguish an SL VM from a restricted SIMD processor. There are a total of 38 registers available, of which 26 store vector data, and 12 store scalar data. All registers are 32-bit floatint point format. Below is a description of SL's interface with LRT, all the registers, available access modes, opcodes, and several shader examples.

I. Interfacing with LRT

SL takes a very straight-forward approach to interfacing with LRT: it replaces the BRDF and material classes. When defining a surface, instead of using one of LRT's built-in strings, you provide the name of a shader (sans .slc extension) located in your LRT_SHADER directory. Then, after an intersection with a shader-enabled surface, all the geometry-based registers (as listed below) are initialized to appropriate values. Any subsequent calls to the returned BRDF's fr() method will execute the shading program -- any variable values (e.g., incident light direction) will be modified prior to executing the program. This allows SL shaders to run with very few changes to the provided integrator classes. Currently, only the WhittedIntegrator class has been modified to be SL-aware; however, modifying the other integrators to support SL should be straight-forward.

I. SL Registers

Of the total registers in an SL machine, only 10 are available for read/write access (5 vector, 5 scalar). The other 28 registers are read-only, and initialized by LRT prior to executing your program. Below is a list of all registers (case-sensitive):

Initial Value
C0Primary ColorSurface color attribute vectorNo
C1Secondary ColorSpecular color attribute vectorNo
LLightObject-space light vectorvectorNo
VViewObject-space view direction (Wo)vectorNo
PwPointWorld-space intersection pointvectorNo
xyPointobject-space intersection pointvectorNo
ds*Surface derivativeObject-space derivative of surface with respect to S texture coordvectorNo
dt*Surface derivativeObject-space derivative of surface with respect to T texture coordvectorNo
du*Surface derivativeObject-space derivative of surface with respect to U parametervectorNo
dv*Surface derivativeObject-space derivative of surface with respect to V parametervectorNo
uvSurface parametersSurface parameters of intersection pointvectorNo
NSurface normalObject-space surface normal at intersection point vectorNo
dEIncident lightLight intensity (0 for shadowed sources) for this light directionvectorNo
t0 - t7Texture samplesTexture samples for current (s,t) vectorNo
r0 - r3General purposeSee belowvectorYes
v0Reflected color(0,0,0)vectorYes
s0 - s3General purposeSee belowscalarYes
v1Scalar return value0vectorYes
KdDiffuse reflectance coefficientSurface's Kd,or 0.5scalarNo
KsSpecular reflectance coefficientSurface's Ks,or 0.5scalarNo
SSpecular roughness8./roughness, or 80scalarNo
KrReflection coefficientSurface's Kr,or 0scalarNo
KtTransmission coefficientSurface's Kt,or 0scalarNo
KaAmbient reflectance coefficientSurface's Ka,or 0scalarNo
IIndex of refractionold index / new indexscalarNo
* ds, dt, du, and dv must be calculated in geometry's intersection routine, they are not generated in the shader

I.a. Initializing t0-t7, r0-r3, s0-s3

I have added new tokens to the LRT parser to enable initialization of the texture and general purpose SL registers. Now, when you create a surface with the Surface "<name>" "Kd" <float> ... attribute, the tokens "t0" through "t7", "r0" through "r3", and "s0" through "s3" are recognized.

II. Opcodes

There are 24 opcodes defined for SL virtual machines, which allow shaders to emulate a significant portion of the shaders supported by RenderMan's shading language, and a superset of the shaders supported by Microsoft's DirectX8 pixel shader language. Syntax is similar to MIPS assembly language, with each operation having (at most) 3 operands: 2 source, and 1 destination. These opcodes are converted into 64-bit bytecodes by the assembler, for use in LRT.

Operand 1
Operand 2
Operand 3
nop   Nothing
ret   Return immediately
rndscal  dst = random value
turbscalscal dst = 1-D noise
turbscalvec dst = 3-D noise
Data manipulation
movvecvec dest[x,y,z] = src[x,y,z]
movscalscal dest = src
movc *scalvec dest = src[*]
lc *vecscal dest[*] = src
liscalfloat dest = val
liv *vecfloat dest[*] = val
addvecvecvecdest = src0+src1
addscalscalscaldest = src0+src1
subvecvecvecdest = src0-src1
subscalscalscaldest = src0-src1
mulvecvecvecdest = cross(src0, src1)
mulvecvecscaldest = src0 * src1
mulscalscalscaldest = src0 * src1
dp3scalvecvecdest = dot(src0, src1)
divvecvecscaldest = src0 / src1
divscalscalscaldest = src0 / src1
Additional FP
clampscalscalscaldest = (dest<src0)?src0:
floorscalscal dest=floor(src)
ceilscalscal dest=ceil(src)
normvecvec dest = src / |src|
powscalscalscaldest = src0src1
expscalscal dest = esrc
jmpshort  Jump to rel. address**
bltscalscalshortJump to rel. address
if src0<src1**
tracevecvecvecdest receives value from
a ray traced from world-space
point src0, object-space
direction src1
lookupvectNvecdest receives texels from
texture N, coords src1
* Opcode followed by x, y, z, or w accessor (or, r, g, b, or a)
** Address in instructions to skip, IP pre-incremented

III. Shader Files

Shader source files (.sl) begin with an identification mnemonic (sl 1.0), followed by 1 or more instructions, 1 per line. Comments can be included in source files by putting a hash ('#') character at the start of a comment line. Below are some extremely simple example shaders.
sl 1.0
# - super simple shader. returns the un-attenuated
# surface color
mov v0, C0
sl 1.0
# - assumes all lights intensities to be (1, 1, 1)
dp3 s0, N, L
mul v0, C0, s0
sl 1.0
# - performs dE * Color * (Dot(N,H)^s*Ks + Dot(N,L)*Kd)
dp3 s0, N, L
mul s0, s0, Kd
add r0, V, L
norm r0, r0
dp3 s1, r0, N
pow s1, s1, S
mul s1, s1, Ks
add s0, s0, s1
mul r0, C0, s0
#now perform component-wise multiply
movc r s0, r0
movc r s1, dE
mul s0, s0, s1
lc r r0, s0
movc g s0, r0
movc g s1, dE
mul s0, s0, s1
lc g r0, s0
movc b s0, r0
movc r s1, dE
mul s0, s0, s1
lc b r0, s0
#and return the result
mov v0, r0
sl 1.0
# - simple diffuse bump mapping example
# first, rotate light into point's tangent space
dp3 s3, L, ds
dp3 s2, L, dt
dp3 s1, L, N
lc x r1, s3
lc y r1, s2
lc z r1, s1
norm r1, r1
# and dot this by the normal contained in the bump-map
dp3 s0, r1, t1
# and use this value to modulate a color provided by another texture
mul v0, t0, s0

IV. Notes

The provided assembler for the shading language is extremely particular about whitespaces. Any number of lines may be skipped; however, in between a component operator (i.e., lc, movc, liv) and the component specifier (i.e., x, y, z, w), there must be only one space. Also, a space must be placed between all operands (so "mul x,y,z" is not legal, but "mul x, y, z" is).