UNPKG

@threlte/extras

Version:

Utilities, abstractions and plugins for your Threlte apps

191 lines (147 loc) 5.57 kB
// 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*/ ` #define PI 3.141592653589793 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; } #include <tonemapping_fragment> #include <colorspace_fragment> } `;