Mise à jour 5
Créé un autre violon pour montrer à quoi devrait ressembler. Un skydome invisible et une cubecamera sont ajoutés et une carte d'environnement est utilisée; dans mon cas, aucune de ces techniques ne devrait être utilisée pour les raisons déjà mentionnées.
var MatcapTransformer = function(uvs, face) {
for (var i = uvs.length; i-- > 0;) {
uvs[i].x = face.vertexNormals[i].x * 0.5 + 0.5;
uvs[i].y = face.vertexNormals[i].y * 0.5 + 0.5;
}
};
var TransformUv = function(geometry, xformer) {
// The first argument is also used as an array in the recursive calls
// as there's no method overloading in javascript; and so is the callback.
var a = arguments[0],
callback = arguments[1];
var faceIterator = function(uvFaces, index) {
xformer(uvFaces[index], geometry.faces[index]);
};
var layerIterator = function(uvLayers, index) {
TransformUv(uvLayers[index], faceIterator);
};
for (var i = a.length; i-- > 0;) {
callback(a, i);
}
if (!(i < 0)) {
TransformUv(geometry.faceVertexUvs, layerIterator);
}
};
var SetResizeHandler = function(renderer, camera) {
var callback = function() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
};
// bind the resize event
window.addEventListener('resize', callback, false);
// return .stop() the function to stop watching window resize
return {
stop: function() {
window.removeEventListener('resize', callback);
}
};
};
(function() {
var fov = 45;
var aspect = window.innerWidth / window.innerHeight;
var loader = new THREE.TextureLoader();
var texture = loader.load('https://i.postimg.cc/mTsN30vx/canyon-s.jpg');
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.center.set(1 / 2, 1 / 2);
var cubeCam = new THREE.CubeCamera(.1, 200, 4096);
cubeCam.renderTarget.texture.wrapS = THREE.RepeatWrapping;
cubeCam.renderTarget.texture.wrapT = THREE.RepeatWrapping;
cubeCam.renderTarget.texture.center.set(1 / 2, 1 / 2);
var geoSky = new THREE.SphereGeometry(2, 16, 16);
var matSky = new THREE.MeshBasicMaterial({
'map': texture,
'side': THREE.BackSide
});
var meshSky = new THREE.Mesh(geoSky, matSky);
meshSky.visible = false;
var geometry = new THREE.IcosahedronGeometry(1, 1);
var material = new THREE.MeshBasicMaterial({
'envMap': cubeCam.renderTarget.texture
});
var mesh = new THREE.Mesh(geometry, material);
var geoWireframe = new THREE.WireframeGeometry(geometry);
var matWireframe = new THREE.LineBasicMaterial({
'color': 'red',
'linewidth': 2
});
mesh.add(new THREE.LineSegments(geoWireframe, matWireframe));
var camera = new THREE.PerspectiveCamera(fov, aspect);
camera.position.setZ(20);
var scene = new THREE.Scene();
scene.add(mesh);
scene.add(meshSky);
{
var mirror = new THREE.CubeCamera(.1, 2000, 4096);
var geoPlane = new THREE.PlaneGeometry(16, 16);
var matPlane = new THREE.MeshBasicMaterial({
'envMap': mirror.renderTarget.texture
});
var plane = new THREE.Mesh(geoPlane, matPlane);
plane.add(mirror);
plane.position.setZ(-4);
plane.lookAt(mesh.position);
scene.add(plane);
}
var renderer = new THREE.WebGLRenderer();
var container = document.getElementById('container1');
container.appendChild(renderer.domElement);
SetResizeHandler(renderer, camera);
renderer.setSize(window.innerWidth, window.innerHeight);
var controls = new THREE.TrackballControls(camera, container);
var fixTextureWhenRotateAroundAllAxis = function() {
mesh.rotation.y += 0.01;
mesh.rotation.x += 0.01;
mesh.rotation.z += 0.01;
cubeCam.update(renderer, scene);
};
renderer.setAnimationLoop(function() {
// controls.update();
plane.visible = false;
{
meshSky.visible = true;
mesh.visible = false;
fixTextureWhenRotateAroundAllAxis();
mesh.visible = true;
meshSky.visible = false;
}
mirror.update(renderer, scene);
plane.visible = true;
renderer.render(scene, camera);
});
})();
body {
background-color: #000;
margin: 0px;
overflow: hidden;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>
<div id='container1'></div>
Mise à jour 4
Important: Veuillez noter qu'il y a un plan réfléchissant à l'arrière du maillage cible qui sert à observer si la texture se lie correctement à la surface du maillage, cela n'a rien à voir avec ce que j'essaie de résoudre.
Mise à jour 3
Création d'un nouveau violon pour montrer ce qui n'est PAS le comportement attendu
var MatcapTransformer=function(uvs, face) {
for(var i=uvs.length; i-->0;) {
uvs[i].x=face.vertexNormals[i].x*0.5+0.5;
uvs[i].y=face.vertexNormals[i].y*0.5+0.5;
}
};
var TransformUv=function(geometry, xformer) {
// The first argument is also used as an array in the recursive calls
// as there's no method overloading in javascript; and so is the callback.
var a=arguments[0], callback=arguments[1];
var faceIterator=function(uvFaces, index) {
xformer(uvFaces[index], geometry.faces[index]);
};
var layerIterator=function(uvLayers, index) {
TransformUv(uvLayers[index], faceIterator);
};
for(var i=a.length; i-->0;) {
callback(a, i);
}
if(!(i<0)) {
TransformUv(geometry.faceVertexUvs, layerIterator);
}
};
var SetResizeHandler=function(renderer, camera) {
var callback=function() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect=window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();
};
// bind the resize event
window.addEventListener('resize', callback, false);
// return .stop() the function to stop watching window resize
return {
stop: function() {
window.removeEventListener('resize', callback);
}
};
};
var getVertexShader=function() {
return `
void main() {
gl_Position=projectionMatrix*modelViewMatrix*vec4(position, 1.0);
}
`;
};
var getFragmentShader=function(size) {
return `
uniform sampler2D texture1;
const vec2 size=vec2(`+size.x+`, `+size.y+`);
void main() {
gl_FragColor=texture2D(texture1, gl_FragCoord.xy/size.xy);
}
`;
};
(function() {
var fov=45;
var aspect=window.innerWidth/window.innerHeight;
var loader=new THREE.TextureLoader();
var texture=loader.load('https://i.postimg.cc/mTsN30vx/canyon-s.jpg');
texture.wrapS=THREE.RepeatWrapping;
texture.wrapT=THREE.RepeatWrapping;
texture.center.set(1/2, 1/2);
var geometry=new THREE.SphereGeometry(1, 16, 16);
// var geometry=new THREE.BoxGeometry(2, 2, 2);
// var material=new THREE.MeshBasicMaterial({ 'map': texture });
var material=new THREE.ShaderMaterial({
'uniforms': { 'texture1': { 'type': 't', 'value': texture } }
, 'vertexShader': getVertexShader()
, 'fragmentShader': getFragmentShader({ 'x': 512, 'y': 256 })
});
var mesh=new THREE.Mesh(geometry, material);
var geoWireframe=new THREE.WireframeGeometry(geometry);
var matWireframe=new THREE.LineBasicMaterial({ 'color': 'red', 'linewidth': 2 });
mesh.add(new THREE.LineSegments(geoWireframe, matWireframe));
var camera=new THREE.PerspectiveCamera(fov, aspect);
camera.position.setZ(20);
var scene=new THREE.Scene();
scene.add(mesh);
{
var mirror=new THREE.CubeCamera(.1, 2000, 4096);
var geoPlane=new THREE.PlaneGeometry(16, 16);
var matPlane=new THREE.MeshBasicMaterial({
'envMap': mirror.renderTarget.texture
});
var plane=new THREE.Mesh(geoPlane, matPlane);
plane.add(mirror);
plane.position.setZ(-4);
plane.lookAt(mesh.position);
scene.add(plane);
}
var renderer=new THREE.WebGLRenderer();
var container=document.getElementById('container1');
container.appendChild(renderer.domElement);
SetResizeHandler(renderer, camera);
renderer.setSize(window.innerWidth, window.innerHeight);
var fixTextureWhenRotateAroundYAxis=function() {
mesh.rotation.y+=0.01;
texture.offset.set(mesh.rotation.y/(2*Math.PI), 0);
};
var fixTextureWhenRotateAroundZAxis=function() {
mesh.rotation.z+=0.01;
texture.rotation=-mesh.rotation.z
TransformUv(geometry, MatcapTransformer);
};
var fixTextureWhenRotateAroundAllAxis=function() {
mesh.rotation.y+=0.01;
mesh.rotation.x+=0.01;
mesh.rotation.z+=0.01;
};
var controls=new THREE.TrackballControls(camera, container);
renderer.setAnimationLoop(function() {
fixTextureWhenRotateAroundAllAxis();
controls.update();
plane.visible=false;
mirror.update(renderer, scene);
plane.visible=true;
renderer.render(scene, camera);
});
})();
body {
background-color: #000;
margin: 0px;
overflow: hidden;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>
<div id='container1'></div>
Je devrais peut-être reformuler ma question, mais je n'ai pas les connaissances nécessaires pour décrire avec précision ce que j'essaie de résoudre, aidez-moi s'il vous plaît. peut être .. ?)
Mise à jour 2
(A été déconseillé car un extrait de code est appliqué.)
Mise à jour
OK .. J'ai ajouté 3 méthodes:
TransformUv
accepte une géométrie et une méthode de transformation qui gère la transformation uv. Le rappel accepte un tableau uvs pour chaque face et le correspondant Face3
de geometry.faces[]
comme paramètres.
MatcapTransformer
est le rappel du gestionnaire uv-transform pour effectuer la transformation matcap.
et
fixTextureWhenRotateAroundZAxis
fonctionne comme il l'a nommé.
Jusqu'à présent, aucune des fixTexture..
méthodes ne peut fonctionner ensemble, elle fixTextureWhenRotateAroundXAxis
n'est pas non plus comprise . Le problème reste non résolu, je souhaite que ce qui vient d'être ajouté puisse vous aider à m'aider.
J'essaie de faire en sorte que la texture d'un maillage soit toujours face à une caméra en perspective active, quelles que soient les positions relatives.
Pour construire un cas réel de ma scène et l'interaction serait assez complexe, j'ai construit un exemple minimal pour démontrer mon intention.
- Code
var MatcapTransformer=function(uvs, face) {
for(var i=uvs.length; i-->0;) {
uvs[i].x=face.vertexNormals[i].x*0.5+0.5;
uvs[i].y=face.vertexNormals[i].y*0.5+0.5;
}
};
var TransformUv=function(geometry, xformer) {
// The first argument is also used as an array in the recursive calls
// as there's no method overloading in javascript; and so is the callback.
var a=arguments[0], callback=arguments[1];
var faceIterator=function(uvFaces, index) {
xformer(uvFaces[index], geometry.faces[index]);
};
var layerIterator=function(uvLayers, index) {
TransformUv(uvLayers[index], faceIterator);
};
for(var i=a.length; i-->0;) {
callback(a, i);
}
if(!(i<0)) {
TransformUv(geometry.faceVertexUvs, layerIterator);
}
};
var SetResizeHandler=function(renderer, camera) {
var callback=function() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect=window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();
};
// bind the resize event
window.addEventListener('resize', callback, false);
// return .stop() the function to stop watching window resize
return {
stop: function() {
window.removeEventListener('resize', callback);
}
};
};
(function() {
var fov=45;
var aspect=window.innerWidth/window.innerHeight;
var loader=new THREE.TextureLoader();
var texture=loader.load('https://i.postimg.cc/mTsN30vx/canyon-s.jpg');
texture.wrapS=THREE.RepeatWrapping;
texture.wrapT=THREE.RepeatWrapping;
texture.center.set(1/2, 1/2);
var geometry=new THREE.SphereGeometry(1, 16, 16);
var material=new THREE.MeshBasicMaterial({ 'map': texture });
var mesh=new THREE.Mesh(geometry, material);
var geoWireframe=new THREE.WireframeGeometry(geometry);
var matWireframe=new THREE.LineBasicMaterial({ 'color': 'red', 'linewidth': 2 });
mesh.add(new THREE.LineSegments(geoWireframe, matWireframe));
var camera=new THREE.PerspectiveCamera(fov, aspect);
camera.position.setZ(20);
var scene=new THREE.Scene();
scene.add(mesh);
{
var mirror=new THREE.CubeCamera(.1, 2000, 4096);
var geoPlane=new THREE.PlaneGeometry(16, 16);
var matPlane=new THREE.MeshBasicMaterial({
'envMap': mirror.renderTarget.texture
});
var plane=new THREE.Mesh(geoPlane, matPlane);
plane.add(mirror);
plane.position.setZ(-4);
plane.lookAt(mesh.position);
scene.add(plane);
}
var renderer=new THREE.WebGLRenderer();
var container=document.getElementById('container1');
container.appendChild(renderer.domElement);
SetResizeHandler(renderer, camera);
renderer.setSize(window.innerWidth, window.innerHeight);
var fixTextureWhenRotateAroundYAxis=function() {
mesh.rotation.y+=0.01;
texture.offset.set(mesh.rotation.y/(2*Math.PI), 0);
};
var fixTextureWhenRotateAroundZAxis=function() {
mesh.rotation.z+=0.01;
texture.rotation=-mesh.rotation.z
TransformUv(geometry, MatcapTransformer);
};
// This is wrong
var fixTextureWhenRotateAroundAllAxis=function() {
mesh.rotation.y+=0.01;
mesh.rotation.x+=0.01;
mesh.rotation.z+=0.01;
// Dun know how to do it correctly ..
texture.offset.set(mesh.rotation.y/(2*Math.PI), 0);
};
var controls=new THREE.TrackballControls(camera, container);
renderer.setAnimationLoop(function() {
fixTextureWhenRotateAroundYAxis();
// Uncomment the following line and comment out `fixTextureWhenRotateAroundYAxis` to see the demo
// fixTextureWhenRotateAroundZAxis();
// fixTextureWhenRotateAroundAllAxis();
// controls.update();
plane.visible=false;
mirror.update(renderer, scene);
plane.visible=true;
renderer.render(scene, camera);
});
})();
body {
background-color: #000;
margin: 0px;
overflow: hidden;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>
<div id='container1'></div>
Veuillez noter que bien que le maillage tourne dans cette démonstration, ma véritable intention est de faire bouger la caméra comme en orbite autour du maillage.
J'ai ajouté le filaire pour rendre le mouvement plus clair. Comme vous pouvez le voir, j'utilise fixTextureWhenRotateAroundYAxis
pour le faire correctement, mais ce n'est que pour l'axe des y. Le mesh.rotation.y
dans mon code réel est calculé quelque chose comme
var ve=camera.position.clone();
ve.sub(mesh.position);
var rotY=Math.atan2(ve.x, ve.z);
var offsetX=rotY/(2*Math.PI);
Cependant, je ne sais pas comment faire fixTextureWhenRotateAroundAllAxis
correctement. Il existe certaines restrictions pour résoudre ce problème:
CubeCamera / CubeMap ne peut pas être utilisé car les machines clientes peuvent avoir des problèmes de performances
Ne faites pas simplement du maillage lookAt
la caméra car ils sont finalement de n'importe quelle sorte de géométrie, pas seulement les sphères; des astuces comme lookAt
et restaurer .quaternion
dans un cadre serait ok.
S'il vous plaît, ne vous méprenez pas, je pose un problème XY car je n'ai pas le droit d'exposer du code propriétaire ou je n'aurais pas à payer l'effort de construire un exemple minimal :)
Réponses:
Faire face à la caméra ressemblera à:
Ou, encore mieux, comme dans cette question , où la solution inverse est demandée:
Pour y parvenir, vous devez configurer un shader de fragment simple (comme l'OP l'a fait accidentellement):
Vertex shader
Shader de fragment
Une maquette de travail du shader avec Three.js
Une alternative viable: la cartographie des cubes
Ici, j'ai modifié un jsfiddle sur le mappage de cube , c'est peut-être ce que vous recherchez:
https://jsfiddle.net/v8efxdo7/
Le cube projette sa texture de visage sur l'objet sous-jacent et regarde la caméra.
Remarque: la lumière change avec la rotation car la lumière et l'objet intérieur sont en position fixe, tandis que la caméra et le cube de projection tournent tous les deux autour du centre de la scène.
Si vous regardez attentivement l'exemple, cette technique n'est pas parfaite, mais ce que vous recherchez (appliqué à une boîte) est délicat, car le dépliage UV de la texture d'un cube est en forme de croix, la rotation de l'UV lui-même ne sera pas être efficace et l'utilisation de techniques de projection a aussi ses inconvénients, car la forme de l'objet projecteur et la forme du sujet de projection sont importantes.
Juste pour une meilleure compréhension: dans le monde réel, où voyez-vous cet effet dans l'espace 3D sur les boîtes? Le seul exemple qui me vient à l'esprit est une projection 2D sur une surface 3D (comme la cartographie de projection dans la conception visuelle).
la source