如何在 Three.js 中获取蒙皮网格顶点的全局位置?

2023-12-06

在 Three.js 中,我们现在能够获取 a 顶点的全局位置不带皮的网格感谢这个问题,但是我怎样才能获得 a 顶点的全局位置skinned与骨骼和变形目标进行网格划分?

例如,如何打印(2.5, 1.5, 0.5)在以下情况下?mesh.geometry.vertices[0]原来是在(0.5, 0.5, 0.5). Then, bones[1]将顶点移动到(2.5, 0.5, 0.5)。 最后,变形将顶点移动到(2.5, 1.5, 0.5).

const scene = new THREE.Scene();

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 200);
camera.position.z = 3;
camera.position.y = 2;
camera.lookAt(0, 0, 0);

const renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

const geometry = new THREE.BoxGeometry(1, 1, 1);
geometry.morphTargets.push({name: "morph", vertices: []});
for (const vertex of geometry.vertices) {
  geometry.skinIndices.push(new THREE.Vector4(vertex.x < 0 ? 0 : 1, 0, 0, 0));
  geometry.skinWeights.push(new THREE.Vector4(1, 0, 0, 0));
  geometry.morphTargets[0].vertices.push(vertex.clone().add(new THREE.Vector3(0, 1, 0)));
}

const material = new THREE.MeshPhongMaterial({
  skinning: true,
  emissive: 0xffffff,
  wireframe: true,
  morphTargets: true
});

const mesh = new THREE.SkinnedMesh(geometry, material);

const bones = [new THREE.Bone(), new THREE.Bone()];
for (const bone of bones) {
  mesh.add(bone);
}

const skeleton = new THREE.Skeleton(bones);
mesh.bind(skeleton);

bones[0].position.x = -2;
bones[1].position.x = 2;
mesh.morphTargetInfluences[0] = 1;
scene.add(mesh);

// This code assigns (0.5, 0.5, 0.5) to pos,
// but I want to know the code which assigns (2.5, 1.5, 0.5) to pos. 
const pos = mesh.geometry.vertices[0].clone().applyMatrix4(mesh.matrixWorld);
console.log(`(${pos.x}, ${pos.y}, ${pos.z})`);

(function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
})();
body {
  margin: 0;
  overflow: hidden;
}
canvas {
  width: 100%;
  height: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/94/three.min.js"></script>

所以这就是我所做的试图解决这个问题。我懒得检查它是否适用于所有情况,但是......

首先我使用了着色器编辑器扩展然后我跑了一个带皮的例子并使用着色器编辑器查看生成的着色器

enter image description here

查看顶点着色器,着色器的相关部分是

#ifdef USE_SKINNING
    mat4 boneMatX = getBoneMatrix( skinIndex.x );
    mat4 boneMatY = getBoneMatrix( skinIndex.y );
    mat4 boneMatZ = getBoneMatrix( skinIndex.z );
    mat4 boneMatW = getBoneMatrix( skinIndex.w );
#endif
#ifdef USE_SKINNING
    mat4 skinMatrix = mat4( 0.0 );
    skinMatrix += skinWeight.x * boneMatX;
    skinMatrix += skinWeight.y * boneMatY;
    skinMatrix += skinWeight.z * boneMatZ;
    skinMatrix += skinWeight.w * boneMatW;
    skinMatrix  = bindMatrixInverse * skinMatrix * bindMatrix;
    objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;
#endif

vec3 transformedNormal = normalMatrix * objectNormal;
#ifdef FLIP_SIDED
    transformedNormal = - transformedNormal;
#endif


vec3 transformed = vec3( position );

#ifdef USE_MORPHTARGETS
    transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];
    transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];
    transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];
    transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];
    #ifndef USE_MORPHNORMALS
    transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];
    transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];
    transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];
    transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];
    #endif
#endif

#ifdef USE_SKINNING
    vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );
    vec4 skinned = vec4( 0.0 );
    skinned += boneMatX * skinVertex * skinWeight.x;
    skinned += boneMatY * skinVertex * skinWeight.y;
    skinned += boneMatZ * skinVertex * skinWeight.z;
    skinned += boneMatW * skinVertex * skinWeight.w;
    transformed = ( bindMatrixInverse * skinned ).xyz;
#endif

所以将其翻译成 JavaScript 这就是我的想法

首先,您需要场景拥有所有的世界矩阵 并更新了骨架。

scene.updateMatrixWorld();
skeleton.update();

