三个JS线框和圆形边缘

发布于 2025-01-22 05:21:58 字数 16208 浏览 2 评论 0 原文

我正在研究一个带有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>

I'm working on a project with 3D objects and decided to try using Three.js. I need a wireframe with rounded corners on a single axis. I've been able to achieve this effect mostly with two different methods, but not entirely. I'm not able to get a single outline connecting the two faces on just the outside wall.

I am drawing a shape with a new THREE.Shape() and then extrude it to geometry with a new THREE.ExtrudeGeometry(), getting the edges I care about with a new THREE.EdgesGeometry() and then finally making a mesh with a new THREE.LineSegments().

I'm also rendering a version of the box within it so the lines in the back are not shown.

The first way I tried was using the depth attribute on ExtrudeGeometry. It draws the lines, but it draws way too many lines, and it still draws them on the faces showing. I tried experimenting with the second parameter in EdgesGeometry which allows you to specify the minimum angle, but it either renders all or none of the lines.

Method 1

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.

Method 2

This works much closer to what I wanted, but I'm still not getting the connection at the edges as I want.

Goal

Is this reasonably possible?

Here is a snippet demonstrating method 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>

and here is one for method 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>

Edit

I've tried another method suggested by prisoner849 using conditional line segments here https://discourse.threejs.org/t/ldraw-like-edges/17100. I was able to achieve the same results as method one. Either rendering all the lines or none of the lines on the rounded side, depending on the minimum angle. I feel like I am much closer, but I don't know what I'm missing.

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

疏忽 2025-01-29 05:21:58

让我解释一下,仅使用网格边缘数据来实现想要的目标。

您基本上是在绘制网状边缘,而只想渲染其中的一些(我认为的角落)。为此,您需要在边缘上的属性,这使它们与其他边缘不同,因此您将它们隔离。您已经尝试了角度,但是角度是如此相似(可以在更复杂的网格上使用角度)。

我想不出可以为您提供帮助的其他属性,但是您可以定义自己的属性,例如顶点颜色,但它成为手动过程。如果您可以在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.

beveled box with outlined edges

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.

enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文