Introduction
This sample application demonstrates a simple shader for interpolating two textures.
The screenshot of Interpolation of two bitmaps Kerala.bmp and Shrine.bmp.
Background
Shaders are an very interesting feature in OpenGL. This article shows a simple pixel shader for interpolating two textures.
There are three types of shaders:
- Fragment or Pixel shader
- Vertex Shader
- Geometry Shader
A Pixel Shader is a program which will be processed (executed) for each pixel of a rendering. The output color of each pixel is determined by the shader program.
From GLSL spec,
The fragment processor is a programmable unit that operates on fragment values and their associated data. Compilation units written in the OpenGL Shading Language to run on this processor are called fragment shaders.
Using the Code
First time I created a shader program in CG. At runtime, it requires cg.dll and CGGL.dll. When I deployed my binaries, installation of Cg is necessary for running my application. If we are using GLSL, we can avoid this dependency with Cg.dll and CgGl.dll. Only dependency is that the PC should have shader support.
opengl32.dll exposes functions for basic opengl functionalities, others (GPU dependent functionalities) are implemented as opengl extensions. Opengl provides GL_ARB_fragment_shader
extension for pixel shader programs.
GLExtension
class handles all opengl extension operations. This class exposes the following functions (the required functions for GLSL pixels shader with texturing).
class GLExtension
{
public:
GLhandleARB glCreateProgramObjectARB();
GLhandleARB glCreateShaderObjectARB(GLenum shaderType);
void glShaderSourceARB( GLhandleARB shader, GLuint number_strings,
const GLcharARB** strings, GLint * length);
void glAttachObjectARB(GLhandleARB program, GLhandleARB shader);
void glLinkProgramARB(GLhandleARB program);
void glCompileShader(GLhandleARB program);
void glUseProgramObjectARB(GLhandleARB program);
void glDeleteObjectARB(GLhandleARB object);
void glGetInfoLogARB(GLhandleARB object, GLsizei maxLenght, GLsizei *length,
GLbyte*infoLog);
GLint glGetUniformLocationARB(GLhandleARB program, const GLbyte* name);
GLint glGetAttribLocationARB(GLhandleARB program, const GLbyte* name);
void glUniform1iARB(GLuint index, int val);
void glUniform1fARB(GLuint index, float fval);
void glActiveTexture(GLenum Texture);
public:
static GLExtension* GetInstance();
bool GetWglProcAddress( TCHAR*& pFailedFunction );
private:
GLExtension(void);
~GLExtension(void);
};
GLSLShader
class is created to create a GLSL shader with opengl extensions. CreateProgram
can create a shader with the help of GLExtension
class.
class GLSLShader
{
public:
GLSLShader(void);
bool CreateProgram( const int nProgramID_i, GLenum glSlShaderType_i );
bool DeleteShader();
bool EnableShader();
bool DisableShader();
bool SetTexture( const CHAR* pParamName_i, const int nTextureID_i, int nIndex = 0 );
bool SetParam( const CHAR* pParamName_i, const float fValue_i );
~GLSLShader(void);
private:
static GLhandleARB m_hProgramObject;
GLhandleARB m_hShaderHandle;
};
Finally, I’ll show interpolation pixel shader, which can interpolate two textures based on an interpolation value.
uniform sampler2D texture1;
uniform sampler2D texture2;
uniform float fInterpValue;
void main()
{
vec4 Color2 = texture2D(texture2,gl_TexCoord[0].st);
vec4 Color1 = texture2D(texture1,gl_TexCoord[0].st);
gl_FragColor = ( 1.0 - fInterpValue )* Color1 + fInterpValue * Color2;
}
The value of fInterpValue
determines the contribution of texture1
and texture2
. If fInterpValue
is 0
, then color1(texture1)
will be output and if fInterpValue
is 1
, then colro2( texture2 )
will be the output.
A scrollbar is created to change the interpolation value. Whenever scrollbar changes its position, it will be updated to shader.
CInterpolationDlg
calls GLSetup::InitGL()
, GLSetup
wraps opengl initialisation and deletion of opengl rendering context. Most of the opengl function calls are avoided from Dialog class.
CInterpolationDlg::OnInitDialog()
creates a Timer
for displaying 60 frames in a second.
Points of Interest
If we are creating shader from a file, the shader file is also required with binary. Here I'm creating shader from resource. I just added the shader file pixelshader.cg into the resource. GLSLShader::CreateProgram()
receives the resource id of pixelshader.cg
. Then calls LoadResource()
and LockResource()
for getting the data from resource. And Interpolation.exe can run without pixelshader.cg, because it's already embedded in Interpolation.exe as a resource.
bool GLSLShader::CreateProgram( const int nProgramID_i, GLenum glSlShaderType_i )
{
HRSRC hrResInfo = FindResource( 0, MAKEINTRESOURCE( nProgramID_i ),
TEXT("IDR_DATA"));
if( 0 == hrResInfo )
{
return false;
}
HGLOBAL hRcResource = LoadResource(0, hrResInfo );
const CHAR* pBufferResData = (CHAR* )LockResource( hRcResource );
m_hShaderHandle = GLExtension::GetInstance()->
glCreateShaderObjectARB( GL_FRAGMENT_PROGRAM_ARB );
}
And textures are also created from two bitmaps. Here also bitmap files are hidden as resource and avoided dependency with a bitmap file.
#define GET_PROC_ADDRESS(fname, failedFunName)\
{\
Obj_##fname = (FPTR_##fname)wglGetProcAddress( #fname ); \
if( 0 == Obj_##fname ) \
{ \
failedFunName = new TCHAR[strlen(#fname)+1]; \
wcscpy( failedFunName, L#fname ); \
return false; \
} \
}\
History