godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
89.35k stars 20.24k forks source link

Odd Graphical Glitch. Shader too complex? #27346

Closed ZainlessBrombie closed 5 years ago

ZainlessBrombie commented 5 years ago

Godot version: 3.1 stable

OS/device including version: Linux Ubuntu 18, 4.15.0-46-generic, GTX 1060, Driver 410.48

Issue description: I was writing a shader that was supposed to render a planet. The shader is highly complex, computing a simplex noise for every fragment, as it was only supposed to be used for cinematic rendering. When calling the simplex noise function twice instead of once (and not using the result nor references), the whole screen glitches out. Could it have been that the shader hit some execution time threshold?

Steps to reproduce: Use the shader below on a sphere with 1000 radial segments and 1000 rings:


uniform sampler2D noise;

//  Simplex 3D Noise 
//  by Ian McEwan, Ashima Arts
//
vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}
vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}

float snoise(vec3 v){ 
  vec2  C = vec2(1.0/6.0, 1.0/3.0) ;
  vec4  D = vec4(0.0, 0.5, 1.0, 2.0);

// First corner
  vec3 i  = floor(v + dot(v, vec3(C.yy, C.y)));
  vec3 x0 =   v - i + dot(i, vec3(C.xx, C.x));

// Other corners
  vec3 g = step(x0.yzx, x0.xyz);
  vec3 l = 1.0 - g;
  vec3 i1 = min( g.xyz, l.zxy );
  vec3 i2 = max( g.xyz, l.zxy );

  //  x0 = x0 - 0. + 0.0 * C 
  vec3 x1 = x0 - i1 + 1.0 * vec3(C.xx, C.x);
  vec3 x2 = x0 - i2 + 2.0 * vec3(C.xx, C.x);
  vec3 x3 = x0 - 1. + 3.0 * vec3(C.xx, C.x);

// Permutations
  i = mod(i, 289.0 ); 
  vec4 p = permute( permute( permute( 
             i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
           + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) 
           + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));

// Gradients
// ( N*N points uniformly over a square, mapped onto an octahedron.)
  float n_ = 1.0/7.0; // N=7
  vec3  ns = n_ * D.wyz - D.xzx;

  vec4 j = p - 49.0 * floor(p * ns.z *ns.z);  //  mod(p,N*N)

  vec4 x_ = floor(j * ns.z);
  vec4 y_ = floor(j - 7.0 * x_ );    // mod(j,N)

  vec4 x = x_ *ns.x + vec4(ns.yyy, ns.y);
  vec4 y = y_ *ns.x + vec4(ns.yyy, ns.y);
  vec4 h = 1.0 - abs(x) - abs(y);

  vec4 b0 = vec4( x.xy, y.xy );
  vec4 b1 = vec4( x.zw, y.zw );

  vec4 s0 = floor(b0)*2.0 + 1.0;
  vec4 s1 = floor(b1)*2.0 + 1.0;
  vec4 sh = -step(h, vec4(0.0));

  vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
  vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;

  vec3 p0 = vec3(a0.xy,h.x);
  vec3 p1 = vec3(a0.zw,h.y);
  vec3 p2 = vec3(a1.xy,h.z);
  vec3 p3 = vec3(a1.zw,h.w);

//Normalise gradients
  vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
  p0 *= norm.x;
  p1 *= norm.y;
  p2 *= norm.z;
  p3 *= norm.w;

// Mix final noise value
  vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
  m = m * m;
  return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), 
                                dot(p2,x2), dot(p3,x3) ) );
}

float sampleNoise(float x, float y) {
    vec4 gotten = texture(noise, vec2(x, y));
    return (gotten.x + gotten.y + gotten.z) / 3f; // Does this make sense? are they always equal?
}

float sample3D(float x, float y, float z) {
    float ab = sampleNoise(x, y);
    float bc = sampleNoise(y, z);
    float ac = sampleNoise(x, z);

    float ba = sampleNoise(y, x);
    float cb = sampleNoise(z, y);
    float ca = sampleNoise(y, x);

    return (ab + bc + ac + ba + cb + ca) / 6f;
}

void vertex() {
    //VERTEX.x += sin(TIME); // offset vertex x by sine function on time elapsed
    COLOR.r = sample3D(VERTEX.x, VERTEX.y, VERTEX.z);
    COLOR.b = sample3D(VERTEX.x * 10f, VERTEX.y * 10f, VERTEX.z * 10f);
    if (VERTEX.y > 0.85f) {
        COLOR.g = (VERTEX.y - 0.85) * (1f / 0.15);
    } else if (VERTEX.y < -0.85) {
        COLOR.g = VERTEX.y * (1f / 0.15);
    } else {
        COLOR.g = 0f;
    }
    if (COLOR.r > 0.5) {
        VERTEX *= (COLOR.r * 0.5 + 1f);
    } else {
        VERTEX *= 1.25;
    }
}

float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

