关于javascript:Three.js 统一虚线相对于相机

Three.js uniform dashed line relative to camera

我正在使用 three.js 以 3D 显示几何图形。

当您(手动)将隐藏线绘制为虚线时,"虚线"对于所有这些都是常规的。这意味着平行于相机平面的线或垂直于相机平面的线(几乎)应该具有相同的长度和间隙。

但这似乎不适用于 LineDashedMaterial。

></p>
<p>对于附加的示例,我正在使用这个(非常)基本的代码:</p>
<div class=

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();

renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

var geometry = new THREE.BoxGeometry( 2, 2, 2 );
var LINES_DASHED = new THREE.LineSegments(
    new THREE.EdgesGeometry(geometry),
    new THREE.LineDashedMaterial({
        linewidth: 2,
        color: 0x000000,
        dashSize: 0.2,
        gapSize: 0.1,
        depthTest: false,
        polygonOffset: true, polygonOffsetFactor: 1, polygonOffsetUnits: 1
    })
);
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 );
};

animate();
1
2
body { margin: 0; }
canvas { width: 100%; height: 100% }
1
<script src="https://threejs.org/build/three.min.js">

工作示例:

https://bs4.scolcours.ch/_dev/3js_ex.php

我认为使用:

1
line.computeLineDistance();

将解决问题。但它似乎计算了 3D 空间中的线长(这似乎是合乎逻辑的)。

有什么我错过的吗?

感谢您的帮助!


那是国外的任务。似乎 THREE.LineDashedMaterial 不支持这一点。
但是可以编写着色器并使用 THREE.ShaderMaterial.

诀窍是在片段着色器中知道一行的开始。一般来说,这很容易通过使用 flat 插值限定符来实现。
遗憾的是 WebGL 1.0 / GLSL ES 1.00 不支持这个。所以我们必须使用 WebGL 2.0 / GLSL ES 3.00。
在 OpenGL ES 中存在扩展 GL_NV_shader_noperspective_interpolation。不幸的是,似乎没有相应的 WebGL 扩展。 (参见 WebGL 扩展注册表)

所以让我们用 WebGL2 上下文创建一个 THREE.WebGLRenderer。请参阅如何使用 WebGL2:

1
2
3
var canvas = document.createElement( 'canvas' );
var context = canvas.getContext( 'webgl2' );
var renderer = new THREE.WebGLRenderer( { canvas: canvas, context: context } );

顶点着色器必须将标准化的设备坐标传递给片段着色器。一次使用默认插值,一次没有 (flat) 插值。这导致在片段阴影中,第一个输入参数包含行上实际位置的 NDC 坐标,然后是行开始的 NDC 坐标。

1
2
3
4
5
6
7
8
9
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 以像素为单位的间隙长度。

所以可以计算出从开始到实际片段的线长度:

1
2
vec2  dir  = (vertPos.xy-startPos.xy) * u_resolution/2.0;
float dist = length(dir);

并且可以通过 discard 命令丢弃 gab 上的片段。

1
2
if (fract(dist / (u_dashSize + u_gapSize)) > u_dashSize/(u_dashSize + u_gapSize))
    discard;

片段着色器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 和制服:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 的值:

例如

1
2
LINES_DASHED.material.uniforms.u_resolution.value.x = window.innerWidth;
LINES_DASHED.material.uniforms.u_resolution.value.y = window.innerHeight;

我将这些建议应用于您的原始代码。查看预览和示例:

></p>
<div class=

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<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 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 src="https://rawcdn.githack.com/mrdoob/three.js/r128/build/three.js">