Panda3D Manual: Shader Basics
<<prev | top | next>> |
## Writing Panda3D Shaders Currently, Panda3D only supports the `Cg` shading language. This section assumes that you have a working knowledge of the Cg shader language. If not, it would be wise to read about Cg before trying to understand how Cg fits into Panda3D. To write a shader, you must create a shader program that looks much like the one shown below. This example preserves position but switches the red and green channels of everything it is applied to:
    //Cg

    void vshader(float4 vtx_position : POSITION,
      float4 vtx_color: COLOR,
      out float4 l_position : POSITION,
      out float4 l_color0 : COLOR0,
      uniform float4x4 mat_modelproj)
    {
      l_position=mul(mat_modelproj, vtx_position);
      l_color0=vtx_color;
    }

    void fshader(
      float4 l_color0 : COLOR0,
      out float4 o_color : COLOR)
    {
      o_color=float4(l_color0[1],l_color0[0], l_color0[2],l_color0[3]);
    }
The first line of a Cg shader needs to be //Cg. Do not put a space between the two slashes and the word "Cg". In the future, we may support other shader languages, in which case, those shader languages will have their own header identifiers. The shader must contain the two subroutines named `vshader` and `fshader`, the vertex shader and fragment shader. In addition, it may contain additional routines named `vshader1`, `fshader1`, `vshader2`, `fshader2`, and so forth. These latter pairs of subroutines represent fallback codepaths, to be used when the video card doesn't support the first pair. If none of the pairs is supported, the shader is disabled and has no effect (ie, rendering proceeds normally using the standard pipeline). In the following code sample, a shader is loaded and applied to a model:
    myShader = Shader.load("myshader.sha")
    myModel.setShader(myShader)
In the first line, the shader is loaded. The object returned is of class `Shader`. The call to `setShader` causes myModel to be rendered with that shader. Shaders propagate down the scene graph: the node and everything beneath it will use the shader. ## The Shader can Fetch Data from the Panda Runtime Each shader program contains a parameter list. Panda3D scans the parameter list and interprets each parameter name as a request to extract data from the panda runtime. For example, if the shader contains a parameter declaration `float3 vtx_position : POSITION`, Panda3D will interpret that as a request for the vertex position, and it will satisfy the request. Panda3D will only allow parameter declarations that it recognizes and understands. Panda3D will generate an error if the parameter qualifiers do not match what Panda3D is expecting. For example, if you declare the parameter `float3 vtx_position`, then Panda3D will be happy. If, on the other hand, you were to declare `uniform shader2d vtx_position`, then Panda3D would generate two separate errors: Panda3D knows that vtx_position is supposed to be a float-vector, not a texture, that it is supposed to be varying, not uniform. Again, all parameter names must be recognized. There is a list of possible shader inputs that shows all the valid parameter names, and the data that Panda3D will supply. ## Supplying data to the Shader Manually Most of the data that the shader could want can be fetched from the panda runtime system by using the appropriate parameter names. However, it is sometimes necessary to supply some user-provided data to the shader. For this, you need `setShaderInput`. Here is an example:
    myModel.setShaderInput("tint", Vec4(1.0, 0.5, 0.5, 1.0))
The method `setShaderInput` stores data that can be accessed by the shader. It is possible to store data of type Texture, NodePath, and Vec4. The `setShaderInput` method also accepts separate floating point numbers, which it combines into a Vec4. The data that you store using `setShaderInput` isn't necessarily used by the shader. Instead, the values are stored in the node, but unless the shader explicitly asks for them, they will sit unused. So the `setShaderInput("tint", Vec4(1.0, 0.5, 0.5, 1.0))` above simply stores the vector, it is up to the shader whether or not it is interested in a data item labeled "tint." To fetch data that was supplied using `setShaderInput`, the shader must use the appropriate parameter name. See the list of possible shader inputs, many of which refer to the data that was stored using `setShaderInput`. Shader Inputs propagate down the scene graph, and accumulate as they go. For example, if you store `setShaderInput("x",1)` on a node, and `setShaderInput("y",2)` on its child, then the child will contain both values. If you store `setShaderInput("z",1)` on a node, and `setShaderInput("z",2)` on its child, then the latter will override the former. The method `setShaderInput` accepts a third parameter, priority, which defaults to zero. If you store `setShaderInput("w",1,1000)` on a node, and `setShaderInput("w",2,500)` on the child, then the child will contain ("w"==1), because the priority 1000 overrides the priority 500. ## Shader Render Attributes The functions `nodePath.setShader` and `nodePath.setShaderInput` are used to apply a shader to a node in the scene graph. Internally, these functions manipulate a render attribute of class `ShaderAttrib` on the node. In rare occasions, it is necessary to manipulate ShaderAttrib objects explicitly. The code below shows how to create a ShaderAttrib and apply it to a camera, as an example.
    myShaderAttrib = ShaderAttrib.make()
    myShaderAttrib = myShaderAttrib.setShader(Shader.load("myshader.sha"))
    myShaderAttrib = myShaderAttrib.setShaderInput("tint", Vec4(1.0,0.5,0.5,1.0))
    base.cam.node().setInitialState(render.getState().addAttrib(myShaderAttrib))
Be careful: attribs are immutable objects. So when you apply a function like `setShader` or `setShaderInput` to a ShaderAttrib, you aren't modifying the attrib. Instead, these functions work by returning a new attrib (which contains the modified data). ## Deferred Shader Compilation When you create an object of class shader, it compiles the shader, checking for syntax errors. But it does not check whether or not your video card is powerful enough to handle the shader. Panda3D only does that later on, when you try to render something with the shader. In the unusual event that your computer contains multiple video cards, the shader may be compiled more than once. It is possible that the compilation could succeed for one video card, and fail for the other.
<<prev | top | next>> |