User Guide Cancel

Pixel Processor Ray tracer | Substance 3D Automation ToolKit

  1. Substance 3D home
  2. Home
  3. Command Line Tools
    1. Command Line overview
    2. sbsbaker
      1. sbsbaker overview
      2. sbsbaker command line options
      3. sbsbaker Example Command Lines
    3. sbscooker
      1. sbscooker overview
      2. sbscooker command line options
      3. sbscooker pattern variables
    4. sbsmtools
      1. sbsmtools overview
      2. sbsmtools command line options
    5. sbsmutator
      1. sbsmutator overview
      2. sbsmutator command line options
      3. sbsmutator Example Command Lines
    6. sbsrender
      1. sbsrender overview
      2. sbsrender base parameters and pattern variables
      3. sbsrender command line options
      4. sbsrender example command lines
    7. sbsupdater
      1. sbsupdater overview
      2. sbsupdater command line options
  4. Pysbs - Python API
    1. Pysbs - Python API overview
    2. Getting started
    3. General topics
      1. Basic manipulation
      2. Substance creation
      3. Substances modification
      4. Dependencies management
      5. PySbs batchtools module
      6. metadata manipulation
      7. SAT demos
      8. Edit sbsar with SBSARManager
      9. Spot Colors
      10. Thumbnail creation with SAT
    4. Examples
      1. demohelloworld
      2. demos
      3. demos_batchtools
      4. script_update_with_sbsupdater
    5. API Content
      1. API Content overview
      2. Substance definitions
        1. Common interfaces
          1. basegraph
          2. package
          3. sbsarobject
          4. sbsobject
        2. compnode
          1. compnode overview
          2. common
          3. compimplementation
          4. paramgraph
        3. context projectmgr
        4. graph
          1. graph overview
          2. function
          3. inputparameters
          4. output
        5. mdl
          1. mdlannotation
          2. mdlcommon
          3. mdldictionaries
          4. mdlenum
          5. mdlgraph
          6. mdllibclasses
          7. mdlmanager
          8. mdlnode
          9. mdlnodeimpl
          10. mdloperand
          11. mdlsbsbridge
        6. modelgraphindex
          1. modelannotationnames
          2. modelgraph
          3. modelgraphgenerator
          4. modelgraphimplementation
          5. modelnodenames
          6. modeloperand
          7. modulegraphindex
          8. moduleannotation
          9. moduleconnection
          10. modulegraph
          11. modulegraphgenerator
          12. modulegraphimplementation
          13. modulegraphlibrary
          14. modulegraphregister
          15. modulenode
          16. modulenodeimplementation
          17. modulenodeinstance
          18. moduleoperand
          19. moduleoutputbridging
          20. moduleparaminput
        7. params
          1. params overview
          2. dynamicvalue
          3. paramnode
        8. projectmgrdoc
        9. sbsarchive
          1. sbsarchive overview
          2. sbsarenum
          3. sbsargraph
          4. sbsargui
          5. sbsarguiwidgets
          6. sbsarmanager
        10. sbscommon
          1. connections
          2. gui
          3. nodes
          4. values
        11. sbspreset
        12. sbsproject
        13. substance
          1. substance overview
          2. content
          3. resource
      3. Libraries
        1. sbsenum
        2. sbslibrary
          1. sbslibrary overview
          2. sbsdictionaries
          3. sbsfilters
          4. sbsfunctions
          5. sbsfxmapnodes
          6. sbslibclasses
          7. sbswidgets
        3. sbsbakerslibrary
          1. sbsbakerslibrary overview
          2. sbsbakersdef
          3. sbsbakersdefaultprops
          4. sbsbakersdictionaries
          5. sbsbakersenum
          6. sbsbakingconverter
          7. sbsbakingconverterparam
          8. sbsbakingparameters
          9. sbsdialogstate
          10. sbsscenedata
        4. Helpers
          1. sbscleaner
          2. sbsexporter
          3. sbsgenerator
          4. sbsparser
          5. sbswriter
          6. qtclasses
            1. qtclasses overview
            2. qtvariantreader
            3. qtvariantwriter
          7. psdparser
          8. sbsimpactmanager
          9. batchtools
          10. autograph
            1. ag_functions
            2. ag_layout
            3. ag_types
          11. info_mesh_parser
          12. sbsbaker_info_handlers
          13. sbsrender_render_handlers
          14. output_handlers
          15. spotcolorinfo_handler
          16. thumbnail
          17. batchtools overview
        5. Execution context
          1. context
          2. functions
        6. API Change log
  5. Samples
    1. Samples overview
    2. Texturing Template Demo
    3. Batch Tools Demo
    4. Variations
    5. Texture Mat
    6. Pixel Processor Ray tracer
  6. Setup and Getting Started
    1. Setup and Getting Started overview
    2. Compatibility
    3. Frequently asked Questions
    4. Known issues
    5. SAT Cookbook
    6. Use Pysbs in different python interpreter (maya, sd, blender...)
  7. Integrations
    1. Substance Maya toolset
      1. Substance Maya Toolset overview
      2. Installing
      3. Launching
      4. Baking
        1. Baking overview
        2. Export parameters
        3. Baker parameters
        4. Mesh setup
        5. Using a template
      5. Changelog
  8. Changelog overview

