### OSLRosette

#### Introduction

This tutorial outlines the steps taken in developing a shader that creates a simple rosette pattern - figure 1. The OSL code is given in listing 3.

The technique is based on the observation that each triangular "spoke" of the rosette is, diagonally, half of a rectangle - figure 2.

It is far easier to test whether an 'st' location is within the half diagonal, shown in red, if it's enclosing rectangle is "moved" to the origin of the texture coordinate system. Put another way, it is always easier to test the interior of shape when it is in its cannonical position ie. at the origin of the ('st') coordinate system.

##### Step 1 - Diagonal

Figures 2 and 3 shows a 1x1 'st' texture space. Notice the micropolygons in the area outlined in blue share this relationship with respect to their 's' and 't' texture coordinates.

`s + t <= 1.0`

While the micropolygon in the area outlined in red share this relationship.

`s + t <= 0.4`

The relationship can also be expressed as,
`s/0.4 + t/0.4 <= 0.4/0.4`
or, more simply,
`s/0.4 + t/0.4 <= 1.0`
To enable an arbitrary diagonal to be created two scaling factors, s_scale and t_scale can be used.
`s/s_scale + t/t_scale <= 1.0`
This expression can be ncorported into a simple incandescent shader.

Listing 1 - diagonal.osl

 ```shader diagonal(float s_scale = 0.4, float t_scale = 0.2, float s = 0 [[int lockgeom = 0]], float t = 0 [[int lockgeom = 0]], output color resultRGB = 0) { resultRGB = color(0.8,0.8,0.8); if( s/s_scale + t/t_scale <= 1.0) resultRGB = color(1,0,0); }```

##### Step 2 - Triangle

To create a single triangle located at s = 0.6, t = 0.5 the 'st' coordinates are given corresponding offsets. Additionally, using the absolute value (abs()) of the offset 't' ensures the "mirrored" half of the offset triangle is also rendered.

Listing 2 - triangle.osl

 ```shader triangle(float s_scale = 0.3, float t_scale = 0.1, float s_offset = 0.6, float t_offset = 0.5, float s = 0 [[int lockgeom = 0]], float t = 0 [[int lockgeom = 0]], output color resultRGB = 0) { resultRGB = color(0.8,0.8,0.8); float ss = (s - s_offset)/s_scale; float tt = (t - t_offset)/t_scale; tt = abs(tt); if(s >= s_offset && ss + tt <= 1.0) resultRGB = color(1,0,0); }```

##### Step 3 - Radial Repititions

The final step in creating the rosette pattern is to rotate the current 'st' location in increments equal to the angle that separates the "spokes" of the pattern and for each rotated 'st' location perform the "triangle" test.

Listing 3 - rosette.osl

 ```shader rosette(float spoke_length = 0.15, float spoke_width = 0.05, float spoke_num = 12, float spoke_radius = 0.25, float s_center = 0.5, float t_center = 0.5, color bakcolor = color(0.4,0.4,1), color patcolor = color(1,0,0), float s = 0 [[int lockgeom = 0]], float t = 0 [[int lockgeom = 0]], output color resultRGB = 0) { float angle = radians(360/spoke_num); point centerPnt = point(s_center, t_center, 0); point axisPnt = point(s_center, t_center, 1); point stPnt = point(s, t, 0); point rotPnt; float n, rotS, rotT; float soffset = s_center + spoke_radius; float toffset = t_center; resultRGB = bakcolor; for(n = 0; n < spoke_num; n += 1) { rotPnt = rotate(stPnt, n * angle, centerPnt, axisPnt); rotS = rotPnt[0]; rotT = rotPnt[1]; float ss = (rotS - soffset)/spoke_length; float tt = abs(rotT - toffset)/spoke_width; if(rotS >= soffset && ss + tt <= 1) { resultRGB = patcolor; break; } } } ``` Using PxrFracal with the OSL node produces more "interesting" effects.