【发布时间】:2016-06-04 09:38:17
【问题描述】:
我从相同的几何体创建了两个网格并运行相同的动画。如果我对网格完全不做任何事情,它们就会保持完美的同步,这就是我想要的。但如果我改变它们的位置或旋转,它们就会不同步。
Here's a jsfiddle of an example. 顶部有一团缩小的 js,其中包含来自 r77 源的 EffectComposer.js、ShaderPass.js、RenderPass.js、MaskPass.js 和 CopyShader.js 的内容——这三个。 js CDN 不包含它们,并且 jsfiddle 无法从 three.js github repo 链接到它们。示例问题的开始是 THREE.OutlineShader 的定义:
THREE.OutlineShader = {
uniforms: {
"offset": {
type: "f",
value: 2.0
},
"boneTexture": {
type: "t",
value: null
},
"boneTextureWidth": {
type: "i",
value: null
},
"boneTextureHeight": {
type: "i",
value: null
},
},
vertexShader: [
"uniform sampler2D boneTexture;",
"uniform int boneTextureWidth;",
"uniform int boneTextureHeight;",
"uniform float offset;",
"mat4 getBoneMatrix(const in float i) {",
"float j = i * 4.0;",
"float x = mod(j, float(boneTextureWidth));",
"float y = floor(j / float(boneTextureWidth));",
"float dx = 1.0 / float(boneTextureWidth);",
"float dy = 1.0 / float(boneTextureHeight);",
"y = dy * (y + 0.5);",
"vec4 v1 = texture2D(boneTexture, vec2(dx * (x + 0.5), y));",
"vec4 v2 = texture2D(boneTexture, vec2(dx * (x + 1.5), y));",
"vec4 v3 = texture2D(boneTexture, vec2(dx * (x + 2.5), y));",
"vec4 v4 = texture2D(boneTexture, vec2(dx * (x + 3.5), y));",
"mat4 bone = mat4(v1, v2, v3, v4);",
"return bone;",
"}",
"void main() {",
"mat4 boneMatX = getBoneMatrix(skinIndex.x);",
"mat4 boneMatY = getBoneMatrix(skinIndex.y);",
"mat4 boneMatZ = getBoneMatrix(skinIndex.z);",
"mat4 boneMatW = getBoneMatrix(skinIndex.w);",
"vec4 skinVertex = vec4(position + normal * offset, 1.0);",
"vec4 skinned = boneMatX * skinVertex * skinWeight.x;",
"skinned += boneMatY * skinVertex * skinWeight.y;",
"skinned += boneMatZ * skinVertex * skinWeight.z;",
"skinned += boneMatW * skinVertex * skinWeight.w;",
"vec4 mvPosition = modelViewMatrix * skinned;",
"gl_Position = projectionMatrix * mvPosition;",
"}"
].join("\n"),
fragmentShader: [
"uniform int boneTextureWidth;",
"void main() {",
"gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);",
"}"
].join("\n")
};
var camera, light, renderer, composer, clock;
var sceneMain, sceneOutline;
var meshMain = null,
meshOutline = null;
var mixerMain, mixerOutline;
var animMain, animOutline;
var height = 500,
width = 500;
var objData = '{"metadata":{"formatVersion":3.1,"generatedBy":"Blender 2.7 Exporter","vertices":24,"faces":22,"normals":18,"colors":0,"uvs":[],"materials":1,"morphTargets":0,"bones":2},"scale":1.000000,"materials":[{"DbgColor":15658734,"DbgIndex":0,"DbgName":"Material","blending":"NormalBlending","colorDiffuse":[0.1569801711586143,0.17312412519937936,0.6400000190734865],"colorEmissive":[0.0,0.0,0.0],"colorSpecular":[0.2535329759120941,0.0,0.007157782092690468],"depthTest":true,"depthWrite":true,"shading":"Lambert","specularCoef":50,"opacity":1.0,"transparent":false,"vertexColors":false}],"vertices":[1.51034,-1,-1,1.51034,-1,1,-0.489661,-1,1,-0.489661,-1,-1,1.51034,1,-1,1.51034,1,1,-0.489662,1,1,-0.489661,1,-1,3.23233,-1,-0.999999,3.23233,-1,1,3.23233,1,-0.999999,3.23233,1,1,-1.98848,-1,1,-1.98848,-1,-1,-1.98848,1,0.999999,-1.98848,1,-1,1.51034,-5.70811,-1,1.51034,-5.70811,1,3.23233,-5.70811,-0.999999,3.23233,-5.70811,1,-0.489661,-5.62708,1,-0.48966,-5.62708,-1,-1.98848,-5.62708,1,-1.98848,-5.62708,-1],"morphTargets":[],"normals":[-0.301492,-0.301492,-0.904508,-0.301492,-0.301492,0.904508,0.301492,-0.301492,0.904508,0.301492,-0.301492,-0.904508,0,0.707083,-0.707083,0,0.707083,0.707083,0.577349,0.577349,0.577349,0.577349,0.577349,-0.577349,-0.577349,0.577349,-0.577349,-0.577349,0.577349,0.577349,0.707083,0,-0.707083,0.707083,0,0.707083,0.577349,-0.577349,-0.577349,-0.577349,-0.577349,-0.577349,-0.707083,0,0.707083,-0.707083,0,-0.707083,-0.577349,-0.577349,0.577349,0.577349,-0.577349,0.577349],"colors":[],"uvs":[],"faces":[35,0,1,2,3,0,0,1,2,3,35,4,5,11,10,0,4,5,6,7,35,1,5,6,2,0,1,5,5,2,35,6,7,15,14,0,5,4,8,9,35,4,0,3,7,0,4,0,3,4,35,8,10,11,9,0,10,7,6,11,35,5,1,9,11,0,5,1,11,6,35,0,4,10,8,0,0,4,7,10,35,0,8,18,16,0,0,10,12,13,35,12,14,15,13,0,14,9,8,15,35,7,3,13,15,0,4,3,15,8,35,2,6,14,12,0,2,5,9,14,35,2,12,22,20,0,2,14,16,17,35,17,16,18,19,0,16,13,12,17,35,9,1,17,19,0,11,1,16,17,35,8,9,19,18,0,10,11,17,12,35,1,0,16,17,0,1,0,13,16,35,21,20,22,23,0,12,17,16,13,35,13,3,21,23,0,15,3,12,13,35,12,13,23,22,0,14,15,13,16,35,3,2,20,21,0,3,2,17,12,35,4,7,6,5,0,4,4,5,5],"bones":[{"parent":-1,"name":"leg.R","pos":[-1.24994,0.43791,0.191651],"rotq":[-0.00523508,-0.706875,-0.707296,-0.00579363],"scl":[1,1,1]},{"parent":-1,"name":"leg.L","pos":[2.49995,0.280193,0.066556],"rotq":[-0.00523507,-0.706875,-0.707296,-0.00579363],"scl":[1,1,1]}],"skinIndices":[1,0,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,-1,1,-1,1,-1,1,-1,0,-1,0,-1,0,-1,0,1,1,-1,1,-1,1,-1,1,-1,0,-1,0,-1,0,-1,0,-1],"skinWeights":[0.928373,0.0680346,0.937978,0.0587701,0.949888,0.0463839,0.937937,0.0591265,0.821856,0.122838,0.79233,0.145709,0.876929,0.0825711,0.830405,0.115734,0.989868,0,0.992278,0,0.968805,0,0.966368,0,0.993762,0,0.989439,0,0.978637,0,0.962526,0.00173758,0.997334,0,0.997776,0,0.999229,0,0.999402,0,0.998345,0,0.997508,0,0.99955,0,0.999106,0],"animations":[{"name":"ArmatureAction","fps":24,"length":0.416667,"hierarchy":[{"parent":-1,"keys":[{"time":0,"pos":[-1.24994,0.43791,0.191651],"rot":[-0.00643926,-0.522937,-0.852335,-0.0044168],"scl":[1,1,1]},{"time":0.0416667,"pos":[-1.24994,0.43791,0.191651],"rot":[-0.00683821,-0.561328,-0.827555,-0.00415746],"scl":[1,1,1]},{"time":0.0833333,"pos":[-1.24994,0.43791,0.191651],"rot":[-0.00791262,-0.665775,-0.746103,-0.00335735],"scl":[1,1,1]},{"time":0.125,"pos":[-1.24994,0.43791,0.191651],"rot":[0.00910612,0.78443,0.620147,0.00222404],"scl":[1,1,1]},{"time":0.166667,"pos":[-1.24994,0.43791,0.191651],"rot":[0.00983298,0.859093,0.511724,0.00131562],"scl":[1,1,1]},{"time":0.208333,"pos":[-1.24994,0.43791,0.191651],"rot":[0.0100438,0.881367,0.472325,0.000997505],"scl":[1,1,1]},{"time":0.25,"pos":[-1.24994,0.43791,0.191651],"rot":[0.00983298,0.859093,0.511724,0.00131562],"scl":[1,1,1]},{"time":0.291667,"pos":[-1.24994,0.43791,0.191651],"rot":[0.00910612,0.78443,0.620147,0.00222404],"scl":[1,1,1]},{"time":0.333333,"pos":[-1.24994,0.43791,0.191651],"rot":[-0.00791262,-0.665775,-0.746103,-0.00335735],"scl":[1,1,1]},{"time":0.375,"pos":[-1.24994,0.43791,0.191651],"rot":[-0.00683821,-0.561328,-0.827555,-0.00415746],"scl":[1,1,1]},{"time":0.416667,"pos":[-1.24994,0.43791,0.191651],"rot":[-0.00643926,-0.522937,-0.852335,-0.0044168],"scl":[1,1,1]}]},{"parent":0,"keys":[{"time":0,"pos":[2.49995,0.280193,0.066556],"rot":[0.0033329,0.881416,0.472275,0.00706144],"scl":[1,1,1]},{"time":0.0416667,"pos":[2.49995,0.280193,0.066556],"rot":[0.00316317,0.858922,0.512045,0.00734349],"scl":[1,1,1]},{"time":0.0833333,"pos":[2.49995,0.280193,0.066556],"rot":[0.00263566,0.783706,0.621074,0.00807219],"scl":[1,1,1]},{"time":0.125,"pos":[2.49995,0.280193,0.066556],"rot":[-0.00187897,-0.664887,-0.74689,-0.00880854],"scl":[1,1,1]},{"time":0.166667,"pos":[2.49995,0.280193,0.066556],"rot":[-0.00126496,-0.561008,-0.827759,-0.00919252],"scl":[1,1,1]},{"time":0.208333,"pos":[2.49995,0.280193,0.066556],"rot":[-0.00104832,-0.522977,-0.852295,-0.009288],"scl":[1,1,1]},{"time":0.25,"pos":[2.49995,0.280193,0.066556],"rot":[-0.00126496,-0.561007,-0.827759,-0.00919252],"scl":[1,1,1]},{"time":0.291667,"pos":[2.49995,0.280193,0.066556],"rot":[-0.00187897,-0.664887,-0.74689,-0.00880854],"scl":[1,1,1]},{"time":0.333333,"pos":[2.49995,0.280193,0.066556],"rot":[0.00263566,0.783706,0.621074,0.00807219],"scl":[1,1,1]},{"time":0.375,"pos":[2.49995,0.280193,0.066556],"rot":[0.00316317,0.858921,0.512045,0.0073435],"scl":[1,1,1]},{"time":0.416667,"pos":[2.49995,0.280193,0.066556],"rot":[0.0033329,0.881416,0.472275,0.00706144],"scl":[1,1,1]}]}]}]}';
load();
function load() {
var loader = new THREE.JSONLoader();
clock = new THREE.Clock();
sceneMain = new THREE.Scene();
sceneOutline = new THREE.Scene();
var obj = loader.parse(JSON.parse(objData));
for (var k in obj.materials) {
obj.materials[k].skinning = true;
}
setModel(obj.geometry, obj.materials);
init();
animate();
}
function init() {
camera = new THREE.PerspectiveCamera(40, height / width, 1, 10000);
camera.position.set(0, 0, 25);
light = new THREE.DirectionalLight(0xffffff)
light.position.set(1, 1, 1);
sceneMain.add(light);
renderer = new THREE.WebGLRenderer({
width: width,
height: height,
antialias: true,
});
renderer.setSize(width, height);
renderer.setClearColor(0x666666);
renderer.autoClear = false;
renderer.gammaInput = true;
renderer.gammaOutput = true;
document.body.appendChild(renderer.domElement);
var renderTarget = new THREE.WebGLRenderTarget(width, height, {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat,
stencilBuffer: true,
});
composer = new THREE.EffectComposer(renderer, renderTarget);
composer.renderTarget1.stencilBuffer = true;
composer.renderTarget2.stencilBuffer = true;
var pMain = new THREE.RenderPass(sceneMain, camera);
var pOut = new THREE.RenderPass(sceneOutline, camera);
pOut.clear = false;
var pCopy = new THREE.ShaderPass(THREE.CopyShader);
pCopy.renderToScreen = true;
composer.addPass(pMain);
composer.addPass(pOut);
composer.addPass(pCopy);
animMain.play();
animOutline.play();
}
function setModel(geometry, materials) {
meshMain = new THREE.SkinnedMesh(geometry,
new THREE.MeshFaceMaterial(materials));
sceneMain.add(meshMain);
mixerMain = new THREE.AnimationMixer(meshMain);
animMain = mixerMain.clipAction(geometry.animations[0]);
var shader = THREE.OutlineShader;
var shaderMaterial = new THREE.ShaderMaterial({
uniforms: THREE.UniformsUtils.clone(shader.uniforms),
vertexShader: shader.vertexShader,
fragmentShader: shader.fragmentShader,
skinning: true,
side: THREE.BackSide,
});
meshOutline = new THREE.SkinnedMesh(geometry, shaderMaterial);
shaderMaterial.uniforms['boneTextureWidth'].value = meshOutline.skeleton.boneTextureWidth;
shaderMaterial.uniforms['boneTextureHeight'].value = meshOutline.skeleton.boneTextureHeight;
shaderMaterial.uniforms['boneTexture'].value = meshOutline.skeleton.boneTexture;
shaderMaterial.uniforms['offset'].value = 0.5;
shaderMaterial.uniforms['boneTextureWidth'].value.needsUpdate = true;
shaderMaterial.uniforms['boneTextureHeight'].value.needsUpdate = true;
shaderMaterial.uniforms['boneTexture'].value.needsUpdate = true;
shaderMaterial.uniforms['offset'].value.needsUpdate = true;
sceneOutline.add(meshOutline);
mixerOutline = new THREE.AnimationMixer(meshOutline);
animOutline = mixerOutline.clipAction(geometry.animations[0]);
}
function animate() {
var delta = clock.getDelta();
requestAnimationFrame(animate);
update(delta);
render(delta);
}
function update(delta) {
if (meshMain && meshOutline) {
meshMain.rotation.y += 1 * delta;
meshOutline.rotation.y += 1 * delta;
mixerMain.update(delta);
mixerOutline.update(delta);
}
}
function render(delta) {
composer.render(delta);
}
问题显然是由于 ShaderMaterial 和/或着色器本身,例如将第二个网格的材质更改为MeshBasicMaterial 产生预期的行为(两个网格保持同步)。
着色器是从不久前发布的this jsfiddle 中提取的。它使用了一个古老版本的three.js。在创建 ShaderMaterial 实例时,我对填充 boneTexture、boneTextureWidth 和 boneTextureHeight 制服的预期/正确方式并不完全清楚。我从网格骨架中的值手动执行此操作,但如果这是错误的,我不会感到惊讶。
再次,我只是想了解为什么同时以相同的方式翻译两个网格会导致它们不同步,如第一个 jsfiddle 示例所示。
编辑:我观察到使用 ShaderMaterial (meshOutline) 的网格与其他网格 (meshMain) 同步,如果 meshOutline 正好旋转一半和meshMain一样多。例如,在 update() 函数中:
meshMain.rotation.y += 1 * delta;
meshOutline.rotation.y += 1 * delta / 2;
...将导致两个网格明显同步旋转。如果将旋转替换为坐标(例如 x)移动,情况也是如此:
//meshMain.rotation.y += 1 * delta;
//meshOutline.rotation.y += 1 * delta / 2;
var dx = Math.random() - 0.5;
meshMain.position.x += dx;
meshOutline.position.x += dx / 2;
...将导致两个网格一起来回移动。但如果两者 合起来,即:
meshMain.rotation.y += 1 * delta;
meshOutline.rotation.y += 1 * delta / 2;
var dx = Math.random() - 0.5;
meshMain.position.x += dx;
meshOutline.position.x += dx / 2;
它们完全不同步。
这显然意味着我不了解着色器如何从 three.js 获取顶点位置。我知道着色器正在计算顶点位置并使用它们,因为这就是当您使用 ShaderMaterial 时会发生。我不明白的是如何使着色器正在使用的数据与three.js中的网格发生的情况保持同步。这显然发生在我上面链接的第二个 jsfiddle 示例中。
【问题讨论】:
-
我删除了与问题无关的所有内容。 jsfiddle.net/oju429Ld/3 一个合理的猜测是问题出在您的顶点着色器中。
-
绝对——这或多或少是我从一开始的假设。任何洞察 r66 和 r77 之间的变化导致行为差异(如我在下面的回答中所讨论的)?我之所以问,是因为我相信我链接的第二个 jsfiddle 中的代码(带有工作示例的那个)最初是由您发布的,以回答其他人的问题。