Three.js 相对于相机的均匀虚线
Three.js uniform dashed line relative to camera
我正在使用 three.js 显示 3D 几何图形。
当您(手动)将隐藏线绘制为虚线时,'dashes' 对所有这些线都是规则的。这意味着平行于相机平面的线或(几乎)垂直于相机平面的线应该具有相同的长度和间隙。
但这似乎不适用于 LineDashedMaterial。
对于附加的示例,我使用的是这个(非常)基本的代码:
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();
body { margin: 0; }
canvas { width: 100%; height: 100% }
<script src="https://threejs.org/build/three.min.js"></script>
工作示例:
https://bs4.scolcours.ch/_dev/3js_ex.php
我认为使用:
line.computeLineDistance();
将解决问题。但它似乎在3D中计算线长space(这似乎是合乎逻辑的)。
我错过了什么吗?
感谢您的帮助!
那是国外的任务。 THREE.LineDashedMaterial
好像不支持这个
但是可以编写着色器并使用 THREE.ShaderMaterial
.
诀窍是知道片段着色器中一行的开始。一般来说,这很容易通过使用 flat
插值限定符。
悲哀WebGL 1.0 / GLSL ES 1.00 doesn't support this. So we have to use WebGL 2.0 / GLSL ES 3.00.
在 OpenGL ES 中存在扩展 GL_NV_shader_noperspective_interpolation
. Unfortunately there doesn't seem to be a corresponding WebGL extension. (See WebGL Extension Registry)
所以让我们创建一个 THREE.WebGLRenderer
with a WebGL2 context. See How to use 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);
并且可以通过 discard
命令丢弃 gab 上的片段。
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);
注意,如果 canvas 的分辨率发生变化,则必须设置 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://rawcdn.githack.com/mrdoob/three.js/r128/build/three.js"></script>
我正在使用 three.js 显示 3D 几何图形。
当您(手动)将隐藏线绘制为虚线时,'dashes' 对所有这些线都是规则的。这意味着平行于相机平面的线或(几乎)垂直于相机平面的线应该具有相同的长度和间隙。
但这似乎不适用于 LineDashedMaterial。
对于附加的示例,我使用的是这个(非常)基本的代码:
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();
body { margin: 0; }
canvas { width: 100%; height: 100% }
<script src="https://threejs.org/build/three.min.js"></script>
工作示例:
https://bs4.scolcours.ch/_dev/3js_ex.php
我认为使用:
line.computeLineDistance();
将解决问题。但它似乎在3D中计算线长space(这似乎是合乎逻辑的)。
我错过了什么吗?
感谢您的帮助!
那是国外的任务。 THREE.LineDashedMaterial
好像不支持这个
但是可以编写着色器并使用 THREE.ShaderMaterial
.
诀窍是知道片段着色器中一行的开始。一般来说,这很容易通过使用 flat
插值限定符。
悲哀WebGL 1.0 / GLSL ES 1.00 doesn't support this. So we have to use WebGL 2.0 / GLSL ES 3.00.
在 OpenGL ES 中存在扩展 GL_NV_shader_noperspective_interpolation
. Unfortunately there doesn't seem to be a corresponding WebGL extension. (See WebGL Extension Registry)
所以让我们创建一个 THREE.WebGLRenderer
with a WebGL2 context. See How to use 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);
并且可以通过 discard
命令丢弃 gab 上的片段。
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);
注意,如果 canvas 的分辨率发生变化,则必须设置 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://rawcdn.githack.com/mrdoob/three.js/r128/build/three.js"></script>