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 引言 ...
随机推荐
- python3 进程_multiprocessing模块
'''多进程优点:可以利用多核,实现并行运算缺点:1.开销太大: 2.通信困难使用方式跟开多线程一样''' 多进程 import multiprocessing import time,os def ...
- python3 面向对象、类、继承、组合、派生、接口、子类重用父类方法
对象是特征(变量)与技能(函数)的结合体而类是一系列对象共同的特征与技能的集合体 class teacher: lesson = "python" def __init__(sel ...
- 【bzoj3439】KPM的MC密码
这题乍一看后缀相等很烦的样子…… 其实如果把字符串倒过来,那么相等的后缀就可以转化成前缀,前缀相等扔进trie就可以了. 剩下无非是Trie的树链kth,主席树随便维护就好. 注意一个串彻底结束才能打 ...
- Newtonsoft.Json 序列化和反序列化 以及时间格式 2 高级使用
手机端应用讲究速度快,体验好.刚好手头上的一个项目服务端接口有性能问题,需要进行优化.在接口多次修改中,实体添加了很多字段用于中间计算或者存储,然后最终用Newtonsoft.Json进行序列化返回数 ...
- [转]C++ 指针和引用
转自http://www.cnblogs.com/tangxiaobo199181/ 作者:算法生活 微信公众号:算法生活 出处:http://www.cnblogs.com/tangxiaobo19 ...
- Selenium2+python自动化28-table定位【转载】
前言 在web页面中经常会遇到table表格,特别是后台操作页面比较常见.本篇详细讲解table表格如何定位. 一.认识table 1.首先看下table长什么样,如下图,这种网状表格的都是table ...
- node.js的全局变量的注意
在node.js中,如果一个变量没有用var来声明,就会变为全局变量: 看如下代码: 1)6.js function myadd(a) { return a+abc; } function conta ...
- 第一篇:Hello World
Hello World RabbitMQ是一个消息代理:它接受和转发消息.你可以把它想象成一个邮局:当你把邮件放在邮箱里时,你可以确定邮差先生最终会把邮件发送给你的收件人.在这个比喻中,RabbitM ...
- BMP文件组成
BMP文件组成 BMP文件由文件头.位图信息头.颜色信息和图形数据四部分组成. 如图: 位图文件头BITMAPFILEHEADER 位图信息头BITMAPINFOHEADER 调色板Palette 实 ...
- React Native - 3 View, Text简介以及onPress & onLongPress事件
我们要生成如下的构图 直接上图,不解释. 如下图所示,定义函数,函数之间不需要逗号,在元素上添加事件,使用关键字this.{function name}