Vertex Shaders

Vertex attribute data may be associated with IDLgrPlot, IDLgrPolygon, IDLgrPolyline, and IDLgrSurface objects using the GetVertexAttributeData and SetVertexAttributeData. While uniform variables are useful for passing concise control parameters to a shader program, they are not suited for passing large amounts of data where the data might contain values that are associated with each vertex. The ability to pass frequently changing per-vertex attribute data to a shader program in an attribute variable lets you show movement within a display (as described in Attribute Variables).

The following example uses an attribute variable to replicate the effect of wind on a set of particles. Each particle has an initial position and a velocity assigned to it. The initial position of the particle can be easily represented by the already-familiar vertex [x, y, z] information.

Example Code
See shader_vertexwinds_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_vertexwinds_doc at the IDL command prompt or view the file in an IDL Editor window by entering .EDIT shader_vertexwinds_doc.pro.

Attribute and Uniform Variables for Vertex Shader

This example uses the global wind data (globalwinds.dat) that is shipped as part of the IDL distribution. There is also a [2,n] vector of (u,v) pairs, representing the wind velocity at each point.

; Get initial positions and wind velocity data. 
RESTORE, FILE=FILEPATH('globalwinds.dat', $ 
   SUBDIRECTORY=['examples', 'data']) 
 
; Set up point grid. 
pts = FLTARR(2, 128*64) 
FOR i=0, 63 DO BEGIN 
   pts[0, i*128:(i+1)*128-1] = x 
   pts[1, i*128:(i+1)*128-1] = y[i] 
ENDFOR 
 
; Set up per-sample velocity information. 
u = REFORM(u, 128*64) 
v = REFORM(v, 128*64) 
uv = TRANSPOSE([[u],[v]]) 

This code fragment creates an IDLgrPolygon object with the initial sample (particle) locations and uses STYLE=0, which simply draws a dot at each vertex. Instead of placing the velocity data in the shader object, you store it in the graphic object, the polygon, using the SetVertexAttributeData method:

; Create graphical object and associate the wind data. 
oPoints = OBJ_NEW('IDLgrPolygon', pts, STYLE=0, THICK=3) 
oPoints->SetVertexAttributeData, 'uv', uv 

A time uniform variable is used to determine the amount of displacement of a particle from its original location since time will be multiplied by velocity in the vertex shader program.

; Animate and track time. 
t0 = SYSTIME(1) 
frames = 0L 
FOR i=0, 2 DO BEGIN 
   FOR time=0.0, 2, 0.01 DO BEGIN 
      oShader->SetUniformVariable, 'Time', time 
      oWin->Draw 
      frames++ 
   ENDFOR 
ENDFOR  

Warning
Attribute and uniform variable names are case-sensitive, unlike most variable names in IDL.

Hardware Shader Program for Vertex Shader

The vertex program does the majority of the work. The wind velocity data contained in the attribute variable, uv, is passed to the vertex program by calling SetVertexAttributeData. The vertex program runs once for every vertex in the primitive. In this case, OpenGL finds the attribute data associated with each vertex as it calls this shader program, and places that attribute data in uv. As uv is a frequently changing attribute variable, the value will likely be different each time the vertex shader is called.

The uniform variable, Time, specifies how long the particle has been moving with the velocity uv. Therefore the actual displacement is simply the velocity multiplied by time. The IDL application sets the value of Time for each frame in the animation.

vertexProgram = [ $ 
   'attribute vec2 uv;', $ 
   'uniform float Time;', $ 
   'void main() {', $ 
      'vec4 vert;', $ 
      'vert = gl_Vertex + vec4(uv * Time, 0.0, 0.0);', $ 
      'gl_Position = gl_ModelViewProjectionMatrix * vert;', $ 
   '}'] 

OpenGL sets the special, pre-defined GLSL uniform variable, gl_Vertex, to the current vertex position [x, y, z, w] for each vertex. The expression uv*Time multiplies the 2-element velocity vector by the scalar time value, resulting in another 2-element vector. This result is expanded to a 4-element vector and then added to the vertex location. Finally, the new vertex location is transformed from world to screen space and passed back to OpenGL via the special GLSL variable gl_Position.

The fragment program is rather trivial. The only thing this program does is set the color of the point.

   fragmentProgram = [ $ 
   'void main() {', $ 
   'gl_FragColor = vec4(1.0, 0.44, 0.122, 0.8);', $ 
   '}'] 

Note
Any color set using the COLOR property of the IDLgrPolygon object is ignored since the fragment shader rendering takes precedence over the fixed-function OpenGL pipeline rendering. If the fragment portion of the shader program does not set a fragment color, the fragment (pixel) is drawn with color set to black.

Assign Vertex Shader Program to Shader Object

Since the fragment and vertex shader programs were defined inline, associate them with a newly created shader object using the VERTEX_PROGRAM_STRING and FRAGMENT_PROGRAM_STRING properties. Then assign the shader to the polygon object (oPoints).

; Set up shader object 
oShader = OBJ_NEW('IDLgrShader') 
oShader->SetProperty, $ 
   VERTEX_PROGRAM_STRING=STRJOIN(vertexProgram, STRING(10b)), $ 
   FRAGMENT_PROGRAM_STRING=STRJOIN(fragmentProgram, STRING(10b)) 
oPoints->SetProperty, SHADER=oShader 

The only remaining task is to create the display objects. See shader_vertexwinds_doc.pro in the examples/doc/shaders directory if needed.

When you run the program, the time loop cycles through the animation three times. The frame rate is printed to the output window when the program finishes. An IDL application could duplicate this example without using shaders by applying the velocity-multiplied-by-time factor repeatedly to all the vertex data and repeatedly updating the vertex data stored in the polygon object. However, this would be a much slower process than the average 200 frames per second achieved by the shader program. The following figure shows a subset of the world map and the final positions of wind vector points.

Figure 14-8: Vertex Shader Example Mapping Wind Velocity Data

shader_vertexwind.gif