用于插值两个纹理的 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; \
} \
}\
历史
- 初始版本