问题
I've recreated a bag model for my application and exported it into ThreeJs as an .obj:
I've assigned a different colour to every face found in the models geometry like this:
var geometry = new THREE.Geometry().fromBufferGeometry( bagMesh.children[0].geometry );
for (var i = 0; i < geometry.faces.length; i ++ ) {
var face = geometry.faces[i];
// 7 & 8 = front side
// can we flip its normal?
if(i === 7 || i === 8) {
face.color.setHex( 0xff0000 );
} else {
face.color.setHex( Math.random() * 0xffffff );
}
}
geometry.translate( 0, -1, 0.75);
mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial({ vertexColors: THREE.FaceColors, side: THREE.DoubleSide }) );
scene.add(mesh);
I've identified the faces of the front-side at indices 7 and 8 of the faces array and turned them red.
The problem is that this colour can be seen when I look inside of the bag too:
I realize that this is because I've set the object to THREE.DoubleSide
but if I change it to THREE.FrontSide
then the sides only partially visible.
So my question is how do I assign a different unique colour to each side (all 11 of them, counting the inside too) without that colour appearing on that sides respective opposite?
I'm trying to keep things simple here by only using colours as opposed to mapping images onto it, which is what I'll want to eventually get to.
Note - My previous model solved this problem by treating each side as a seperate mesh but this caused other issues like z-hiding and flickering problems.
Thanks
EDIT
@WestLangley I've setup a fiddle to demonstrate what you added in your comment. Assuming that I got it right it didn't have the desired affect:
(function onLoad() {
var canvasElement;
var width, height;
var scene, camera;
var renderer;
var controls;
var pivot;
var bagMesh;
var planeMesh;
const objLoader = new THREE.OBJLoader2();
const fileLoader = new THREE.FileLoader();
init();
function init() {
container = document.getElementById('container');
initScene();
addGridHelper();
addCamera();
addLighting();
addRenderer();
addOrbitControls();
loadPlaneObj();
// Logic
var update = function() {};
// Draw scene
var render = function() {
renderer.render(scene, camera);
};
// Run game logic (update, render, repeat)
var gameLoop = function() {
requestAnimationFrame(gameLoop);
update();
render();
};
gameLoop();
}
/**** Basic Scene Setup ****/
function initScene() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0xd3d3d3);
var axis = new THREE.AxesHelper();
scene.add(axis);
}
function addCamera() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(3,3,3);
scene.add(camera);
}
function addGridHelper() {
var planeGeometry = new THREE.PlaneGeometry(2000, 2000);
planeGeometry.rotateX(-Math.PI / 2);
var planeMaterial = new THREE.ShadowMaterial({
opacity: 0.2
});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.position.y = -200;
plane.receiveShadow = true;
scene.add(plane);
var helper = new THREE.GridHelper(2000, 100);
helper.material.opacity = 0.25;
helper.material.transparent = true;
scene.add(helper);
var axis = new THREE.AxesHelper();
scene.add(axis);
}
// *********** Lighting settings **********************
function addLighting() {
var light = new THREE.HemisphereLight(0xffffff, 0xffffff, 1);
scene.add(light);
}
// ************** Material settings **************
function setMaterial(materialName) {
// get the object from the scene
var bagMesh = scene.getObjectByName('bag');
var material;
if (!materialName) {
materialName = materials.material;
}
if (bagMesh) {
var colour = parseInt(materials.colour);
switch (materialName) {
case 'MeshBasicMaterial':
material = new THREE.MeshBasicMaterial({
color: colour
});
break;
case 'MeshDepthMaterial':
material = new THREE.MeshDepthMaterial();
break;
case 'MeshLambertMaterial':
material = new THREE.MeshLambertMaterial({
color: colour
});
break;
case 'MeshNormalMaterial':
material = new THREE.MeshNormalMaterial();
break;
case 'MeshPhongMaterial':
material = new THREE.MeshPhongMaterial({
color: colour
});
break;
case 'MeshPhysicalMaterial':
material = new THREE.MeshPhysicalMaterial({
color: colour
});
break;
case 'MeshStandardMaterial':
material = new THREE.MeshStandardMaterial({
color: colour
});
break;
case 'MeshToonMaterial':
material = new THREE.MeshToonMaterial({
color: colour
});
break;
}
bagMesh.children.forEach(function(c) {
c.material = material;
});
}
}
function setMaterialColour(colour) {
materials.colour = colour;
setMaterial(null);
}
// ************** End of materials ***************
function addRenderer() {
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
container.appendChild(renderer.domElement);
}
function addOrbitControls() {
var controls = new THREE.OrbitControls(camera, renderer.domElement);
}
function addPivot() {
var cubeGeo = new THREE.BoxBufferGeometry(5, 5, 5);
var cubeMat = new THREE.MeshBasicMaterial();
pivot = new THREE.Mesh(cubeGeo, cubeMat);
bagMesh.position.x -= 15;
bagMesh.position.z -= 55;
pivot.add(bagMesh);
pivot.add(handle);
scene.add(pivot);
}
function loadPlaneObj() {
loadObj('Plane', 'https://rawgit.com/Katana24/threejs-experimentation/master/models/Plane.obj', 'https://rawgit.com/Katana24/threejs-experimentation/master/models/Plane.mtl', addPlaneToSceneSOAnswer);
}
function loadObj(objName, objUrl, mtlUrl, onLoadFunc) {
var onLoadMtl = function(materials) {
objLoader.setModelName(objName);
objLoader.setMaterials(materials);
fileLoader.setPath('');
fileLoader.setResponseType('arraybuffer');
fileLoader.load(objUrl,
function(onLoadContent) {
var mesh = objLoader.parse(onLoadContent);
onLoadFunc(mesh);
},
function(inProgress) {},
function(error) {
throw new Error('Couldnt load the model: ', error);
});
};
objLoader.loadMtl(mtlUrl, objName+'.mtl', onLoadMtl);
}
function addPlaneToSceneSOAnswer(mesh) {
var frontMaterial = new THREE.MeshBasicMaterial( { color : 0xff0000, side: THREE.FrontSide } );
var backMaterial = new THREE.MeshBasicMaterial( { color : 0x00ff00, side: THREE.BackSide } );
var geometry = new THREE.Geometry().fromBufferGeometry( mesh.children[0].geometry );
var length = geometry.faces.length;
geometry.faces.splice(14, 1);
for (var i = 0; i < geometry.faces.length; i ++ ) {
var face = geometry.faces[i];
face.color.setHex(Math.random() * 0xffffff);
}
mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial({ vertexColors: THREE.FaceColors, side: THREE.DoubleSide }) );
mesh.material.side = THREE.FrontSide;
var mesh2 = new THREE.Mesh( geometry, mesh.material.clone() );
mesh2.material.side = THREE.BackSide;
// mesh2.material.vertexColors = THREE.NoColors;
mesh2.material.vertexColors = [new THREE.Color(0xff0000), new THREE.Color(0x00ff00), new THREE.Color(0x0000ff)];
mesh.add( mesh2 );
scene.add(mesh);
}
})();
body {
background: transparent;
padding: 0;
margin: 0;
font-family: sans-serif;
}
#canvas {
margin: 10px auto;
width: 800px;
height: 350px;
margin-top: -44px;
}
<body>
<div id="container"></div>
<script src="https://threejs.org/build/three.js"></script>
<script src="https://threejs.org/examples/js/libs/dat.gui.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://threejs.org/examples/js/loaders/MTLLoader.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/loaders/LoaderSupport.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/loaders/OBJLoader2.js"></script>
</body>
What am I missing here?
回答1:
I followed along with Don's suggestion about the different materials but didn't know entirely what he meant.
I examined this question which details setting the materialIndex
. I investigated what this means and what it means is that when you pass a geometry
and an array of materials
to a mesh like this:
mesh = new THREE.Mesh( geometry, [frontMaterial, backMaterial, otherMaterial] );
then that face will get the material (frontMaterial because it's at position 0) assigned to it.
Coming back to my original question, I decided to simplify (for the moment) and see if I could apply what I want to just a Plane mesh exported from Blender.
The Plane has two Faces when added into 3JS. I found I could flip each face or assign a different material to each but I needed to duplicate the faces in order to achieve this:
function addMeshTwoToScene() {
var frontMaterial = new THREE.MeshBasicMaterial( { color : 0xff0000, side: THREE.FrontSide } );
var backMaterial = new THREE.MeshBasicMaterial( { color : 0x00ff00, side: THREE.BackSide } );
var geometry = new THREE.Geometry().fromBufferGeometry( planeMesh.children[0].geometry );
// Duplicates the face
var length = geometry.faces.length;
for (var i = 0; i < length; i++ ) {
var face = geometry.faces[i];
var newFace = Object.assign({}, face);
geometry.faces.push(newFace);
}
for (var i = 0; i < geometry.faces.length; i ++ ) {
var face = geometry.faces[i];
if(i === 0 || i === 3) {
face.materialIndex = 0;
} else {
face.materialIndex = 1;
}
}
var mesh = new THREE.Mesh( geometry, [frontMaterial, backMaterial] );
scene.add(mesh);
}
This results in the following:
I'm not going to mark this as the accepted answer yet as I still need to apply it to the more complex model in the question plus I think there could still be a better way to do this, like flipping a particular vertex to some other value.
回答2:
One solution would be to use a ShaderMaterial and define the colors based on whether the face is front or back facing.
Let me walk you through this simple example
Hold left click to rotate the mesh. If you're not familiar with ShaderFrog, click "Edit Source" on the right and scroll down the bottom of the fragment shader.
if (!gl_FrontFacing) gl_FragColor = vec4(vec3(0.0, 0.0, 1.0) * brightness, 1.0);
gl_FrontFacing
is a boolean. Quite self explanatory, it'll return true
if a face is front, and false otherwise.
The line reads "if the face is not front facing, render it blue at with alpha = 1.0.
Hoping that helps.
来源:https://stackoverflow.com/questions/48177470/adding-a-different-colour-to-each-side-of-this-obj