|
Object Programming: Advanced Rendering Using Shader Objects |
|
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 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. |
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:
READ_JPEG, 'Day.jpg', day
oDay = OBJ_NEW('IDLgrImage', day)
shader_multitexture_doc.pro.
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. |
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.
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.
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.
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:
; Tell the shader program about our textures. oShader->SetUniformVariable, 'EarthDay', oDay oShader->SetUniformVariable, 'EarthNight', oNight oShader->SetUniformVariable, 'EarthCloudGloss', oClouds
oEarth->SetMultiTextureCoord, 0, tc
| Note 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:
earthVert.txt), fetch the texture coordinates for both the daytime and nighttime textures from the predefined GLSL uniform variable gl_MultiTexCoord[n], where n corresponds to the numbers used in the IDLgrPolygon::SetMultiTextureCoord method calls. These texture coordinates are passed to the fragment shader with varying variables.
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 by entering shader_earthmulti at the IDL command prompt or view the file in an IDL Editor window by entering .EDIT shader_earthmulti. The associated shader program files earthVert.txt and earthFrag.txt are located in the same directory. |
IDL Online Help (March 06, 2007)