Hardwired OpenGL Shaders

Deprecated

OpenGL 1.x does not support native vertex or fragment shaders, unlike the forthcoming OpenGL 2.0 standard. (There are extensions for OpenGL 1.5 that will not be considered here.) This means that rendering must entirely rely on C/C++ code in the shader support library, which is optional for NVIDIA. The shader prefix for OpenGL is gl_, and since C/C++ functions in shader support libraries have an extra mi prefix, all OpenGL shader function names begin with migl_. For example, the OpenGL equivalent of the software shader mib_illum_phong must be called migl_mib_illum_phong.

Since shader graphs and subshaders are not available for OpenGL, it is not possible to separately implement the subshaders of a Phenomenon and have OpenGL 1.4 put them together the way Cg can. Instead, mental ray will call the root shader of the Phenomenon, and recursively evaluate its subshaders. Here is a sample implementation of an OpenGL shader that implements the software material shader mib_illum_phong:

    #include "mi_opengl.h"

    extern "C" DLLEXPORT int migl_mib_illum_phong_version(void) { return(2); }

    extern "C" DLLEXPORT miBoolean migl_mib_illum_phong(
        miColor                *result,
        miOgl_render           *render,
        struct mib_illum_phong *paras)
    {
        miColor     ambc, ambi, diff, spec;
        miTag       *light;         /* tag of light instance */
        int         n_l;            /* number of light sources */
        int         i_l;            /* offset of light sources */
        int         m;              /* light mode: 0=all, 1=incl, 2=excl */
        miScalar    expo;           /* Phong exponent (cosine power) */

        ambc  = render->eval(&paras->ambience);
        ambi  = render->eval(&paras->ambient);
        diff  = render->eval(&paras->diffuse);
        spec  = render->eval(&paras->specular);
        expo  = render->eval(&paras->exponent);

        *result = diff;

        m     = render->eval(&paras->mode);
        n_l   = render->eval(&paras->n_light);
        i_l   = render->eval(&paras->i_light);
        light = render->eval( paras->light) + i_l;

        glLightModeli (GL_LIGHT_MODEL_COLOR_CONTROL,
                       GL_SEPARATE_SPECULAR_COLOR);
        glLightModelfv(GL_LIGHT_MODEL_AMBIENT, (float *)&ambc);
        glMaterialfv  (GL_FRONT_AND_BACK, GL_AMBIENT,   (float *)&ambi);
        glMaterialfv  (GL_FRONT_AND_BACK, GL_DIFFUSE,   (float *)&diff);
        glMaterialfv  (GL_FRONT_AND_BACK, GL_SPECULAR,  (float *)&spec);
        glMaterialf   (GL_FRONT_AND_BACK, GL_SHININESS, expo);
        render->lights_enable(render, n_l, light);

        return(miTRUE);
    }

Like for software shaders, a version number is provided. Like software shaders, it is necessary to use an mi_eval equivalent since subshaders will be called. These subshaders must return a constant value, which will not change for different samples. The extra miOgl_render class that provides useful OpenGL support functions is defined by the OpenGL driver library mi_opengl.so, which also provides the mi_opengl.h include file that defines the available methods.

mi_opengl.so also provides the lights_enable function, which accepts a list of light shader instances. It will register these light sources with the OpenGL hardware by calling the light shader for each one. These light shaders are also part of the OpenGL shader support library, like the material shader. Note that OpenGL hardware generally supports only a small number of lights, typically eight.

    extern "C" DLLEXPORT int migl_mib_light_point_version(void) { return(1); }

    extern "C" DLLEXPORT miBoolean migl_mib_light_point(
        miColor                 *result,
        miOgl_render            *render,
        struct mib_light_point  *paras)
    {
        miColor   col     = render->eval(&paras->color);
        miBoolean atten   = render->eval(&paras->atten);
        const int lgt_idx = render->light_index();

        glLightfv(lgt_idx, GL_AMBIENT,       black);
        glLightfv(lgt_idx, GL_DIFFUSE,       (float *)&col);
        glLightfv(lgt_idx, GL_SPECULAR,      (float *)&col);
        glLightf (lgt_idx, GL_SPOT_CUTOFF,   180.0f);
        glLightf (lgt_idx, GL_SPOT_EXPONENT, 128.0f);

        if (miTRUE == atten) {
            GLfloat k = 1.0f /
                (render->eval(&paras->start) + render->eval(&paras->stop));
            glLightf(lgt_idx, GL_LINEAR_ATTENUATION, k);
        }

        // We need to load the camera transformation, since we are in
        // object space
        glPushMatrix();
        glLoadMatrixf(render->cam_trafo());
        oglLight light;
        if (!render->light_get(&light))
            return miFALSE;

        float pos[4];
        pos[0] = light.org.x;
        pos[1] = light.org.y;
        pos[2] = light.org.z;
        pos[3] = 1.0f;
        glLightfv(lgt_idx, GL_POSITION, pos);
        glPopMatrix();

        *result = col;
        return miTRUE;
    }

Lights must be set in world space, and the OpenGL driver library provides a method cam_trafo that returns the camera matrix which can be loaded on the modelview matrix stack. The information about the position, direction and other characteristics of the light can be retrieved with the light_get method. The current light index can be set and restored with the light_index method.

Copyright © 1986-2008 by mental images GmbH