简体   繁体   中英

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. 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. I'll call this material 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

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. 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.

A third thought was to go into TubeGeometry.faceVertexUvs and shift all the face coordinates around, modding by one, as in:

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?

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). This will allow you to keep incrementing the UVs and the texture will wrap on itself. You could also try changing map.offset which may just offset the texture for you.

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:

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

Complete working example here: http://rwoodley.org/MyContent/SO/01/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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