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