Steps for creating a new Visual Studio project based on existing code files:
Create an empty project folder and name it your intended project name.
Inside the new project folder, create a folder for your source code files. * I call is “Source”, not sure if it has to named that way..
Copy your existing code files to the source folder.
Launch Visual Studio, and open it without code:
Select: File > New > Project From Existing Code.. To open the Create New Project From Existing Code Files wizard:
Note: The documentation on this operation states that the wizard will copy files by itself, my own experience is that it doesn’t, it just links them to the project, that’s why I copy the source files prior to this step. In the Create New Project From Existing Code Files wizard, 1. Set the path to your project folder. 2. Specify a project name. * I set this to be the same name as the project folder name, not sure if otherwise it will create a sub-folder.. 3. This is set to the same folder as the project folder. * It’s possible that I don’t understand this correctly.. but I think theoretically, the intention is that you would add external folders to this list, from which source files would be copied, but like I said, when I tried that the files where not copied to the project.
Set your new projects settings and press Finish to create the new project:
The following is an introduction to basic OSL shader syntax using a simple color blending shader example. a more general explanation of the subject can be read here.
Notes: > It’s highly recommended to get acquainted with basic C language syntax, since it’s the basis for common shading languages like OSL, HLSL and GLSL. > More detailed information about writing OSL shaders can be found in the osl-languagespec PDF document from ImageWorks’s OSL GitHub.
This example shader blends 2 color sources according to the surface viewing angle (aka “facing ratio” or “incident angle” or “Perpendicular-Parallel”). the user can choose a facing (“front”) color or texture, a side color or texture, and the shader’s output bell be a mix of the these 2 inputs that depends on the angle the surface is viewed at.
[[ string help = "Blend colors by view angle" ]]
color facing_color = color(0, 0, 0)
[[ string help = "The color (or texture) that will appear at perpendicular view angle" ]],
color side_color = color(1, 1, 1)
[[ string help = "The color (or texture) that will appear at grazing view angle" ]],
float base_blend = 0.0
[[ string help = "The percent of side_color that is mixed with facing_color at perpendicular view angle",
float min = 0.0, float max = 1.0]],
float curve_exponent = 1.0
[[ string help = "A power exponent value by which the blend value is raised to control the blend curve",
float min = 0.001, float max = 10.0]],
output color color_out = color(1, 1, 1))
// calculate the linear facing ratio:
float facing_ratio = acos(abs(dot(-I,N))) / M_PI_2;
// calculate the curve facing ratio:
float final_blend_ratio = pow(facing_ratio , curve_exponent);
// blend the facing color:
color final_facing_color = (facing_color * (1 - base_blend)) + (side_color * base_blend);
// blend and output the final color:
color_out = ((final_facing_color * (1 - final_blend_ratio)) + (side_color * final_blend_ratio));
The first statment:
The #include statement is a standard C compiler directive to link the OSL source code library code file stdosl.h to the shader’s code, so that the OSL data types and functions in the code will be recognized.
* Some systems compile the code successfully without this statement. I’m not sure if their compiler links stdosl.h automatically or not.
The double-square bracketed statements provide both help annotations and value range limits for the shader parameters:
[[ string help = "The percent of side_color that is mixed with facing_color at perpendicular view angle", float min = 0.0, float max = 1.0]]
Note that these statements are appended to single parameters in the shader right before the comma character that ends the parameter statement.
* Not all shading systems that supprt OSL also implement the annotations in the shader’s interface generated by the host software (the shader will work, but it’s parameters wont be described and limited to the defined value range).
Removing the #include statement, annotations and comments,
We can see that the OSL shader structure is very similar to a C function:
First the data type, in this case shader, followed by the shader identifier, in this case “cglColorAngleBlend”:
After the shader’s type and identifier, a list of parameters is defined within parentheses, separated by comma’s. these parameters define the shader’s input’s, outputs, and default values. Output parameters are preceded by the outputmodifier.
color facing_color = color(0, 0, 0),
color side_color = color(1, 1, 1),
float base_blend = 0.0,
float curve_exponent = 1.0,
output color color_out = color(1, 1, 1)
In this case the shader has 4 user input parameters, and 1 output parameter.
2 colortype parameters, “facing_color” and “side_color” for the facing and side color that will be blended together, a float*parameter “base_blend” that specifies how much of the side color will be mixed with the facing color regardless of view angle, and a second floatparameter “curve_exponent” specifying a power exponent by which the blend value will be raised to create a non-linear blend curve.
The outputparameter “color_out” is a colorthat will calculated by the shader.
* Note that even though the the output parameter will be calculated by the shader, it is required to define a default value for it for the shader to compile.
After the shader parameters, enclosed within curly braces is the actual body of the shader code, containing the instructions, each ending by a semicolon ; character:
calculates incident angle, i.e. angle between 2 vectors, originating at the surface shading point, one pointing towards the origin of the incoming ray, and the other the surface normal, as a factor of 0 to 1 representing 0 – 90 degrees.
These 2 vector are easily obtained through the built in OSL global variables N and I. N is the surface normal at the shading point, and I is the incoming ray vector pointing to the shading point which is inverted in this case to point backwards by typing a minus before it: -I.
The incident angle is calculated in radians as the arc-cosine of the dot-product of N and -I and then divided by half a π to convert it to a linear factor of 0 to 1 representing 0 to 90 degrees in radians, M_PI_2 being a convenient half π constant.
* M_PI being a full π, M_2PI being 2π representing 180 degrees in radians and 360 degrees respectively (OSL provides there are more constants in this series).
The second instruction raises the facing ratio that was calculated in the previous instruction by a power value provided by the curve_exponent input parameter, to create a non linear angle/color blend in values other than 1.0.
The resulting modified blend value is stored in a new internal variable final_blend_ratio:
But I decided to keep it separated into 2 variable and 2 instructions for clarity.
* try modifying the code as an exercise
The third instruction modifies the input color facing_color by premixing it with the input side_color according to the percent give by the input parameter base_blend and assigns the resulting color to a new internal variable named final_facing_color:
Calculates a linear combination** (linear interpolation) between the 2 input colors using the base_blend as a 0 – 1 factor between them.
* Note that OSL allows to define arithmatic operations freely between colors and floats.
The forth and final instruction creates the final mix between the modified facing color stored in final_facing_color variable and the side color given by the input color parameter side_color, by again, calculating a linear combination between the 2 colors, this time using final_blend_ratio variable value we calculated previously as the combination factor, and very importantly, finally, assigning the mixed result to the shader output parameter color_out so it will be the final output of the shader:
This screen capture shows this shader at work in Blender and Cycles, connected to a Principled BSDF shader as it’s base color source:
Thats it! 🙂
Hope you find this article informative and useful.
* A “float” data type is simply the the computer-science geeky way of saying “accurate non-integer number”. when we have to store numbers that can describe geometry and color, we need a data type that isn’t limited to integers so for that purpose we use float values. there’s actually a lot more to the float formal definition in computer science, but for our purpose here this will suffice.
** A Linear Combination, or Linear interpolation (lerp) is one of the most useful numerical operations in 3D geometry and color processing (vector math): A * ( 1 – t ) + B * t
A and B being your source and target locations or colors or any other value you need to interpolate and t being the blending factor from 0 – 1.