Reputation: 43
I'm quite starter of using GLES 2.0 on Android and I am trying to create a Flame-shaped shader using GLSL. I tried applying the shader from the following link: https://www.shadertoy.com/view/MdKfDh
However, the result I got is not satisfactory.
The image I created looks like the flames are flowing diagonally and the shape is too stretched vertically, while the flame shader on "shadertoy" gives the impression of flames bursting out. The glsl fragment code I wrote is like this:
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);
}
As you can see, the GLSL code I wrote is very similar to the code in "shadertoy". The difference is, I pass in the screenSize
(pixels) and progress
(float seconds) from the Android side. For your information, the code above is fragment shader, and I've already adjust vertex shader using gl_Position
for entire screen. I am not sure why the result is not what I expected.
Also, I have another problem where after about 10 seconds, the image becomes pixelated and gradually turns into a square flame.
It seems to be a problem when the progress
value becomes too large, but I am not sure of the reason, and don't know how to fix it.
If anyone knows the cause and solution to these issues, I would greatly appreciate your response. Thank you.
Upvotes: 1
Views: 201
Reputation: 6391
The noise function is broken for huge progress
values (large displacement). It's suffering from precision loss, up to the point where you can see the surviving bits with your own eyes.
You'll need to somehow wrap progress
around once exceeds 100 or so seconds. Given that your noise isn't tileable, you'll need to sample it twice with a modulo while wrapping around and lerp for the transition phase.
Upvotes: 1
Reputation: 1775
I can tell you that the issue is not with your shader code. I took your shader code, placed it directly into ShaderToy, and exchanged the progress
variable for the iTime
variable, exchanged gl_FragCoord.xy
for fragCoord
, and exchanged screenSize
for iResolution.xy
.
Note, I also changed all references of normalized
to uv
to match the original.
Therefore, the issue must with the inputs not matching the ShaderToy inputs correctly. I expect your screensize
is being transferred wrong, as it looks like the screen is either providing rotated values, or has in some other way become compressed.
EDIT: Also, try changing you phone to landscape mode or making a free floating quad with the same aspect ratio as the example. Because its using numerical noise, I expect the numbers have been chosen so they only work with a certain aspect ratio. Landscape not Portrait.
Also, check to make sure you're passing in progress as a 0 sec -> X sec value rather than just a system clock call.
Otherwise, I can say that there are no errors in the code, and a reverse implementation back to ShaderToy works as expected.
My reimplementation of your Shader using ShaderToy with the structural changes can be found at: https://www.shadertoy.com/view/ct3GDS
Upvotes: 3