Adding Lighting and Shading to a Surface
This example displays an IDLgrSurface, and uses the vertex shader to displace part of it up and down in an animation sequence. It also changes the color of the displaced part slightly for additional emphasis. An ambient light and a positional light illuminate the surface.
Example Code
See shader_lightsurf_doc.pro, located in the examples/doc/shaders subdirectory of the IDL distribution, for the complete, working example. Run the example procedure by entering shader_lightsurf_doc at the IDL command prompt or view the file in an IDL Editor window by entering .EDIT shader_lightsurf_doc.pro.
First create the surface:
; Generate surface data and create surface object. surfdata = BESELJ(SHIFT(dist(100), 50, 50) / 2,0) * 40 oSurface = OBJ_NEW('IDLgrSurface', surfdata, STYLE=2, COLOR=[200,200,40]) ; Create model for the visibile surface and rotate angle ; for good viewing. oModel = OBJ_NEW('IDLgrModel') oModel->Add, oSurface oModel->Translate, -50, -50, 0 oModel->Rotate, [0,0,1], -30 oModel->Rotate, [1,0,0], -60 oModel->Translate, 50, 50, 0
Then define the ambient and positional lights. The directional light has an arbitrary light index value (4 in this example) in order to identify it in the shader program.
oLightModel = OBJ_NEW('IDLgrModel')
oLightModel->Add, OBJ_NEW('IDLgrLight', TYPE=0, $
COLOR=[100, 50, 40])
oLightModel->Add, OBJ_NEW('IDLgrLight', TYPE=1,
LOCATION=[200,200,500], $
COLOR=[255,255,255], INTENSITY=0.8, LIGHT_INDEX=4)
Uniform and Attribute Variables for Lighting Shader
The IDL application (shader_lightsurf_doc.pro) creates and passes two uniform variables and an attribute variable containing per-vertex information to the shader program.
- Displacement — this attribute variable contains a "displacement mask", which describes the part of the surface to displace. There is a value for each vertex, where a zero means no displacement will be applied at that point, and a non-zero value describes the magnitude of the relative displacement.
- DirectionalLightIndex — this uniform variable identifies the one non-ambient light's index value that is being passed to the shader program. The value for this uniform variable matches the LIGHT_INDEX value.
- Time - this uniform variable is incremented during IDL application execution and the updated value is used within the shader program to vary the amount of displacement with respect to time.
disp = FLTARR(100,100) disp[50:99, 50:99] = MAX(surface) oSurface->SetVertexAttributeData,'Displacement', $ REFORM(disp, 100*100)
oShader->SetUniformVariable, 'DirectionalLightIndex', 4Note
The generated shader program requires an integer (4) rather than a table entry (gl_LightSource[4]) to identify the light. While defining a uniform variable is not a requirement, using DirectionalLightIndex in the shader program code makes it easier to understand than hard-coding the number4.
Hardware Shader Program for Lighting Shader
The vertex shader program for this example was largely generated by 3Dlabs' ShaderGen program. Only a small amount of code needed to be added or modified to make the generated code work with the example IDL application. See the code comments for details.
Example Code
See lightSurfVert.txt, located in the examples/doc/shaders subdirectory of the IDL distribution, for the complete, working example.
The fragment shader program (lightsurf.frag) is very simple:
Assign Lighting Shader Program to Shader Object
The vertex shader program is rather long and complex, so it is stored in an external file, as is the fragment shader program. Associate the shader program components with the IDLgrShader object using the VERTEX_PROGRAM_FILE and FRAGMENT_PROGRAM_FILE properties.
; Access shader program files. vertexFile=FILEPATH('lightSurfVert.txt', $ SUBDIRECTORY=['examples','doc', 'shaders']) fragmentFile=FILEPATH('lightSurfFrag.txt', $ SUBDIRECTORY=['examples','doc', 'shaders']) ; Create shader and associate vertex and fragment programs. oShader = OBJ_NEW('IDLgrShader') oShader->SetProperty, VERTEX_PROGRAM_FILENAME=vertexFile, $ FRAGMENT_PROGRAM_FILENAME=fragmentFile ; Associate shader with the surface. You can comment out ; this line to run without the shader program. oSurface->SetProperty, SHADER=oShader
With the appropriate display objects and a FOR loop to increment the uniform variable Time, you can visualize the results of applying the shader program lighting calculations to the surface. A detail of the surface during program execution appears in the following figure.
