three.js太線フライラインアニメーション



Three Js Thick Line Fly Line Animation



/** * @author WestLangley / http://github.com/WestLangley * * parameters = { * color: , * linewidth: , * dashed: , * dashScale: , * dashSize: , * gapSize: , * resolution: , // to be set by renderer * } */ import { ShaderLib, ShaderMaterial, UniformsLib, UniformsUtils, Vector2 } from '../../../build/three.module.js' UniformsLib.line = { linewidth: { value: 1 }, resolution: { value: new Vector2(1, 1) }, dashScale: { value: 1 }, dashSize: { value: 1 }, gapSize: { value: 1 }, // todo FIX - maybe change to totalSize } ShaderLib['line'] = { uniforms: UniformsUtils.merge([ UniformsLib.common, UniformsLib.fog, UniformsLib.line, // toumingdu ]), vertexShader: ` #include #include #include #include #include uniform float linewidth uniform vec2 resolution attribute vec3 instanceStart attribute vec3 instanceEnd attribute vec3 instanceColorStart attribute vec3 instanceColorEnd varying vec2 vUv #ifdef USE_DASH uniform float dashScale attribute float instanceDistanceStart attribute float instanceDistanceEnd varying float vLineDistance #endif void trimSegment( const in vec4 start, inout vec4 end ) { // trim end segment so it terminates between the camera plane and the near plane // conservative estimate of the near plane float a = projectionMatrix[ 2 ][ 2 ] // 3nd entry in 3th column float b = projectionMatrix[ 3 ][ 2 ] // 3nd entry in 4th column float nearEstimate = - 0.5 * b / a float alpha = ( nearEstimate - start.z ) / ( end.z - start.z ) end.xyz = mix( start.xyz, end.xyz, alpha ) } void main() { #ifdef USE_COLOR vColor.xyz = ( position.y <0.5 ) ? instanceColorStart : instanceColorEnd #endif #ifdef USE_DASH vLineDistance = ( position.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd #endif float aspect = resolution.x / resolution.y vUv = uv // camera space vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 ) vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 ) // special case for perspective projection, and segments that terminate either in, or behind, the camera plane // clearly the gpu firmware has a way of addressing this issue when projecting into ndc space // but we need to perform ndc-space calculations in the shader, so we must address this issue directly // perhaps there is a more elegant solution -- WestLangley bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ) // 4th entry in the 3rd column if ( perspective ) { if ( start.z = 0.0 ) { trimSegment( start, end ) } else if ( end.z = 0.0 ) { trimSegment( end, start ) } } // clip space vec4 clipStart = projectionMatrix * start vec4 clipEnd = projectionMatrix * end // ndc space vec2 ndcStart = clipStart.xy / clipStart.w vec2 ndcEnd = clipEnd.xy / clipEnd.w // direction vec2 dir = ndcEnd - ndcStart // account for clip-space aspect ratio dir.x *= aspect dir = normalize( dir ) // perpendicular to dir vec2 offset = vec2( dir.y, - dir.x ) // undo aspect ratio adjustment dir.x /= aspect offset.x /= aspect // sign flip if ( position.x < 0.0 ) offset *= - 1.0 // endcaps if ( position.y 1.0 ) { offset += dir } // adjust for linewidth offset *= linewidth // adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ... offset /= resolution.y // select end vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd // back to clip space offset *= clip.w clip.xy += offset gl_Position = clip vec4 mvPosition = ( position.y < 0.5 ) ? start : end // this is an approximation #include #include #include } `, fragmentShader: ` uniform vec3 diffuse uniform float opacity #ifdef USE_DASH uniform float dashSize uniform float gapSize #endif varying float vLineDistance #include #include #include #include #include varying vec2 vUv void main() { #include #ifdef USE_DASH if ( vUv.y 1.0 ) discard // discard endcaps if ( mod( vLineDistance, dashSize + gapSize )> dashSize ) discard // todo - FIX #endif if ( abs( vUv.y ) > 1.0 ) { float a = vUv.x float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0 float len2 = a * a + b * b if ( len2 > 1.0 ) discard } // vec4 diffuseColor = vec4( diffuse, 0.5 ) vec4 diffuseColor = vec4( diffuse, opacity ) #include #include // gl_FragColor = vec4( diffuseColor.rgb, diffuseColor.a ) gl_FragColor = vec4( diffuseColor.rgb, diffuseColor.a ) #include #include #include #include } ` } var LineMaterial = function (parameters) { let uniforms = UniformsUtils.clone(ShaderLib['line'].uniforms) ShaderMaterial.call(this, { type: 'LineMaterial', uniforms: UniformsUtils.clone(ShaderLib['line'].uniforms), vertexShader: ShaderLib['line'].vertexShader, fragmentShader: ShaderLib['line'].fragmentShader }) this.dashed = false Object.defineProperties(this, { color: { enumerable: true, get: function () { return this.uniforms.diffuse.value }, set: function (value) { this.uniforms.diffuse.value = value } }, opacity: { enumerable: true, get: function () { return this.uniforms.opacity.value }, set: function (value) { this.uniforms.opacity.value = value } }, linewidth: { enumerable: true, get: function () { return this.uniforms.linewidth.value }, set: function (value) { this.uniforms.linewidth.value = value } }, dashScale: { enumerable: true, get: function () { return this.uniforms.dashScale.value }, set: function (value) { this.uniforms.dashScale.value = value } }, dashSize: { enumerable: true, get: function () { return this.uniforms.dashSize.value }, set: function (value) { this.uniforms.dashSize.value = value } }, gapSize: { enumerable: true, get: function () { return this.uniforms.gapSize.value }, set: function (value) { this.uniforms.gapSize.value = value } }, resolution: { enumerable: true, get: function () { return this.uniforms.resolution.value }, set: function (value) { this.uniforms.resolution.value.copy(value) } } }) this.setValues(parameters) } LineMaterial.prototype = Object.create(ShaderMaterial.prototype) LineMaterial.prototype.constructor = LineMaterial LineMaterial.prototype.isLineMaterial = true LineMaterial.prototype.copy = function (source) { ShaderMaterial.prototype.copy.call(this, source) this.color.copy(source.color) this.linewidth = source.linewidth this.resolution = source.resolution this.opacity = source.opacity // todo return this } export { LineMaterial }