[glsl]着色器周记02——火焰特效

3D入魔 3D入魔     2022-08-28     699

关键词:

http://www.cnblogs.com/tkgamegroup/p/4214081.html

这周学了好多。包括伪随机数。柏林噪声。
先说伪随机数。伪随机数我们用的是周期函数而不是那种由前一项乘一个超大的数取余数的方法。使用周期函数的好处就是可以让其随时间均匀变化。不过使用周期函数一定要保证周期非常长,不然就会出现重复的图样。
这是我在网上找到的一个伪随机函数:
cos(x * (12.9898) + y * (4.1414)) * 43758.5453
它使用x, y作为参数,刚好对应像素的坐标。(PS:发现好多GLSL的例子都用类似的随机函数,应该里面蕴含了什么数学吧。) 
这个函数的周期应该挺长的, 会发现在cos的后面还乘上了一个超大的数,这样可以达到不错的伪随机效果,不过还需要很重要的一步。
最终的随机函数是这样的:
fract(cos(x * (12.9898) + y * (4.1414)) * 43758.5453)
fract这个glsl预定义的函数的作用是返回小数部分,会达到非常不错的效果。
 
↑这是没有加上fract的结果,因为所有大于1的值都变成1了。(GL中像素的颜色值范围是0.0-1.0) 
 
↑这是加上fract的效果,看起来还不错是吧。
PS:
在想随机数的时候,我想人工智能是不是其实是可以实现的呢。因为智能可能就是对一个个小事件的反应,当这些对各种事情的反应多起来的时候,数量趋近于无穷,或者达到一个阀值,就会表现出智能。因为人类的大脑说不定也是这样,是不是也是对一个个小事件的反应呢,我想是的,大脑里面这种反应非常多,多到了一个阀值,所以才会变现出“智能”,而自由意志应该就是量子随机性造成的,而至于为什么不觉得这种随机不想是“随机”的呢,而想是可控的呢?因为这些随机性的东西都会经过一个个在大脑中设定好的程序,被这样处理过的信息之后,看起来就变得“有序”了。(稍后你会看到上面这些这样随机的图样,最终会变成一幅不错的图画)所以,人工智能终有一天是会实现的,只要不断地设定对各种事件的反应,让它看起来“好像是智能的样子呢”,当这种“好像是智能的样子呢”多起来的时候,达到了一个阀值,那么这个东西就是真正的智能了,这里可没有双引号。这里也稍微的用一下数学里面的极限思想了呢,0.999...这样无穷下去,数学上的结果就是1。不过目前想要达到这个阀值,需要的是非常高性能的计算机还有聪明的程序员,至少前者还不能实现,在未来20年之后,估计就能实现看起来非常智能的人工智能了。
哎呀,扯多了。

接下来是一个火焰特效,看起來像20年前的技術,哈哈。由浅入深嘛。慢慢地就会学到现代的技术了。
 
 

看起来还阔以吧。

不知道gif能不能播放,不过gif画质太渣了,还是不用gif了。你就想想这个火焰是会动的就行啦。

接下来是柏林噪声(Perlin噪声)了,Ken Perlin在1981年为电影创造了这种噪声,它可以模拟自然的云、火焰、水面等,为电影特效做出了功不可没的贡献,为此,他获得了“奥斯卡技术成就奖”。

Perlin噪声就是n个函数的叠加,这n个函数满足,下一个的频率是前一个的两倍,幅度是二分之一。而函数嘛,当然是选择我们上面的伪随机函数啦。

不过首先,我们要对屏幕的像素分组。我的分组是250*250个一组。 也就是这250*250个像素共享同一个随机的值。然后,对于每个小组,每个小组里面的像素,进行一次插值,(可以是线性插值(假如在A,,B两点间插值,t(0<=t<=1)是距离A的距离比例,那么t位置的值是(1 - t) * A + t * B,(1 - t)是插值函数),不过效果差,Perlin本人推荐 3 * t * t - 2 * t * t * t,后来推荐..哎呦 这个好长,就不写出来了)二维的插值是这样的,首先取本小组的值和右边小组的值在x方向插值得到a,然后是下面小组的值和右下小组的值在x方向插值得到b,最后就是a和b在y方向插值得到最终结果。
glsl里面有预定义插值函数,线性的mix和平滑的smoothstep。

