Android GLES - Flame shader

160 Views Asked by At

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.

Flame Shader

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.

Pixelated Flame shader

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.

2

There are 2 best solutions below

1
On

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

1
On

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.