Pixel Processor Ray tracer

Overview

The pixel processor ray tracer uses the Python API and the sbsMath library to generate a complex pixel processor node that ray traces a sphere

  • raytracer.py Script that creates the pixel processor functions required for the sample and creates a network so it can be visualized.
  • sbsmath.py A python module introducing python types for generating pixel processors, functions and dynamic values in a convenient way.
  • sbsmath_tools.py A set of higher level operations to make it more convenient to work with sbsmath.py

The output will be put in ray tracer directory in the samples directory and consists of a new sbs file with a pixel processor ray tracing a sphere

In order to run the demo, make sure python is installed and in your path environment and that the substance python api is installed. Then go to the samples directory and run:

python raytracer.py

It will generate a substance file containing a node that ray traces a sphere and lights it.

Details

Ray tracing example here is not chosen as a good example of how to use pixel processor nodes, it's mainly an example of computations that would be hard to create manually in a pixel processor and how using the autograph library it gets easier to understand and deal with.

The fundamental process is described here. Briefly what happens is:

  • A camera ray is generated for each pixel.
  • The ray is intersected with a sphere
  • The intersection point is turned into a uv coordinate
  • Textures for roughness and diffuse are sampled
  • The point is being lit by a directional light source

Also note how there are some oddities in how samples missing the sphere are dealt with. They are still being lit but the result is discarded in the end which is a consequence of the programming model used in pixel processors.

The bulk of this demo is in the sbsmath.py where a set of new python types with overloaded operators for arithmetic are introduced. In order to understand it better, let's take a look at a snippet of code for generating a function in the substance document.

def sphere_uv(fn):    """    Substance function for generating a uv coordinate from a sphere normal and a tiling factor    :param fn: The function context to create the function in    :type fn: FunctionContext    :return: function, a function to call to instantiate the function     """    normal = fn.input_parameter('normal', widget_type=sbsenum.WidgetEnum.COLOR_FLOAT3)    tiling = fn.input_parameter('tiling', widget_type=sbsenum.WidgetEnum.COLOR_FLOAT1)    fn_pi = fn.import_external_function('sbs://functions.sbs/Functions/Math/Pi')    pi = fn_pi()    two_pi = pi * 2.0    u = (fn.atan2(fn.expand(normal[0], normal[2])) + pi) / two_pi    v = fn.atan2(fn.expand(1.0, normal[1])) / pi    uv = fn.expand(u * tiling, v * tiling)    return fn.generate(uv)

		
	





This code generates a node that computes uv's for a position on a sphere using the normal of the sphere as input together with a tiling parameter to set how many times it should wrap along the u and v direction.

  • The fn parameter represents a function context which is a helper object for code generation. You never need to create these function contexts yourself, it will be handled behind the scene by the library. 
  • The calls to fn.input_parameter generates the inputs for the function. 
  • The call to fn.import_external_function imports a function (in this case a constant) from the substance library of functions.
  • The calls to sm.atan2 etc calls a built in function.
  • The use of operators such as +, - * are overloaded to generate nodes for addition, subtraction, multiplication etc.
  • The call to fn.generate takes the output node for the function as input. Any function created in the system needs a signature like this.

