GraphicsLab Project之辉光(Glare,Glow)效果 【转】
作者:i_dovelemon
日期:2016 / 07 / 02
来源:CSDN
主题:Render to Texture, Post process, Glare, Glow, Multi-pass rendering
引言
辉光(Glare,Glow)
原理
下面给大家讲述下这种效果的实现方式。其实说白了它的实现原理很简单,我们只要对要添加辉光效果的物体进行一次模糊操作,然后将模糊过后的图片与原先的图片进行Alpha
Blend,就能够得到上面所示的效果。操作如图所展示的那样(这里为了简便,只在场景中绘制Glare物体,并且只添加了些许光照,没有添加其他效果):
OpenGL实现
渲染到纹理(Render to Texture, RTT)
在图形学处理中,我们经常需要对渲染好的场景进行一些处理。也就是说,我们需要一种方式,能够让我们通过图形API(OpenGL,
DirectX)绘制的场景保存到我们指定的内存(显存)中去,以便于我们后期对他进行一些处理。而在OpenGL中,我们能够很容易的实现这一点。这种技术在我的博客中也有讲到。大家可以根据博客中的描述和代码对此进行了解。
后处理(Post-processing)
后处理在图形学中经常被用到。我们在游戏中绘制的场景,往往是没有办法仅仅依靠光照模型计算就能够实现出来的。很多的特殊效果,都是通过对渲染好的场景进行图像空间的后期出理来得到。比如本篇文章中实现的辉光效果,就使用了这种技术。还有很多其他在图像空间进行后处理的特效,感兴趣的同学可以阅读Real-time
Rendering中关于此技术的章节。
图像模糊(Blur)
高斯模糊在图像处理的时候,是使用2维的高斯分布函数对一个像素的上下左右包围的像素进行采样来实现的。我们假设模糊的半径为20个像素,按照高斯模糊的原始计算方式,每一个像素都需要计算(2
* 20) ^ 2次采样。这在图像处理上面可以接受,但是对于实时性的游戏来说,计算量就有点大了。
从图中,我们可以看到,我们先对原场景图进行一次横向的模糊计算,此次计算我们只需要采用一维的高斯分布进行计算即可。然后对横向模糊之后的图像进行纵向模糊,与横向模糊一样,使用一维的高斯分布函数进行。这样就能够得到一张高斯模糊之后的图像,它的模糊效果不比二维高斯模糊效果来的差。
OpenGL绘制流程
- void glb_display() {
- glb_draw_normal_scene();
- glb_draw_render_target_gauss_blur_h();
- glb_draw_render_target_gauss_blur_v();
- glb_draw_blend();
- glutSwapBuffers();
- }
上面的代码给出了我绘制的流程:
Shader计算
- light_pixel.vs
- //--------------------------------------------------------------------
- // Declaration: Copyright (c), by i_dovelemon, 2016. All right reserved.
- // Author: i_dovelemon[1322600812@qq.com]
- // Date: 2016 / 06 / 29
- // Brief: Phong lights's vertex shader
- //--------------------------------------------------------------------
- #version 330
- in vec3 vertex;
- in vec3 normal;
- uniform mat4 proj;
- uniform mat4 view;
- uniform mat4 world;
- uniform mat4 trans_inv_world;
- out vec3 vs_vertex;
- out vec3 vs_normal;
- void main() {
- gl_Position = proj * view * world * vec4(vertex, 1.0);
- vs_vertex = (world * vec4(vertex, 1.0)).xyz;
- vs_normal = (trans_inv_world * vec4(normal, 0.0)).xyz;
- }
- light_pixel.ps
- //--------------------------------------------------------------------
- // Declaration: Copyright (c), by i_dovelemon, 2016. All right reserved.
- // Author: i_dovelemon[1322600812@qq.com]
- // Date: 2016 / 06 / 29
- // Brief: Phong lights's pixel shader
- //-------------------------------------------------------------------
- #version 330
- in vec3 vs_vertex;
- in vec3 vs_normal;
- out vec4 color;
- uniform vec3 parallel_light_dir;
- uniform vec3 parallel_light_ambient;
- uniform vec3 parallel_light_diffuse;
- uniform vec3 parallel_light_specular;
- uniform vec3 material_ambient;
- uniform vec3 material_diffuse;
- uniform vec3 material_specular;
- uniform float material_specular_pow;
- uniform vec3 eye_pos;
- vec3 calc_diffuse(vec3 light_vec, vec3 normal, vec3 diffuse, vec3 light_color) {
- float ratio = dot(light_vec, normal);
- ratio = max(ratio, 0.0);
- return diffuse * light_color * ratio;
- }
- vec3 calc_specular(vec3 light_vec, vec3 normal, vec3 view_vec, vec3 specular, vec3 light_color, float pow_value) {
- vec3 ref_light_vec = reflect(light_vec, normal);
- float ratio = dot(ref_light_vec, view_vec);
- ratio = max(ratio, 0.0);
- ratio = pow(ratio, pow_value);
- return specular * light_color * ratio;
- }
- void main() {
- vec3 light_vec = -parallel_light_dir;
- vec3 normal = normalize(vs_normal);
- vec3 view_vec = normalize(eye_pos - vs_vertex);
- vec3 ambient = material_ambient * parallel_light_ambient;
- vec3 diffuse = calc_diffuse(light_vec, normal, material_diffuse, parallel_light_diffuse);
- vec3 specular = calc_specular(light_vec, normal, view_vec, material_specular, parallel_light_specular, material_specular_pow);
- color = vec4(ambient + diffuse + specular, 1.0);
- }
这两个Shader只是为了实现Phong式光照计算,给场景一点光亮,对于绘制辉光效果并不是必须的,但你总得有东西才能绘制辉光效果吧!
- gauss_blur.vs
- //--------------------------------------------------------------------
- // Declaration: Copyright (c), by i_dovelemon, 2016. All right reserved.
- // Author: i_dovelemon[1322600812@qq.com]
- // Date: 2016 / 06 / 29
- // Brief: Gauss blur pass through vertex shader
- //--------------------------------------------------------------------
- #version 330
- in vec3 vertex;
- in vec2 texcoord;
- out vec2 vs_texcoord;
- void main() {
- gl_Position = vec4(vertex, 1.0);
- vs_texcoord = texcoord;
- }
- gauss_blurh.ps
- //--------------------------------------------------------------------
- // Declaration: Copyright (c), by i_dovelemon, 2016. All right reserved.
- // Author: i_dovelemon[1322600812@qq.com]
- // Date: 2016 / 06 / 29
- // Brief: Gauss blur horizontal pass shader
- //--------------------------------------------------------------------
- #version 330
- in vec2 vs_texcoord;
- out vec4 color;
- uniform sampler2D tex;
- uniform float tex_width;
- uniform float gauss_num[21];
- void main() {
- color = texture2D(tex, vs_texcoord) * gauss_num[0];
- float step = 1.0 / tex_width;
- for (int i = 1; i < 21; i++) {
- if (vs_texcoord.x - i * step >= 0.0) {
- color += texture2D(tex, vec2(vs_texcoord.x - i * step, vs_texcoord.y)) * gauss_num[i];
- }
- if (vs_texcoord.x + i * step <= 1.0) {
- color += texture2D(tex, vec2(vs_texcoord.x + i * step, vs_texcoord.y)) * gauss_num[i];
- }
- }
- }
- gauss_blurv.ps
- //--------------------------------------------------------------------
- // Declaration: Copyright (c), by i_dovelemon, 2016. All right reserved.
- // Author: i_dovelemon[1322600812@qq.com]
- // Date: 2016 / 06 / 29
- // Brief: Gauss blur vertical pass shader
- //--------------------------------------------------------------------
- #version 330
- in vec2 vs_texcoord;
- out vec4 color;
- uniform sampler2D tex;
- uniform float tex_height;
- uniform float gauss_num[21];
- void main() {
- color = texture2D(tex, vs_texcoord) * gauss_num[0];
- float step = 1.0 / tex_height;
- for (int i = 0; i <21; i++) {
- if (vs_texcoord.y - i * step >= 0.0) {
- color += texture2D(tex, vec2(vs_texcoord.x, vs_texcoord.y - i * step)) * gauss_num[i];
- }
- if (vs_texcoord.y + i * step <= 1.0) {
- color += texture2D(tex, vec2(vs_texcoord.x, vs_texcoord.y + i * step)) * gauss_num[i];
- }
- }
- }
上面三个Shader组合完成了高斯模糊的计算。这里有几点需要注意。
(1)进行模糊计算的时候,逻辑上是以一个像素单位为步进值,但是由于纹理坐标是从[0,1],所以我们需要将一个像素的步进值计算为对应的纹理坐标步进。这个操作可以在Application阶段计算完成,然后传递到Shader中,这里博主偷懒了,直接在Shader里面计算了。
- float glb_gauss_num(int x) {
- float pi = 3.1415927f;
- float e = 2.71828f;
- float theta = 0.1f;
- float theta2 = theta * theta;
- float temp1 = 1.0f / (theta * sqrt(2 * pi));
- float temp2 = pow(e, -(x * x) / 2 * theta2);
- return temp1 * temp2;
- }
- void glb_calc_gauss_nums() {
- g_GaussNum[0] = 1.0f;
- for (int32_t i = 1; i < sizeof(g_GaussNum) / sizeof(g_GaussNum[0]); i++) {
- g_GaussNum[i] = glb_gauss_num(i);
- }
- float total = 0.0f;
- for (int32_t i = 0; i < sizeof(g_GaussNum) / sizeof(g_GaussNum[0]); i++) {
- total += g_GaussNum[i];
- }
- for (int32_t i = 0; i < sizeof(g_GaussNum) / sizeof(g_GaussNum[0]); i++) {
- g_GaussNum[i] = g_GaussNum[i] / total;
- }
- }
对于步骤(4),使用了如下的两个Shader:
- blend.vs
- //--------------------------------------------------------------------
- // Declaration: Copyright (c), by i_dovelemon, 2016. All right reserved.
- // Author: i_dovelemon[1322600812@qq.com]
- // Date: 2016 / 06 / 29
- // Brief: Blend pass through vertex shader
- //--------------------------------------------------------------------
- #version 330
- in vec3 vertex;
- in vec2 texcoord;
- out vec2 vs_texcoord;
- void main() {
- gl_Position = vec4(vertex, 1.0);
- vs_texcoord = texcoord;
- }
- blend.ps
- //--------------------------------------------------------------------
- // Declaration: Copyright (c), by i_dovelemon, 2016. All right reserved.
- // Author: i_dovelemon[1322600812@qq.com]
- // Date: 2016 / 06 / 29
- // Brief: Alpha blend shader
- //--------------------------------------------------------------------
- #version 330
- in vec2 vs_texcoord;
- out vec4 color;
- uniform sampler2D blur_tex;
- uniform sampler2D scene_tex;
- void main() {
- vec4 blur_color = texture2D(blur_tex, vs_texcoord);
- vec4 scene_color = texture2D(scene_tex, vs_texcoord);
- color = blur_color * 0.5 + scene_color * 0.5;
- }
最后的混合计算,我选取了模糊贴图的一半加上场景贴图的一半来进行混合操作。最终的效果如下图所示:
总结
上面文章为了简化操作,做了很多在实际项目中不可以的操作。比如在Shader中计算纹理步进值。而在实际的情况下,实现辉光效果远远要比这里展示的复杂的多,比如场景中有不发光的物体,和发光的物体,此时需要通过Alpha贴图来标识出场景图中哪些是需要进行模糊的,而哪些是不需要进行。又比如说,发光物体在某个不发光物体的后面,那么进行模糊计算的时候,必然会让发光物体渗透到不发光物体中,会产生比较粗糙的感觉。
参考文献
GraphicsLab Project之辉光(Glare,Glow)效果 【转】的更多相关文章
- GraphicsLab Project学习项目
作者:i_dovelemon 日期:2016 / 05 / 30 主题:3D,Graphics 引言 进公司以来,主要在学习的就是如何保证代码的质量,以前热爱的图形学也放置了.但是,作为游戏程序员,特 ...
- 支持辉光效果的Label
支持辉光效果的Label 效果 源码 https://github.com/YouXianMing/UI-Component-Collection 中的 FBGlowLabel // // FBGlo ...
- Shimmer辉光动画效果
Shimmer辉光动画效果 效果 源码 https://github.com/facebook/Shimmer https://github.com/YouXianMing/Animations // ...
- 使用CALayer制作View的辉光效果
使用CALayer制作View的辉光效果 实现以下的辉光效果: 思路是这样子的: 1. 创建好需要实现辉光效果的View 2. 对这个View进行截图 3. 将这个截图重新添加进View中 4. 对这 ...
- GLSL实现Glow效果 [转]
http://blog.csdn.net/a3070173/archive/2008/11/04/3220940.aspx Glow即辉光效果现在已成为3D图形中一个引人注目的特效.本文主要介绍如何使 ...
- 辉光UIView的category
辉光UIView的category 本人视频教程系类 iOS中CALayer的使用 效果如下: 源码: UIView+GlowView.h 与 UIView+GlowView.m // // UI ...
- 辉光的UIView
辉光的UIView 辉光UIView使用了一个UIView的一个category,名为UIView+Glow,请自行到github上查找. 源码如下: // // RootViewController ...
- GLSL实现Glow效果 【转】
http://blog.csdn.net/a3070173/article/details/3220940 Glow即辉光效果现在已成为3D图形中一个引人注目的特效.本文主要介绍如何使用GLSL实现一 ...
- GraphicsLab Project之Diffuse Irradiance Environment Map
作者:i_dovelemon 日期:2020-01-04 主题:Rendering Equation,Irradiance Environment Map,Spherical Harmonic 引言 ...
随机推荐
- Python小程序之用户登陆接口
编写登陆接口 输入用户名密码 认证成功后显示欢迎信息 输错三次后锁定 程序逻辑图: 代码: #!/usr/bin/env python #_*_ coding:UTF-8 _*_ #__author_ ...
- C++学习笔记之——内联函数,引用
本文为原创作品,转载请注明出处 欢迎关注我的博客:http://blog.csdn.net/hit2015spring和http://www.cnblogs.com/xujianqing/ 作者:晨凫 ...
- (12)Linux shell之read 用法
Linux shell之read 用法 #!/bin/bash#read 用来读取屏幕输入或是读取文件内容.read -p "please input you name: " ...
- 创建第一个maven项目的那些坑
1.记事本方式: class所在的目录结构: class的代码书写: package com.imooc.maven01.mode1; public class HelloWorld { public ...
- python--fnmatch
import fnmatch ''' 这个库专门是用来做文件名匹配的,可以使用通配符如下 * 匹配任何数量的任意字符 ? 匹配单个数量的任意字符 [seq] 匹配seq中的任意字符 [!seq] 匹配 ...
- JS:body元素对象的clientWidth、offsetWidth、scrollWidth、clientLeft、offsetLeft、scrollLeft
document.body.clientWidth 获取body元素对象的内容可视区域的宽度,即clientWidth=width+padding,不包括滚动条. document.body.clie ...
- 精确解释Unicode
来自:http://blog.csdn.net/gqqnb/article/details/6266542 ---------------------------------------------- ...
- serialVersionUID的作用(zz)
http://www.cnblogs.com/guanghuiqq/archive/2012/07/18/2597036.html 简单来说,Java的序列化机制是通过在运行时判断类的serialVe ...
- hdu 5747(数学,贪心)
Aaronson Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total ...
- DockerFile的编写和注意的一些知识点
CMD,RUN,ENTRYPOINT之类的差别. VOLUMN和-V之间的差别. EXPOSE和-P的对应等. 今天上午写了一个脚本,可以传参数进IMAGE,让启动的CONTAINER具有不同的行为. ...