syildiz
syildiz

Reputation: 23

Mapping image (Frontside-Backside) onto a sphere in three js

I have a question regarding the Three JS.

Fiddle https://jsfiddle.net/syildiz/fk8thLsq/17/

I want to creat an eye that follows the mouse movements. The images i upload are visible in the background at the same time and there is a white line at the joining of the image.

In the example, I have added pictures I want to use in the front and back. I want to use the image at the front, in the back and the image (or color) at the back, in the front. I also want to get rid of the white line, how can i do this?

Frontside Image https://image.ibb.co/mmsJ7J/logo_front.png

Backside Image https://image.ibb.co/bE8i7J/logo_back.png (or color)

I am still fairly new with Three JS, i would appreciate if you could help me on this matter.

Upvotes: 2

Views: 1524

Answers (1)

Brakebein
Brakebein

Reputation: 2237

You have to assign two materials to the object, one for the front and one for the back. Then you need to set the materialIndex of each face, so the faces know which material to use.

// use material array to apply multiple materials
var material = [
  new THREE.MeshBasicMaterial({
    map: new THREE.TextureLoader().load( "https://image.ibb.co/mmsJ7J/logo_front.png")
  }),
  new THREE.MeshBasicMaterial({
    map: new THREE.TextureLoader().load( "https://image.ibb.co/bE8i7J/logo_back.png")
  })
];

// in your faces loop, set materialIndex according to front or back side of geometry
if (v1.z < 0)
    faces[i].materialIndex = 1;

// after loop, set groupsNeedUpdate -> "Set to true if a face3 materialIndex has been updated."
geometry.groupsNeedUpdate = true;

Regarding the white line: this is because you are at the very edge of the texture (respectively the opaque edge of the circle). I added some offset, so the texture will slightly overlap.

var max = geometry.boundingBox.max.clone().add(new THREE.Vector3(1,1,1)),
    min = geometry.boundingBox.min.clone().add(new THREE.Vector3(-1,-1,-1));

// If the radius of the sphere is smaller, you should also set a smaller offset.
// in this example it's 1:60, which is ok.

Here's a snippet (or have a look at your updated jsFiddle: https://jsfiddle.net/fk8thLsq/36/)

//Setup:

var container = document.querySelector('#container');


var renderer = new THREE.WebGLRenderer({alpha: true});
var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;
renderer.setSize(WIDTH, HEIGHT);

var VIEW_ANGLE = 40;
var ASPECT = WIDTH / HEIGHT;
var NEAR = 0.1;
var FAR = 1000;

var camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
camera.position.set(0, 0, 50);

var scene = new THREE.Scene();
scene.background = null;
scene.add(camera);

container.appendChild(renderer.domElement);

var RADIUS = 200;
var SEGMENTS = 50;
var RINGS = 50;


var group = new THREE.Group();
scene.add(group);

var material = [
	new THREE.MeshBasicMaterial({
    map: new THREE.TextureLoader().load( "https://image.ibb.co/mmsJ7J/logo_front.png" ),
    overdraw: 0
	}),
	new THREE.MeshBasicMaterial({
    map: new THREE.TextureLoader().load( "https://image.ibb.co/bE8i7J/logo_back.png" ),
    overdraw: 0
	})
];

scene.add(new THREE.AmbientLight(0xffffff, 0.2));

var light = new THREE.PointLight(0xffffff, 0.2);
camera.add(light);

var geometry = new THREE.SphereGeometry(60, 64, 32);

geometry.computeBoundingBox();

var max = geometry.boundingBox.max.clone().add(new THREE.Vector3(1,1,1)),
    min = geometry.boundingBox.min.clone().add(new THREE.Vector3(-1,-1,-1));
    
var offset = new THREE.Vector2(0 - min.x, 0 - min.y);
var range = new THREE.Vector2(max.x - min.x, max.y - min.y);
var faces = geometry.faces;

geometry.faceVertexUvs[0] = [];

for (var i = 0; i < faces.length; i++) {

    var v1 = geometry.vertices[faces[i].a],
        v2 = geometry.vertices[faces[i].b],
        v3 = geometry.vertices[faces[i].c];
        
    if (v1.z < 0)
    		faces[i].materialIndex = 1;

    geometry.faceVertexUvs[0].push([
        new THREE.Vector2((v1.x + offset.x) / range.x, (v1.y + offset.y) / range.y),
        new THREE.Vector2((v2.x + offset.x) / range.x, (v2.y + offset.y) / range.y),
        new THREE.Vector2((v3.x + offset.x) / range.x, (v3.y + offset.y) / range.y)
    ]);
}

geometry.groupsNeedUpdate = true;
geometry.uvsNeedUpdate = true;

var mesh = new THREE.Mesh(geometry, material);

group.add(mesh);
group.position.z = -270;

var pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.x = -100;
pointLight.position.y = 0;
pointLight.position.z = 200;

scene.add(pointLight);


function update() {
    renderer.render(scene, camera);
    requestAnimationFrame(update);
}

requestAnimationFrame(update);

function animationBuilder(direction) {
    return function animateRotate() {
        switch (direction) {
            case 'up':
                group.rotation.x -= 0.2;
                break;
            case 'down':
                group.rotation.x += 0.2;
                break;
            case 'left':
                group.rotation.y -= 0.2;
                break;
            case 'right':
                group.rotation.y += 0.2;
                break;
            default:
                break;
        }
    };
}

var animateDirection = {
    up: animationBuilder('up'),
    down: animationBuilder('down'),
    left: animationBuilder('left'),
    right: animationBuilder('right')
};

function checkKey(e) {
    e = e || window.event;
    e.preventDefault();
    if (e.keyCode == '38') {
        animateDirection.up();
    } else if (e.keyCode == '40') {
        animateDirection.down();
    } else if (e.keyCode == '37') {
        animateDirection.left();
    } else if (e.keyCode == '39') {
        animateDirection.right();
    }
}

document.onkeydown = checkKey;

var lastMove = [window.innerWidth / 2, window.innerHeight / 2];

function rotateOnMouseMove(e) {
    e = e || window.event;

    var moveX = e.clientX - lastMove[0];
    var moveY = e.clientY - lastMove[1];

    group.rotation.y += moveX * .004;
    group.rotation.x += moveY * .004;

    lastMove[0] = e.clientX;
    lastMove[1] = e.clientY;
}

document.addEventListener('mousemove', rotateOnMouseMove);
body{margin:0;background:#ddd;}
.logo{position:absolute;top:10px;width:100px;height:100px;background-size:100% auto;background-repeat:no-repeat;}
.front{left:10px;background-image:url("https://image.ibb.co/mmsJ7J/logo_front.png")}
.back{left:120px;background-image:url("https://image.ibb.co/bE8i7J/logo_back.png")}
<div class="logo front"></div>
<div class="logo back"></div>
<div id="container" width="100vw" height="100vh"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/94/three.min.js"></script>

You will notice that there is still a seam between both materials. Maybe you should use a plain color for your textures and achieve the slightly specular behaviour by setting the material properties accordingly.

Upvotes: 3

Related Questions