Does anyone know how to achieve this effect on a polygon mesh? It’s similar to a CSS inset box-shadow effect.
I’ve created a shader, but it does not work very well and lacks rendering performance.
Expected:
Output:
varying vec2 vUv;
varying vec3 vPosition;
varying vec3 vWorldPosition;
void main() {
vUv = uv;
vPosition = position;
vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
uniform float shadowIntensity;
uniform float shadowDepth;
uniform float edgeSmoothness;
uniform float gradientSoftness;
uniform vec3 fillColor;
uniform vec3 shadowColor;
uniform float polygonPoints[${pointCount * 2}];
uniform int pointCount;
varying vec2 vUv;
varying vec3 vPosition;
varying vec3 vWorldPosition;
bool pointInPolygon(vec2 point, int count) {
bool inside = false;
for (int i = 0, j = count - 1; i < count; j = i++) {
if (i >= count || i >= ${pointCount}) break;
vec2 pi = vec2(polygonPoints[i * 2], polygonPoints[i * 2 + 1]);
vec2 pj = vec2(polygonPoints[j * 2], polygonPoints[j * 2 + 1]);
if (((pi.y > point.y) != (pj.y > point.y)) &&
(point.x < (pj.x - pi.x) * (point.y - pi.y) / (pj.y - pi.y) + pi.x)) {
inside = !inside;
}
}
return inside;
}
float distanceToPolygonEdge(vec2 point, int count) {
float minDist = 1000.0;
for (int i = 0; i < count; i++) {
if (i >= count || i >= ${pointCount}) break;
int next = (i + 1) % count;
if (next >= count) next = 0;
vec2 a = vec2(polygonPoints[i * 2], polygonPoints[i * 2 + 1]);
vec2 b = vec2(polygonPoints[next * 2], polygonPoints[next * 2 + 1]);
vec2 pa = point - a;
vec2 ba = b - a;
float t = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
vec2 closest = a + t * ba;
float dist = length(point - closest);
minDist = min(minDist, dist);
}
return minDist;
}
void main() {
vec2 uv = vUv * 2.0 - 1.0;
bool inside = pointInPolygon(uv, pointCount);
if (!inside) {
discard;
}
float edgeDistance = distanceToPolygonEdge(uv, pointCount);
float shadowFactor = 1.0;
if (edgeDistance < shadowDepth) {
float shadowAmount = 1.0 - (edgeDistance / shadowDepth);
shadowAmount = pow(shadowAmount, gradientSoftness);
shadowFactor = 1.0 - shadowAmount * shadowIntensity;
}
float alpha = 1.0 - smoothstep(0.0, edgeSmoothness, edgeDistance);
vec3 finalColor = mix(shadowColor, fillColor, shadowFactor);
gl_FragColor = vec4(finalColor, alpha);
}