目录
1 WebGLCubeRenderTarget
2 TransformControls
3 CCDIKSolver
4 CCDIKHelper
4 全部脚本
1 WebGLCubeRenderTarget
球体亮
//WebGLCubeRenderTarget(size : Number, options : Object)
//size - the size, in pixels. Default is 1.
//options - (可选)一个保存着自动生成的目标纹理的纹理参数以及表示是否使用深度缓存/模板缓存的布尔值的对象。const cubeRenderTarget = new THREE.WebGLCubeRenderTarget( 1024 );mirrorSphereCamera = new THREE.CubeCamera( 0.05, 50, cubeRenderTarget );//创建6个渲染到WebGLCubeRenderTarget的摄像机scene.add( mirrorSphereCamera );const mirrorSphereMaterial = new THREE.MeshBasicMaterial( { envMap: cubeRenderTarget.texture } );OOI.sphere.material = mirrorSphereMaterial;//球体材质设置
2 TransformControls
可以用来拖拽模型
transformControls = new TransformControls( camera, renderer.domElement );//camera是渲染场景的相机 renderer.domElement是控制器附加到的html元素transformControls.size = 0.75;//以像素为单位的变换控制器的大小transformControls.showX = false;//指示是否显示沿 X轴的控制手柄。transformControls.space = 'world';//指定变换空间(‘local’ 或 ‘world’),其中应用变换transformControls.attach( OOI.target_hand_l );//将控制器附加到指定的对象以进行操作scene.add( transformControls );
3 CCDIKSolver
CCDIKSolver 用 CCD 算法解决逆运动学问题。 CCDIKSolver 设计用于与 SkinnedMesh 配合使用,但也可与 MMDLoader 或 GLTFLoader 配合使用。
构造函数
CCDIKSolver( mesh : SkinnedMesh, iks : Array )
mesh — SkinnedMesh 用于 CCDIKSolver 解决 IK 问题
iks — 指定 IK 参数的对象 Object 数组。target、effector 和 link-index 是 .sculptor.bones 中的索引整数。骨骼关系从父级到子级的顺序应为“links[ n ]、 links[ n - 1 ]、...、 links[ 0 ]、effector”。
- target — 目标骨骼
- effector — 效应器骨
- links — 指定链接骨骼的对象Object 数组
- index — 链接骨骼
- limitation — (可选)旋转轴。默认值 undefined
- rotationMin — (可选)旋转最小限制。默认值 undefined
- rotationMax — (可选)旋转最大限制。默认值 undefined
- enabled — (可选)默认值为 true。
- iteration — (可选)计算的迭代次数。越小速度越快,但精度较差。默认值为 1。
- minAngle — (可选)一步中的最小旋转角度。默认值 undefined
- maxAngle — (可选)一步中的最大旋转角度。默认值 undefined
4 CCDIKHelper
IKSolver = new CCDIKSolver( OOI.kira, iks );//动画const ccdikhelper = new CCDIKHelper( OOI.kira, iks, 0.01 );//辅助scene.add( ccdikhelper );
4 全部脚本
<!DOCTYPE html>
<html lang="en"><head><title>three.js webgl - animation - skinning - ik</title><meta charset="utf-8"><meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"><meta name="author" content="Antoine BERNIER (abernier)" /><link type="text/css" rel="stylesheet" href="main.css"><style>body {color:white;}#info a {color:#4d6675;}</style></head><body><div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - webgl - inverse kinematics<br />Character model by <a href="https://assetstore.unity.com/packages/3d/characters/humanoids/humans/kira-lowpoly-character-100303" target="_blank" rel="noopener">Aki</a>, furnitures from <a href="https://poly.pizza" target="_blank" rel="noopener">poly.pizza</a>, scene by <a href="https://abernier.name/three.js/examples/webgl_esher.html" target="_blank" rel="noopener">abernier</a>. CC0.</div><script type="importmap">{"imports": {"three": "../build/three.module.js","three/addons/": "./jsm/"}}</script><script type="module">import * as THREE from 'three';import { OrbitControls } from 'three/addons/controls/OrbitControls.js';//控制器引入import { TransformControls } from 'three/addons/controls/TransformControls.js';//转换控件 控制器 交互操作import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';//GLTF模型加载器import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';//一个用于加载经过Draco压缩的图形库 和GLTFLoader 一同出现import { CCDIKSolver, CCDIKHelper } from './jsm/animation/CCDIKSolver.js';//动画相关库 一种基于 CCD Algorithm 的 IK 解算器import Stats from 'three/addons/libs/stats.module.js';//性能检测import { GUI } from 'three/addons/libs/lil-gui.module.min.js';//UIlet scene, camera, renderer, orbitControls, transformControls,transformControls2 ;let mirrorSphereCamera;const OOI = {};let IKSolver;let stats, gui, conf;const v0 = new THREE.Vector3();init().then( animate );//先后顺序async function init() {conf = {followSphere: false,turnHead: true,ik_solver: true,update: updateIK};//定义一些用到的字段//场景scene = new THREE.Scene();scene.fog = new THREE.FogExp2( 0xffffff, .17 );scene.background = new THREE.Color( 0xffffff );//相机camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 0.001, 5000 );camera.position.set( 0.9728517749133652, 1.1044765132727201, 0.7316689528482836 );camera.lookAt( scene.position );//灯光const ambientLight = new THREE.AmbientLight( 0xffffff, 8 ); // soft white lightscene.add( ambientLight );//渲染器renderer = new THREE.WebGLRenderer( { antialias: true, logarithmicDepthBuffer: true } );renderer.setPixelRatio( window.devicePixelRatio );renderer.setSize( window.innerWidth, window.innerHeight );document.body.appendChild( renderer.domElement );//性能检测stats = new Stats();document.body.appendChild( stats.dom );//控制器orbitControls = new OrbitControls( camera, renderer.domElement );orbitControls.minDistance = 0.2;orbitControls.maxDistance = 1.5;orbitControls.enableDamping = true;//orbitControls.addEventListener( 'change', animate );//开始下载模型const dracoLoader = new DRACOLoader();dracoLoader.setDecoderPath( 'jsm/libs/draco/' );const gltfLoader = new GLTFLoader();gltfLoader.setDRACOLoader( dracoLoader ); const gltf = await gltfLoader.loadAsync( 'models/gltf/kira.glb' );//异常加载 console.log(OOI);console.log(gltf.scene);gltf.scene.traverse( n => {if ( n.name === 'head' ) OOI.head = n;//头if ( n.name === 'lowerarm_l' ) OOI.lowerarm_l = n;if ( n.name === 'Upperarm_l' ) OOI.Upperarm_l = n;if ( n.name === 'hand_l' ) OOI.hand_l = n;if ( n.name === 'target_hand_l' ) OOI.target_hand_l = n;if ( n.name === 'boule' ) OOI.sphere = n;//球if ( n.name === 'Kira_Shirt_left' ) OOI.kira = n;} );scene.add( gltf.scene );//orbitControls.target.copy( new THREE.Vector3(1.7835944890975952,1.023118257522583,-0.25029030442237854) );orbitControls.target.copy( OOI.sphere.position ); // orbit controls lookAt the sphere 看向球OOI.hand_l.attach( OOI.sphere );//.attach ( object : Object3D ) : 将object作为子级来添加到该对象中,同时保持该object的世界变换。// // mirror sphere cube-camera//WebGLCubeRenderTarget(size : Number, options : Object)
//size - the size, in pixels. Default is 1.
//options - (可选)一个保存着自动生成的目标纹理的纹理参数以及表示是否使用深度缓存/模板缓存的布尔值的对象。const cubeRenderTarget = new THREE.WebGLCubeRenderTarget( 1024 );mirrorSphereCamera = new THREE.CubeCamera( 0.05, 50, cubeRenderTarget );//创建6个渲染到WebGLCubeRenderTarget的摄像机scene.add( mirrorSphereCamera );const mirrorSphereMaterial = new THREE.MeshBasicMaterial( { envMap: cubeRenderTarget.texture } );OOI.sphere.material = mirrorSphereMaterial;//球体材质设置transformControls = new TransformControls( camera, renderer.domElement );//camera是渲染场景的相机 renderer.domElement是控制器附加到的html元素transformControls.size = 0.75;//以像素为单位的变换控制器的大小transformControls.showX = false;//指示是否显示沿 X轴的控制手柄。transformControls.space = 'world';//指定变换空间(‘local’ 或 ‘world’),其中应用变换transformControls.attach( OOI.target_hand_l );//将控制器附加到指定的对象以进行操作scene.add( transformControls );// disable orbitControls while using transformControlstransformControls.addEventListener( 'mouseDown', () => orbitControls.enabled = false );//鼠标按下时 控制器失效transformControls.addEventListener( 'mouseUp', () => orbitControls.enabled = true );OOI.kira.add( OOI.kira.skeleton.bones[ 0 ] );//骨骼const iks = [{target: 22, // "target_hand_l"effector: 6, // "hand_l"links: [{index: 5, // "lowerarm_l"rotationMin: new THREE.Vector3( 1.2, - 1.8, - .4 ),rotationMax: new THREE.Vector3( 1.7, - 1.1, .3 )},{index: 4, // "Upperarm_l"rotationMin: new THREE.Vector3( 0.1, - 0.7, - 1.8 ),rotationMax: new THREE.Vector3( 1.1, 0, - 1.4 )},],}];IKSolver = new CCDIKSolver( OOI.kira, iks );//动画const ccdikhelper = new CCDIKHelper( OOI.kira, iks, 0.01 );//辅助scene.add( ccdikhelper );//UI部分gui = new GUI();gui.add( conf, 'followSphere' ).name( 'follow sphere' );//跟随球体gui.add( conf, 'turnHead' ).name( 'turn head' );//转动头gui.add( conf, 'ik_solver' ).name( 'IK auto update' );//自动更新gui.add( conf, 'update' ).name( 'IK manual update()' );//gui.open();window.addEventListener( 'resize', onWindowResize, false );const geometry = new THREE.BoxGeometry();const spherMaterial = new THREE.MeshLambertMaterial({ color: 'red' });const mesh=new THREE.Mesh(geometry,spherMaterial);mesh.position.set(0,1,-1);scene.add(mesh);transformControls2 = new TransformControls( camera, renderer.domElement );//camera是渲染场景的相机 renderer.domElement是控制器附加到的html元素transformControls2.size = 0.75;//以像素为单位的变换控制器的大小//transformControls2.showX = false;//指示是否显示沿 X轴的控制手柄。transformControls2.space = 'world';//指定变换空间(‘local’ 或 ‘world’),其中应用变换transformControls2.attach( mesh );//将控制器附加到指定的对象以进行操作scene.add( transformControls2 ); transformControls2.addEventListener('dragging-changed',function(event){orbitControls.enabled=!event.value;});}function animate( ) {if ( OOI.sphere && mirrorSphereCamera ) {OOI.sphere.visible = false;OOI.sphere.getWorldPosition( mirrorSphereCamera.position );mirrorSphereCamera.update( renderer, scene );OOI.sphere.visible = true;}if ( OOI.sphere && conf.followSphere ) {// orbitControls follows the sphereOOI.sphere.getWorldPosition( v0 );orbitControls.target.lerp( v0, 0.1 );}if ( OOI.head && OOI.sphere && conf.turnHead ) {// turn headOOI.sphere.getWorldPosition( v0 );OOI.head.lookAt( v0 );OOI.head.rotation.set( OOI.head.rotation.x, OOI.head.rotation.y + Math.PI, OOI.head.rotation.z );}if ( conf.ik_solver ) {updateIK();}orbitControls.update();renderer.render( scene, camera );stats.update(); // fps statsrequestAnimationFrame( animate );}function updateIK() {
//IKSolver.update () : this 通过求解 CCD 算法更新 IK 骨骼四元数if ( IKSolver ) IKSolver.update();scene.traverse( function ( object ) {//计算边界球体,更新 .boundingSphere 属性。
//默认情况下不计算边界球体。它们需要显式计算,否则它们是 . 如果 SkinnedMesh 的实例是动画的,则应按帧调用此方法以计算正确的边界球体。nullif ( object.isSkinnedMesh ) object.computeBoundingSphere();} );}function onWindowResize() {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize( window.innerWidth, window.innerHeight );}</script></body>
</html>