I have a hexagon tiled geometry I created in Blender

enter image description here

Which I then load in WebGL, using THREE.js library. Below is the loading code, I've cut the code of creating scene, lights, etc. I create rendered with antialiasing flag.

grassTexture = new THREE.ImageUtils.loadTexture( 'textures/GrassTileModo.png');
grassTexture.wrapS = grassTexture.wrapT = THREE.RepeatWrapping; 

var loader = new THREE.JSONLoader();
loader.load("models/hexmap.json", function(geometry)
        var attr = []
        for(var i = 0; i < geometry.faces.length; i++)
            if(i % 4 == 0)
                att = [new THREE.Vector3(1.0, 0.0, 0.0), new THREE.Vector3(0.0, 1.0, 0.0), new THREE.Vector3(0.0, 0.0, 1.0)];
            if(i % 4 == 1 || i % 4 == 2)
                att = [new THREE.Vector3(1.0, 0.0, 0.0), new THREE.Vector3(0.0, 0.0, 1.0), new THREE.Vector3(0.0, 1.0, 0.0)];
            if(i % 4 == 3)
                att = [new THREE.Vector3(1.0, 1.0, 1.0), new THREE.Vector3(1.0, 1.0, 1.0), new THREE.Vector3(1.0, 1.0, 1.0)];


        var material = new THREE.ShaderMaterial( {
                    diffuse: {type: 'c', value: new THREE.Color(1, 1, 1)},
                    opacity: {type: 'f', value: 1.0},
                    map: {type: 't', value: grassTexture},
                    aBorderMap: {type: 'v3', value: attr, boundTo: 'faceVertices'}
            vertexShader: document.getElementById('shader-vs').firstChild.textContent,
            fragmentShader: document.getElementById('shader-fs').firstChild.textContent
        } );

        plane = new THREE.Mesh(geometry, material);
        scene.add( plane );


Basically, the idea is I assign each vertex barycentric coords, and use them to know which edge I need to outline. Since my hexagons are getting tesselated by blender to 4 faces each, I assign different attributes to different faces, the way edges I don't need to outline are not outlined. In my case on the edges I don't want to outline, z barycentric coordinate will be zero.

This is how blender tesselates:

enter image description here

Vertex shader code:

precision highp float;
precision highp int;

uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec3 position;
attribute vec2 uv;
varying vec2 vUv;
attribute vec3 aBorderMap;
varying vec3 vBorderMap;

void main()
    vUv = uv;
    vBorderMap = aBorderMap;
    vec4 mvPosition;
    mvPosition = modelViewMatrix * vec4( position, 1.0 );
    gl_Position = projectionMatrix * mvPosition;

Fragment shader code:

precision highp float;
precision highp int;

uniform vec3 diffuse;
uniform float opacity;
varying vec2 vUv;
uniform sampler2D map;
varying vec3 vBorderMap;

void main()
    gl_FragColor = vec4( diffuse, opacity );

    vec4 texelColor;
    if(vBorderMap.x <= 0.05 || vBorderMap.y <= 0.05)
        texelColor = texture2D( map, vUv ) * 0.5  + vec4(0.0, 0.0, 0.0, 1.0) * 0.5;
        texelColor = texture2D( map, vUv );
    gl_FragColor = gl_FragColor * texelColor;

When I zoom out those artifacts can be clearly seen:

enter image description here

Although zoomed in it looks much better:

enter image description here

At first I thought lack of antialaising was a problem, but when I added it, it didn't help.


js fiddle link ---> https://jsfiddle.net/denisve/ad74pf77/5/

full screen ---> https://jsfiddle.net/denisve/ad74pf77/5/embedded/result/

WARNING: Huge embedded image there, so will take some time to load, sorry about that, didn't find a way to use external images on jsfiddle.

  • \$\begingroup\$ EDIT: Also tried to use mipmapping and anisotropy filter, didn't help. \$\endgroup\$ – user1617735 Sep 22 '15 at 20:09
  • \$\begingroup\$ ANOTHER EDIT: Texture is power of 2, of course. \$\endgroup\$ – user1617735 Sep 22 '15 at 20:11
  • \$\begingroup\$ AA and AA filters are still filters, there are "frequencies" which'll go through them, also do you actually have geometry meeting there? If so neither kind of filtering will help that. Try creating TINY gaps in the blender mesh. \$\endgroup\$ – Alec Teal Sep 22 '15 at 20:16
  • \$\begingroup\$ The geometry meets properly. Without the code that creates outlines there is no artifacts. Also if I just put hardcoded color instead of sampling the texture, it also looks fine, and there's no artifacts. \$\endgroup\$ – user1617735 Sep 22 '15 at 20:24
  • \$\begingroup\$ Add the texture please \$\endgroup\$ – Alec Teal Sep 22 '15 at 20:24

You need to use texture linear filtering or nearest filtering.

texture.minFilter = THREE.LinearFilter;
  • 2
    \$\begingroup\$ Actually I solved this by removing ifs from fragment shader code. Apparently shaders don't like sampling under branching. So the solution was to sample both textures and then take the appropriate pixel for fragment color calculations. \$\endgroup\$ – user1617735 Nov 16 '15 at 16:58
  • 6
    \$\begingroup\$ @user1617735 it would be good to add this as its own answer and mark it accepted, so users with similar problems can easily find your solution. \$\endgroup\$ – DMGregory Jan 15 '16 at 17:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.