Это задача за границей.Кажется, что THREE.LineDashedMaterial
не поддерживает это.
Но можно написать шейдер и использовать THREE.ShaderMaterial
.
Хитрость заключается в том, чтобы узнать начало строки в фрагментном шейдере.В общем, это легко, используя flat
спецификатор интерполяции.
К сожалению WebGL 1.0 / GLSL ES 1.00 не поддерживает это.Поэтому мы должны использовать WebGL 2.0 / GLSL ES 3.00 .Возможно, кто-то может дать здесь информацию и знает расширение WebGL / OES, которое также поддерживает квалификатор flat
.
Так что давайте создадим THREE.WebGLRenderer
с контекстом WebGL2.См. Как использовать WebGL2 :
var canvas = document.createElement( 'canvas' );
var context = canvas.getContext( 'webgl2' );
var renderer = new THREE.WebGLRenderer( { canvas: canvas, context: context } );
Вершинный шейдер должен передать нормализованную координату устройства фрагментному шейдеру.Один раз с интерполяцией по умолчанию и один раз без (flat
) интерполяции.Это приводит к тому, что в оттенке фрагмента первый входной параметр содержит координату NDC фактической позиции на линии, а затем координату NDC начала строки.
flat out vec3 startPos;
out vec3 vertPos;
void main() {
vec4 pos = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
gl_Position = pos;
vertPos = pos.xyz / pos.w;
startPos = vertPos;
}
Кроме того, переменные входыФрагмент шейдера имеет одинаковые переменные.u_resolution
содержит ширину и высоту области просмотра.u_dashSize
содержит длину строки и u_gapSize
длину пробела в пикселях.
Таким образом, длина строки от начала до фактического фрагмента может быть рассчитана:
vec2 dir = (vertPos.xy-startPos.xy) * u_resolution/2.0;
float dist = length(dir);
И фрагмент на gab можно сбросить командой discard
.
if (fract(dist / (u_dashSize + u_gapSize)) > u_dashSize/(u_dashSize + u_gapSize))
discard;
Фрагмент шейдера:
precision highp float;
flat in vec3 startPos;
in vec3 vertPos;
uniform vec3 u_color;
uniform vec2 u_resolution;
uniform float u_dashSize;
uniform float u_gapSize;
void main(){
vec2 dir = (vertPos.xy-startPos.xy) * u_resolution/2.0;
float dist = length(dir);
if ( fract(dist / (u_dashSize + u_gapSize)) > u_dashSize/(u_dashSize + u_gapSize) )
discard;
gl_FragColor = vec4(u_color.rgb, 1.0);
}
Настройка THREE.ShaderMaterial
и униформа:
var uniforms = {
u_resolution: {type: 'v2', value: {x: vpSize[0], y: vpSize[1]}},
u_dashSize : {type:'f', value: 10.0},
u_gapSize : {type:'f', value: 5.0},
u_color : {type: 'v3', value: {x:0.0, y:0.0, z:0.0} }
};
var material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertex-shader').textContent,
fragmentShader: document.getElementById('fragment-shader').textContent
});
var LINES_DASHED = new THREE.LineSegments(
new THREE.EdgesGeometry(geometry),
material);
Обратите внимание, что при изменении разрешения холста необходимо установить значения u_resolution
:
например,
LINES_DASHED.material.uniforms.u_resolution.value.x = window.innerWidth;
LINES_DASHED.material.uniforms.u_resolution.value.y = window.innerHeight;
Я применил предложения к вашему исходному коду.Смотрите предварительный просмотр и пример:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 60, window.innerWidth/window.innerHeight, 0.1, 1000 );
var canvas = document.createElement( 'canvas' );
var context = canvas.getContext( 'webgl2' );
var renderer = new THREE.WebGLRenderer( { canvas: canvas, context: context } );
var vpSize = [window.innerWidth, window.innerHeight];
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var geometry = new THREE.BoxGeometry( 2, 2, 2 );
var uniforms = {
u_resolution: {type: 'v2', value: {x: vpSize[0], y: vpSize[1]}},
u_dashSize : {type:'f', value: 10.0},
u_gapSize : {type:'f', value: 5.0},
u_color : {type: 'v3', value: {x:0.0, y:0.0, z:0.0} }
};
var material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertex-shader').textContent,
fragmentShader: document.getElementById('fragment-shader').textContent
});
var LINES_DASHED = new THREE.LineSegments(
new THREE.EdgesGeometry(geometry),
material);
LINES_DASHED.computeLineDistances();
scene.add( LINES_DASHED );
scene.background = new THREE.Color( 0xffffff);
camera.position.z = 5;
var animate = function () {
requestAnimationFrame( animate );
LINES_DASHED.rotation.x += 0.01;
LINES_DASHED.rotation.y += 0.01;
renderer.render( scene, camera );
};
window.onresize = function() {
vpSize = [window.innerWidth, window.innerHeight];
LINES_DASHED.material.uniforms.u_resolution.value.x = window.innerWidth;
LINES_DASHED.material.uniforms.u_resolution.value.y = window.innerHeight;
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
animate();
<script type='x-shader/x-vertex' id='vertex-shader'>
flat out vec3 startPos;
out vec3 vertPos;
void main() {
vec4 pos = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
gl_Position = pos;
vertPos = pos.xyz / pos.w;
startPos = vertPos;
}
</script>
<script type='x-shader/x-fragment' id='fragment-shader'>
precision highp float;
flat in vec3 startPos;
in vec3 vertPos;
uniform vec3 u_color;
uniform vec2 u_resolution;
uniform float u_dashSize;
uniform float u_gapSize;
void main(){
vec2 dir = (vertPos.xy-startPos.xy) * u_resolution.xy/2.0;
float dist = length(dir);
if (fract(dist / (u_dashSize + u_gapSize)) > u_dashSize/(u_dashSize + u_gapSize))
discard;
gl_FragColor = vec4(u_color.rgb, 1.0);
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/100/three.min.js"></script>