简体   繁体   English

THREE.js-在从TubeGeometry构造的网格上移动纹理

[英]THREE.js - Shifting a texture on a Mesh constructed from a TubeGeometry

I have a problem that I suspect has a very simple answer (one or two lines), but nothing I've tried so far is working. 我有一个问题,我怀疑答案很简单(一两行),但到目前为止我没有尝试过。 Many thanks for any suggestions. 非常感谢您的任何建议。

What I have is a TubeGeometry and a Texture that I've made via canvas drawing. 我所拥有的是我通过画布绘制制作的TubeGeometry和Texture。 I then make a mesh (multi-material object if that makes a difference), and one of the materials has the map property specified as my texture. 然后,我制作一个网格物体(如果有区别,可以使用多种材质的物体),其中一种材质的map属性指定为我的纹理。 I'll call this material Mat. 我将这种材料称为Mat。

The problem is that I need a way to continuously rotate the texture around the tube (in torus terms, around the meridians, not the equators). 问题在于,我需要一种方法来连续地围绕管旋转纹理(以圆环的形式,围绕子午线而不是赤道)。 Kind of like kneading circular bread. 有点像揉圆形面包。 Previously I was specifying an offset (0-1) and had a function createTexture(offset) for redrawing the texture on the canvas to mimic wraparound, constantly using the code 以前,我指定偏移量(0-1),并具有一个函数createTexture(offset),用于在画布上重绘纹理以模拟环绕,并不断使用代码

Mat.map = new THREE.Texture( createTexture(offset) ); Mat.map.needsUpdate = true; Mat.needsUpdate = true;

Aesthetically, this works fine... except that that the canvas drawing is way too expensive and my performance suffers massively for it. 从美学上讲,这很好用...除了画布绘图过于昂贵而且我的表现因此而遭受巨大损失之外。 So it's not a reasonable solution. 因此,这不是一个合理的解决方案。

I also tried messing around with the Mat.map.offset property, but that isn't working at all. 我还尝试弄乱Mat.map.offset属性,但这根本不起作用。 Seems to be leaving the original texture present and overwriting only parts of it. 似乎保留了原始纹理并仅覆盖了其中的一部分。 I can't discern exactly what's going on there, and wonder if it's a problem with using TubeGeometry because a related stackExchange question about Spheres was solved by this method. 我无法确切地了解发生了什么,并想知道使用TubeGeometry是否存在问题,因为有关此方法的与球有关的stackExchange问​​题已解决。

A third thought was to go into TubeGeometry.faceVertexUvs and shift all the face coordinates around, modding by one, as in: 第三个想法是进入TubeGeometry.faceVertexUvs并移动所有面坐标,将它们修改一个,如下所示:

function transform(n){       
       var faceVertexUvs = tubeGeometry.faceVertexUvs[0];
       for (var i =0; i < faceVertexUvs.length; i++){
               for (var j=0; j< 3; j++){
                        faceVertexUvs[i][j].y = ((faceVertexUvs[i][j].y + n) % 1);     
               }     
        }
        tubeGeometry.uvsNeedUpdate = true;
}

This comes sooo close to working, but always leaves one equatorial line where everything goes wrong. 这太接近工作了,但是总是在一条赤道线上出现一切问题。 The texture looks terrible like it were having a great crisis of indecision, and the ray-casting I'm doing goes nuts at this spot too. 纹理看起来很糟糕,犹如犹豫不决,我正在做的射线投射在这个地方也发疯了。 99% of it works just fine tho... Maybe there's a way to salvage this last attempt? 99%的效果很好,也许...也许有一种方法可以挽救这最后一次尝试?

I'm not attached to any one method, though. 不过,我没有任何一种方法。 maximum efficiency is extra appreciated! 最大的效率是额外的赞赏!

Thanks again for any help! 再次感谢任何帮助!

The seam is created by the % 1. Remove the % 1 and enable wrapping on the texture (map.wrapS = THREE.RepeatWrapping and/or map.wrapT). 接缝是由%1创建的。删除%1并启用在纹理上的环绕(map.wrapS = THREE.RepeatWrapping和/或map.wrapT)。 This will allow you to keep incrementing the UVs and the texture will wrap on itself. 这将使您能够继续增加UV,并且纹理将自动包裹。 You could also try changing map.offset which may just offset the texture for you. 您也可以尝试更改map.offset,它可能只是为您偏移纹理。

You can do this with a fragment shader: 您可以使用片段着色器执行此操作:

<script id="fs" type="x-shader/x-fragment">
uniform float iGlobalTime;
uniform sampler2D iChannel0;

varying vec2 vUv;  
void main() {
    vec2 uv = vUv;
    uv.y = uv.y + iGlobalTime;
    if (uv.y > 1.)
        uv.y = uv.y - 1.;
    gl_FragColor = texture2D( iChannel0,  vec2(uv.x, uv.y));
}
</script>

Define your shader material: 定义着色器材质:

_uniforms = {
    iGlobalTime:    { type: 'f', value: 0.1 },
    iChannel0:  { type: 't', value: THREE.ImageUtils.loadTexture( 'textures/cat.png') },
};
newMaterial = new THREE.ShaderMaterial( {
        uniforms: _uniforms,
        vertexShader: document.getElementById( 'vs' ).textContent,
        fragmentShader: document.getElementById( 'fs' ).textContent,
} );

Then set iGlobalTime in your render loop: 然后在渲染循环中设置iGlobalTime:

_uniforms.iGlobalTime.value += clock.getDelta();
while (_uniforms.iGlobalTime.value > 1.)
    _uniforms.iGlobalTime.value -= 1.;

Complete working example here: http://rwoodley.org/MyContent/SO/01/ 完整的工作示例在这里: http : //rwoodley.org/MyContent/SO/01/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM