three.js 中挤出形状的奇怪工件和空纹理

Posted

技术标签:

【中文标题】three.js 中挤出形状的奇怪工件和空纹理【英文标题】:Odd artifacts and Empty texture in extruded shape in three.js 【发布时间】:2013-04-12 22:37:07 【问题描述】:

我尝试通过使用 Three.js 挤压形状来为 SVG 路径(lineto 和 moveto)赋予 3D 感觉,但该过程会导致一些我无法移除的伪影。

什么会导致我渲染的 3D 形状出现奇怪的伪影?有没有办法去除它们?

在下面的示例图像中,工件用箭头标记。

现场示例如下: http://jsfiddle.net/pHn2B/24/

代码在这里:

// 使用回调挑选 // 三.js r.52 var 容器, 信息, 相机, 场景, 光, 几何学, 网, 投影仪, 渲染器, 控制; 对象 = []; // dom 容器 = document.createElement('div'); document.body.appendChild( 容器 ); // 信息 info = document.createElement('div'); info.style.position = '绝对'; info.style.top = '10px'; info.style.width = '100%'; info.style.textAlign = '中心'; info.innerhtml = "拖动旋转相机;点击选择"; 容器.appendChild(信息); // 渲染器 渲染器 = 新的 THREE.CanvasRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); container.appendChild(renderer.domElement); // 场景 场景 = 新的三个场景(); // 相机 camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000); camera.position.set(0, 300, 500); 场景。添加(相机); // 控件 控制=新的三个。轨道控制(相机); // 光 场景.add(新的三个环境光(0x222222)); // 光 灯 = 新的三点灯(0xaaaaaa); light.position = camera.position; 场景.添加(灯光); // 几何学 几何=新的三立方几何(100、100、500); // 材料 材料=新三。MeshLambertMaterial(颜色:0xff0000,环境:0xff0000,过度绘制:真); // 网 网格=新的三。网格(几何,材料); 网格.position.set(-100, -100, 200); mesh.name = "红色对象"; mesh.callback = function() info.innerHTML = this.name; 场景.add(网格); 对象.push(网格); // 几何学 //////////// // 风俗 // //////////// var starPoints2 = new THREE.Shape(); starPoints2.moveTo(307.94,275.49); starPoints2.lineTo(296.26,275.23); starPoints2.lineTo(286.64,272.99); starPoints2.lineTo(279.78,269.31); starPoints2.lineTo(274.14,263.55); starPoints2.lineTo(271.65,260.21); starPoints2.lineTo(269.2,261.06); starPoints2.lineTo(254.83,268.51); starPoints2.lineTo(242.11,272.97); starPoints2.lineTo(227.59,275.23); starPoints2.lineTo(209.91,275.48); starPoints2.lineTo(197.47,273.63); starPoints2.lineTo(187.91,270.13); starPoints2.lineTo(180.48,265.09); starPoints2.lineTo(175.32,258.88); starPoints2.lineTo(172.2,251.44); starPoints2.lineTo(171.1,242.23); starPoints2.lineTo(172.24,233.63); starPoints2.lineTo(175.49,226.24); starPoints2.lineTo(181,219.54); starPoints2.lineTo(189.42,213.3); starPoints2.lineTo(201.36,207.73); starPoints2.lineTo(217.23,203.25); starPoints2.lineTo(238.28,200.1); starPoints2.lineTo(269.37,198.47); starPoints2.lineTo(269.98,182.93); starPoints2.lineTo(268.74,171.32); starPoints2.lineTo(266.05,163.7); starPoints2.lineTo(261.58,157.72); starPoints2.lineTo(255.24,153.24); starPoints2.lineTo(247.06,150.32); starPoints2.lineTo(235.44,149.13); starPoints2.lineTo(224.71,150.05); starPoints2.lineTo(215.91,153); starPoints2.lineTo(210.23,156.86); starPoints2.lineTo(207.64,160.85); starPoints2.lineTo(207.19,165.28); starPoints2.lineTo(209.34,169.86); starPoints2.lineTo(212.01,174.15); starPoints2.lineTo(212.14,177.99); starPoints2.lineTo(209.8,181.78); starPoints2.lineTo(204.22,185.79); starPoints2.lineTo(197.62,187.68); starPoints2.lineTo(188.65,187.43); starPoints2.lineTo(182.41,185.39); starPoints2.lineTo(178.45,181.77); starPoints2.lineTo(176.2,176.9); starPoints2.lineTo(176.03,170.64); starPoints2.lineTo(178.2,164.13); starPoints2.lineTo(183.09,157.69); starPoints2.lineTo(191.04,151.36); starPoints2.lineTo(202.01,145.82); starPoints2.lineTo(216.09,141.57); starPoints2.lineTo(232.08,139.24); starPoints2.lineTo(250.07,139.18); starPoints2.lineTo(266.13,141.23); starPoints2.lineTo(279.05,145.06); starPoints2.lineTo(289.15,150.3); starPoints2.lineTo(295.91,156.19); starPoints2.lineTo(300.73,163.41); starPoints2.lineTo(303.85,172.47); starPoints2.lineTo(305.07,183.78); starPoints2.lineTo(305.07,241.97); starPoints2.lineTo(306,251.51); starPoints2.lineTo(308.18,256.39); starPoints2.lineTo(311.72,259.09); starPoints2.lineTo(317.31,260.01); starPoints2.lineTo(324.71,259.01); starPoints2.lineTo(332.45,255.86); starPoints2.lineTo(335.57,257.53); starPoints2.lineTo(337.6,260.44); starPoints2.lineTo(336.94,262.33); starPoints2.lineTo(328.27,268.74); starPoints2.lineTo(317.89,273.41); starPoints2.lineTo(307.94,275.49); /* starPoints2.moveTo(245.79,125.33); starPoints2.lineTo(232.93,124.53); starPoints2.lineTo(222.21,121.74); starPoints2.lineTo(213.14,117.11); starPoints2.lineTo(207.36,111.92); starPoints2.lineTo(203.7,105.75); starPoints2.lineTo(201.94,98.18); starPoints2.lineTo(202.34,90.12); starPoints2.lineTo(204.86,83.4); starPoints2.lineTo(210.01,76.81); starPoints2.lineTo(217.49,71.33); starPoints2.lineTo(227.17,67.31); starPoints2.lineTo(238.35,65.2); starPoints2.lineTo(243.99,64.95); starPoints2.lineTo(255.92,66.06); starPoints2.lineTo(266.21,69.28); starPoints2.lineTo(274.98,74.44); starPoints2.lineTo(280.64,80.19); starPoints2.lineTo(284.02,86.85); starPoints2.lineTo(285.26,94.52); starPoints2.lineTo(284.27,102.84); starPoints2.lineTo(281.24,109.66); starPoints2.lineTo(276.03,115.43); starPoints2.lineTo(267.89,120.46); starPoints2.lineTo(257.68,123.93); starPoints2.lineTo(245.79,125.33); */ var smileyEye1Path = new THREE.Path(); 笑脸1Path.moveTo(221.69,258.13); 笑脸1Path.lineTo(215.2,255.08); 笑脸1Path.lineTo(210.86,250.57); 笑脸1Path.lineTo(208.4,244.49); 笑脸1Path.lineTo(207.92,237.03); 笑脸1Path.lineTo(209.69,230.71); 笑脸1Path.lineTo(213.82,224.85); 笑脸1Path.lineTo(220.9,219.34); 笑脸1Path.lineTo(230.95,214.67); 笑脸1Path.lineTo(245.76,210.86); 笑脸1Path.lineTo(266.59,208.36); 笑脸1Path.lineTo(269.48,208.76); 笑脸1Path.lineTo(269.99,212.88); 笑脸1Path.lineTo(269.99,244.81); 笑脸1Path.lineTo(269.34,247.02); 笑脸1Path.lineTo(266.07,250.04); 笑脸1Path.lineTo(255.27,255.23); 笑脸1Path.lineTo(242.52,258.58); 笑脸1Path.lineTo(230.57,259.43); 笑脸1Path.lineTo(221.69,258.13); /* 笑脸1Path.moveTo(238.44,116.65); 笑脸1Path.lineTo(231.99,114.29); 笑脸1Path.lineTo(227.23,110.22); 笑脸1Path.lineTo(223.94,104.53); 笑脸1Path.lineTo(222.41,96.92); 笑脸1Path.lineTo(223.05,88.57); 笑脸1Path.lineTo(225.65,82.21); 笑脸1Path.lineTo(230.07,77.36); 笑脸1Path.lineTo(235.93,74.4); 笑脸1Path.lineTo(243.68,73.34); 笑脸1Path.lineTo(246.08,73.43); 笑脸1Path.lineTo(253.37,75.08); 笑脸1Path.lineTo(258.65,78.43); 笑脸1Path.lineTo(262.47,83.41); 笑脸1Path.lineTo(264.59,90.25); 笑脸1Path.lineTo(264.64,98.93); 笑脸1Path.lineTo(262.63,106.12); 笑脸1Path.lineTo(258.87,111.5); 笑脸1Path.lineTo(253.73,115.1); 笑脸1Path.lineTo(246.81,116.94); 笑脸1Path.lineTo(238.44,116.65); */ var starShape = starPoints2; starShape.holes.push(smileyEye1Path); 变种挤压设置 = //大小:1,高度:1,曲线段:100,步长= 10, // 字体,粗细,样式, 数量:20, bevelEnabled:真, 斜面厚度:0.5, 斜角尺寸:0.5, bevelSegments: 8, // 拉伸路径: //弯曲路径: 材料:0, 挤出材料:1 //, //uvGenerator: BoundingUVGenerator //uvGenerator: THREE.ExtrudeGeometry.WorldUVGenerator ; var starGeometry = new THREE.ExtrudeGeometry(starShape,extrusionSettings); var materialFront = new THREE.MeshLambertMaterial(颜色:0xffff00,环境:0xffff00,过度绘制:false,透明:false,不透明度:1.0,侧面:THREE.DoubleSide); var materialSide = new THREE.MeshLambertMaterial(颜色:0xff8800,环境:0xff8800,过度绘制:false,透明:false,不透明度:1.0,侧面:THREE.DoubleSide); //var crateTexture = new THREE.ImageUtils.loadTexture('http://www.kahkonen.com/asiakkaat/crate2.gif'); //var crateTexture = new THREE.ImageUtils.generateDataTexture(10,10,r:255,g:0,b:0); //var materialFront = new THREE.MeshBasicMaterial( map: crateTexture ); var materialArray = [ materialFront, materialSide ]; var materialArray = [ materialFront, materialSide ]; var starMaterial = new THREE.MeshFaceMaterial(materialArray); var star = new THREE.Mesh(starGeometry, starMaterial); star.position.set(-150,-150,0); 场景.add(star); /* // 为模型添加线框 var wireframeTexture = new THREE.MeshBasicMaterial(颜色:0x000000,线框:真,透明:假); var star = new THREE.Mesh(starGeometry,wireframeTexture); 星.position.set(50,10,0); 场景.add(star); */ 对象.push(明星); // 投影仪 投影仪=新三。投影仪(); // 监听器 document.addEventListener('mousedown', onDocumentMouseDown, false) // 键盘处理程序 函数 onDocumentMouseDown( 事件 ) event.preventDefault(); var 向量 = 新的 THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5); 投影仪.unprojectVector(矢量,相机); var ray = new THREE.Ray(camera.position, vector.subSelf(camera.position).normalize()); var intersects = ray.intersectObjects( 对象 ); if ( intersects.length > 0 ) 相交[0].object.callback(); // 使成为 函数渲染() 控制.更新() renderer.render(场景,相机); // 动画 (函数动画() requestAnimationFrame( 动画 ); 使成为(); ());

