[SOLVED] Weird moire in a custom shader

Hi there, guys!

I’m having a trouble making custom shader work properly, so may be you could give me a suggestion or two.

The shader in question is a simple 3D noise, applied to every texel based on its world position:

this.material.chunks.diffuseConstPS = app.assets.get (this.shader).resource;

Shader chunk:

uniform vec3 material_diffuse;

// we have 512x512 noise map, which is used as 64x64x64 3D noise
uniform sampler2D noise_map;

void getAlbedo()
{
    // texel world position
    vec3 pos = vPositionW;

    // we have 8x8 quadrants of 64x64 tiles
    // Y coordinate is converted to quadrant index

    // calculating quadrant index: 0, 1, 2, ..., 63
    float index = floor (64.0 * fract (pos.y));

    // quadrant [i, j] coords in 0...7 range
    float j = floor (index * 0.125);
    float i = index - j * 8.0;
    
    // where to sample noise texture based on texel world position
    vec2 uv = 64.0 * vec2 (i + fract (pos.x), j + fract (pos.z)) / 512.0;
    
    vec4 noise = texture2D (noise_map, uv);

    dAlbedo = material_diffuse.rgb * noise.g;
}

Here is the result:

http://playcanv.as/p/pytDp8lx

Use WASD and Mouse Drag to move around.

While moving around you will notice a strange view-dependent moire on walls:

When you’re close to the walls it disappears, but when you move away from them it starts showing up.
It doesn’t show up on the floor, so I think that’s connected with how quadrants are calculated from Y coord.
I assume that’s some sort of floating point rounding error happening, but can’t exactly understand where.

Any ideas? :slight_smile:

After further investigation and stackoverflow searching I came to conclusion that there is something wrong with floor () function, and that could be related to the way how PlayCanvas executes GLSL shaders.

First of all, here is this post:

I made similar tests:

float value; // [0.0 ... 1.0]
float a = value * 63.999;
float b = floor (a);
float threshold = 0.1;    

if ((a - b) / a < threshold)
{
    gl_FragColor = vec4 (0, 0, 0, 1);
}
else
{
    gl_FragColor = vec4 (1, 1, 1, 1);
}

Then I’ve started reducing threshold to see the lowest value after everything will be white. Turns out (!!!) the threshold value is ~0.0039 which is 1 / 256. Which leads me to believe that floor () in PlayCanvas is executed at lowp precision, even on a hardware that supports highp or mediump;

Seems like a bug to me :grin:

PlayCanvas doesn’t execute shaders in any special way, your code goes straight to GLSL compiler.

The problem, I believe, is in the mipmapping, which is enabled on your texture by default.
Try this in your JS code after you have the texture:

noiseTex.minFilter = pc.FILTER_NEAREST;
1 Like

Thank you big time!
That solved the problem with this and other similar shader I’ve been working on.
The texture itself was set to Point filtering in the editor, but seems like that was still producing mipmaps, so setting minFilter explicitly helped.

Cheers!