zoukankan      html  css  js  c++  java
  • GLSL实现Glow效果 [转]

    http://blog.csdn.net/a3070173/archive/2008/11/04/3220940.aspx

     Glow即辉光效果现在已成为3D图形中一个引人注目的特效.本文主要介绍如何使用GLSL实现一个典型的GLow效果.
     实现步骤:1.渲染整个场景到一个祯缓冲区中
        2.将场景中需要进行GLow处理的物体绘制第二个FBO纹理A中
        3.在FBO纹理A和B之间进行横和纵"高斯"过滤
        4.将进行过GLow处理后的FBO纹理A与祯缓冲区中的场景图像以glBlendFunc(GL_ONE, GL_ONE)方式进行混合处理
       
     GLSL文件功能简介:
     FullScreen.vert - 用于绘制覆盖整个视口的四边形以进行Glow效果的高斯过滤
     Filter.frag - 用于横和纵的高斯过滤
     Blend.frag - 用于处理过的GLowFBO纹理与原始场景图像进行混合

     为了直接进行生成Glow效果的介绍,这里假设程序已正确处理了OpenGL和GLSL的初始化.

    void RenderOrigionalScene()
    {
     if (g_bUseFillRender)
     {
      glPolygonMode(GL_FRONT, GL_FILL);
     }
     else
     {
      glPolygonMode(GL_FRONT, GL_LINE);
     }
     RenderObject();
    }
     首先让我们绘制原始场景,由于本Demo未绘制除辉光物体外的其它事物,所以此处就直接绘制为进行具有辉光效果的物体.

    void RenderGlowObject()
    {
     // 设置视口
     glViewport(0, 0, g_uiTextureWidth, g_uiTextureHeight);
     // 将原始场景绘制到第二个FBO
     glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_uiFboColorOne);
     // 清除第一个FBO颜色和深度缓存
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     // 绘制辉光物体
     RenderObject();
    }
     然后设置绘制目标到FBO纹理A并将欲进行Glow处理的物体绘制到上面来,这里需要注意的是必须根据FBO纹理的尺寸设置一个
     视口使其跟FBO纹理一样大,以使物体能够准确地映射到整个FBO纹理上.清除FBO颜色缓冲区和绘制深度缓冲区是必要的,因为
     每次绘制到FBO纹理中的图像都不一样.

    void FilterGlowObject()
    {
     glPolygonMode(GL_FRONT, GL_FILL);
     // 将水平过滤后的图像绘制第二个FBO
     glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_uiFboColorTwo);
     // 清除第二个FBO颜色
     glClear(GL_COLOR_BUFFER_BIT);
     // 重新设置片元着色器
     glUseProgram(g_ProgramObjectOne);
     // 设置水平过滤标志
     GLint iUniformIndex = glGetUniformLocation(g_ProgramObjectOne, "g_bFiterMode");
     glUniform1i(iUniformIndex, 1);
     // 设置纹理
     glBindTexture(GL_TEXTURE_2D, g_uiIDOne);
     // 绘制
     RenderFullScreen();

     // 将竖直过滤后的图像绘制第一个FBO
     glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_uiFboColorOne);
     // 清除第一个FBO颜色和深度缓存
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     // 设置竖直过滤标志
     iUniformIndex = glGetUniformLocation(g_ProgramObjectOne, "g_bFiterMode");
     glUniform1i(iUniformIndex, 0);
     // 设置纹理
     glBindTexture(GL_TEXTURE_2D, g_uiIDTwo);
     // 绘制
     RenderFullScreen();
    }
     下面到Glow效果处理的重头戏,是否能生成完美的辉光效果关键就在于此步的处理.但其实也很简单,主要就是为Filter着色器设
     置进行合适横,纵两次过滤的标志和绘制目标,然后绘制全视口四边形,剩下的过滤工作则由GLSL的高斯过滤着色器全权负责.

    void RenderToScreen()
    {
     // 恢复视口
     glViewport(0, 0, g_uiCurrentWindowWidth, g_uiCurrentWindowHeight);
     // 恢复绘制目标为祯缓冲区
     glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, NULL);
     // 启动混合
     glEnable(GL_BLEND);
     glBlendFunc(GL_ONE, GL_ONE);
     // 绑定纹理
     glBindTexture(GL_TEXTURE_2D, g_uiIDOne);
     // 重新设置片元着色器
     glUseProgram(g_ProgramObjectTwo);
     // 绘制
     RenderFullScreen();
     // 恢复固定功能管线
     glUseProgram(0);
     // 关闭混合
     glDisable(GL_BLEND);
    }
     最后一步无非就是将过滤好的Glow纹理与原始场景图像进行混合,当然使用OpenGL固定功能管线或GLSL都可以轻易实现,但首先必
     须把视口设置回原来的状态.

     以下是高斯过滤的GLSL着色器代码,粘贴于此以方便读者查阅.
     顶点着色器:
    void main()
    {
     gl_TexCoord[0] = gl_MultiTexCoord0;
        gl_Position = gl_Vertex;
    }
     高斯过滤着色器:
    const int g_iFilterTime = 9; // 过滤次数
    const float g_fGene = (1.0/(1.0 + 2.0*(0.93 + 0.8 + 0.7 + 0.6 + 0.5 + 0.4 + 0.3 + 0.2 + 0.1))); // 衰减因子

    uniform sampler2D g_Decal;
    uniform bool g_bFiterMode;
    uniform float g_fGlowGene;
    uniform vec2 g_vec2HorizontalDir; // 水平过滤方向
    uniform vec2 g_vec2VerticalDir;  // 竖直过滤方向
    uniform float g_fFilterOffset; // 过滤偏移

    void main()
    {
     float aryAttenuation[g_iFilterTime];
     aryAttenuation[0] = 0.93;
     aryAttenuation[1] = 0.8;
     aryAttenuation[2] = 0.7;
     aryAttenuation[3] = 0.6;
     aryAttenuation[4] = 0.5;
     aryAttenuation[5] = 0.4;
     aryAttenuation[6] = 0.3;
     aryAttenuation[7] = 0.2;
     aryAttenuation[8] = 0.1;

     // 采样原始颜色
     vec2 vec2Tex0 = gl_TexCoord[0].st;
     vec4 vec4Color = texture2D(g_Decal, vec2Tex0)*g_fGene;

     // 计算过滤方向
     vec2 vec2FilterDir = g_vec2HorizontalDir + vec2(g_fFilterOffset, 0.0); // 水平过滤
     if (!g_bFiterMode)
     {
      vec2FilterDir = g_vec2VerticalDir + vec2(0.0, g_fFilterOffset); // 竖直过滤
     }

     // 进行过滤
     vec2 vec2Step = vec2FilterDir;
     for(int i = 0; i< g_iFilterTime; ++i)
     {
      vec4Color += texture2D(g_Decal, vec2Tex0 + vec2Step)*aryAttenuation[i]*g_fGene;
      vec4Color += texture2D(g_Decal, vec2Tex0 - vec2Step)*aryAttenuation[i]*g_fGene;
      vec2Step += vec2FilterDir;
     }

     if (g_bFiterMode)
     {
      gl_FragColor = vec4Color*g_fGlowGene;
     }
     else
     {
      gl_FragColor = vec4Color;
     }
    }

     混合着色器:
    uniform sampler2D g_Decal;

    void main()
    {
     gl_FragColor = texture2D(g_Decal, gl_TexCoord[0].st);
    }

    Demo效果图:


     

    参考资料:Nvidia OpenGL SDK 10.5 Simple Glow
    exe文件:http://www.fileupyours.com/view/219112/GLSL/Glow%20Demo.rar

  • 相关阅读:
    C#基本知识
    C#集合
    python学习DAY10(异常)
    python学习DAY10(属性方法)
    python学习DAY10(反射)
    python学习DAY10(类方法)
    python学习DAY10(模块)
    基于Servlet构建基础的后台服务器
    时间类型及格式化
    SpringCloud中服务消费者接受前台传参问题(报错415)
  • 原文地址:https://www.cnblogs.com/mazhenyu/p/3806607.html
Copyright © 2011-2022 走看看