我尝试通过附加纹理来去除伪影,但根本没有显示纹理:

其他问题(可能与工件问题相关也可能不相关)是可以通过边缘看到背景。

纹理不是必须的,但去除伪影是必须的。

我用来添加纹理的代码在下面,完整的代码在小提琴中(链接在代码后面):

var crateTexture = new THREE.ImageUtils.loadTexture( '@987654322@'); var crateTexture = new THREE.ImageUtils.generateDataTexture(10,10,r:255,g:0,b:0); var materialFront = new THREE.MeshBasicMaterial( map: crateTexture );

// http://jsfiddle.net/pHn2B/27/

示例图片来自 Chrome。同样的行为也出现在 Firefox 中。

【问题讨论】:

这不是挤压中的神器,它是 CanvasRenderer 中与 z 排序相关的已知错误。如果您切换到 WebGLRenderer,您会看到它停止了。 ***.com/questions/11199049/… 谢谢!它确实解决了工件问题,但没有解决纹理问题:jsfiddle.net/pHn2B/28。字形(正面)应该有纹理,但那里什么都没有。 THREE.ImageUtils.loadTexture 是一个辅助函数,所以它不需要一个新的。您还可以使用 var mesh = THREE.SceneUtils.createMultiMaterialObject( starGeometry, [ materialFront, materialSide ] ); 更轻松地创建网格 我得到了立方体每一面的纹理(通过将所有东西放在我自己的本地服务器上,这使得 securityError 消失),但是字形前面的 materialFront 是纯棕色的,没有纹理。以前是黑色的。我真的不知道为什么纹理不存在。 所以我假设 three.js 仅支持预定义形​​状的纹理,而不支持拉伸的自定义形状。如果自定义形状也可以具有纹理,那就太好了。也许除了three.js 之外的其他一些库支持拉伸路径并在其上附加纹理? 【参考方案1】:

