三个JS线框和圆形边缘
我正在研究一个带有3D对象的项目,并决定尝试使用三个。我需要一个线框,在一个轴上有圆角。我已经能够用两种不同的方法来实现这种效果,但并非完全可以实现这种效果。我无法获得单一轮廓,将两个面连接在外墙上。
我正在用 new trix.shape()
绘制形状 new Trix.EdgesGeometry()
,然后最终用
new Trix.lin.linesegments()
制作网格。
我还渲染其中的一个版本,因此未显示背面的线条。
我尝试使用的第一种方法是在挤出测定法上使用深度属性
。它绘制了线条,但是它绘制了太多的线条,并且仍将它们绘制在显示的脸上。我尝试在 edgesgeemetry
中尝试使用第二个参数,该参数允许您指定最小角度,但它呈现全行或无。
The second thing I tried was making two separate shapes, extruding them with a depth of 0
and then repositing the two meshes slightly to give them depth.
这更接近我想要的东西,但是我仍然没有根据需要在边缘获得连接。
这是合理的吗?
这是一个摘要演示方法1
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer( { alpha: true });
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
//Shape code starts here
const w = 1.5;
const h = 1.5;
const r = 0.2;
const d = 0.4;
//Generate the rounded rect shape
const s = new THREE.Shape();
s.moveTo(-w / 2, -h / 2 + r);
s.lineTo(-w / 2, h / 2 - r);
s.absarc(-w / 2 + r, h / 2 - r, r, 1 * Math.PI, 0.5 * Math.PI, true);
s.lineTo(w / 2 - r, h / 2);
s.absarc(w / 2 - r, h / 2 - r, r, 0.5 * Math.PI, 0 * Math.PI, true);
s.lineTo(w / 2, -h / 2 + r);
s.absarc(w / 2 - r, -h / 2 + r, r, 2 * Math.PI, 1.5 * Math.PI, true);
s.lineTo(-w / 2 + r, -h / 2);
s.absarc(-w / 2 + r, -h / 2 + r, r, 1.5 * Math.PI, 1 * Math.PI, true);
const outlineMat = new THREE.LineBasicMaterial( {
color: 0x000000,
linewidth: 2,
polygonOffset: true,
polygonOffsetFactor: 1,
polygonOffsetUnits: 1
} );
//This is the material that is used to stop the
//lines in the back from showing
var innerMat = new THREE.MeshBasicMaterial( {
color: false,
side: THREE.DoubleSide,
depthTest: true,
polygonOffset: true,
polygonOffsetFactor: 1,
polygonOffsetUnits: 1
} );
//Using the shape, generate the meshes
const outlineMesh = new THREE.LineSegments(
new THREE.EdgesGeometry(
new THREE.ExtrudeGeometry(
s,
{
depth: d,
bevelEnabled: false
}
),
1 //This is the default min angle
),
outlineMat
);
const innerMesh = new THREE.Mesh(
new THREE.ExtrudeGeometry(
s,
{
depth: d,
bevelEnabled: false
}
),
innerMat
);
const group = new THREE.Group();
group.add(outlineMesh);
group.add(innerMesh);
scene.add(group);
//Shape code ends here
camera.position.z = 5;
function animate() {
requestAnimationFrame( animate );
group.rotateX(0.01);
group.rotateY(0.01);
renderer.render( scene, camera );
}
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
这是方法2
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer( { alpha: true });
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
//Shape code starts here
const w = 1.5;
const h = 1.5;
const r = 0.2;
const d = 0.4;
//Generate the rounded rect shape
const s = new THREE.Shape();
s.moveTo(-w / 2, -h / 2 + r);
s.lineTo(-w / 2, h / 2 - r);
s.absarc(-w / 2 + r, h / 2 - r, r, 1 * Math.PI, 0.5 * Math.PI, true);
s.lineTo(w / 2 - r, h / 2);
s.absarc(w / 2 - r, h / 2 - r, r, 0.5 * Math.PI, 0 * Math.PI, true);
s.lineTo(w / 2, -h / 2 + r);
s.absarc(w / 2 - r, -h / 2 + r, r, 2 * Math.PI, 1.5 * Math.PI, true);
s.lineTo(-w / 2 + r, -h / 2);
s.absarc(-w / 2 + r, -h / 2 + r, r, 1.5 * Math.PI, 1 * Math.PI, true);
const outlineMat = new THREE.LineBasicMaterial( {
color: 0x000000,
linewidth: 2,
polygonOffset: true,
polygonOffsetFactor: 1,
polygonOffsetUnits: 1
} );
//This is the material that is used to stop the
//lines in the back from showing
var innerMat = new THREE.MeshBasicMaterial( {
color: false,
side: THREE.DoubleSide,
depthTest: true,
polygonOffset: true,
polygonOffsetFactor: 1,
polygonOffsetUnits: 1
} );
//Using the shape, generate the meshes
const frontMesh = new THREE.LineSegments(
new THREE.EdgesGeometry(
new THREE.ExtrudeGeometry(
s,
{
depth: 0,
bevelEnabled: false
}
),
1 //This is the default min angle
),
outlineMat
);
const backMesh = new THREE.LineSegments(
new THREE.EdgesGeometry(
new THREE.ExtrudeGeometry(
s,
{
depth: 0,
bevelEnabled: false
}
),
1 //This is the default min angle
),
outlineMat
);
const innerMesh = new THREE.Mesh(
new THREE.ExtrudeGeometry(
s,
{
depth: d,
bevelEnabled: false
}
),
innerMat
);
//Position the meshes
frontMesh.translateZ(d / 2);
backMesh.translateZ(-d / 2);
innerMesh.translateZ(-d / 2);
const group = new THREE.Group();
group.add(frontMesh);
group.add(backMesh);
group.add(innerMesh);
scene.add(group);
//Shape code ends here
camera.position.z = 5;
function animate() {
requestAnimationFrame( animate );
group.rotateX(0.01);
group.rotateY(0.01);
renderer.render( scene, camera );
}
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
编辑
我尝试了另一种使用条件线段建议的Arader849建议的方法 https:/https:/ /discourse.threejs.org/t/ldraw-like-edges/17100 。我能够获得与方法一号相同的结果。根据最小角度,呈现所有线路,或者没有线上的线。我觉得自己更近了,但我不知道我缺少什么。
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer( { alpha: true });
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
//Shape code starts here
const w = 1.5;
const h = 1.5;
const r = 0.2;
const d = 0.4;
//Generate the rounded rect shape
const s = new THREE.Shape();
s.moveTo(-w / 2, -h / 2 + r);
s.lineTo(-w / 2, h / 2 - r);
s.absarc(-w / 2 + r, h / 2 - r, r, 1 * Math.PI, 0.5 * Math.PI, true);
s.lineTo(w / 2 - r, h / 2);
s.absarc(w / 2 - r, h / 2 - r, r, 0.5 * Math.PI, 0 * Math.PI, true);
s.lineTo(w / 2, -h / 2 + r);
s.absarc(w / 2 - r, -h / 2 + r, r, 2 * Math.PI, 1.5 * Math.PI, true);
s.lineTo(-w / 2 + r, -h / 2);
s.absarc(-w / 2 + r, -h / 2 + r, r, 1.5 * Math.PI, 1 * Math.PI, true);
//This is the material that is used to stop the
//lines in the back from showing
var innerMat = new THREE.MeshBasicMaterial( {
color: false,
side: THREE.DoubleSide,
depthTest: true,
polygonOffset: true,
polygonOffsetFactor: 1,
polygonOffsetUnits: 1
} );
//Using the shape, generate the meshes
const outlineMesh = createOutlinedMesh(
new THREE.ExtrudeGeometry(
s,
{
depth: d,
bevelEnabled: false
}
),
'black'
);
const innerMesh = new THREE.Mesh(
new THREE.ExtrudeGeometry(
s,
{
depth: d,
bevelEnabled: false
}
),
innerMat
);
const group = new THREE.Group();
group.add(outlineMesh);
group.add(innerMesh);
scene.add(group);
//Shape code ends here
camera.position.z = 5;
function animate() {
requestAnimationFrame( animate );
group.rotateX(0.01);
group.rotateY(0.01);
renderer.render( scene, camera );
}
animate();
//Added code
function createOutlinedMesh(geometry){
let eg = EdgesGeometry(geometry, 3);
let m = new THREE.ShaderMaterial({
vertexShader: conditionalLineVertShader,
fragmentShader: conditionalLineFragShader,
uniforms: {
diffuse: {
value: 0x000000
},
opacity: {
value: 0
}
},
transparent: false
});
let o = new THREE.LineSegments(eg, m);
// let b = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
// color: 0x444444,
// map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/uv_grid_opengl.jpg"),
// polygonOffset: true,
// polygonOffsetFactor: 1
// }));
// o.add(b);
return o;
}
var conditionalLineVertShader = /* glsl */`
attribute vec3 control0;
attribute vec3 control1;
attribute vec3 direction;
attribute float collapse;
attribute vec3 instPos;
#include <common>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
void main() {
#include <color_vertex>
// Transform the line segment ends and control points into camera clip space
vec4 c0 = projectionMatrix * modelViewMatrix * vec4( control0 + instPos, 1.0 );
vec4 c1 = projectionMatrix * modelViewMatrix * vec4( control1 + instPos, 1.0 );
vec4 p0 = projectionMatrix * modelViewMatrix * vec4( position + instPos, 1.0 );
vec4 p1 = projectionMatrix * modelViewMatrix * vec4( position + instPos + direction, 1.0 );
c0.xy /= c0.w;
c1.xy /= c1.w;
p0.xy /= p0.w;
p1.xy /= p1.w;
// Get the direction of the segment and an orthogonal vector
vec2 dir = p1.xy - p0.xy;
vec2 norm = vec2( -dir.y, dir.x );
// Get control point directions from the line
vec2 c0dir = c0.xy - p1.xy;
vec2 c1dir = c1.xy - p1.xy;
// If the vectors to the controls points are pointed in different directions away
// from the line segment then the line should not be drawn.
float d0 = dot( normalize( norm ), normalize( c0dir ) );
float d1 = dot( normalize( norm ), normalize( c1dir ) );
float discardFlag = float( sign( d0 ) != sign( d1 ) );
vec3 p = position + instPos + ((discardFlag > 0.5) ? direction * collapse : vec3(0));
vec4 mvPosition = modelViewMatrix * vec4( p, 1.0 );
gl_Position = projectionMatrix * mvPosition;
#include <logdepthbuf_vertex>
#include <clipping_planes_vertex>
#include <fog_vertex>
}
`;
var conditionalLineFragShader = /* glsl */`
uniform vec3 diffuse;
uniform float opacity;
#include <common>
#include <color_pars_fragment>
#include <fog_pars_fragment>
#include <logdepthbuf_pars_fragment>
#include <clipping_planes_pars_fragment>
void main() {
#include <clipping_planes_fragment>
vec3 outgoingLight = vec3( 0.0 );
vec4 diffuseColor = vec4( diffuse, opacity );
#include <logdepthbuf_fragment>
#include <color_fragment>
outgoingLight = diffuseColor.rgb; // simple shader
gl_FragColor = vec4( outgoingLight, diffuseColor.a );
#include <tonemapping_fragment>
#include <encodings_fragment>
#include <fog_fragment>
#include <premultiplied_alpha_fragment>
}
`;
function EdgesGeometry( geometry, thresholdAngle ) {
let g = new THREE.BufferGeometry();
g.type = 'EdgesGeometry';
g.parameters = {
thresholdAngle: thresholdAngle
};
thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1;
// buffer
const vertices = [];
const control0 = [];
const control1 = [];
const direction = [];
const collapse = [];
// helper variables
const thresholdDot = Math.cos( THREE.MathUtils.DEG2RAD * thresholdAngle );
const edge = [ 0, 0 ], edges = {};
let edge1, edge2, key;
const keys = [ 'a', 'b', 'c' ];
// prepare source geometry
let geometry2;
if ( geometry.isBufferGeometry ) {
geometry2 = new THREE.Geometry();
geometry2.fromBufferGeometry( geometry );
} else {
geometry2 = geometry.clone();
}
geometry2.mergeVertices();
geometry2.computeFaceNormals();
const sourceVertices = geometry2.vertices;
const faces = geometry2.faces;
// now create a data structure where each entry represents an edge with its adjoining faces
for ( let i = 0, l = faces.length; i < l; i ++ ) {
const face = faces[ i ];
for ( let j = 0; j < 3; j ++ ) {
edge1 = face[ keys[ j ] ];
edge2 = face[ keys[ ( j + 1 ) % 3 ] ];
edge[ 0 ] = Math.min( edge1, edge2 );
edge[ 1 ] = Math.max( edge1, edge2 );
key = edge[ 0 ] + ',' + edge[ 1 ];
if ( edges[ key ] === undefined ) {
edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ], face1: i, face2: undefined };
} else {
edges[ key ].face2 = i;
}
}
}
// generate vertices
const v3 = new THREE.Vector3();
const n = new THREE.Vector3();
const n1 = new THREE.Vector3();
const n2 = new THREE.Vector3();
const d = new THREE.Vector3();
for ( key in edges ) {
const e = edges[ key ];
// an edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree.
if ( e.face2 === undefined || faces[ e.face1 ].normal.dot( faces[ e.face2 ].normal ) <= thresholdDot ) {
let vertex1 = sourceVertices[ e.index1 ];
let vertex2 = sourceVertices[ e.index2 ];
vertices.push( vertex1.x, vertex1.y, vertex1.z );
vertices.push( vertex2.x, vertex2.y, vertex2.z );
d.subVectors(vertex2, vertex1);
collapse.push(0, 1);
n.copy(d).normalize();
direction.push(d.x, d.y, d.z);
n1.copy(faces[ e.face1 ].normal);
n1.crossVectors(n, n1);
d.subVectors(vertex1, vertex2);
n.copy(d).normalize();
n2.copy(faces[ e.face2 ].normal);
n2.crossVectors(n, n2);
direction.push(d.x, d.y, d.z);
v3.copy(vertex1).add(n1); // control0
control0.push(v3.x, v3.y, v3.z);
v3.copy(vertex1).add(n2); // control1
control1.push(v3.x, v3.y, v3.z);
v3.copy(vertex2).add(n1); // control0
control0.push(v3.x, v3.y, v3.z);
v3.copy(vertex2).add(n2); // control1
control1.push(v3.x, v3.y, v3.z);
}
}
// build geometry
g.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
g.setAttribute( 'control0', new THREE.Float32BufferAttribute( control0, 3 ) );
g.setAttribute( 'control1', new THREE.Float32BufferAttribute( control1, 3 ) );
g.setAttribute( 'direction', new THREE.Float32BufferAttribute( direction, 3 ) );
g.setAttribute( 'collapse', new THREE.Float32BufferAttribute( collapse, 1 ) );
return g;
}
<script src="https://rawgithub.com/mrdoob/three.js/r118/build/three.js"></script>
<script src="https://rawgithub.com/mrdoob/three.js/r118/examples/js/utils/BufferGeometryUtils.js"></script>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
让我解释一下,仅使用网格边缘数据来实现想要的目标。
您基本上是在绘制网状边缘,而只想渲染其中的一些(我认为的角落)。为此,您需要在边缘上的属性,这使它们与其他边缘不同,因此您将它们隔离。您已经尝试了角度,但是角度是如此相似(可以在更复杂的网格上使用角度)。
我想不出可以为您提供帮助的其他属性,但是您可以定义自己的属性,例如顶点颜色,但它成为手动过程。如果您可以在3D建模工具(例如Blender)中设计形状,则可以轻松地为这些角边缘设置顶点颜色,并在渲染中使用该颜色以仅渲染这些顶点(可能具有自定义的着色器)。如果您要生成网格(如您现在正在做的事情),则在生成过程中,您应该以某种方式更改这些边缘的顶点颜色(我不确定如何在
three.js )。如果此方法甚至可以工作,则很难将其脱掉,因为您需要根据相机角度颜色并呈现不同的边缘……。
我做了一个插图,以说明为什么这不起作用:
这是我们的对象,您可以看到它在每个角落都有9个边缘。
为了使其更加可见,我已经为角落的边缘着色。如您所见,当我围绕网格旋转时,每个边缘每次都会变成对象角概述, so 您不能仅显示1个边缘的对象轮廓。
Using only mesh edges data to achieve what you want is hard, let me explain.
You are basically drawing mesh edges and you want to render only some of them (the corners I think). To do this you need a property on that edges that makes them different from other edges, so you isolate them. You have tried angle but the angles are so similar (angle can be used on much more complex meshes).
I can't think of other property that can help you on this, but you can define your own property like vertex color, but it becomes a manual process. If you can design your shapes in 3D modeling tools like Blender, you can easily set vertex color for these corner edges and use that colors in rendering to only render those vertexes (with a custom shader probably). If you are generating your mesh (like what you are doing now), in the process of generating you should somehow change the vertex color of those edges (I'm not sure how to do it in
three.js
). If this method even works, it's really hard to get it off, because you need to color all the edges and render different edges based on the camera angle….I have made an illustration to show why this doesn't work:
This is our object, you can see it has 9 edges in each corner.
To make it more visible, I have colored the edges of a corner. As you can see, when I rotate around the mesh, each edge becomes the object corner outline each time, SO you can't show the object outline with only 1 of these edges.