A Pen by Andreas Borgen on CodePen.
Created
January 31, 2020 17:31
-
-
Save Sphinxxxx/46a47f3dbc5510ff09309668075d9252 to your computer and use it in GitHub Desktop.
Multi-texture sphere
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script>console.clear();</script> | |
<h2>Click the sphere to change/reset textures</h2> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//window.onerror = function(msg, url, linenumber) { alert('Error message: '+msg+'\nURL: '+url+'\nLine Number: '+linenumber); } | |
/* https://jeromeetienne.github.io/threejsboilerplatebuilder/ */ | |
var scene, renderer, canvas; | |
var camera, cameraControls; | |
var geom, materials, mesh; | |
var textureLoader = new THREE.TextureLoader(); | |
var loaderMaterial = createMaterial( | |
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' style='background:white;'%3E%3Ctext y='55' x='15' font-weight='bold'%3ELoading...%3C/text%3E%3C/svg%3E", | |
function() { | |
init(); | |
animate(); | |
} | |
) | |
window.onclick = function(e) { | |
//https://barkofthebyte.azurewebsites.net/post/2014/05/05/three-js-projecting-mouse-clicks-to-a-3d-scene-how-to-do-it-and-how-it-works | |
//https://github.com/mrdoob/three.js/issues/5587 | |
var raycaster = new THREE.Raycaster(), | |
mouse = { | |
x: (e.clientX/canvas.width) * 2 - 1, | |
y: -(e.clientY/canvas.height) * 2 + 1 | |
}; | |
raycaster.setFromCamera(mouse, camera); | |
var intersects = raycaster.intersectObjects([mesh]); | |
if(intersects.length) { | |
swapMaterial(intersects[0].face.materialIndex); | |
} | |
} | |
// init the scene | |
function init() { | |
renderer = new THREE.WebGLRenderer({ | |
antialias: true, // to get smoother output | |
preserveDrawingBuffer: true // to allow screenshot | |
}); | |
renderer.setClearColor(0xA9E2F3); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
canvas = renderer.domElement; | |
document.body.appendChild(canvas); | |
// create a scene | |
scene = new THREE.Scene(); | |
// put a camera in the scene | |
camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 10000); | |
camera.position.set(0, 0, 100); | |
scene.add(camera); | |
// create a camera contol | |
//cameraControls = new THREEx.DragPanControls(camera) | |
// lights | |
var light = new THREE.AmbientLight(Math.random() * 0xffffff); | |
scene.add(light); | |
// 3d objects | |
// var imgs = []; | |
// //for(var i=200; i<220; i++) { imgs.push('https://placekitten.com/'+i+'/'+(i+1)); } | |
// for(var i=200; i<210; i++) { imgs.push('https://source.unsplash.com/category/people/'+i+'x'+(i+1)); } | |
// for(var i=0; i<3; i++) { imgs = imgs.concat(imgs); } | |
//OpenStreetMap tiles | |
//http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames | |
//Sadly in Mercator projection, while we want Equirectangular here... | |
/* | |
imgs = []; | |
var zoom = 4; | |
for(var y=0; y<Math.pow(2, zoom); y++) { | |
for(var x=0; x<Math.pow(2, zoom); x++) { | |
imgs.push('http://tile.openstreetmap.org/'+zoom+'/'+x+'/'+y+'.png'); | |
} | |
} | |
//*/ | |
geom = new THREE.SphereGeometry(20, 9, 8); | |
var startMat = createMaterial('https://placekitten.com/800/400'); | |
materials = [startMat]; | |
//We must group the faces into as many materialIndex "batches" as we may eventually need, | |
//even though we start out by only having one texture covering the whole sphere: | |
//http://stackoverflow.com/questions/12468906/three-js-updating-geometry-face-materialindex | |
// | |
//Skip the triangle faces at the top and bottom of the sphere, | |
//which will need a slightly different approach: | |
var tris = geom.parameters.widthSegments, | |
textureCounter = 0; | |
for (var i = tris; i < geom.faces.length - tris; i += 2) { | |
textureCounter++; | |
materials.push(startMat); | |
geom.faces[i].materialIndex = textureCounter; | |
geom.faces[i + 1].materialIndex = textureCounter; | |
if((textureCounter % 7) === 1) { | |
swapMaterial(textureCounter); | |
} | |
} | |
console.log('Textures/textures used:', materials.length, textureCounter); | |
//http://stackoverflow.com/questions/35877484/three-js-using-cubetextureloader-to-create-a-different-image-on-each-face-of-a | |
var m = new THREE.MultiMaterial(materials); | |
mesh = new THREE.Mesh(geom, m); | |
scene.add(mesh); | |
} | |
// animation loop | |
function animate() { | |
// loop on request animation loop | |
// - it has to be at the begining of the function | |
// - see details at http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating | |
requestAnimationFrame(animate); | |
// do the render | |
render(); | |
} | |
// render the scene | |
function render() { | |
// variable which is increased by Math.PI every seconds - useful for animation | |
var PIseconds = Date.now() * Math.PI / 10 | |
// update camera controls | |
//cameraControls.update(); | |
// animation of all objects | |
scene.traverse(function(object3d, i) { | |
if (object3d instanceof THREE.Mesh === false) return | |
object3d.rotation.y = PIseconds * 0.0005; | |
object3d.rotation.x = Math.sin(PIseconds * 0.002); | |
}); | |
// actually render the scene | |
renderer.render(scene, camera); | |
} | |
function createMaterial(textureUrl, onLoad) { | |
var texture = textureLoader.load(textureUrl, onLoad), | |
material = new THREE.MeshBasicMaterial({ | |
map: texture | |
}); | |
//Disable the "not power of two" warnings (this also gives us a little sharper details): | |
//http://stackoverflow.com/questions/36059642/how-to-disable-three-js-to-resize-images-in-power-of-two | |
texture.minFilter = THREE.LinearFilter; | |
//Already the default setting: | |
//texture.wrapS = THREE.ClampToEdgeWrapping; | |
//texture.wrapT = THREE.ClampToEdgeWrapping; | |
return material; | |
} | |
function swapMaterial(matIndex) { | |
//console.log('swap', matIndex, materials[matIndex] === materials[0]); | |
if(matIndex === 0) { | |
//When clicking the top or bottom of the sphere. | |
//Don't allow this, because it will change the "base texture" of the sphere at materials[0]. | |
return; | |
} | |
if(materials[matIndex] !== materials[0]) { | |
//Reset | |
doSwapMaterial(matIndex, null); | |
return; | |
} | |
var textureUrl = 'https://placekitten.com/150/' + (100 + Math.round(Math.random()*100)); | |
// //Make sure the image is in the browser cache before we apply it to the sphere, | |
// //or else the affected faces will be black while loading: | |
// var img = new Image(); | |
// img.onload = function() { doSwapMaterial(matIndex, textureUrl); } | |
// img.src = texture; | |
//textureUrl = "data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='550,-55,810,650' width='810' height='650'%3E %3Cpath d='M1017,584C1016,430,770,383,737,561L661,561C403,240,791,-75,981,87C1084,190,971,308,888,262C767,192,825,-31,1088,-26C1401,2,1392,439,1168,485C1041,517,994,338,1096,323C1157,312,1185,374,1142,408' fill='none' stroke-width='45' stroke='royalblue' /%3E %3C/svg%3E"; | |
doSwapMaterial(matIndex, loaderMaterial); | |
//http://stackoverflow.com/questions/35540880/three-textureloader-is-not-loading-images-files | |
var material = createMaterial(textureUrl, function() { | |
doSwapMaterial(matIndex, material); | |
}) | |
} | |
var originalUVs = []; | |
function doSwapMaterial(matIndex, material) { | |
//Find the two faces which have this materialIndex: | |
var faceIndex, | |
uvs = geom.faceVertexUvs[0]; | |
for (faceIndex = 0; faceIndex < geom.faces.length; faceIndex++) { | |
if(geom.faces[faceIndex].materialIndex === matIndex) { | |
break; | |
} | |
} | |
if(!originalUVs[faceIndex]) { | |
originalUVs[faceIndex] = uvs[faceIndex]; | |
originalUVs[faceIndex + 1] = uvs[faceIndex + 1]; | |
} | |
if(material) { | |
//http://stackoverflow.com/questions/18305318/three-js-whats-the-best-way-to-put-multiple-textures-images-on-a-single-spher | |
materials[matIndex] = material; | |
//Change the faceVertexUvs on the two affected faces, | |
//so the whole texture image is contained inside that "square": | |
var v00 = new THREE.Vector2(0, 0), | |
v01 = new THREE.Vector2(0, 1), | |
v10 = new THREE.Vector2(1, 0), | |
v11 = new THREE.Vector2(1, 1); | |
uvs[faceIndex] = [v11, v01, v10]; | |
uvs[faceIndex + 1] = [v01, v00, v10]; | |
} | |
else { | |
materials[matIndex] = materials[0]; | |
uvs[faceIndex] = originalUVs[faceIndex]; | |
uvs[faceIndex + 1] = originalUVs[faceIndex + 1]; | |
} | |
geom.elementsNeedUpdate = true; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.min.js"></script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
margin: 0; | |
overflow: hidden; | |
} | |
h2 { | |
position: absolute; | |
width: 100%; | |
text-align: center; | |
z-indez: 999 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment