gpt4 book ai didi

three.js - 如何让 Three.js LineSegments 仅渲染可见线

转载 作者:行者123 更新时间:2023-12-02 22:35:22 25 4
gpt4 key购买 nike

我试图让 Three.js 仅渲染几何图形的 FrontSide 轮廓。我想要实现的是看起来尽可能接近这个:

enter image description here

使用 BoxGeomtry 我已经接近了我想要的效果,但是在 CylinderGeometry 上使用 LineSegments 会给出垂直线,这是有道理的。你能想出一种方法让我只绘制“可见”轮廓吗?

Cylinder with LineSegments here

这是我迄今为止尝试过的:

let coloredMaterial = new THREE.MeshBasicMaterial({
color: 0xFFD033,
polygonOffset: true,
polygonOffsetFactor: 1,
polygonOffsetUnits: 1
});

let brick = new THREE.Mesh(geometry, coloredMaterial);

let edges = new THREE.EdgesGeometry(brick.geometry);
var outline = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({
color: 0x1B3740,
linewidth: 1.5
}));

let knobGeometry = new THREE.CylinderGeometry(7, 7, 7, 20);
let knob = new THREE.Mesh(knobGeometry, coloredMaterial);

let knobOutline = new THREE.LineSegments(
new THREE.EdgesGeometry(knob.geometry),
new THREE.LineBasicMaterial({
color: 0x1B3740,
linewidth: 1.5
})
);

最佳答案

这个答案基于 @WestLangley 对评论的建议,特别是 LDrawLoader 在条件行上使用的模型。

<小时/>

控制点模型

条件线背后的想法是使用控制点来确定应绘制哪些线。

如果两个控制点位于通过将线外推到无穷大而创建的剪裁平面的同一侧,则绘制该线。否则将被丢弃。

https://www.ldraw.org/uploads/images/Articles/opline.gif

让我们考虑两条线(E,B)(F,C):

对于(E,B),我们使用(A)(C)作为控制点。我们可以清楚地看到,两个控制点都位于(E,B)创建的平面的同一侧。因此,绘制了这条线。

对于(F,C),我们使用(B)(D)作为控制点。现在,两个控制点位于平面的不同侧。因此,这一行被丢弃。

<小时/>

由于该模型的实现可能相当冗长,我设置了 JSFiddle可以作为引用。它并不十分完美,但我相信它应该足够有帮助。

enter image description here

我们不能使用CylinderBufferGeometry作为边缘几何图形的基础,因为它使用索引缓冲区几何图形。由于控制点是由每条线而不是顶点确定的,因此我们不使用索引。

对于没有条件的边缘,我们可以对两个控件使用相同的点,例如顶部和底部的圆圈。

需要注意的重要一点是,使用此模型,我们无法确定一条线是否会被几何体(您所描述的 frontSide)遮挡。因此,我使用实际的旋钮来遮挡背线。

var conditionalLineVertShader = /* glsl */ `

attribute vec3 control0;
attribute vec3 control1;
attribute vec3 direction;

varying float discardFlag;

#include <common>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>

void main() {

#include <color_vertex>

vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;

// Transform the line segment ends and control points into camera clip space
vec4 c0 = projectionMatrix * modelViewMatrix * vec4( control0, 1.0 );
vec4 c1 = projectionMatrix * modelViewMatrix * vec4( control1, 1.0 );
vec4 p0 = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
vec4 p1 = projectionMatrix * modelViewMatrix * vec4( position + 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 ) );

discardFlag = float( sign( d0 ) != sign( d1 ) );

#include <logdepthbuf_vertex>
#include <clipping_planes_vertex>
#include <fog_vertex>

}
`;

var conditionalLineFragShader = /* glsl */ `

uniform vec3 diffuse;
varying float discardFlag;

#include <common>
#include <color_pars_fragment>
#include <fog_pars_fragment>
#include <logdepthbuf_pars_fragment>
#include <clipping_planes_pars_fragment>

void main() {

if ( discardFlag > 0.5 ) discard;

#include <clipping_planes_fragment>

vec3 outgoingLight = vec3( 0.0 );
vec4 diffuseColor = vec4( diffuse, 1.0 );

#include <logdepthbuf_fragment>
#include <color_fragment>

outgoingLight = diffuseColor.rgb; // simple shader

gl_FragColor = vec4( outgoingLight, diffuseColor.a );

#include <premultiplied_alpha_fragment>
#include <tonemapping_fragment>
#include <encodings_fragment>
#include <fog_fragment>

}
`;

var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
var controls = new THREE.OrbitControls(camera, renderer.domElement);

camera.position.set(10, 13, 10);
controls.target.set(0, 0, 0);


// cube

var cubeGeometry = new THREE.BoxBufferGeometry(10, 5, 10);
var cubeMaterial = new THREE.MeshBasicMaterial({
color: 0xFFD033,
polygonOffset: true,
polygonOffsetFactor: 1,
polygonOffsetUnits: 1
});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
scene.add(cube);

var edgesGeometry = new THREE.EdgesGeometry(cubeGeometry);
var edgesCube = new THREE.LineSegments(edgesGeometry, new THREE.LineBasicMaterial({
color: 0x1B3740,
linewidth: 1.5
}));
edgesCube.position.y += 0.6;
scene.add(edgesCube);


// knob

var knobGeometry = new THREE.CylinderGeometry(1.4, 1.4, 0.8, 30);
var knob = new THREE.Mesh(knobGeometry, cubeMaterial);
knob.position.set(-2.5, 2.9, -2.5);
scene.add(knob);

var knob = new THREE.Mesh(knobGeometry, cubeMaterial);
knob.position.set(2.5, 2.9, 2.5);
scene.add(knob);

var knob = new THREE.Mesh(knobGeometry, cubeMaterial);
knob.position.set(-2.5, 2.9, 2.5);
scene.add(knob);

var knob = new THREE.Mesh(knobGeometry, cubeMaterial);
knob.position.set(2.5, 2.9, -2.5);
scene.add(knob);


// knob edges

var edgesMaterial = new THREE.ShaderMaterial({
vertexShader: conditionalLineVertShader,
fragmentShader: conditionalLineFragShader,
uniforms: {
diffuse: {
value: new THREE.Color(0x1B3740)
}
},
linewidth: 1.5
});

var edgesKnob = createCylinderEdges(1.4, 0.8, 30);
edgesKnob.position.set(-2.5, 2.9 + 0.6, -2.5);
scene.add(edgesKnob);

var edgesKnob = createCylinderEdges(1.4, 0.8, 30);
edgesKnob.position.set(2.5, 2.9 + 0.6, 2.5);
scene.add(edgesKnob);

var edgesKnob = createCylinderEdges(1.4, 0.8, 30);
edgesKnob.position.set(-2.5, 2.9 + 0.6, 2.5);
scene.add(edgesKnob);

var edgesKnob = createCylinderEdges(1.4, 0.8, 30);
edgesKnob.position.set(2.5, 2.9 + 0.6, -2.5);
scene.add(edgesKnob);


window.addEventListener('resize', onResize);


function animate() {

requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);

};


function createCylinderEdges(radius, height, segments) {

var geometry = new THREE.BufferGeometry();

var v0 = new THREE.Vector3();
var v1 = new THREE.Vector3();

var vertices = [];
var control0 = [];
var control1 = [];
var directions = [];

// top / bottom circles

for (var v = 0; v <= 1; v++) {

for (var x = 0; x < segments; x++) {

var th = (x / segments) * Math.PI * 2;
var c0 = ((x - 1) / segments) * Math.PI * 2;
var c1 = ((x + 1) / segments) * Math.PI * 2;

var sinTheta = Math.sin(th);
var cosTheta = Math.cos(th);

v0.x = radius * sinTheta;
v0.y = -v * height + height / 2;
v0.z = radius * cosTheta;

sinTheta = Math.sin(c1);
cosTheta = Math.cos(c1);

v1.x = radius * sinTheta;
v1.y = -v * height + height / 2;
v1.z = radius * cosTheta;

vertices.push(v0.x, v0.y, v0.z);
vertices.push(v1.x, v1.y, v1.z);

control0.push(v0.x, v0.y, v0.z);
control0.push(v0.x, v0.y, v0.z);

control1.push(v0.x, v0.y, v0.z);
control1.push(v0.x, v0.y, v0.z);

directions.push(v1.x - v0.x, v1.y - v0.y, v1.z - v0.z);
directions.push(v1.x - v0.x, v1.y - v0.y, v1.z - v0.z);

}


}

// vertical edges

for (var x = 0; x < segments; x++) {

var th = (x / segments) * Math.PI * 2;
var c0 = ((x - 1) / segments) * Math.PI * 2;
var c1 = ((x + 1) / segments) * Math.PI * 2;

var sinTheta = Math.sin(th);
var cosTheta = Math.cos(th);

v0.x = radius * sinTheta;
v0.y = height / 2;
v0.z = radius * cosTheta;

v1.x = radius * sinTheta;
v1.y = -height + height / 2;
v1.z = radius * cosTheta;

vertices.push(v0.x, v0.y, v0.z);
vertices.push(v1.x, v1.y, v1.z);

directions.push(v1.x - v0.x, v1.y - v0.y, v1.z - v0.z);
directions.push(v1.x - v0.x, v1.y - v0.y, v1.z - v0.z);

var sinTheta = Math.sin(c0);
var cosTheta = Math.cos(c0);

v0.x = radius * sinTheta;
v0.y = height / 2;
v0.z = radius * cosTheta;

control0.push(v0.x, v0.y, v0.z);
control0.push(v0.x, v0.y, v0.z);

var sinTheta = Math.sin(c1);
var cosTheta = Math.cos(c1);

v0.x = radius * sinTheta;
v0.y = height / 2;
v0.z = radius * cosTheta;

control1.push(v0.x, v0.y, v0.z);
control1.push(v0.x, v0.y, v0.z);

}


geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setAttribute('control0', new THREE.Float32BufferAttribute(control0, 3, false));
geometry.setAttribute('control1', new THREE.Float32BufferAttribute(control1, 3, false));
geometry.setAttribute('direction', new THREE.Float32BufferAttribute(directions, 3, false));

return new THREE.LineSegments(geometry, edgesMaterial);

}

function onResize() {

var w = window.innerWidth;
var h = window.innerHeight;

camera.aspect = w / h;
camera.updateProjectionMatrix();

renderer.setSize(w, h);

}

animate();
body {
margin: 0;
position: fixed;
}

canvas {
width: 100%;
height: 100%;
display: block;
}
<script src="https://cdn.jsdelivr.net/npm/three@0.122.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.122.0/examples/js/controls/OrbitControls.min.js"></script>

关于three.js - 如何让 Three.js LineSegments 仅渲染可见线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56795062/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com