@react-three/drei
Version:
useful add-ons for react-three-fiber
218 lines (191 loc) • 8.05 kB
JavaScript
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 };