我是在 Android 上使用 GLES 2.0 的新手,我正在尝试使用 GLSL 创建一个火焰形着色器。我尝试从以下链接应用着色器: https://www.shadertoy.com/view/MdKfDh
然而,我得到的结果并不令人满意。
我创建的图像看起来像火焰在对角线流动并且形状在垂直方向上过于拉伸,而“shadertoy”上的火焰着色器给人以火焰爆发的感觉。我写的glsl片段代码是这样的:
precision mediump float;
#define timeScale iTime * 1.0
#define fireMovement vec2(-0.01, -0.5)
#define distortionMovement vec2(-0.01, -0.3)
#define normalStrength 40.0
#define distortionStrength 0.1
uniform vec2 screenSize;
uniform float progress;
// #define DEBUG_NORMAL
/** NOISE **/
float rand(vec2 co) {
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
vec2 hash( vec2 p ) {
p = vec2( dot(p,vec2(127.1,311.7)),
dot(p,vec2(269.5,183.3)) );
return -1.0 + 2.0*fract(sin(p)*43758.5453123);
}
float noise( in vec2 p ) {
const float K1 = 0.366025404; // (sqrt(3)-1)/2;
const float K2 = 0.211324865; // (3-sqrt(3))/6;
vec2 i = floor( p + (p.x+p.y)*K1 );
vec2 a = p - i + (i.x+i.y)*K2;
vec2 o = step(a.yx,a.xy);
vec2 b = a - o + K2;
vec2 c = a - 1.0 + 2.0*K2;
vec3 h = max( 0.5-vec3(dot(a,a), dot(b,b), dot(c,c) ), 0.0 );
vec3 n = h*h*h*h*vec3( dot(a,hash(i+0.0)), dot(b,hash(i+o)), dot(c,hash(i+1.0)));
return dot( n, vec3(70.0) );
}
float fbm ( in vec2 p ) {
float f = 0.0;
mat2 m = mat2( 1.6, 1.2, -1.2, 1.6 );
f = 0.5000*noise(p); p = m*p;
f += 0.2500*noise(p); p = m*p;
f += 0.1250*noise(p); p = m*p;
f += 0.0625*noise(p); p = m*p;
f = 0.5 + 0.5 * f;
return f;
}
/** DISTORTION **/
vec3 bumpMap(vec2 uv, vec2 resolution) {
vec2 s = 1. / resolution;
float p = fbm(uv);
float h1 = fbm(uv + s * vec2(1., 0));
float v1 = fbm(uv + s * vec2(0, 1.));
vec2 xy = (p - vec2(h1, v1)) * normalStrength;
return vec3(xy + .5, 1.);
}
vec3 constructCampfire(vec2 resolution, vec2 normalized, float time) {
vec3 normal = bumpMap(normalized * vec2(1.0, 0.3) + distortionMovement * time, resolution);
vec2 displacement = clamp((normal.xy - .5) * distortionStrength, -1., 1.);
normalized += displacement;
vec2 uvT = (normalized * vec2(1.0, 0.5)) + time * fireMovement;
float n = pow(fbm(8.0 * uvT), 1.0);
float gradient = pow(1.0 - normalized.y, 2.0) * 5.;
float finalNoise = n * gradient;
return finalNoise * vec3(2.*n, 2.*n*n*n, n*n*n*n);
}
void main() {
vec2 resolution = screenSize;
vec2 normalized = gl_FragCoord.xy / resolution;
vec3 campfire = constructCampfire(resolution, normalized, progress);
gl_FragColor = vec4(campfire, 1.0);
}
如您所见,我编写的 GLSL 代码与“shadertoy”中的代码非常相似。不同之处在于,我从 Android 端传入了
screenSize
(像素)和 progress
(浮秒)。供您参考,上面的代码是片段着色器,我已经使用 gl_Position
调整了整个屏幕的顶点着色器。我不确定为什么结果不是我所期望的。
另外,我还有一个问题,大约 10 秒后,图像变得像素化并逐渐变成方形火焰。
当
progress
值变大时似乎是个问题,但我不确定原因,也不知道如何解决。
如果有人知道这些问题的原因和解决方法,我将不胜感激。谢谢。
我可以告诉你,问题不在于你的着色器代码。我拿了你的shader代码,直接放到ShaderToy里面,把
progress
变量换成iTime
变量,把gl_FragCoord.xy
换成fragCoord
,把screenSize
换成iResolution.xy
。
注意,我也将
normalized
的所有引用更改为 uv
以匹配原始内容。
因此,问题必须与输入不匹配 ShaderToy 输入正确。我希望您的
screensize
传输错误,因为看起来屏幕要么提供旋转值,要么以其他方式被压缩。
编辑:此外,尝试将手机更改为横向模式或制作一个与示例具有相同纵横比的自由浮动四边形。因为它使用数字噪声,所以我希望选择数字以便它们仅适用于特定的纵横比。风景不是肖像。
此外,检查以确保您将进度传递为 0 秒 -> X 秒值,而不仅仅是系统时钟调用。
否则,我可以说代码中没有错误,并且返回到 ShaderToy 的反向实现按预期工作。
我使用 ShaderToy 对您的着色器进行结构更改的重新实现可以在以下位置找到:https://www.shadertoy.com/view/ct3GDS
噪声功能因巨大的
progress
值(大位移)而被破坏。它遭受精度损失,直到你可以用自己的眼睛看到幸存的比特。
一旦超过 100 秒左右,您就需要以某种方式将
progress
包裹起来。鉴于您的噪音不可平铺,您需要在环绕和 lerp 过渡阶段时使用模数对其进行两次采样。