Panda3D Manual: Using Cg Shaders
  <<prev top next>>  


Writing Panda3D Shaders

The shader support has been completely overhauled in Panda3D 1.1.0.

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 this:

//Cg

void vshader(float4 vtx_position : POSITION,
  float2 vtx_texcoord0 : TEXCOORD0,
  out float4 out_position : POSITION,
  out float2 out_texcoord0 : TEXCOORD0,
  uniform float4x4 mat_modelproj)
{
  out_position=mul(mat_modelproj, vtx_position);
  out_texcoord0=vtx_texcoord0;
}

void fshader(float2 vtx_texcoord0 : TEXCOORD0,
  sampler2D arg_tex : TEXUNIT0,
  out float4 out_color : COLOR)
{
  out_color=tex2D(arg_tex, vtx_texcoord0);
}

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).

Panda uses the parameter names to determine what data to pass into the shader. For example, Panda3D will recognize the parameter name vtx_tangent0, and it knows that it is supposed to pass the vertex tangent into this parameter. As another example, Panda3D will recognize the parameter name trans_model_to_world, and it will pass in a matrix that transforms from model-space into world-space. There are hundreds of available data items that can be passed into a shader, the complete list is at the end of this section.

Supplying Shader Input Data

In the following code sample, a shader is loaded and applied to a model:

myShader = Shader.load("myshader.sha")
myModel.setShader(myShader)
myModel.setShaderInput("tint", Vec4(1.0, 0.5, 0.5, 1.0))

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 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." Later, we will discuss how a shader can pull data from the node.

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"==2), because the priority 1000 overrides the priority 500.

Deferred Shader Compilation

When you create an object of class shader, you are just storing the shader's body. You are not (yet) compiling the shader. The actual act of compilation takes place during the rendering process.

Therefore, if the shader contains a syntax error, or if the shader is not supported by your video card, then you will not see any error messages until 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.

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))

Passing data from the Panda3D runtime into Cg

Each Cg main program contains a parameter list. Panda3D scans the parameter list and interprets each parameter name as a request for data. For example, if the shader contains a parameter declaration float3 vtx_position : POSITION, Panda3D will arrange to pass the vertex position into the shader. Panda3D will only allow parameter declarations that it recognizes and understands.

Panda3D will generate an error if the Cg parameter qualifiers do not match what Panda3D is expecting. For example, if you declare the parameter float3 vtx_position: POSITION, then Panda3D will be happy. If, on the other hand, you were to declare uniform shader2d vtx_position: NORMAL, then Panda3D would generate three 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, and that the correct semantic string is POSITION, not NORMAL.

The following is a list of the valid parameter declarations, and the data that Panda3D will supply:

</table>

uniform sampler2D tex_0The model's first texture. This requires that the model be textured in the normal manner. You may also use tex_1, tex_2, and so forth, if the model is multitextured. If the model uses a 3D texture or a cubemap, you may also specify sampler3D or samplerCUBE.
uniform sampler2D tex_0_suffixObtain a texture by concatenating a hyphen and the suffix to the filename of the model's first texture. For example, if tex_0 is "woman.jpg", then tex_0_normalmap is "woman-normalmap.jpg", and tex_0_specular is "woman-specular.jpg". You may also use tex_1_suffix, tex_2_suffix, and so forth, if the model is multitextured. If the model uses a 3D texture or a cubemap, you may also specify sampler3D or samplerCUBE.
float3 vtx_position: POSITIONVertex Position. Vertex shader only. You may also use float4, in which case (w==1).
float3 vtx_normal: NORMALVertex Normal. Vertex shader only.
float2 vtx_texcoord0: TEXCOORD0Texture coordinate associated with the model's first texture. This requires that the model be textured in the normal manner. You may also use vtx_texcoord1, vtx_texcoord2, and so forth if the model is multitextured. Vertex shader only.
float3 vtx_tangent0Tangent vector associated with the model's first texture. This can only be used if the model has been textured in the normal manner, and if binormals have been precomputed. You may also use vtx_tangent1, vtx_tangent2, and so forth if the model is multitextured. Vertex shader only.
float3 vtx_binormal0Binormal vector associated with vtx_texcoord0. This can only be used if the model has been textured in the normal manner, and if binormals have been precomputed. You can also use vtx_binormal1, vtx_binormal2, and so forth if the model has been multitextured. Vertex shader only.
floatX vtx_anythingPanda makes it possible to store arbitrary data in the vertex array. You can access this data using this syntax. For example, vtx_chicken will look for a column named "chicken" in the vertex array. Vertex shader only.
uniform float4x4 trans_x_to_yA matrix that transforms from coordinate system X to coordinate system Y. The words X and Y can be "camera", "world", "model", "view", or the name of a nodepath stored with setShaderInput. For example, trans_camera_to_model is a matrix that transforms from camera-space to model-space. Similarly, trans_light1_to_camera is a matrix that transforms from the local space of light1 to camera-space. In order for the latter to work, you would first have to make light1 accessible to the shader using setShaderInput("light1", light1nodepath).

The four keywords are as follows. "world" means panda's global coordinate system. "model" means whatever coordinate system the vertex arrays are in. "camera" means the coordinate system of the panda camera node. "view" is identical to "camera" except that the coordinate system may be reversed to match the handedness of the rendering API. The matrix trans_model_to_view is equal to the modelview matrix.

uniform float4x4 tpose_x_to_yTranspose of trans_x_to_y
uniform float4 row0_x_to_yRow 0 of trans_x_to_y.
uniform float4 row1_x_to_yRow 1 of trans_x_to_y.
uniform float4 row2_x_to_yRow 2 of trans_x_to_y.
uniform float4 row3_x_to_yRow 3 of trans_x_to_y.
uniform float4 col0_x_to_yCol 0 of trans_x_to_y.
uniform float4 col1_x_to_yCol 1 of trans_x_to_y.
uniform float4 col2_x_to_yCol 2 of trans_x_to_y.
uniform float4 col3_x_to_yCol 3 of trans_x_to_y.
uniform float4x4 mstrans_xModel-Space Transform of X, aka trans_x_to_model
uniform float4x4 cstrans_xCamera-Space Transform of X, aka trans_x_to_camera
uniform float4x4 wstrans_xWorld-Space Transform of X, aka trans_x_to_world
uniform float4 mspos_xModel-Space Position of X, aka row3_x_to_model
uniform float4 cspos_xCamera-Space Position of X, aka row3_x_to_camera
uniform float4 wspos_xWorld-Space Position of X, aka row3_x_to_world
uniform float4x4 mat_modelviewModelview Matrix
uniform float4x4 inv_modelviewInverse Modelview Matrix
uniform float4x4 tps_modelviewTransposed Modelview Matrix
uniform float4x4 itp_modelviewInverse Transposed Modelview Matrix
uniform float4x4 mat_projectionProjection Matrix
uniform float4x4 inv_projectionInverse Projection Matrix
uniform float4x4 tps_projectionTransposed Projection Matrix
uniform float4x4 itp_projectionInverse Transposed Projection Matrix
uniform float4x4 mat_modelprojComposed Modelview/Projection Matrix
uniform float4x4 inv_modelprojInverse ModelProj Matrix
uniform float4x4 tps_modelprojTransposed ModelProj Matrix
uniform float4x4 itp_modelprojInverse Transposed ModelProj Matrix
uniform float4 k_anythingA constant vector that was stored using setShaderInput. Parameter k_anything would match data supplied by the call setShaderInput("anything", Vec4(x,y,z,w))
uniform sampler2d k_anythingA constant texture that was stored using setShaderInput. Parameter k_anything would match data supplied by the call setShaderInput("anything", myTex)
uniform float4x4 k_anythingA constant matrix that was stored using setShaderInput. Parameter k_anything would match data supplied by the call setShaderInput("anything", myNodePath). The matrix supplied is the nodepath's local transform.
uniform float2 sys_cardcenterTexture coordinates of center of this window's texture card. To generate texture coords for this window's texture card, use (clipx,clipy) * cardcenter + cardcenter.
floatX l_position: POSITIONLinearly interpolated Position, as supplied by the vertex shader to the fragment shader. Declare "out" in the vertex shader, "in" in the fragment shader.
floatX l_color0: COLOR0Linearly interpolated Primary color, as supplied by the vertex shader to the fragment shader. Declare "out" in the vertex shader, "in" in the fragment shader.
floatX l_color1: COLOR1Linearly interpolated Secondary color, as supplied by the vertex shader to the fragment shader. Declare "out" in the vertex shader, "in" in the fragment shader.
floatX l_texcoord0: TEXCOORD0Linearly interpolated Texture Coordinate 0, as supplied by the vertex shader to the fragment shader. You may also use l_texcoord1, l_texcoord2, and so forth. Declare "out" in the vertex shader, "in" in the fragment shader.
out floatX o_color: COLOROutput Color, as supplied by the fragment shader to the blending units. Fragment shader only.
  <<prev top next>>