@threlte/extras
Version:
Utilities, abstractions and plugins for your Threlte apps
191 lines (147 loc) • 5.57 kB
JavaScript
// Credits to Evan Wallace https://madebyevan.com/shaders/grid/
export const vertexShader = /*glsl*/ `
varying vec3 localPosition;
varying vec4 worldPosition;
uniform vec3 worldCamProjPosition;
uniform vec3 worldPlanePosition;
uniform float fadeDistance;
uniform bool infiniteGrid;
uniform bool followCamera;
uniform int coord0;
uniform int coord1;
uniform int coord2;
void main() {
localPosition = vec3(
position[coord0],
position[coord1],
position[coord2]
);
if (infiniteGrid) {
localPosition *= 1.0 + fadeDistance;
}
worldPosition = modelMatrix * vec4(localPosition, 1.0);
if (followCamera) {
worldPosition.xyz += (worldCamProjPosition - worldPlanePosition);
localPosition = (inverse(modelMatrix) * worldPosition).xyz;
}
gl_Position = projectionMatrix * viewMatrix * worldPosition;
}
`;
export const fragmentShader = /*glsl*/ `
varying vec3 localPosition;
varying vec4 worldPosition;
uniform vec3 worldCamProjPosition;
uniform float cellSize;
uniform float sectionSize;
uniform vec3 cellColor;
uniform vec3 sectionColor;
uniform float fadeDistance;
uniform float fadeStrength;
uniform vec3 fadeOrigin;
uniform float cellThickness;
uniform float sectionThickness;
uniform vec3 backgroundColor;
uniform float backgroundOpacity;
uniform bool infiniteGrid;
uniform int coord0;
uniform int coord1;
uniform int coord2;
// 0 - default; 1 - lines; 2 - circles; 3 - polar
uniform int gridType;
// lineGrid coord for lines
uniform int lineGridCoord;
// circlegrid max radius
uniform float circleGridMaxRadius;
// polar grid dividers
uniform float polarCellDividers;
uniform float polarSectionDividers;
float getSquareGrid(float size, float thickness, vec3 localPos) {
vec2 coord = localPos.xy / size;
vec2 grid = abs(fract(coord - 0.5) - 0.5) / fwidth(coord);
float line = min(grid.x, grid.y) + 1.0 - thickness;
return 1.0 - min(line, 1.0);
}
float getLinesGrid(float size, float thickness, vec3 localPos) {
float coord = localPos[lineGridCoord] / size;
float line = abs(fract(coord - 0.5) - 0.5) / fwidth(coord) - thickness * 0.2;
return 1.0 - min(line, 1.0);
}
float getRadiusMask(float radius) {
if (infiniteGrid || circleGridMaxRadius <= 0.0) {
return 1.0;
}
float width = max(fwidth(radius), 0.0001);
return 1.0 - smoothstep(circleGridMaxRadius, circleGridMaxRadius + width, radius);
}
float getPolarDividerMask(float radius) {
if (infiniteGrid || circleGridMaxRadius <= 0.0) {
return 1.0;
}
float width = max(fwidth(radius), 0.0001);
return 1.0 - smoothstep(circleGridMaxRadius - width, circleGridMaxRadius, radius);
}
float getCirclesGrid(float size, float thickness, float radius) {
float coord = radius / size;
float line = abs(fract(coord - 0.5) - 0.5) / fwidth(coord) - thickness * 0.2;
return 1.0 - min(line, 1.0);
}
float getPolarGrid(float size, float thickness, float polarDividers, vec3 localPos, float radius) {
if (polarDividers <= 0.0) {
return getCirclesGrid(size, thickness, radius);
}
float rad = radius / size;
vec2 coord = vec2(rad, atan(localPos.x, localPos.y) * polarDividers / PI) ;
vec2 wrapped = vec2(coord.x, fract(coord.y / (2.0 * polarDividers)) * (2.0 * polarDividers));
vec2 coordWidth = fwidth(coord);
vec2 wrappedWidth = fwidth(wrapped);
vec2 width = (coord.y < -polarDividers * 0.5 || coord.y > polarDividers * 0.5 ? wrappedWidth : coordWidth) * (1.+thickness*0.25);
// Compute anti-aliased world-space grid lines
vec2 grid = abs(fract(coord - 0.5) - 0.5) / width;
float circle = 1.0 - min(grid.x, 1.0);
float divider = 1.0 - min(grid.y, 1.0);
return max(circle, divider * getPolarDividerMask(radius));
}
void main() {
float g1 = 0.0;
float g2 = 0.0;
vec3 localPos = vec3(localPosition[coord0], localPosition[coord1], localPosition[coord2]);
float radiusMask = 1.0;
if (gridType == 0) {
g1 = getSquareGrid(cellSize, cellThickness, localPos);
g2 = getSquareGrid(sectionSize, sectionThickness, localPos);
} else if (gridType == 1) {
g1 = getLinesGrid(cellSize, cellThickness, localPos);
g2 = getLinesGrid(sectionSize, sectionThickness, localPos);
} else if (gridType == 2) {
float radius = length(localPos.xy);
g1 = getCirclesGrid(cellSize, cellThickness, radius);
g2 = getCirclesGrid(sectionSize, sectionThickness, radius);
radiusMask = getRadiusMask(radius);
} else if (gridType == 3) {
float radius = length(localPos.xy);
g1 = getPolarGrid(cellSize, cellThickness, polarCellDividers, localPos, radius);
g2 = getPolarGrid(sectionSize, sectionThickness, polarSectionDividers, localPos, radius);
radiusMask = getRadiusMask(radius);
}
float dist = distance(fadeOrigin, worldPosition.xyz);
float d = 1.0 - min(dist / fadeDistance, 1.0);
float fadeFactor = pow(d, fadeStrength) * 0.95;
vec3 color = mix(cellColor, sectionColor, min(1.0, sectionThickness * g2));
if (backgroundOpacity > 0.0) {
float linesAlpha = clamp((g1 + g2) * fadeFactor, 0.0,1.0);
vec3 finalColor = mix(backgroundColor, color, linesAlpha);
float blendedAlpha = max(linesAlpha, backgroundOpacity * fadeFactor);
gl_FragColor = vec4(finalColor, blendedAlpha);
} else {
gl_FragColor = vec4(color, (g1 + g2) * pow(d, fadeStrength));
gl_FragColor.a = mix(0.75 * gl_FragColor.a, gl_FragColor.a, g2);
}
gl_FragColor.a *= radiusMask;
if (gl_FragColor.a <= 0.0) {
discard;
}
}
`;