Atualização 5
Criou outro violino para mostrar como seria o esperado. Um skydome invisível e uma câmera de cubo são adicionados e o mapa do ambiente é usado; no meu caso, nenhuma dessas técnicas deve ser usada pelos motivos já mencionados.
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>
Atualização 4
Importante: Observe que existe um plano reflexivo atrás da malha de destino, que é para observar se a textura se liga à superfície da malha corretamente, não tem nada a ver com o que estou tentando resolver.
Atualização 3
Criou um novo violino para mostrar qual NÃO é o comportamento esperado
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>
Talvez eu deva reformular minha pergunta, mas não tenho o conhecimento necessário para descrever com precisão o que estou tentando resolver, por favor ajude .. (Transformação panorâmica com textura olhando para a direção travada na câmera talvez .. ?)
Atualização 2
(Descontinuado quando o snippet de código é aplicado.)
Atualizar
OK .. eu adicionei 3 métodos:
TransformUvaceita uma geometria e um método de transformador que manipula a transformação uv. O retorno de chamada aceita um array uvs para cada face e o correspondente Face3de geometry.faces[]como seus parâmetros.
MatcapTransformer é o retorno de chamada do manipulador de transformação uv para fazer a transformação matcap.
e
fixTextureWhenRotateAroundZAxis funciona como o nome dele.
Até agora, nenhum dos fixTexture..métodos pode funcionar em conjunto, também fixTextureWhenRotateAroundXAxisnão está resolvido. O problema permanece sem solução. Desejo que o que foi adicionado possa ajudar você a me ajudar.
Estou tentando fazer com que a textura de uma malha fique sempre de frente para uma câmera de perspectiva ativa, independentemente das posições relativas.
Para construir um caso real da minha cena e a interação seria bastante complexa, construí um exemplo mínimo para demonstrar minha intenção.
- Código
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>
Observe que, embora a própria malha gire nesta demonstração, minha verdadeira intenção é fazer a câmera se mover como orbitando em torno da malha.
Eu adicionei a estrutura de arame para tornar o movimento mais claro. Como você pode ver, eu costumo fixTextureWhenRotateAroundYAxisfazer isso corretamente, mas é apenas para o eixo y. O mesh.rotation.yno meu código real é calculado algo como
var ve=camera.position.clone();
ve.sub(mesh.position);
var rotY=Math.atan2(ve.x, ve.z);
var offsetX=rotY/(2*Math.PI);
No entanto, não tenho conhecimento de como fazer fixTextureWhenRotateAroundAllAxiscorretamente. Existem algumas restrições para resolver isso:
O CubeCamera / CubeMap não pode ser usado, pois as máquinas clientes podem ter problemas de desempenho
Não basta fazer da malha lookAta câmera, uma vez que são eventualmente de qualquer tipo de geometria, não apenas as esferas; truques como lookAte restaurar .quaternionem um quadro seria ok.
Por favor, não me entenda mal, estou perguntando um problema XY, pois não tenho o direito de expor código proprietário ou não precisaria pagar um esforço para criar um exemplo mínimo :)