Manipulating Multiple Textures Using Shaders

The following multi-texturing shader program example provides the ability to interactively scrape away the section of clouds under the mouse cursor to see the earth below. Because this requires blending only a section of the image, using a shader program in this case is far easier than duplicating the outcome using only IDL.

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

Uniform Variables for Multi-texture Shader

This example uses three uniform variables that define the map of the earth, the clouds, and the position of the mouse cursor on the map where you want to reveal the earth below the clouds. These are:

Hardware Shader Program for Multi-texture Shader

The vertex shader program (multitextureVert.txt located in examples/doc/shaders) is very simple since the example requires only display-related transformation of the vertices.

void main() 
{ 
   gl_TexCoord[0]  = gl_MultiTexCoord0; 
   gl_Position     = ftransform(); 
} 

This basic vertex program passes along the texture coordinate and then applies a transform to the vertex to correctly position it on the screen. The gl_TexCoord[0] is a varying variable that transmits data from the vertex program to the fragment shader program.

Note
If you need to align or change the position of a texture in relation to other textures you can use the SetMultitextureCoord method. See Repositioning Textures for details.

The fragment shader program (multitextureFrag.txt located in examples/doc/shaders) uses the three uniform variables to determine which portion of the clouds needs to be removed.

uniform sampler2D Clouds; 
uniform sampler2D _IDL_ImageTexture; 
uniform vec2 Scrape; 
 
void main() 
{ 
vec3 clouds  = vec3(texture2D(Clouds, gl_TexCoord[0].st).r); 
vec3 daytime = texture2D(_IDL_ImageTexture, 
   gl_TexCoord[0].st).rgb; 
 
   vec3 color = daytime; 
   vec2 f = Scrape - gl_TexCoord[0].st;  
   f.s *= 2.0;  // aspect ratio correction 
 
   if (length(f) > 0.02) 
      color = mix(daytime, clouds, clouds.r); 
 
   gl_FragColor = vec4(color, 1.0); 
} 

The shader program mixes the map and cloud data according to the cloud intensity, but only when greater than a certain distance away from the specified position (the Scrape location). If close enough to the specified position, the program just draws the map color. The shader program is fast enough to let you interactively change the Scrape location to reflex the position of the mouse cursor. Attempting the same operation in IDL would likely be too slow to be useful.

Assign Multi-texture Shader Program to Shader Object

You need to supply the program code to the shader object so that it is available to the graphics card when it is needed. To accomplish this, you can use shader object properties VERTEX_PROGRAM_FILE and FRAGMENT_PROGRAM_FILE to associate external shader program components with the shader object.

vertexFile=filepath('multitextureVert.txt', $ 
   SUBDIRECTORY=['examples','doc', 'shaders']) 
fragmentFile=filepath('multitextureFrag.txt', $ 
   SUBDIRECTORY=['examples','doc', 'shaders']) 
 
; Create the shader object, link the shader programs, and  
; associate the shader with the base image object, the daytime 
; map of the earth (oDay).  
oShader = OBJ_NEW('IDLgrShader') 
oShader->SetProperty, $ 
   VERTEX_PROGRAM_FILENAME='multitexture.vert' 
oShader->SetProperty,$ 
   FRAGMENT_PROGRAM_FILENAME='multitexture.frag' 
oDay->SetProperty, SHADER=oShader 

At this point, you can easily add image display code and a window observer to your program and test your multi-texture shader.

When you run shader_multitexture_doc.pro, click in the window to turn on the cloud "scraper" and move your mouse cursor to reveal the ground beneath. The following figure shows the upper Baja peninsula with clouds (left) and without (right) as the shader interactively blends the two textures under the mouse cursor.

Figure 14-10: Multi-texture Blending Example

Manipulating_Multiple_Textures_Using_Shaders-09.jpg

Repositioning Textures

When working with multiple textures, the textures may all map the same way onto the object. However, if one texture needs to be repositioned or if you want to animate a texture, you can assign individual texture coordinates to each texture. Using the map and cloud example, you could either shift the position of the clouds or animate the clouds to move across the map.

To achieve such results, you need to supply a different set of texture coordinates for each texture using the IDLgrPolygon::SetMultiTextureCoord or the IDLgrSurface::SetMultiTextureCoord method. This method has the signature of:

obj->SetMultiTextureCoord, Unit, TexCoord 

where Unit specifies a texture coordinate unit and TexCoord contains the texture coordinates. This effort begins in your IDL application:

tcMap =  < code that generates the texture coords > 
tcClouds = tcMap 
tcClouds[0,*,*] += 0.2  ;; shift the clouds to the west 
oPolygon->SetMultiTextureCoord, 0, tcMap 
oPolygon->SetMultiTextureCoord, 1, tcClouds 

The last two lines associate two sets of texture coordinates with the polygon object. Access these texture coordinates in the vertex program where texture coordinate 0 (zero) relates to the map texture and texture coordinate 1 is the cloud texture. Your vertex shader program must collect these and pass them to the fragment shader:

gl_TexCoord[0]  = gl_MultiTexCoord0; 
gl_TexCoord[1]  = gl_MultiTexCoord1; 

The fragment shader then uses the appropriate texture coordinate to lookup the color from each texture. That is, it uses gl_TexCoord[1] to lookup the cloud texture, and gl_TexCoord[0] to lookup the map texture.

vec3 clouds  = vec3(texture2D(Clouds,  
   gl_TexCoord[1].st).r); 
vec3 map = texture2D(Map, gl_TexCoord[0].st).rgb; 
 
vec3 color = mix(map, clouds, clouds.r); 
gl_FragColor = vec4(color, 1.0); 

Thus, you can use two sets of texture coordinates to control the display of two different textures.

Rotating Earth with Multiple Textures

This example loads three images, a base day image of the earth, a night image and an image of clouds, into textures. It then draws the rotating earth showing a day scene on one side and night scene (lights of big cities) on the other.

Figure 14-11: Sample Image from Multi-texture Shader Application

shader_earthdaynight.gif

In the IDL code:

In the Shader Program:

This shader program was taken directly from Chapter 10 of the "Orange Book" ("OpenGL Shading Language", Second Edition, by Randi J. Rost) and required no modifications to work with the IDL application, shader_earthmulti.pro.

Example Code
See shader_earthmulti.pro, located in the examples/doc/shaders subdirectory of the IDL distribution, for the complete, working example. Run the example procedure by entering shader_earthmulti at the IDL command prompt or view the file in an IDL Editor window by entering .EDIT shader_earthmulti.pro. The associated shader program files earthVert.txt and earthFrag.txt are located in the same directory.