Three.js 单个对象上的多个 UV 贴图
Three.js Multiple UV maps on a single object
我想弄清楚如何在盒子的正面和背面使用两种不同的纹理。
每当我缩放我的盒子 (ExtrudeGeometry) 时,UV 贴图似乎都没有更新。因此,我为盒子的正面和背面定义了自己的 UV 贴图。
定义正面 UV 贴图我使用:
geometry.faceVertexUvs[0]
相应地工作。
对于背面 UV 贴图,我使用:
geometry.faceVertexUvs[1];
但是我无法访问此 'second' 图层 UV 贴图。
所以我的问题是:
- 是否可以根据对象的比例更新 UV 贴图?
- 是否可以在 material 中访问 'second' 层 UV 贴图?
我在这里创建了一个示例:jsfiddle。
我创建了三个不同的盒子,具有 3 种不同的比例。从左到右:0.01 x 2.97 x 2.1、0.01 x 1 x 1 和 0.01 x 0.297 x 0.21。在最左边的盒子上,纹理只覆盖了盒子的一小部分。中间框有正确的纹理。右边的框有更新的 uv 贴图(否则只会显示一小部分纹理)。
(要求我使用盒子上的小刻度尺!)
希望有人能帮帮我!
Three.js 84
您可以在顶点着色器中定义 attribute vec2 uv2
,然后正常访问它。
precision highp int;
precision highp float;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec2 uv;
attribute vec2 uv2;
attribute vec3 normal;
attribute vec3 position;
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
作为对@danyim 的回答。
最后我也避免了使用着色器代码。我选择完全 uv-unwrap 我的对象并使用 WebGL 绘制我自己的纹理(因为我只需要一个简单的正面和背面纹理)。
在下面的函数中,我绘制了一个具有可变细分数量的对象并将其展开。
使用函数 drawTexture(在下文中进一步介绍),我提供了一个 'O' 和一个 'X' 作为对象前后的纹理。
希望这对您(以及可能的其他人)有所帮助。如果您还有任何问题,请随时提问。
drawObject() {
//Draw a shape with the measures of a object.
var length = 0.01, width = 0.297;
var shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.lineTo(0, width);
shape.lineTo(length, width);
shape.lineTo(length, 0);
shape.lineTo(0, 0);
var canvasTexture = drawTexture('x', 'o');
var textures = [
new THREE.MeshPhongMaterial({ color: 0xf9f9f9, side: THREE.BackSide, overdraw: 0.5 }),//GENERAL 0
new THREE.MeshPhongMaterial({ map: canvasTexture, side: THREE.BackSide, overdraw: 0.5 }), //FRONT 1
new THREE.MeshPhongMaterial({ map: canvasTexture, side: THREE.BackSide, overdraw: 0.5 }), //BACK 2
new THREE.MeshPhongMaterial({ color: 0x00ff00, side: THREE.BackSide, overdraw: 0.5 })
];
var material = new THREE.MultiMaterial(textures);
subDivs = parseInt(objectLength / 40); //Amount of subdivision (more provides a smoother result)
subDivs = 5;
var extrudeSettings = {
amount: 0.21,
steps: this.subDivs,
bevelEnabled: false
};
//Create a UVMap(u,v).
var uvMap = [];
for (let i = 0; i <= (subDivs) * 2; i++) {
var u = i / (subDivs * 2);
uvMap.push(new THREE.Vector2(u, 0));
uvMap.push(new THREE.Vector2(u, 1));
}
//Create the object.
var geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
var tempI = (uvMap.length - 2) / 2;
//Map the vertices to the UVMap (only mapping top and bottom of the object (for 'x' and 'o' texture))
for (let i = 0; i < geometry.faceVertexUvs[0].length; i++) {
if (i >= 4 && i < 4 + (subDivs * 2)) {
if (isOdd(i)) {
geometry.faceVertexUvs[0][i] = [uvMap[i - 4], uvMap[i - 2], uvMap[i - 3]];
} else {
geometry.faceVertexUvs[0][i] = [uvMap[i - 4], uvMap[i - 3], uvMap[i - 2]];
}
}
else if (i >= 4 + (subDivs * 4) && i < 4 + (subDivs * 4) + (subDivs * 2)) {
if (isOdd(i)) {
geometry.faceVertexUvs[0][i] = [uvMap[tempI], uvMap[tempI + 2], uvMap[tempI + 1]];
} else {
geometry.faceVertexUvs[0][i] = [uvMap[tempI], uvMap[tempI + 1], uvMap[tempI + 2]];
}
tempI++;
}
}
//Assigning different materialIndices to different faces
for (var i = 4; i <= 13; i++) { //Front
geometry.faces[i].materialIndex = 1;
}
for (var i = 4 + (subDivs * 4); i < 4 + (subDivs * 4) + (subDivs * 2); i++) { //Back
geometry.faces[i].materialIndex = 2;
}
for (var i = 0; i <= 1; i++) {
geometry.faces[i].materialIndex = 3;
}
var plane = new THREE.Mesh(geometry, material);
return plane;
function drawTexture (msg1, msg2) {
var canvas = document.createElement('canvas'); //Create a canvas element.
var size = 128;
canvas.width = size;
canvas.height = size;
var ctx = canvas.getContext('2d');
//Draw a white background
ctx.beginPath();
ctx.rect(0, 0, size, size);
ctx.fillStyle = 'white';
ctx.fill();
//Draw the message (e.g. 'x' or 'o')
ctx.fillStyle = 'black';
ctx.font = '64px Arial';
//Determien the size of the letters.
var metrics1 = ctx.measureText(msg1);
var textWidth1 = metrics1.width;
var textHeight = parseInt(ctx.font);
var metrics2 = ctx.measureText(msg2);
var textWidth2 = metrics2.width;
ctx.fillText(msg1, size / 4 - textWidth1 / 2, size / 2 + (textHeight / 4));
ctx.fillText(msg2, (3 * size / 4) - textWidth2 / 2, size / 2 + (textHeight / 4));
//Store the canvas in a THREE.js texture.
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
return texture; //Return Three.Texture
我想弄清楚如何在盒子的正面和背面使用两种不同的纹理。 每当我缩放我的盒子 (ExtrudeGeometry) 时,UV 贴图似乎都没有更新。因此,我为盒子的正面和背面定义了自己的 UV 贴图。
定义正面 UV 贴图我使用:
geometry.faceVertexUvs[0]
相应地工作。
对于背面 UV 贴图,我使用:
geometry.faceVertexUvs[1];
但是我无法访问此 'second' 图层 UV 贴图。
所以我的问题是:
- 是否可以根据对象的比例更新 UV 贴图?
- 是否可以在 material 中访问 'second' 层 UV 贴图?
我在这里创建了一个示例:jsfiddle。
我创建了三个不同的盒子,具有 3 种不同的比例。从左到右:0.01 x 2.97 x 2.1、0.01 x 1 x 1 和 0.01 x 0.297 x 0.21。在最左边的盒子上,纹理只覆盖了盒子的一小部分。中间框有正确的纹理。右边的框有更新的 uv 贴图(否则只会显示一小部分纹理)。
(要求我使用盒子上的小刻度尺!)
希望有人能帮帮我!
Three.js 84
您可以在顶点着色器中定义 attribute vec2 uv2
,然后正常访问它。
precision highp int;
precision highp float;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec2 uv;
attribute vec2 uv2;
attribute vec3 normal;
attribute vec3 position;
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
作为对@danyim 的回答。
最后我也避免了使用着色器代码。我选择完全 uv-unwrap 我的对象并使用 WebGL 绘制我自己的纹理(因为我只需要一个简单的正面和背面纹理)。 在下面的函数中,我绘制了一个具有可变细分数量的对象并将其展开。
使用函数 drawTexture(在下文中进一步介绍),我提供了一个 'O' 和一个 'X' 作为对象前后的纹理。
希望这对您(以及可能的其他人)有所帮助。如果您还有任何问题,请随时提问。
drawObject() {
//Draw a shape with the measures of a object.
var length = 0.01, width = 0.297;
var shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.lineTo(0, width);
shape.lineTo(length, width);
shape.lineTo(length, 0);
shape.lineTo(0, 0);
var canvasTexture = drawTexture('x', 'o');
var textures = [
new THREE.MeshPhongMaterial({ color: 0xf9f9f9, side: THREE.BackSide, overdraw: 0.5 }),//GENERAL 0
new THREE.MeshPhongMaterial({ map: canvasTexture, side: THREE.BackSide, overdraw: 0.5 }), //FRONT 1
new THREE.MeshPhongMaterial({ map: canvasTexture, side: THREE.BackSide, overdraw: 0.5 }), //BACK 2
new THREE.MeshPhongMaterial({ color: 0x00ff00, side: THREE.BackSide, overdraw: 0.5 })
];
var material = new THREE.MultiMaterial(textures);
subDivs = parseInt(objectLength / 40); //Amount of subdivision (more provides a smoother result)
subDivs = 5;
var extrudeSettings = {
amount: 0.21,
steps: this.subDivs,
bevelEnabled: false
};
//Create a UVMap(u,v).
var uvMap = [];
for (let i = 0; i <= (subDivs) * 2; i++) {
var u = i / (subDivs * 2);
uvMap.push(new THREE.Vector2(u, 0));
uvMap.push(new THREE.Vector2(u, 1));
}
//Create the object.
var geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
var tempI = (uvMap.length - 2) / 2;
//Map the vertices to the UVMap (only mapping top and bottom of the object (for 'x' and 'o' texture))
for (let i = 0; i < geometry.faceVertexUvs[0].length; i++) {
if (i >= 4 && i < 4 + (subDivs * 2)) {
if (isOdd(i)) {
geometry.faceVertexUvs[0][i] = [uvMap[i - 4], uvMap[i - 2], uvMap[i - 3]];
} else {
geometry.faceVertexUvs[0][i] = [uvMap[i - 4], uvMap[i - 3], uvMap[i - 2]];
}
}
else if (i >= 4 + (subDivs * 4) && i < 4 + (subDivs * 4) + (subDivs * 2)) {
if (isOdd(i)) {
geometry.faceVertexUvs[0][i] = [uvMap[tempI], uvMap[tempI + 2], uvMap[tempI + 1]];
} else {
geometry.faceVertexUvs[0][i] = [uvMap[tempI], uvMap[tempI + 1], uvMap[tempI + 2]];
}
tempI++;
}
}
//Assigning different materialIndices to different faces
for (var i = 4; i <= 13; i++) { //Front
geometry.faces[i].materialIndex = 1;
}
for (var i = 4 + (subDivs * 4); i < 4 + (subDivs * 4) + (subDivs * 2); i++) { //Back
geometry.faces[i].materialIndex = 2;
}
for (var i = 0; i <= 1; i++) {
geometry.faces[i].materialIndex = 3;
}
var plane = new THREE.Mesh(geometry, material);
return plane;
function drawTexture (msg1, msg2) {
var canvas = document.createElement('canvas'); //Create a canvas element.
var size = 128;
canvas.width = size;
canvas.height = size;
var ctx = canvas.getContext('2d');
//Draw a white background
ctx.beginPath();
ctx.rect(0, 0, size, size);
ctx.fillStyle = 'white';
ctx.fill();
//Draw the message (e.g. 'x' or 'o')
ctx.fillStyle = 'black';
ctx.font = '64px Arial';
//Determien the size of the letters.
var metrics1 = ctx.measureText(msg1);
var textWidth1 = metrics1.width;
var textHeight = parseInt(ctx.font);
var metrics2 = ctx.measureText(msg2);
var textWidth2 = metrics2.width;
ctx.fillText(msg1, size / 4 - textWidth1 / 2, size / 2 + (textHeight / 4));
ctx.fillText(msg2, (3 * size / 4) - textWidth2 / 2, size / 2 + (textHeight / 4));
//Store the canvas in a THREE.js texture.
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
return texture; //Return Three.Texture