UNPKG

@react-three/drei

Version:

useful add-ons for react-three-fiber

218 lines (191 loc) 8.05 kB
import * as THREE from 'three'; import * as React from 'react'; import { shaderMaterial } from '../core/shaderMaterial.js'; const WireframeMaterialShaders = { uniforms: { strokeOpacity: 1, fillOpacity: 0.25, fillMix: 0, thickness: 0.05, colorBackfaces: false, dashInvert: true, dash: false, dashRepeats: 4, dashLength: 0.5, squeeze: false, squeezeMin: 0.2, squeezeMax: 1, stroke: /* @__PURE__ */new THREE.Color('#ff0000'), backfaceStroke: /* @__PURE__ */new THREE.Color('#0000ff'), fill: /* @__PURE__ */new THREE.Color('#00ff00') }, vertex: /* glsl */` attribute vec3 barycentric; varying vec3 v_edges_Barycentric; varying vec3 v_edges_Position; void initWireframe() { v_edges_Barycentric = barycentric; v_edges_Position = position.xyz; } `, fragment: /* glsl */` #ifndef PI #define PI 3.1415926535897932384626433832795 #endif varying vec3 v_edges_Barycentric; varying vec3 v_edges_Position; uniform float strokeOpacity; uniform float fillOpacity; uniform float fillMix; uniform float thickness; uniform bool colorBackfaces; // Dash uniform bool dashInvert; uniform bool dash; uniform bool dashOnly; uniform float dashRepeats; uniform float dashLength; // Squeeze uniform bool squeeze; uniform float squeezeMin; uniform float squeezeMax; // Colors uniform vec3 stroke; uniform vec3 backfaceStroke; uniform vec3 fill; // This is like float wireframe_aastep(float threshold, float dist) { float afwidth = fwidth(dist) * 0.5; return smoothstep(threshold - afwidth, threshold + afwidth, dist); } float wireframe_map(float value, float min1, float max1, float min2, float max2) { return min2 + (value - min1) * (max2 - min2) / (max1 - min1); } float getWireframe() { vec3 barycentric = v_edges_Barycentric; // Distance from center of each triangle to its edges. float d = min(min(barycentric.x, barycentric.y), barycentric.z); // for dashed rendering, we can use this to get the 0 .. 1 value of the line length float positionAlong = max(barycentric.x, barycentric.y); if (barycentric.y < barycentric.x && barycentric.y < barycentric.z) { positionAlong = 1.0 - positionAlong; } // the thickness of the stroke float computedThickness = wireframe_map(thickness, 0.0, 1.0, 0.0, 0.34); // if we want to shrink the thickness toward the center of the line segment if (squeeze) { computedThickness *= mix(squeezeMin, squeezeMax, (1.0 - sin(positionAlong * PI))); } // Create dash pattern if (dash) { // here we offset the stroke position depending on whether it // should overlap or not float offset = 1.0 / dashRepeats * dashLength / 2.0; if (!dashInvert) { offset += 1.0 / dashRepeats / 2.0; } // if we should animate the dash or not // if (dashAnimate) { // offset += time * 0.22; // } // create the repeating dash pattern float pattern = fract((positionAlong + offset) * dashRepeats); computedThickness *= 1.0 - wireframe_aastep(dashLength, pattern); } // compute the anti-aliased stroke edge float edge = 1.0 - wireframe_aastep(computedThickness, d); return edge; } ` }; const WireframeMaterial = /* @__PURE__ */shaderMaterial(WireframeMaterialShaders.uniforms, WireframeMaterialShaders.vertex + /* glsl */` void main() { initWireframe(); gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, WireframeMaterialShaders.fragment + /* glsl */` void main () { // Compute color float edge = getWireframe(); vec4 colorStroke = vec4(stroke, edge); #ifdef FLIP_SIDED colorStroke.rgb = backfaceStroke; #endif vec4 colorFill = vec4(fill, fillOpacity); vec4 outColor = mix(colorFill, colorStroke, edge * strokeOpacity); gl_FragColor = outColor; } `); function setWireframeOverride(material, uniforms) { material.onBeforeCompile = shader => { shader.uniforms = { ...shader.uniforms, ...uniforms }; shader.vertexShader = shader.vertexShader.replace('void main() {', ` ${WireframeMaterialShaders.vertex} void main() { initWireframe(); `); shader.fragmentShader = shader.fragmentShader.replace('void main() {', ` ${WireframeMaterialShaders.fragment} void main() { `); shader.fragmentShader = shader.fragmentShader.replace('#include <color_fragment>', /* glsl */` #include <color_fragment> float edge = getWireframe(); vec4 colorStroke = vec4(stroke, edge); #ifdef FLIP_SIDED colorStroke.rgb = backfaceStroke; #endif vec4 colorFill = vec4(mix(diffuseColor.rgb, fill, fillMix), mix(diffuseColor.a, fillOpacity, fillMix)); vec4 outColor = mix(colorFill, colorStroke, edge * strokeOpacity); diffuseColor.rgb = outColor.rgb; diffuseColor.a *= outColor.a; `); }; material.side = THREE.DoubleSide; material.transparent = true; } function useWireframeUniforms(uniforms, props) { React.useEffect(() => { var _props$fillOpacity; return void (uniforms.fillOpacity.value = (_props$fillOpacity = props.fillOpacity) !== null && _props$fillOpacity !== void 0 ? _props$fillOpacity : uniforms.fillOpacity.value); }, [props.fillOpacity]); React.useEffect(() => { var _props$fillMix; return void (uniforms.fillMix.value = (_props$fillMix = props.fillMix) !== null && _props$fillMix !== void 0 ? _props$fillMix : uniforms.fillMix.value); }, [props.fillMix]); React.useEffect(() => { var _props$strokeOpacity; return void (uniforms.strokeOpacity.value = (_props$strokeOpacity = props.strokeOpacity) !== null && _props$strokeOpacity !== void 0 ? _props$strokeOpacity : uniforms.strokeOpacity.value); }, [props.strokeOpacity]); React.useEffect(() => { var _props$thickness; return void (uniforms.thickness.value = (_props$thickness = props.thickness) !== null && _props$thickness !== void 0 ? _props$thickness : uniforms.thickness.value); }, [props.thickness]); React.useEffect(() => void (uniforms.colorBackfaces.value = !!props.colorBackfaces), [props.colorBackfaces]); React.useEffect(() => void (uniforms.dash.value = !!props.dash), [props.dash]); React.useEffect(() => void (uniforms.dashInvert.value = !!props.dashInvert), [props.dashInvert]); React.useEffect(() => { var _props$dashRepeats; return void (uniforms.dashRepeats.value = (_props$dashRepeats = props.dashRepeats) !== null && _props$dashRepeats !== void 0 ? _props$dashRepeats : uniforms.dashRepeats.value); }, [props.dashRepeats]); React.useEffect(() => { var _props$dashLength; return void (uniforms.dashLength.value = (_props$dashLength = props.dashLength) !== null && _props$dashLength !== void 0 ? _props$dashLength : uniforms.dashLength.value); }, [props.dashLength]); React.useEffect(() => void (uniforms.squeeze.value = !!props.squeeze), [props.squeeze]); React.useEffect(() => { var _props$squeezeMin; return void (uniforms.squeezeMin.value = (_props$squeezeMin = props.squeezeMin) !== null && _props$squeezeMin !== void 0 ? _props$squeezeMin : uniforms.squeezeMin.value); }, [props.squeezeMin]); React.useEffect(() => { var _props$squeezeMax; return void (uniforms.squeezeMax.value = (_props$squeezeMax = props.squeezeMax) !== null && _props$squeezeMax !== void 0 ? _props$squeezeMax : uniforms.squeezeMax.value); }, [props.squeezeMax]); React.useEffect(() => void (uniforms.stroke.value = props.stroke ? new THREE.Color(props.stroke) : uniforms.stroke.value), [props.stroke]); React.useEffect(() => void (uniforms.fill.value = props.fill ? new THREE.Color(props.fill) : uniforms.fill.value), [props.fill]); React.useEffect(() => void (uniforms.backfaceStroke.value = props.backfaceStroke ? new THREE.Color(props.backfaceStroke) : uniforms.backfaceStroke.value), [props.backfaceStroke]); } export { WireframeMaterial, WireframeMaterialShaders, setWireframeOverride, useWireframeUniforms };