65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.55/5 (11投票s)

2010年7月4日

CPOL

2分钟阅读

viewsIcon

52061

downloadIcon

2319

一个简单的着色器程序, 用于在图像中创建噪声效果

NoiseImage/screenshot.jpg

噪声应用程序的截图

引言

这是一个简单的像素着色器程序,它会使用噪声值改变输入的纹理坐标,并创建一个噪声输出图像。

背景 

使用像素着色器在图像中创建任何效果都非常简单。本文演示了一种在图像中创建简单的噪声效果的方法。

使用方法  

片段程序将为渲染的每个像素执行。当渲染整个纹理片段时,着色器将获得渲染的每个像素对应的纹理坐标。

如果使用提供的纹理坐标进行纹理查找以进行显示,则渲染的图像将与输入图像相同。

一个示例着色器显示与输入图像相同的纹理图像

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 运行时。
© . All rights reserved.