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:
- Day — the base image of the map of the earth that is added to the IDLgrModel. This base image object is stored in the reserved uniform variable _IDL_ImageTexture by default and need not be explicitly passed to the shader program using SetUniformVariable.
- Clouds — this image of the cloud cover is explicitly passed to the shader program using SetUniformVariable in the main IDL application,
shader_multitexture_doc.pro. - Scrape — this provides the position of the mouse cursor, which scrapes away a circle of clouds when the scraper has been activated. This information is passed using SetUniformVariable in the OnMouseUp and OnMouseMotion methods of the window observer.
READ_JPEG, 'Clouds.jpg', clouds oClouds = OBJ_NEW('IDLgrImage', clouds) oShader->SetUniformVariable, 'Clouds', oClouds
Example Code
The window observer object file is located in winobserver__define.pro in the examples/doc/shaders subdirectory of the IDL distribution. Run the example procedure by entering winobserver__define at the IDL command prompt or view the file in an IDL Editor window by entering .EDIT winobserver__define.pro.
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.
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.
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:
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:
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.
In the IDL code:
- Create three IDLgrImage objects to hold the daytime, nighttime and cloud textures. Assign the object references for these image objects to uniform variables, using the IDLgrShader::SetUniformVariable method.
- Use the SetMultiTextureCoord method for IDLgrPolygon to set texture coordinates (tc) for the textures.
; Tell the shader program about our textures. oShader->SetUniformVariable, 'EarthDay', oDay oShader->SetUniformVariable, 'EarthNight', oNight oShader->SetUniformVariable, 'EarthCloudGloss', oClouds
oEarth->SetMultiTextureCoord, 0, tcNote
If the textures did not share the same coordinates, you could call SetMultiTextureCoord multiple times. See Repositioning Textures for additional information.
In the Shader Program:
- In the vertex program (
earthVert.txt), fetch the texture coordinates for both the daytime and nighttime textures from the predefined GLSL uniform variablegl_MultiTexCoord[n], wherencorresponds to the numbers used in the IDLgrPolygon::SetMultiTextureCoord method calls. These texture coordinates are passed to the fragment shader with varying variables. - The fragment shader (
earthFrag.txt) then decides on what side of the earth the fragment is on, and chooses the appropriate texture and texture coordinates to use to look up the texel value to use as the fragment color.
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.

