How to project a texture to curved surface?

Viewed 77

Screen capture

I tried to make a 3/4 cylinder surface, and I made it from extruting by a ellipe path, but when I tried to load a texture to the surface, It does not as I exprected: uniformly painted to the surface, it's stretched

I know it's about texture projection, But I dont know how to set options.

class EllipseCurve3 extends THREE.Curve {
  
  ellipse = null
 
  constructor (ellipse) {
    super()
    this.ellipse = ellipse
  }
  
  
  getPoint(t, optionalTarget = new THREE.Vector3()) {
    const point = this.ellipse.getPoint(t, optionalTarget)
    return new THREE.Vector3(
      point.x,
      point.y,
      0
    )
  }
}

// Scene
const scene = new THREE.Scene();

var shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.moveTo(0, 1);
shape.lineTo(50, 1);
shape.moveTo(50, 0);
shape.lineTo(0, 0);

// var curve = new THREE.CatmullRomCurve3([
//   new THREE.Vector3(0, 0, 50),
//   new THREE.Vector3(-50, 0, 0),
//   new THREE.Vector3(0, 0, -50)
// ]);

const arc = new THREE.EllipseCurve(
    0,
    0, // ax, aY
    100,
    100, // xRadius, yRadius
    0,
    1.5 * Math.PI, // aStartAngle, aEndAngle
    false, // aClockwise
    0 // aRotation
  ),
  path = new EllipseCurve3(arc)
  geometry = new THREE.ExtrudeGeometry(shape, {
    bevelEnabled: false,
    extrudePath: path,
    steps: 50,
    depth: 5,
    amount: 20,
    material: 0,
    extrudeMaterial: 1
  });


// Set up lights
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);

const axesHelper = new THREE.AxesHelper(500);
scene.add(axesHelper);

const directionalLight = new THREE.DirectionalLight(0xffffff, 0.6);
directionalLight.position.set(100, 200, 100); // x, y, z
scene.add(directionalLight);

// Camera
const width = 200;
const height = width * (window.innerHeight / window.innerWidth);
const camera = new THREE.OrthographicCamera(
  width / -2, // left
  width / 2, // right
  height / 2, // top
  height / -2, // bottom
  0.1, // near
  1000 // far
);

camera.position.set(400, 400, 400);
camera.lookAt(0, 0, 0);

// Renderer
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);

const controls = new THREE.OrbitControls(camera, renderer.domElement);

renderer.render(scene, camera);

// Add it to HTML
document.body.appendChild(renderer.domElement);
var textureLoader = new THREE.TextureLoader();
textureLoader.crossOrigin = true;

const picture = 'https://threejs.org/examples/textures/uv_grid_opengl.jpg'
textureLoader.load(picture, function(texture) {
    //  repeat pattern
    texture.wrapS = texture.wrapT = THREE.MirroredRepeatWrapping;

    // zoom in on pattern
    texture.repeat.set(.01, .01);

    // assign texture via MeshBasicMaterial
    var material = new THREE.MeshBasicMaterial({
        map: texture,
    needsUpdate: true,
        // transparent: true,
        // premultipliedAlpha: true,
        // side: THREE.DoubleSide,
        // blending: THREE.AdditiveBlending
    });
  
  // var material = new THREE.MeshPhongMaterial({ color: 0x0048ff });
  var mesh = new THREE.Mesh(geometry, material)
  mesh.rotation.x = Math.PI / 2
  // mesh.rotation.z = Math.PI / 2
  scene.add(mesh)
  scene.add(cube)
})

function render() {
  renderer.render(scene, camera);
  // Rotate out group
  // svgGroup.rotation.y -= 0.005
  controls.update();
  requestAnimationFrame(render);
}
render();

Code here https://codepen.io/mike-xu/pen/RwQeEXJ

1 Answers

"I know it's about texture projection" - It's about computing UV, based on vertices coordinates.

CylinderGeometry also may help to achieve the result you described. With less code, and more convenient and predictable way.

body{
  overflow: hidden;
  margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three@0.136.0";
import {OrbitControls} from "https://cdn.skypack.dev/three@0.136.0/examples/jsm/controls/OrbitControls.js";

console.clear();

let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 10, 10);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", event => {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(innerWidth, innerHeight);
});

let controls = new OrbitControls(camera, renderer.domElement);

scene.add(new THREE.AxesHelper(10));

let g = new THREE.CylinderGeometry(5, 5, 5, 100, 20, true, 0, Math.PI * 1.5);
let m = new THREE.MeshBasicMaterial({
  side: THREE.DoubleSide,
  map: new THREE.TextureLoader().load(
    "https://threejs.org/examples/textures/uv_grid_opengl.jpg",
    tex => {
      tex.wrapS = tex.wrapT = THREE.MirroredRepeatWrapping;
      tex.repeat.set(3, 1);
    }
  )
});
let c = new THREE.Mesh(g, m);
scene.add(c);

renderer.setAnimationLoop(() => {
  renderer.render(scene, camera);
});
</script>

Related