Deprecated
NVIDIA's Cg 1.2 release introduced interfaces to the Cg programming language, which correspond to mental ray's shader graph and Phenomenon design, and enable mental ray to construct a composite Cg shader that corresponds exactly, subshader by subshader, to the software shader graph. Cg versions prior to 1.2 are not supported by mental ray.
In almost all cases, it is sufficient to write only the fragment shader in Cg. If no vertex shader is supplied, mental ray supplies a built-in default vertex shader. All illumination calculations are performed in the fragment shader.
Also, Cg shaders do not normally depend on a shader support library the way OpenGL shaders do. No C/C++ code is required to use any of the standard Cg shaders shipped with mental ray, so no support libraries are supplied. Only the Cg files, named according to the conventions described above, such as myphong_f.cg, must be available on the hardware search path (defined with -hardware_path). This makes it very simple to make a third-party Cg shader available to mental ray. Each shader must be in a separate file, or pair of files if a vertex shader is required as well.
Hardware shaders read their parameters from a set of registers on the graphics board called uniform variables, so mental ray needs to know how to copy the shader parameters to the uniform variables before the shader is started, and the Cg compiler needs to have a matching definition. Here is the Cg syntax for defining shader parameters:
interface miiBoolean { bool eval(vert2frag param); }; interface miiInteger { int eval(vert2frag param); }; interface miiScalar { float eval(vert2frag param); }; interface miiVector { float3 eval(vert2frag param); }; interface miiColor { float4 eval(vert2frag p); }; interface miiColorTexture { float4 eval(vert2frag param, float3 uv); }; interface miiMatrix { float4x4 eval(vert2frag param); }; interface miiLight { misLightOut eval(vert2frag param); };
This is the complete list of available types. For example, the mental ray data type miBoolean is declared to the Cg compiler as miiBoolean, implemented as the Cg bool type. The eval function tells the Cg compiler that the parameter might be attached to another shader, which must also be compiled and downloaded to the graphics board. (Attaching shader outputs to shader parameters is how both mental ray and Cg implement shader graphs.)
Cg shader parameters are declared in the shader code, right in the function body. For example, if the software shader declaration is defined in .mi syntax as
declare shader color "myshader" ( color "param1", scalar "param2", vector "param3", color texture "param4" ) end declare
then it is sufficient to declare these four parameters in the Cg shader body, using the mii* data types listed above, using the same names. Note that you can omit parameters from the declaration. For example, here is a possible implementation of the shader in Cg:
struct myshader : miiColor { miiColor param1; miiScalar param2; miiVector param3; miiColorTexture param4; float4 eval(vert2frag p) { float4 c = param1.eval(p); float s = param2.eval(p); float3 v = param3.eval(p); float4 tex = param4.eval(p, float3(u,v,w)); return float4(1,1,1,1); } };
The Cg compiler will be run automatically by mental ray at render time. It will collect all required shaders by following the eval links, and create a single binary shader ready for downloading to the graphics board. Users only need to supply the Cg source code and leave compilation and downloading to mental ray. For more information on the Cg language, see NVIDIA's Cg 1.2 compiler reference manual.
mental ray implicitly supplies several extra parameters for light and material shaders. They are automatically downloaded to the fragment shader when matching names are found in the Cg declaration.
usage | name | type | description |
material | miLocal2Global | CG_FLOAT4x4 | Matrix for transforming local coordinates found in vert2frag.position to world space. |
light | light_origin | CG_FLOAT3 | Position of the light in camera space |
light_dir | CG_FLOAT3 | Direction to the light in camera space | |
light_spread | CG_FLOAT | Spread angle for spot lights | |
light_type | CG_INT | Type of the light from miLight_type | |
light_face | CG_INT | Illuminate which face of objects: 0=both, 1=front side only, 2=backside only | |
camera | cam_aspect | CG_FLOAT | Aspect ratio (height*aspect = width) |
cam_aperture | CG_FLOAT | Aperture | |
cam_focal | CG_FLOAT | Focal (atan(focal/aperture) = field of view) | |
cam_clip | CG_FLOAT2 | Clip plane (x=near, y=far) | |
cam_size | CG_FLOAT2 | Resolution of the window | |
cam_ortho | CG_BOOL | Is camera orthographic? | |
cam_transfo | CG_FLOAT4x4 | Camera transformation | |
shadowmap | shdmap_on | CG_BOOL | Tell if the shadowmap option is on for the light |
shdmap_tex[] | samplerRECT | Number of shadowmap textures, 1 for spot and directional lights, 6 for point lights | |
shdmap_transfo | CG_FLOAT4x4 | Matrix for transforming vert2frag.cPosition to light space | |
shdmap_window | CG_FLOAT2 | The transformation to apply to UV coordinates for perspective and parallel views | |
shdmap_size | CG_FLOAT2 | The resolution of the shadowmap | |
shdmap_bias | CG_FLOAT | The bias to apply to the shadowmap |
In case a Cg shader is not sufficient, and extra work is needed, a C/C++ support function can be supplied in a separate DSO/DLL. Those functions are mostly useful when the information we want to pass to the shader is not part of the shader declaration. For example, a noise function could compute a lattice data structure and pass it automatically to the shader. The C/C++ function name must begin with the prefix micg_. For example, here is a C++ support function for the Cg shader nvo_img_intensity:
extern "C" DLLEXPORT miBoolean micg_nvo_img_intensity( CGparameter *result, miCg_render *render, miHW_shader *shader) { static bool binit = true; if (binit) { CreateRandomMap(); binit = false; } CGparameter sParam = 0; CGcontext ctx = render->context_get(); // Create an instance of the shader CGtype cgtype = cgGetNamedUserType(render->program_get()); sParam = cgCreateParameter(ctx, cgtype); *result = sParam; // Set automatically all known parameters from the // shader declaration int i, nb_param = shader->nbparam_get(); for (i=0; i < nb_param; i++) { render->param_eval(sParam, shader->param_get(i)); } // Set the random texture CGparameter prnd = cgGetNamedStructParameter(sParam, "random_txt"); cgGLSetTextureParameter(prnd, random_map); prnd = cgGetNamedStructParameter(sParam, "random_size"); cgGLSetParameter1f(prnd, RES); return miTRUE; }
This support function installs a Cg shader including parameters, and also loads a texture.
Here is a Cg 1.2 implementation of the mib_illum_phong base material shader:
struct mib_illum_phong : miiColor { miiColor ambience, ambient, diffuse, specular; miiScalar exponent; miiInteger mode; miiLight lights[]; float4 eval(vert2frag p) { float4 result; float4 lambience = ambience.eval( p); float4 lambient = ambient.eval( p); float4 ldiffuse = diffuse.eval( p); float4 lspecular = specular.eval( p); float lexponent = exponent.eval(p); float3 vdir = normalize(p.cPosition); result = lambience * lambient; // Material calculation for each light for (int i=0; i < lights.length; i++) { misLightOut light = lights[i].eval(p); // Lambert's cosine law if (light.dot_nl > 0) { result += light.dot_nl * ldiffuse * light.color; // Phong's cosine power float s = mi_phong_specular(lexponent, light.dir, vdir, p.cNormal); result += s * lspecular * light.color; } } return result; } };
Light Shaders work the same way as material shader, but instead of implementing miiColor they must implement the miiLight interface:
struct mib_light_spot : miiLight { miiColor color; miiBoolean shadow; miiScalar factor; miiBoolean atten; miiScalar start; miiScalar stop; miiScalar cone; // Information from the light instance float3 origin; // position of the light float3 ldir; // direction of the light float spread; // cos angle of the spread samplerRECT shdmap_tex[1]; // light shadowmap float4x4 shdmap_transfo; // From eye to local light space float2 shdmap_window; // size of the window float2 shdmap_size; // size of the shadowmap float shdmap_bias; // Evaluation of the light misLightOut eval(vert2frag p) { misLightOut ret_val; float4 lcolor = color.eval(p); // same for each light? ret_val.dir = normalize(origin - p.cPosition); // vector to light ret_val.dot_nl = dot(p.cNormal, ret_val.dir); // shadow if (shadow.eval(p)) { float lfactor = factor.eval(p); // Shadow coordinates are created form the eye-space // position, then compare with the shadowmap float4 lPosition = mul(shdmap_transfo, float4(p.cPosition,1)); float2 shadowUV = (lPosition.xy/(-lPosition.z * shdmap_window) + 0.5) * shdmap_size; float4 dist = length(lPosition.xyz); float4 pdepth; shadowUV -= 0.5; pdepth.x = f1texRECT(shdmap_tex[0], float2(shadowUV.x, shadowUV.y)); pdepth.y = f1texRECT(shdmap_tex[0], float2(shadowUV.x, shadowUV.y+1)); pdepth.z = f1texRECT(shdmap_tex[0], float2(shadowUV.x+1, shadowUV.y)); pdepth.w = f1texRECT(shdmap_tex[0], float2(shadowUV.x+1, shadowUV.y+1)); pdepth = pdepth == float4(0,0,0,0) ? dist*2 : pdepth; float4 shd = dist - shdmap_bias - pdepth; float map_z = dot(shd < float4(0,0,0,0) ? float4(1,1,1,1) : float4(0,0,0,0), float4(1,1,1,1)); lcolor.rgb *= lerp(lfactor,1, map_z/4); } // cone float d = dot(ret_val.dir, ldir); if (d <= 0) { lcolor.rgb = 0; } if (d < spread) { lcolor.rgb = 0; } float lcone = cone.eval(p); if (d < lcone) { lcolor.rgb *= 1 - (d - lcone) / (spread - lcone); lcolor = saturate(lcolor); } // dist attenuation bool latten = atten.eval(p); if (latten) { float dist = length(origin - p.cPosition); float lstop = stop.eval(p); if (dist >= lstop) { lcolor.rgb = 0; } else { float lstart = start.eval(p); float t = 1 - (dist - lstart) / (lstop - lstart); lcolor.rgb *= t; } } ret_val.color = lcolor; return ret_val; } };
Copyright © 1986-2009 by mental images GmbH