用于插值两个纹理的 GLSL 着色器






4.60/5 (5投票s)
一个用 GLSL 开发的简单着色器,
引言
这个示例应用程序演示了一个用于插值两个纹理的简单着色器。
Kerala.bmp 和 Shrine.bmp 两个位图插值的截图。
 
 
背景
着色器是 OpenGL 中一个非常有趣的功能。本文展示了一个用于插值两个纹理的简单像素着色器。
着色器有三种类型
- 片段着色器或像素着色器
- 顶点着色器
- 几何着色器
像素着色器是一个程序,它将为渲染的每个像素进行处理(执行)。每个像素的输出颜色由着色器程序确定。
根据 GLSL 规范,
片段处理器是一个可编程单元,它对片段值及其相关数据进行操作。为该处理器编写的 OpenGL 着色语言编译单元称为片段着色器。
Using the Code
我第一次使用 CG 创建着色器程序。运行时,它需要 cg.dll 和 CGGL.dll。当我部署二进制文件时,运行我的应用程序需要安装 Cg。如果使用 GLSL,我们可以避免与 Cg.dll 和 CgGl.dll 相关的依赖。唯一的依赖是 PC 应该支持着色器。
opengl32.dll 暴露了基本 OpenGL 功能的函数,其他(GPU 依赖的功能)作为 OpenGL 扩展实现。OpenGL 提供了 GL_ARB_fragment_shader 扩展用于像素着色器程序。
GLExtension 类处理所有 OpenGL 扩展操作。该类暴露了以下函数(使用纹理的 GLSL 像素着色器所需的函数)。
// This Singleton class hold all function pointers of opengl Extensions.
// Functions similar to opengl extension are provided in this class. 
// All function will call corresponding function pointer.
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();
    // pFailedFunction Return NULL is all wglGetProcAddress are success.
    // Else return the name of failed function. Application should delete this buffer.
    bool GetWglProcAddress( TCHAR*& pFailedFunction );
private:
    // Constructor and destructor defined in private section for Singleton behaviour.
    GLExtension(void);
    ~GLExtension(void);
};
创建了 GLSLShader 类来使用 OpenGL 扩展创建 GLSL 着色器。CreateProgram 可以借助 GLExtension 类创建着色器。
// Shader class handles creating of a Shader, and parameter setting to the 
// GLSL Shader. This class uses GLExtension for calling opengl extension functions.
class GLSLShader
{
public:
    GLSLShader(void);
    // nProgramID_i is the resource ID of Shader.
    // glSlShaderType_i should be GL_FRAGMENT_PROGRAM_ARB, GL_VERTEX_PROGRAM_ARB
    bool CreateProgram( const int nProgramID_i, GLenum glSlShaderType_i );
    bool DeleteShader();
    bool EnableShader();
    bool DisableShader();
    // This function attach a texture to shader parameter.
    // pParamName_i is parameter name given in shader.
    bool SetTexture( const CHAR* pParamName_i, const int nTextureID_i, int nIndex = 0 );
    // This function passes a float value to Shader float parameter.
    bool SetParam( const CHAR* pParamName_i, const float fValue_i );
    ~GLSLShader(void);
private:
   static GLhandleARB m_hProgramObject;
   GLhandleARB m_hShaderHandle;
}; 
最后,我将展示插值像素着色器,它可以根据插值值插值两个纹理。
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;
}  
fInterpValue 的值决定了 texture1 和 texture2 的贡献。如果 fInterpValue 为 0,则输出 color1(texture1),如果 fInterpValue 为 1,则输出 colro2( texture2 ) 。
创建了一个滚动条来更改插值值。每当滚动条更改其位置时,它将被更新到着色器。
CInterpolationDlg 调用 GLSetup::InitGL(),GLSetup 封装了 OpenGL 初始化和 OpenGL 渲染上下文的删除。大多数 OpenGL 函数调用都避免在对话框类中进行。
CInterpolationDlg::OnInitDialog() 创建了一个 Timer ,用于每秒显示 60 帧。
关注点
如果从文件创建着色器,则还需要将着色器文件与二进制文件一起提供。这里我从资源创建着色器。我只是将着色器文件 pixelshader.cg 添加到资源中。GLSLShader::CreateProgram() 接收 pixelshader.cg 的资源 ID。然后调用 LoadResource() 和 LockResource() 从资源获取数据。并且 Interpolation.exe 可以在没有 pixelshader.cg 的情况下运行,因为它已经作为资源嵌入到 Interpolation.exe 中。
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 );
} 
纹理也是从两个位图创建的。这里位图文件也隐藏为资源,并避免了对位图文件的依赖。
  // Macro for getting function pointers of Extension functions.
#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; \
    } \
}\ 
历史
- 初始版本