我之前也遇到过同样的问题。 我还替换了成员函数 generateSideWallUV() 这些行:

if ( Math.abs( ay - by ) < 0.01 ) 
            return [
                new THREE.Vector2( ax, 1 - az ),
                new THREE.Vector2( bx, 1 - bz ),
                new THREE.Vector2( cx, 1 - cz ),
                new THREE.Vector2( dx, 1 - dz )
            ];
 else 
            return [
                new THREE.Vector2( ay, 1 - az ),
                new THREE.Vector2( by, 1 - bz ),
                new THREE.Vector2( cy, 1 - cz ),
                new THREE.Vector2( dy, 1 - dz )
            ];
        

这些行:

var amount = extrudeOptions.amount;
if ( Math.abs( ay - by ) < 0.01 ) 
            return [
                new THREE.Vector2( (ax - bb_minX) / bb_width, 1-(az / amount) ),
                new THREE.Vector2( (bx - bb_minX) / bb_width, 1-(bz  / amount) ),
                new THREE.Vector2( (cx - bb_minX) / bb_width, 1-(cz  / amount) ),
                new THREE.Vector2( (dx - bb_minX) / bb_width, 1- (dz  / amount) )
            ];
         else 
            return [
                new THREE.Vector2( (ay - bb_minY) / bb_height, 1-(az / amount )),
                new THREE.Vector2( (by - bb_minY) / bb_height, 1-(bz / amount )),
                new THREE.Vector2( (cy - bb_minY) / bb_height, 1-(cz / amount )),
                new THREE.Vector2( (dy - bb_minY) / bb_height, 1-(dz / amount ))
            ];
        `

【讨论】:

【参考方案2】:

仅供参考。 @Westlangley 的回答让我找到了正确的方向来获得形状上的纹理。关键是将值从一个范围缩放到另一个范围,从世界坐标到形状内部 UV 范围,必须为 0-1。

所以我复制了 THREE.ExtrudeGeometry.WorldUVGenerator 并将其重命名为 THREE.ExtrudeGeometry.BoundingBoxUVGenerator 并在成员函数 generateTopUV() 中替换了这些行:

返回 [ 新的 THREE.Vector2(ax, ay), 新的 THREE.Vector2(bx, by ), 新的 THREE.Vector2(cx, cy) ];

这些:

var bb = extrudedShape.getBoundingBox(); var bb_minX = bb.minX; var bb_minY = bb.minY; var bb_width = bb.maxX - bb_minX; var bb_height = bb.maxY - bb_minY; 如果 (bb_width == 0) bb_width = 1; 如果 (bb_height == 0) bb_height = 1; 返回 [新的 THREE.Vector2( (ax - bb_minX) / bb_width, (ay - bb_minY) / bb_height ), 新的 THREE.Vector2( (bx - bb_minX) / bb_width, (by - bb_minY) / bb_height ), 新的 THREE.Vector2( (cx - bb_minX) / bb_width, (cy - bb_minY) / bb_height ) ];

并且纹理“神奇地”出现在正确的位置!

(对于尝试过 https://github.com/mrdoob/three.js/issues/1811 中类似名称的函数的人。它不起作用,因为范围转换是以某种奇怪的方式进行的。)

这也适用于顶点坐标实际上在 0-1 范围内的情况,以及坐标为负数的情况。如果由于某种原因宽度和高度为 0,则返回 0。

对于大多数可能的目的,这比默认的 THREE.ExtrudeGeometry.WorldUVGenerator 有用得多。我什至无法想象默认生成器的任何用途,因为它仅适用于坐标在 0-1 范围内的顶点。但我是three.js 的新手,可能有一些我无法想象的原因。

【讨论】:

【参考方案3】:

这是由 z 缓冲问题引起的 CanvasRenderer 的已知限制。您的几何形状具有许多细长的面,这使情况变得更糟。使用WebGLRenderer 正确渲染模型。

ExtrudeGeometry 最初是为文本编写的,如果您查看它生成的 UV,它只使用 UV 顶点位置的 x 和 y 分量,在您的情况下,生成的值超出范围 [0, 1]。您可以选择在回调函数中提供自己的 UV 生成器。

确保您可以先成功地将纹理添加到立方体。

three.js r.58

【讨论】:

使用 CanvasRenderer 会将材质添加到立方体,但不会添加到字形形状:jsfiddle.net/pHn2B/31。但是使用 WebGL 渲染器什么也没显示:jsfiddle.net/pHn2B/32 并给出 Securityerror: the operation is insecure。是否可以将纹理作为 base64 编码的图像数据? 立方体现在有了纹理(加载纹理时不再出现与 CrossOrigin 相关的 SecurityError,因为我将所有代码和 crate.gif 转移到了我自己的本地服务器),但字形形状没有纹理。 “您可以选择在回调函数中提供自己的 UV 生成器”是什么意思。它会解决纹理问题或伪影问题还是两者兼而有之? ExtrudeGeometry 使用 THREE.ExtrudeGeometry.WorldUVGenerator 作为默认的 UV 生成器。您可以用自己的功能替换该功能。您所要做的就是用类似的函数替换该函数,以保证 UV 是合理的并且在 [0, 1] 范围内。 它会修复工件问题(使用 CanvasRenderer)或缺少纹理问题,还是两者兼而有之?我会尝试,但我真的不明白,为什么 ExtrudeGeometry(用于文本和路径挤压)无法处理 UV 生成? 我在上面的帖子中给了你原因。您需要花时间研究代码和它为您计算的 UV,以便您理解。对于CanvasRenderer 问题,我们无能为力。

以上是关于three.js 中挤出形状的奇怪工件和空纹理的主要内容,如果未能解决你的问题,请参考以下文章

如何使用Three.js获取纹理尺寸

在 Blender 中挤出面时保持纹理不拉伸?

Three.js:FBX 骨骼正确旋转,而 GLTF 骨骼奇怪地旋转

Three.js中同一网格面上的多个透明纹理

使用vue学习three.js之加载和使用纹理-使用环境贴图创建虚假的反光效果,使用CubeTextureLoader创建全景贴图,使用envMap创建静态环境贴图

three.js中场景模糊纹理失真的问题