Then

  // These are so we can avoid doing allocations
  // in the inner loop.
  const position = new THREE.Vector3();
  const transformed = new THREE.Vector3();
  const temp1 = new THREE.Vector3();
  const tempBoneMatrix = new THREE.Matrix4();
  const tempSkinnedVertex = new THREE.Vector3();
  const tempSkinned = new THREE.Vector3();

  const bindMatrix = mesh.bindMatrix;
  const bindMatrixInverse = mesh.bindMatrixInverse;

  for (let vndx = 0; vndx < mesh.geometry.vertices.length; ++vndx) {
    position.copy(mesh.geometry.vertices[vndx]);
    transformed.copy(position);

    for (let i = 0; i < mesh.geometry.morphTargets.length; ++i) {
      temp1.copy(mesh.geometry.morphTargets[i].vertices[vndx]);
      transformed.add(temp1.sub(position).multiplyScalar(mesh.morphTargetInfluences[i]));
    }

    tempSkinnedVertex.copy(transformed).applyMatrix4(bindMatrix);
    tempSkinned.set(0, 0, 0);

    const skinIndices = geometry.skinIndices[vndx];
    const skinWeights = geometry.skinWeights[vndx];

    for (let i = 0; i < 4; ++i) {
      const boneNdx = skinIndices.getComponent(i);
      const weight = skinWeights.getComponent(i);
      tempBoneMatrix.fromArray(skeleton.boneMatrices, boneNdx * 16);
      temp1.copy(tempSkinnedVertex);
      tempSkinned.add(temp1.applyMatrix4(tempBoneMatrix).multiplyScalar(weight));
    }

    transformed.copy(tempSkinned).applyMatrix4(bindMatrixInverse);
    transformed.applyMatrix4(mesh.matrixWorld);

Example:

const scene = new THREE.Scene();

const camera = new THREE.PerspectiveCamera(75, 2, 0.1, 200);
camera.position.z = 3;
camera.position.y = 2;
camera.lookAt(0, 0, 0);

const renderer = new THREE.WebGLRenderer({canvas:document.querySelector('canvas')});
renderer.setSize(512, 256, false);
document.body.appendChild(renderer.domElement);

const geometry = new THREE.BoxGeometry(1, 1, 1);
geometry.morphTargets.push({name: "morph", vertices: []});
for (const vertex of geometry.vertices) {
  geometry.skinIndices.push(new THREE.Vector4(vertex.x < 0 ? 0 : 1, 0, 0, 0));
  geometry.skinWeights.push(new THREE.Vector4(1, 0, 0, 0));
  geometry.morphTargets[0].vertices.push(vertex.clone().add(new THREE.Vector3(0, 1, 0)));
}

const material = new THREE.MeshPhongMaterial({
  skinning: true,
  emissive: 0xffffff,
  wireframe: true,
  morphTargets: true
});

const mesh = new THREE.SkinnedMesh(geometry, material);

const bones = [new THREE.Bone(), new THREE.Bone()];
for (const bone of bones) {
  mesh.add(bone);
}

const skeleton = new THREE.Skeleton(bones);
mesh.bind(skeleton);

bones[0].position.x = -2;
bones[1].position.x = 2;
mesh.morphTargetInfluences[0] = 1;
scene.add(mesh);

const putMeshesAtSkinnedVertices = (function() {

  // These are so we can avoid doing allocations
  // in the inner loop.
  const position = new THREE.Vector3();
  const transformed = new THREE.Vector3();
  const temp1 = new THREE.Vector3();
  const tempBoneMatrix = new THREE.Matrix4();
  const tempSkinnedVertex = new THREE.Vector3();
  const tempSkinned = new THREE.Vector3();

  return function putMarkersAtSkinnedVertices(mesh, scene, marker, markerMaterial, markers) {
    const bindMatrix = mesh.bindMatrix;
    const bindMatrixInverse = mesh.bindMatrixInverse;
    const geometry = mesh.geometry;
    const vertices = geometry.vertices;
    const morphTargets = geometry.morphTargets;
    const skeleton = mesh.skeleton;

    for (let vndx = 0; vndx < vertices.length; ++vndx) {
      position.copy(vertices[vndx]);
      transformed.copy(position);

      for (let i = 0; i < morphTargets.length; ++i) {
        temp1.copy(morphTargets[i].vertices[vndx]);
        transformed.add(temp1.sub(position).multiplyScalar(mesh.morphTargetInfluences[i]));
      }

      tempSkinnedVertex.copy(transformed).applyMatrix4(bindMatrix);
      tempSkinned.set(0, 0, 0);

      const skinIndices = geometry.skinIndices[vndx];
      const skinWeights = geometry.skinWeights[vndx];

      for (let i = 0; i < 4; ++i) {
        const boneNdx = skinIndices.getComponent(i);
        const weight = skinWeights.getComponent(i);
        tempBoneMatrix.fromArray(skeleton.boneMatrices, boneNdx * 16);
        temp1.copy(tempSkinnedVertex);
        tempSkinned.add(temp1.applyMatrix4(tempBoneMatrix).multiplyScalar(weight));
      }

      transformed.copy(tempSkinned).applyMatrix4(bindMatrixInverse);
      transformed.applyMatrix4(mesh.matrixWorld);

      // create them the first time this is called
      let markerMesh = markers[vndx];
      if (!markerMesh) {
        markerMesh = new THREE.Mesh(marker, markerMaterial);
        markers[vndx] = markerMesh;
        scene.add(markerMesh);
      }

      markerMesh.position.copy(transformed);
    }
  }
}());


// Make sure all matrices are up to date
scene.updateMatrixWorld();
skeleton.update();

const marker = new THREE.BoxBufferGeometry(0.1, 0.1, 0.1);
const markerMaterial = new THREE.MeshBasicMaterial({color: 0xFF0000});
const markers = [];

putMeshesAtSkinnedVertices(mesh, scene, marker, markerMaterial, markers);

(function render() {
//  requestAnimationFrame(render);
  renderer.render(scene, camera);
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/94/three.min.js"></script>
<canvas></canvas>

我确实测试过这个样本这似乎有效。

enter image description here

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 Three.js 中获取蒙皮网格顶点的全局位置? 的相关文章

  • 对自定义打字稿错误实例实施instanceof检查?

    打字稿有这个instanceof 检查自定义错误 https github com Microsoft TypeScript issues 13965问题 但尚不清楚我们需要做什么才能得到instanceof在职的 例如对于这个异常我们如何
  • 如何从ArrayBuffer中获取二进制字符串?

    JavaScript中如何从ArrayBuffer中获取二进制字符串 我不想对字节进行编码 只需将二进制表示形式获取为字符串 提前致谢 以下代码将一致地转换ArrayBuffer to a String并再次返回 而不会丢失或添加任何额外的
  • ExtJS 4:克隆商店

    我正在尝试找出如何克隆Ext data Store不保留旧的参考 让我用一些代码更好地解释一下 这是源商店 var source Ext create Ext data Store fields name age data name foo
  • 记录jQuery中调用的方法和参数

    假设我有 jQuery 并且加载了几个插件 我运行一些这样的代码 someSelector someMethod someParam someOtherParam someOtherSelector someOtherMethod anot
  • 如何在 Chrome 中实现抓取光标图标?

    我知道可以在 Chrome 中使用抓取光标图标 当然是在 Gmail 中 但我不知道如何在我的代码中实现它 我已经尝试过 在CSS中 body cursor grab body cursor webkit grab body cursor
  • 捕获外部脚本文件中的 javascript 错误

    我有一点 JavaScript Jquery 工具的叠加层 http flowplayer org tools overlay index html 当放到错误使用它的页面上时可能会引发异常 我正在尝试优雅地处理它 我有一个通用的 wind
  • 如何从 URL 字符串中删除某些参数?

    我有这个var存储表示充满参数的 URL 的字符串 我正在使用 AngularJS 我不确定是否有任何有用的模块 或者可能使用纯 JavaScript 来删除不需要的 URL 参数而无需使用正则表达式 例如我需要删除 month 05并且
  • 检测 iframe 内容加载失败

    我可以使用以下命令检测 iframe 的内容何时加载load事件 不幸的是 就我的目的而言 这有两个问题 如果加载页面时出现错误 404 500 等 则永远不会触发加载事件 如果某些图像或其他依赖项加载失败 则会照常触发加载事件 有什么方法
  • React:React 如何确保在浏览器有机会绘制之后调用 useEffect?

    useLayoutEffect 的文档说 useLayoutEffect 内计划的更新将被刷新 在浏览器有机会绘制之前同步进行 useEffect 的文档说 与 componentDidMount 和 componentDidUpdate
  • History.replaceState 仍然向“浏览历史记录”添加条目

    具体来说 调用以下代码片段 history replaceState undefined undefined value 正确地不会影响当前页面的后退按钮行为 但是will在 浏览历史记录 页面添加一个条目 这是我不想要的 下图是 Chro
  • 如何向尚未添加到页面的元素注册 Javascript 事件处理程序

    我正在尝试构建一个greasemonkey 脚本 它将根据用户与其他动态创建的数据表的交互动态创建数据表 我的问题是 每次创建表时 我都必须进行两次传递 一次用于创建表 另一次用于获取表中我想要添加事件处理程序的所有对象 通过 id 并添加
  • 使用 ES6 模块导出/导入单个类方法?

    假设我有一个像这样的简单课程fileA js class foo constructor x this name x fooMethod x return x hello 我想导入并使用fooMethod in fileB js像这样 im
  • 有没有办法在 TypeScript 2+ 中全局添加类型定义?

    我有一堆简单的 ts files 不是项目 即独立的 ts 脚本 他们使用一些 Node js 功能 TypeScript 和节点类型定义通过安装 npm install g typescript npm install g types n
  • Rxjs 可观察等待直到满足某些条件

    我有以下重试逻辑来重试操作 对于单个请求来说它工作得很好 对于多个正在进行的请求 我想在重试之前等待现有的重试逻辑完成 handleError errors Observable
  • 对数字和字母元素的数组进行排序(自然排序)

    假设我有一个数组 var arr 1 5 ahsldk 10 55 3 2 7 8 1 2 75 abc huds 我尝试对其进行排序 我得到了类似的东西 1 1 10 2 2 3 5 55 7 75 8 abc ahsldk huds 注
  • 比较 javascript 元素和 scala 变量的 Play 框架 Twirl 模板

    如下面的代码示例所示 我想比较 scala 辅助元素内的 javascript 元素 然而 即使存在元素 abcde 它也始终返回 false 除了使用标签之外 如何获取 scala 辅助元素内的 javascript 值 appSeq S
  • 如何在 OpenLayers 3 中删除监听器

    我做了一个copy https gis stackexchange com questions 178222 how to delete a listener in openlayers 3我在 stackoverflow 上提出的问题 因
  • Google 地图 v3 中标准缩放控件的样式

    有没有一种简单的方法可以在 Google Maps JavaScript API v3 中设置缩放控件的样式 我想要的只是改变标准图像 http maps gstatic com intl en ALL mapfiles mapcontro
  • Javascript - 如何计算数字的平方?

    使用 JavaScript 函数 function squareIt number return number number 当给定数字 4294967296 时 函数返回 18446744073709552000 每个人都知道真正的答案是
  • Javascript / jQuery - 转换特殊 html 字符

    我有一个pre元素中包含一些 html 代码 该代码中有特殊字符 例如 lt 所以它不会破坏页面 然后我有一个 javascript 函数 它获取此 pre 元素的内容 突出显示它 使用 codemirror 并用突出显示的文本替换元素内容

随机推荐

  • 在片段中实现 GridView 的问题

    我正在开发一个小型应用程序 其中包含 ActivityNavigationDrawer 我已经使用该活动放置了一个片段FragmentManager and FragmentTrancation 问题是 我有一个GridView在未出现的片
  • 合并多个音频缓冲区源

    关于html5 webaudio的问题 是否可以将多首歌曲合并在一起 我有不同的曲目 它们都使用 webaudio 同时播放 但我需要处理音频 因此我需要一个缓冲区内的所有音频 而不是每个曲目都有自己的缓冲区 我尝试通过添加通道数据来合并它
  • 连接目录中的所有文件

    如何加入一个目录中的所有文件 我可以通过显式命名下面的文件来一步完成此操作 有没有一种方法可以在不显式命名文件的情况下完成此操作 join lt join lt join lt join lt sort rpkmDir HS0477 chs
  • 如何使用带有参数列表的 django-filter 包?

    我想用以下方法过滤我的模型Django 过滤器 如果我按一个 id 进行过滤 效果很好 例如 http localhost 8000 accommodations accommodationType id 1 但我不知道如何通过多个 id
  • 在 WooCommerce 中选择变体销售价格后显示折扣百分比

    我下面的代码显示了用户选择变体后选择的变体价格 add filter woocommerce show variation price filter show variation price 10 3 function filter sho
  • 我收到此消息“错误:未找到 FFmpeg/avconv!”

    我正在尝试创建一个不和谐的机器人 但是每次我尝试运行 YouTube 链接时都会收到此错误 Error FFmpeg avconv not found at Function getInfo C Users discord bot node
  • PowerShell 如何获取函数或 cmdlet 的引用?

    我想获得对函数或 cmdlet 的引用 例如 我想引用 Get ChildItem cmdlet 我不想调用它 我想要对该函数的引用 然后我可以将其传递给另一个函数 有语法可以做到这一点吗 我知道我可以使用字符串 Get ChildItem
  • TableView 中的滚动委托

    我想制作一个像这样的链接的动画 这是我的简单截图 就我而言 我希望当我向上滑动时 蓝色标题视图将消失 导航栏将更改为蓝色 这是我的代码 import UIKit class ViewController UIViewController U
  • 读取/解析文本文件输入C++

    一点背景知识 我正在为一个学校项目开发一个滑块拼图 这是我们第一次使用 C 而不是 Java 这是我第一次必须实现从文件中读取数据的功能 我有一个关于从文本文件读取输入的简单问题 我了解如何逐行读取文件并将每一行保存在字符串中 我想知道在读
  • 为什么不能将供应商特定的伪元素/类组合到一个规则集中?

    在 CSS 中可以设置样式placeholder使用特定于供应商的伪类和伪元素的组合来输入输入中的文本 以获得最佳的跨浏览器覆盖率 这些都共享相同的基本属性 即 文本样式和颜色声明 然而 虽然我不可避免地想要应用相同的样式 而不管浏览器供应
  • Android 可绘制,背景和渐变位于左侧

    W 希望有一个drawable左边有背景和渐变 大约是10dp wide 我想要实现的目标的图片 左侧红色渐变 其余部分的背景 我怎样才能做到这一点 我试过了layer list有两种形状但没有运气 项目背景
  • Python:在随机段落中查找最长/最短的句子?

    我使用的是 Python 2 7 需要 2 个函数来查找最长和最短句子 按字数计算 随机段落 例如 如果我选择放入这一段 将您的海滨度假之旅与北加州詹纳葡萄酒之乡的红葡萄酒和白葡萄酒搭配起来 这座位于索诺玛县的沿海小城市坐落在俄罗斯河河口附
  • 使 chrome 中的复选框看起来像 IE 中的复选框

    IE 和 Chrome 中的复选框看起来不同 Chrome IE 从上面的图片中您可以看到差异 我希望 chrome 中的复选框看起来与 IE 中的一样 I tried webkit box shadow和其他类似的 css 属性 但没有运
  • UISegmentedcontrol 外观导致问题

    我需要有关 UISegment 外观的帮助 我在我的应用程序委托中设置了它 一切正常 直到我添加此代码来更改我选择的段颜色 它才引起问题 我在 viewDidLoad 时调用了 IBAction 它应该显示这个 但它显示了这一点 我知道是外
  • TCP服务器IP地址

    当启动H2 tcp服务器并且主机有多个IP地址时 我如何定义服务器将绑定以侦听连接的IP 我们可以定义 tcp 端口 但似乎没有办法定义 ip 地址 谢谢你 奥斯卡 http www h2database com html advanced
  • QueryOver:从子查询中选择列

    如何从不同表的子查询中选择 投影值到我的主查询中 我有一个像这样的 NH 模型 Serializable public class MyModel public virtual int Id get set more mapped valu
  • 在 sed 中查找并替换文本文件中的多个字符串[重复]

    这个问题在这里已经有答案了 下面是一个玩具文本文件 其中包含样本和特征信息以及测量值 Sample3 trait1 8 5 Sample6 trait2 2 2 Sample7 trait1 9 2 Sample3 trait2 1 3 S
  • 向空手道框架添加自定义步骤定义

    我需要提取从 复杂 响应标头解析的字段 并在稍后的测试中使用该值 看来空手道中的 header 关键字是为了设置请求头而设置的 而不是解析响应头 有没有办法添加自定义步骤定义来维护对场景变量存储的访问 看来变量存储在StepDefs类 并且
  • 从小写转换为大写

    我正在尝试从小写转换为大写 我知道这很容易做到 SUB AL 20H 但我得到了另一种解决方案 AND AL 0DFH 请帮助我理解这一点 谢谢 查看位模式 答 0x41 0100 0001 一个 0x61 0110 0001 中号 0x4
  • 如何在 Three.js 中获取蒙皮网格顶点的全局位置?

    在 Three js 中 我们现在能够获取 a 顶点的全局位置不带皮的网格感谢这个问题 但是我怎样才能获得 a 顶点的全局位置skinned与骨骼和变形目标进行网格划分 例如 如何打印 2 5 1 5 0 5 在以下情况下 mesh geo