使用 GLSL 着色器创建噪声图像






4.55/5 (11投票s)
一个简单的着色器程序,
引言
这是一个简单的像素着色器程序,它会使用噪声值改变输入的纹理坐标,并创建一个噪声输出图像。
背景
使用像素着色器在图像中创建任何效果都非常简单。本文演示了一种在图像中创建简单的噪声效果的方法。
使用方法
片段程序将为渲染的每个像素执行。当渲染整个纹理片段时,着色器将获得渲染的每个像素对应的纹理坐标。
如果使用提供的纹理坐标进行纹理查找以进行显示,则渲染的图像将与输入图像相同。
一个示例着色器显示与输入图像相同的纹理图像
uniform sampler2D ImageTexture;
// Shader for displaying a texture.
void main()
{
// Lookup image texture with noise texture coordiante.
gl_FragColor = texture2D(ImageTexture, gl_TexCoord[0].st );
}
gl_TexCoord[0].st
包含纹理坐标,可用于获取纹理像素(来自纹理的一个像素)。 在从 ImageTexture
获取纹理像素后,将该颜色设置为输出颜色,我们将在屏幕上获得 ImageTexture
的显示。
如果我们改变用于纹理查找的纹理坐标会发生什么?
ImageTexture
的显示会略微改变。
下面是一个示例着色器,它显示带有 imageTexture
显示一些变化的纹理图像。
uniform sampler2D ImageTexture;
// Shader for displaying a texture with some change in texture display.
void main()
{
// Lookup image texture with small change in texture coordinate.
// cos( gl_TexCoord[0].s) is added to texture coordinate.
gl_FragColor = texture2D(ImageTexture, gl_TexCoord[0].st +
vec2(0.0, cos( gl_TexCoord[0].s)));
}
这个着色器可以改变图像的显示,但这不适合创建噪声图像。 为了创建噪声输出,我们需要随机改变查找位置。 这就是为什么我创建了一个 NoiseTexture
,它包含可用于改变 ImageTexture
的查找坐标的随机偏移量。 NoiseTexture
的大小与 ImageTexture
相同,以避免相邻像素的噪声偏移量重复。
CreateNoise()
创建随机偏移量以填充 NoiseTexture
。
// This function create a Noise texture of m_nImageWidth and m_nImageHeight.
bool CNoiseDlg::CreateNoise()
{
srand(time(NULL));
if( 0 == m_pnRandomIndexes )
{
int maxRandom = max( m_nImageWidth, m_nImageHeight );
m_pnRandomIndexes = new int[maxRandom];
for (int nIndex = 0; nIndex < maxRandom; nIndex++)
{
m_pnRandomIndexes[nIndex] = max( (int)rand() % 256, (int)1.0);
}
}
FillGrad(1);
BYTE* pbyData = new BYTE[m_nImageWidth * m_nImageHeight *3];
float NoiseHeight = m_NoiseValue / 100.0 * 25.0;
// Creating Noise data of of m_nImageWidth and m_nImageHeight.
for(int i = 0; i<m_nImageHeight; i++)
for(int j = 0; j<m_nImageWidth; j++)
{
int offset = (i*m_nImageWidth+j)*3;
char value = m_pnRandomIndexes[(j+m_pnRandomIndexes[i]) & 0xFF];
pbyData[offset] = m_nRandomValues[value & 0x1F][0] *
NoiseHeight + min( NoiseHeight, m_NoiseValue ); // Gradient x
pbyData[offset+1] = m_nRandomValues[value & 0x1F][1] *
NoiseHeight + min( NoiseHeight, m_NoiseValue ); // Gradient y
pbyData[offset+2] = m_nRandomValues[value & 0x1F][2] *
NoiseHeight + min( NoiseHeight, m_NoiseValue ); // Gradient z
}
// Create texture with m_nImageWidth and m_nImageHeight.
if( !m_NoiseTexture.Create( m_nImageWidth, m_nImageHeight, pbyData ))
{
AfxMessageBox( L"Texture loading failed" );
return FALSE;
}
delete[] pbyData;
return true;
}
对于每个像素,从 NoiseTexture
中检索偏移值,并将此偏移量应用于原始纹理坐标。 这个新的纹理坐标用于查找 ImageTexture
,并获得 ImageTexture
的噪声输出。
创建噪声图像的着色器
uniform sampler2D ImageTexture;
uniform sampler2D NoiseTexture;
// Shader for creating a noise in ImageTexture.
void main()
{
// Get NoiseValue from Noisetexture.
vec4 Noise = texture2D(NoiseTexture, gl_TexCoord[0].st );
vec2 fLookupCoord =
gl_TexCoord[0].st + vec2( Noise.r * 0.065,-Noise.g * 0.025 );
gl_FragColor = texture2D(ImageTexture,fLookupCoord );
}
关注点
在之前的着色器程序中,发布版本在从资源读取像素着色器时会产生错误。 当我使用 strlen()
查找资源数据长度时,一些意外的数据被附加到末尾。 因此,着色器程序的编译失败。 我使用 SizeOfResource()
查找资源数据的长度,并修复了该问题。
bool GLSLShader::CreateProgram( const int nProgramID_i, GLenum glSlShaderType_i )
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
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 );
// Get Length of shader program. When I used strlen() to get length of string,
// I got some unexpected data at the end of progrm
int nLENGTH = SizeofResource( 0, hrResInfo );
// Shder creation code
// .. ... ....
}
历史
- 2010 年 7 月 4 日:初始版本。
- 2010 年 7 月 5 日:更新了二进制文件,因为之前的二进制文件依赖于 Visual Studio 2008 运行时。