Jagged border shader

I have created a mesh from a shapeGeometry, added a shader to draw the borders.
to have a smoother result I have used the tessellation modifier to increase the poly count.

  • Is there a better way to add points to the geometry (the triangles aren’t in the direction of the shape).
  • How could I have a smoother result ( using UVs instead of position in the shader maybe?)


here si the mesh code

 let roomShape = new THREE.Shape(array2D);
      let roomGeo = new THREE.ShapeGeometry(roomShape, 10);
      const tessellateModifier = new TessellateModifier(0.075, 20); // 1, 5) (8, 6)

      // roomGeo.rotateX(-Math.PI / 2);

      let tesselGeo;
      try {
        tesselGeo = tessellateModifier.modify(roomGeo);
      } catch (e) {
        console.warn(e);
      }

vertex shader:

 vertexShader = `

  //varying vec2 vUv;
  out float isBorder;

uniform int shapeLen;
 uniform  vec2 shapeArr[shapeArrayLength];
 uniform float uWidth;

   varying vec3 vNormal;
  varying vec3 vViewDir;
  uniform vec3 vDir;

  void main(){
   
  //  vUv = uv; 
  // vec2 pos = position.xz ;
   vec2 pos = position.xy ;
 
       isBorder = 0.0;

//for each side / corner

  for (int i = 0; i < shapeLen ; i++){

    //egde vector (AB)
    highp vec2 AB;
    highp vec2 B;
   
     //edge vector
    if( i < (shapeLen-1 )){ int next = i+1;
    AB = shapeArr[next] - shapeArr[i] ;  }
    else{ AB  = vec2( shapeArr[0] - shapeArr[i]  ) ; } //close the shape
    vec2 ABnorm = normalize(AB);  //unit vector of the edge
    float ABlen = length(AB);//distance( shapeArr[i], B );


    //vector corner to position (AX)
    vec2 AX =  pos - shapeArr[i] ;
    float  AXlen  = length(AX);  //distance( shapeArr[i],  pos );
     vec2 AXnorm = normalize(AX);      //unit vector


      /* //test corners
  float dist = distance(shapeArr[i], pos );
     if(dist < .150){isBorder = 1.0;}*/


    //dot product to project point to edge

    float dotSide = dot(AX, AB);
   //if(  dotSide >= 0.0 &&  dotSide <= 1.0){
    //isBorder =0.15;


 //angle of both vectors
     float angle = acos(dot(ABnorm, AXnorm));

     //length of projected vector 
  float APlen = cos(angle) * AXlen;

  // distance to edge 
  float XPlen = sqrt( pow(AXlen, 2.0) - pow(APlen, 2.0));

    // check distance to edge
  if(XPlen < uWidth ){
   // straightborders
    isBorder = 1.0;
/*
     if(XPlen < uWidth){
 isBorder = 1.0;
  }
  else{
    isBorder = mix(uWidth, uWidth*2.0, XPlen);

  }*/
   }

  }

   vec4 modelPosition = modelMatrix * vec4(position, 1.0);
    vec4 viewPosition = viewMatrix * modelPosition;
    vec4 clipPosition = projectionMatrix * viewPosition;
    gl_Position = clipPosition;

  }
  `;```

If the shape is relatively simple (I guess, it is a profile of a room, so it is just 10-20 points), then you might consider creating the border as a mesh. There is some math involved in calculating vertices of this mesh. However, the complexity of this math should be comparable to the one in the shader.

Using a mesh means the calculations are done in JS, but for small polygons, this will not be an issue.

1 Like

thanks, i was considering this as a fallback, the shapeGeometry actually offers wayt to add holes i believe. For this project the design works best with a plane

Have you considered using another texture as a mask for the shape, and then just "discard"ing fragments that are black on the mask? or do you actually need a distorted mesh?

the shape is changing dynamically

As a proof of concept, tried with a mesh + dynamic shape (incl. concave vertices) + dynamic border width. Here is how it looks like. So, your fallback might become leanback.

3 Likes

Looks great, if available the code may be a great time saver!

Believe me, you do not want to use that code. I used THREE.ExtrudeGeometry to calculate the inset of the polygon, because I was too lazy to do the math by myself. In a production app this would be an overkill. That’s why I said it is just a proof of concept. Anyway, here it is:

https://codepen.io/boytchev/pen/NWZYByo

2 Likes