所以,代码看起来是这样的:


#define r 250
float noise(float x, float y)
{
int base_x = int(floor(x / r)); // 小组的x编号
int base_y = int(floor(y / r)); // 小组的y编号
x = fract(x / r); // 像素在小组内x方向的权值
y = fract(y / r); // 像素在小组内y方向的权值
float f_x = smoothstep(0, 1, x); // 平滑后的值
float f_y = smoothstep(0, 1, y); // 平滑后的值
return
mix(
mix(rand(float(base_x), float(base_y)), rand(float(base_x + 1), float(base_y)), f_x) // 第一次的x方向插值
,
mix(rand(float(base_x), float(base_y + 1)), rand(float(base_x + 1), float(base_y + 1)), f_x) // 第二次的x方向插值
,
f_y) // 最后的y方向插值
;



看起来是这样的:
 

哇..看起来好爽。

接下来就是Perlin噪声了, 请看代码:

float fbm(float x, float y)
 {
float total = 0.0, amplitude = 1.0;
for (int i = 0; i < 4; i++) // 4个函数叠加
{
total += noise(x, y) * amplitude; 
x += x;  // 频率乘以2
y += y;  // 频率乘以2
amplitude *= 0.5; // 幅度乘以二分之一
}
return total;



效果是这样的:

我已经看到了一点云的样子了呢。

接下来,加点颜色,你可以自由发挥,这里只是一个例子:

t = float(_t);
float z = fbm(gl_FragCoord.x - t * 100, gl_FragCoord.y - t * 100);
float x = fbm(gl_FragCoord.x + t * 60, gl_FragCoord.y - t * 40);
float c = fbm(gl_FragCoord.x - t * 70, gl_FragCoord.y - t * 80);
vec3 color1 = vec3(1.0, 0.9, 0.0);
vec3 color2 = vec3(1.0, 0.0, 0.0);
vec3 color3 = vec3(0.0, 0.0, 0.0);
vec3 color4 = vec3(0.2, 0.2, 0.2);
vec3 color5 = vec3(0.0, 0.0, 0.6);
vec3 color6 = vec3(0.0, 0.6, 0.0);

gl_FragColor = vec4(mix(color2, color1, z) + mix(color3, color4, x) - mix(color5, color6, c), 1.0); 
就可以达到火焰特效了,要更加逼真的效果就需要不断的尝试颜色的组合,或者加入数学知识。

对了,漏了最重要的一点,就是怎么向着色器里面传一个值。

用到的是uniform技术。

在glsl代码中,你只要这样:

uniform int _t;
 
就可以声明一个_t的uniform变量。uniform在一次渲染中值不会改变,所有着色器都可以访问这个值。程序也可以在每一帧时任意修改uniform的值。用这样的语句:

 
GLint location;
location = glGetUniformLocation(program, "_t");

glUniform1i(location, _t); 

**以上参考:http://www.tuicool.com/articles/VFnAFbB。火焰特效是借鉴http://glslsandbox.com/里面的一个特效,不过原文的地址我忘了T T。

好了。周记完毕,在最后送一个小特效:

void main() 
{
vec3 color = vec3(0.0, 0.0, 0.0);
float angle = t * t * 0.1;
vec2 q;
q.x = p.x + cos(angle * 3.14 / 180) * 10;
q.y = p.y + sin(angle * 3.14 / 180) * 10;
float A = q.y - p.y;
float B = p.x - q.x;
float C = q.x * p.y - p.x * q.y;
float rate;
rate = A * gl_FragCoord.x + B * gl_FragCoord.y + C;
rate = rate * rate;
rate = rate / (A * A + B * B);
rate = 100 / rate;
color.x = rate * 0.0;
color.y += rate * 0.0;
color.z += rate * 0.8;
float x, y;
x = gl_FragCoord.x - 1366.0 / 2;
y = gl_FragCoord.y - 768.0 / 2;
rate = x * x + y * y;
rate = sqrt(rate);
int temp = _t % 20;
rate -= (temp * temp);
if(rate < 0)
rate = -rate;
rate *= rate;
rate = 100 / rate;
color.x = rate * 0.8;
color.y += rate * 0.0;
color.z += rate * 0.0;
gl_FragColor = vec4(color, 1.0);

}

为啥我的着色器中的 GLSL 纹理坐标不是线性的?

】为啥我的着色器中的GLSL纹理坐标不是线性的?【英文标题】:WhyaretheGLSLtexture-coordinatesinmyshadernotlinear?为什么我的着色器中的GLSL纹理坐标不是线性的?【发布时间】:2012-02-1703:35:47【问题描述】:我的Android2.3.4Tegra2设备上的gls... 查看详情

可以将 GLSL 着色器应用于 SDL_Textures 吗?

】可以将GLSL着色器应用于SDL_Textures吗?【英文标题】:CanGLSLshadersbeappliedtoSDL_Textures?【发布时间】:2016-02-2802:41:58【问题描述】:我正在开发一款游戏,我想知道是否可以在不需要OpenGL纹理的情况下将GLSL着色器应用于SDL_Textures... 查看详情

有啥方法可以调试 GLSL 着色器?

】有啥方法可以调试GLSL着色器?【英文标题】:IsThereAWayICanDebugAnGLSLShader?有什么方法可以调试GLSL着色器?【发布时间】:2014-01-1703:24:38【问题描述】:有没有办法调试glsl着色器?包括断点和数据跟踪我看到了一些简单的,让我... 查看详情

GLSL:无法从 FBO 读取纹理并使用片段着色器渲染到另一个 FBO

】GLSL:无法从FBO读取纹理并使用片段着色器渲染到另一个FBO【英文标题】:GLSL:unabletoreadtexturefromaFBOandrendertoanotherFBOwithfragmentshader【发布时间】:2012-02-1015:48:08【问题描述】:我正在尝试“读取”附加到第一个FBO(fboA)的纹理,修... 查看详情

GLSL着色器加载器问题[关闭]

】GLSL着色器加载器问题[关闭]【英文标题】:GLSLshaderloaderproblem[closed]【发布时间】:2010-08-0518:47:06【问题描述】:问题是我的glsl加载器不工作,我看不出我做错了什么。voidcShader::Load(constchar*v_filename,constchar*f_filename)char*vs,*fs;vSha... 查看详情

运行时的 glsl 着色器编译问题

】运行时的glsl着色器编译问题【英文标题】:glslshadercompilationissueatruntime【发布时间】:2011-11-0600:41:01【问题描述】:我正在开发一个使用OpenGL4.0着色器的项目。我必须为glShaderSource()的调用提供一个字符数组数组,它​​表示... 查看详情

GLSL:使用片段着色器进行对象翻译

】GLSL:使用片段着色器进行对象翻译【英文标题】:GLSL:Objecttranslationwithfragmentshader【发布时间】:2015-01-2816:26:32【问题描述】:如下图所示,我试图通过多画两次来表达轮廓:左右移动1个像素。但是,我不知道这应该在顶点着... 查看详情

如何使用 Nsight 调试(GLSL)着色器?

】如何使用Nsight调试(GLSL)着色器?【英文标题】:Howtodebug(GLSL)shadersusingNsight?【发布时间】:2017-01-2403:41:00【问题描述】:如何使用Nsight调试glsl着色器?我正在使用NsightVisualStudio版本5.2。我试过使用NsightVisualStudioEdition5.1。这... 查看详情

GLSL/C++:自定义着色器的默认行为

】GLSL/C++:自定义着色器的默认行为【英文标题】:GLSL/C++:Defaultbehaviorfromcustomshader【发布时间】:2011-11-1200:09:54【问题描述】:我有一些GLSL着色器正在正确编译,并成功地附加到正确链接的程序。我已经确认其他着色器在我的... 查看详情

OpenGL、GLSL 片段着色器无法读取 Sampler2D 纹理

】OpenGL、GLSL片段着色器无法读取Sampler2D纹理【英文标题】:OpenGL,GLSLfragmentshadercan\'treadSampler2Dtexture【发布时间】:2019-02-2007:53:31【问题描述】:我目前正在为我的项目使用OpenGL,并且正在努力将纹理数据发送到sampler2D。我想在... 查看详情

GLSL - 无所事事的顶点着色器?

】GLSL-无所事事的顶点着色器?【英文标题】:GLSL-Ado-nothingvertexshader?【发布时间】:2014-05-1804:20:27【问题描述】:所以我有一个在对象上绘制一组的opengl程序。当我绘制这些对象时,我想使用我的着色器程序是一个顶点着色器... 查看详情

GLSL 着色器中的翻译

】GLSL着色器中的翻译【英文标题】:translationinGLSLshader【发布时间】:2015-11-1915:09:31【问题描述】:我正在尝试在顶点GLSL着色器中移动图形:layout(location=0)invec3Position;layout(location=1)invec3offset;uniformmat4ProjectionViewMatrix;voidmain()vec3new... 查看详情

从 GLSL 着色器中获取常量

】从GLSL着色器中获取常量【英文标题】:GettingaconstantfromaGLSLshader【发布时间】:2014-10-2403:44:22【问题描述】:我有一个用GLSL编写的着色器,其中包含用于保存光照数据的结构数组。我使用一个常量来声明数组大小,这是一种很... 查看详情

默认的 GLSL 着色器是啥样的?对于版本 330

】默认的GLSL着色器是啥样的?对于版本330【英文标题】:HowdoesthedefaultGLSLshaderslooklike?forversion330默认的GLSL着色器是什么样的?对于版本330【发布时间】:2011-12-2602:07:10【问题描述】:版本#330的默认顶点、片段和几何GLSL着色器是... 查看详情

如何在 C++ 中为 GLSL 片段着色器实现 iGlobalTime?

】如何在C++中为GLSL片段着色器实现iGlobalTime?【英文标题】:HowtoimplementiGlobalTimeforaGLSLfragmentshaderinC++?【发布时间】:2016-09-2823:15:31【问题描述】:目前我有一个小型C++应用程序,它通过这两个着色器显示一个三角形:顶点着色... 查看详情

去除 GLSL 着色器产生的波纹图案

】去除GLSL着色器产生的波纹图案【英文标题】:RemovingmoirepatternsproducedbyGLSLshaders【发布时间】:2016-04-0401:59:46【问题描述】:我已经设置了这个最小的测试用例,您可以轻松地看到通过使用自定义片段着色器(jsfiddle)对振荡红色... 查看详情

打破 GLSL 着色器指令限制的错误消息是啥?

】打破GLSL着色器指令限制的错误消息是啥?【英文标题】:WhataretheerrormessagesforbreakingtheGLSLshaderinstructionlimits?打破GLSL着色器指令限制的错误消息是什么?【发布时间】:2009-08-1313:55:25【问题描述】:我们是一个小型开发团队,... 查看详情

为啥 OSX Mavericks 不能编译我的 GLSL 着色器?

】为啥OSXMavericks不能编译我的GLSL着色器?【英文标题】:Whywon\'tOSXMaverickscompilemyGLSLShader?为什么OSXMavericks不能编译我的GLSL着色器?【发布时间】:2014-01-0719:34:16【问题描述】:这个着色器程序编译得很好#version120voidmain()gl_FrontCol... 查看详情