void fragment() {

    if (COLOR.g != 0f && (COLOR.r < 0.5 && COLOR.g * COLOR.g + COLOR.r * 2f > 1.6f) || (COLOR.r >= 0.5 && COLOR.g * COLOR.g + COLOR.r * 2f > 1.49f)) {
        if (COLOR.r >= 0.5) {
            ALBEDO.g = 0.9f;
            ALBEDO.b = 0.8f;
            ALBEDO.r = 0.8f;
        } else {
            ALBEDO.r = 0.9;
            ALBEDO.g = 0.9;
            ALBEDO.b = 0.9;
        }
    } else {
        if (COLOR.r < 0.5) {
            ALBEDO.r = 0f;
            ALBEDO.g = 0f;
            float temp = (COLOR.r - 0.35) * (1f / (0.5 - 0.35));
            temp = temp * temp * temp;
            temp = temp < 0.2 ? 0.2 : temp;
            if (temp < 0.9) {
                ALBEDO.b = temp;
            } else {
                ALBEDO.b = 0.9;
                ALBEDO.r = 0.1;
                ALBEDO.g = 0.3;
            }
        } else {
            // 73 73 73
            // 64 84 64
            // UV x: horizontal position
            // UV y: vertical position
            float xcoord = sin(UV.x * 3.14159265359 * 2f);
            float ycoord = cos(UV.y * 3.14159265359);
            float zcoord = cos(UV.x * 3.14159265359 * 2f);

            vec3 worldNormal = (CAMERA_MATRIX * vec4(NORMAL, 0f)).xyz;
            float snoise = atan((snoise(vec3(xcoord,  ycoord, zcoord) * 10f) * 2f - 1f) * 3f) + 1f;
            float test = atan((snoise(vec3(xcoord,  ycoord, zcoord) * 10f) * 2f - 1f) * 3f) + 1f;
            float specsmall = atan((snoise * 2f - 1f) * 7f) + 1f;
            float rep = 0.0001;
            float height = (COLOR.r - 0.5) * 7f;
            float rd1 = rand(vec2((xcoord - mod(xcoord, rep)) * 100f, (ycoord - mod(ycoord, rep)) * 100f));
            float rd2 = rand(vec2((ycoord - mod(ycoord, rep)) * 100f, (xcoord - mod(xcoord, rep)) * 100f));
            float rd3 = rand(vec2((xcoord - mod(xcoord, rep)) * 1000f, (ycoord - mod(ycoord, rep)) * 1000f));
            ALBEDO.r = ((snoise - height * 4f) * (73f - 64f) + 64f) / 255f; // y = vertical coordinates, x = horizontal coordinates
            ALBEDO.g = ((snoise + height) / 2f * (120f - 73f) + 73f) / 255f; //(COLOR.r - 0.5) * 2f + 0.3;
            ALBEDO.b = ((snoise - height * 4f) * (73f - 64f) + 64f) / 255f;
        }
    }
}

This is what the glitch looks like: image

clayjohn commented 5 years ago

The problem is that you have named a function the same as a variable. snoise is the name of your simplex noise lookup function and of your variable containing the value of the simplex noise lookup. Because they have the same name, the shader doesn't compile.

On my system I get the following error message.

0:1250(24): error: no function with name 'm_snoise'
0:1250(24): error: operands to arithmetic operators must be numeric
0:1250(23): error: operands to arithmetic operators must be numeric
0:1250(22): error: operands to arithmetic operators must be numeric
0:1250(16): error: no matching function for call to `atan(error)'; candidates are:
0:1250(16): error:    float atan(float)
0:1250(16): error:    vec2 atan(vec2)
0:1250(16): error:    vec3 atan(vec3)
0:1250(16): error:    vec4 atan(vec4)
0:1250(16): error:    float atan(float, float)
0:1250(16): error:    vec2 atan(vec2, vec2)
0:1250(16): error:    vec3 atan(vec3, vec3)
0:1250(16): error:    vec4 atan(vec4, vec4)
0:1250(16): error: operands to arithmetic operators must be numeric

The corresponding lines from the shader dump are:

1249: float m_snoise=(atan((((m_snoise((vec3(m_xcoord, m_ycoord, m_zcoord)*10.0))*2.0)-1.0)*3.0))+1.0);
1250: float m_test=(atan((((m_snoise((vec3(m_xcoord, m_ycoord, m_zcoord)*10.0))*2.0)-1.0)*3.0))+1.0);

To fix this issue, either rename your function, or rename the snoise variable.

Calinou commented 5 years ago

@clayjohn If some GLSL shader compilers have trouble with this, the Godot shader language compiler should probably throw an error if a variable has the same name as a function.

clayjohn commented 5 years ago

@Calinou That is good idea. Maybe you could rename the issue to say that?

Calinou commented 5 years ago

@clayjohn I think a new issue should be created about that; it could become confusing if this issue was renamed. I'll close this one when the other issue is opened.

Edit: Done, see https://github.com/godotengine/godot/issues/27363.