Procedural Prototype Grid Shader

Hi everyone,

I’m trying to create a material that will keep the texture at a constant predefined size while repeating it. Often used in the prototyping phase, and very helpful to get a visual reference of the surrounding scale.

Here is preview and a working demo.

GLSL is not my forte, so i’m cheating on the texture generation part, using a 2D canvas, while facing some problems with the GLSL part:

  1. The texture is inverted on the X axis with the following cube faces:
  • Positive X
  • Positive Y
  • Negative Z
  1. The texture is centered. Not sure if it’s possible, but ideally it should stick to the corners.

  2. Nothing serious, but aliasing is also an issue.

Thanks!

4 Likes

For issue №1 try the following code (note the use of sign(…)). I’m not sure whether this is the optimal way, but at least it will allow you to tackle the other two issues.

      if (nor.x >= nor.y && nor.x >= nor.z) {
        x = pos.z * scale.z * sign(-normal.x);
        y = pos.y * scale.y;
      }

      if (nor.y >= nor.x && nor.y >= nor.z) {
        x = pos.x * scale.x;
        y = pos.z * scale.z * sign(-normal.y);
      }

      if (nor.z >= nor.x && nor.z >= nor.y) {
        x = pos.x * scale.x * sign(normal.z);
        y = pos.y * scale.y;
      }
4 Likes

Thanks a lot @PavelBoytchev, that did the trick. BTW the Shader code is inspired by the solution you provided it this thread (should’ve mentioned it :sweat_smile:) So double thanks :blush:

For issues №1 and №2 together:

      if (nor.x >= nor.y && nor.x >= nor.z) {
        x = pos.z * scale.z * sign(-normal.x) + 0.5*scale.z;
        y = pos.y * scale.y + 0.5*scale.y;
      }

      if (nor.y >= nor.x && nor.y >= nor.z) {
        x = pos.x * scale.x + 0.5*scale.x;
        y = pos.z * scale.z * sign(-normal.y) + 0.5*scale.z;
      }

      if (nor.z >= nor.x && nor.z >= nor.y) {
        x = pos.x * scale.x * sign(normal.z) + 0.5*scale.x;
        y = pos.y * scale.y + 0.5*scale.y;
      }
6 Likes
  1. Quick :white_check_mark:
  2. Swift :white_check_mark:
  3. @PavelBoytchev Nailed it :sunglasses: :white_check_mark:

Thanks a lot for the quick, swift response.

1 Like

For issue №3 use these:

texture.minFilter = THREE.LinearMipmapLinearFilter;
texture.anisotropy = 16;

Note, that it is not good to use constants like 16, it is better to use renderer.getMaxAnisotropy(). In the code the texture is created before the renderer, that’s why I did not use the renderer to get the maximal anisotropy.

3 Likes

This is amazing, this changes make a huge deference, it’s crisp and feels like using SDF.

Here is the updated demo.

I know I’m repeating myself, but I feel like I’m not saying it enough, so a huge thank you :smiley:.

3 Likes

@PavelBoytchev = GOAT

5 Likes

Upgraded!

Now instead of using ShaderMaterial, I’m extending the material with material.onBeforeCompile. meaning it can be used with any materials.

const material = new THREE.MeshPhysicalMaterial({
  metalness: 0.3,
  roughness: 0.3,  
  transmission: 1, 
});

extendToGridPrototypeMaterial(material);

This allow it to inherit the base material characteristics, lights, shadow, environment…

Here is the new demo.

4 Likes

excellent.

1 Like

@Fennec with a little bit of tweaking it’s possible to include an “alpha map” with this…

image

I’m wondering is it possible this shader material can be an extension of MeshStandardMaterial so it’d also include all of MSM’s other channel properties ( roughness, metalness, AO, normal etc…)?

EDIT:

I’ll try add the same opacity “alpha map” to this version, i think it’d be handy…

1 Like

It should be possible, here is the part where I’m overriding the map texture:

shader.fragmentShader = shader.fragmentShader.replace(
      '#include <map_fragment>',
      `
      #ifdef USE_MAP
        vec4 sampledDiffuseColor = texture2D( map, vGridUv );
        #ifdef DECODE_VIDEO_TEXTURE
          sampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w );

        #endif
        diffuseColor *= sampledDiffuseColor;
      #endif
      `
    );

This particular line:

vec4 sampledDiffuseColor = texture2D( map, vGridUv );

The same approach can be done to override other mappings. Find the #include <*-fragment> and replace it.

This did the trick:

shader.fragmentShader = shader.fragmentShader.replace(
      '#include <alphamap_fragment>',
      `
      #ifdef USE_ALPHAMAP
        diffuseColor.a *= texture2D( alphaMap, vGridUv ).g;
      #endif
      `
    );

You can see it implemented here.

3 Likes

@Fennec that’s awesome, works a charm…

image
image

3 Likes