This function itself doesn't do anything useful, in order to create it in a substance document it needs to be passed into st.generate_function like below:

st.generate_function(sphere_uv, doc, name='sphere_uv')

This will generate a function that can now be imported in other functions to be called. The first parameter is the function to be processed, second is the sbs_document it should live in and the name one gives a name to the function so it can be found when called from other functions.

The output of this function will look like this in Substance Designer. 

 As you can see, the normal and the tiling inputs are created, together with a set of math nodes and finally going into an output node representing the value.

There are four other similar functions in sample

  • phong_lighting
  • ambient_lighting
  • intersect_sphere
  • render_sphere

These are similar to the sphere_uv function. An interesting part of the render_sphere function looks like this:

fn_sphere_uv = fn.import_local_function('sphere_uv')

In this section the functions defined earlier are imported so they can be called directly in the graph like this:

uv = fn_sphere_uv(i_normal, tiling)

In order to actually make function into a pixel processor node there is a final function called raytracer_pixel_processor. This is not fundamentally different from the other functions but it get created in a slightly different way:

pp = pp_node.getPixProcFunction()
st.generate_function(raytracer_pixel_processor, doc, fn_node=pp)

This generate_function call is almost identical to the previous one but instead of passing in a name parameter we pass in an fn_node parameter. The fn_node is essentially some type of graph network for functions, in our case it comes out of a pixel processor. This means that this function will not be a free function that can be accessed but it will be put inside of the pixel processor node.

There is also code for creating a simple network and saving it out to disk in raytracer.py

The final result looks like this in Substance Designer

All the functions defined are now available in the document and the pixel processor renders out the resulting image.

Another thing covered is how a lot of input parameters are exposed on the top level graph:

graph.addInputParameter('sphere_radius', aWidget=sbsenum.WidgetEnum.SLIDER_FLOAT1, aDefaultValue=30.0)graph.addInputParameter('sphere_origin', aWidget=sbsenum.WidgetEnum.SLIDER_FLOAT3, aDefaultValue=[0.0, 0.0, 130.0])graph.addInputParameter('light_direction', aWidget=sbsenum.WidgetEnum.SLIDER_FLOAT3, aDefaultValue=[1.0, -1.0, -1.0])graph.addInputParameter('light_color', aWidget=sbsenum.WidgetEnum.COLOR_FLOAT4, aDefaultValue=[1.0, 1.0, .7, 0])graph.addInputParameter('ambient_color', aWidget=sbsenum.WidgetEnum.COLOR_FLOAT4, aDefaultValue=[0.3, 0.3, .6, 0])graph.addInputParameter('background_color', aWidget=sbsenum.WidgetEnum.COLOR_FLOAT4, aDefaultValue=[0.3, 0.3, .6, 1.0])graph.addInputParameter('tiling_scale', aWidget=sbsenum.WidgetEnum.SLIDER_FLOAT1, aDefaultValue=10.0)

In the raytracer_pixel_processor function we import references to these so we can control the raytracer using them:

sO = fn.variable('sphere_origin', widget_type=sbsenum.WidgetEnum.SLIDER_FLOAT3)sR = fn.variable('sphere_radius', widget_type=sbsenum.WidgetEnum.COLOR_FLOAT1)light_dir = fn.variable('light_direction', widget_type=sbsenum.WidgetEnum.SLIDER_FLOAT3)light_color = fn.variable('light_color', widget_type=sbsenum.WidgetEnum.COLOR_FLOAT4)ambient_color = fn.variable('ambient_color', widget_type=sbsenum.WidgetEnum.COLOR_FLOAT4)background_color = fn.variable('background_color', widget_type=sbsenum.WidgetEnum.COLOR_FLOAT4)tiling = fn.variable('tiling_scale', widget_type=sbsenum.WidgetEnum.COLOR_FLOAT1)

If editing the pixel processor function it will look like this:

This allows us to tweak parameters in the raytracer from the substance designer gui:

Get help faster and easier

New user?