完成手部模型添加

This commit is contained in:
rucky 2021-12-29 17:19:17 +08:00
parent f1545677c3
commit 74249abc2e
4 changed files with 232 additions and 85 deletions

View File

@ -11,6 +11,7 @@
<style> <style>
#app { #app {
height: 100%;
min-height: 100%; min-height: 100%;
} }
</style> </style>

View File

@ -3,8 +3,7 @@
<video <video
id="gum-local" id="gum-local"
class="video" class="video"
width="100%" :class="{ flip: pcDevice }"
height="100%"
autoplay autoplay
playsinline playsinline
webkit-playsinline webkit-playsinline
@ -20,7 +19,7 @@
ref="showVideoBtn" ref="showVideoBtn"
@click="initVideo($event)" @click="initVideo($event)"
> >
开启 点击开始探索
</button> </button>
<!-- info --> <!-- info -->
@ -29,7 +28,7 @@
当前进度: {{ progressText }}.. 当前进度: {{ progressText }}..
</div> </div>
<div class="status"> <div class="status">
当前手势: <span>{{ handStatus }}</span> <span>{{ handStatus }}</span> :当前手势
</div> </div>
</div> </div>
@ -60,6 +59,7 @@
<script> <script>
// @ is an alias to /src // @ is an alias to /src
import gsap from "gsap"; import gsap from "gsap";
import Stats from "stats";
import { import {
IEngine, IEngine,
Load, Load,
@ -82,6 +82,12 @@ export default {
handStatus: "未检测到手", handStatus: "未检测到手",
handStep: 1, handStep: 1,
meshAdded: false, meshAdded: false,
pcDevice: false, // pc
lineConfig: {
segments: 10000,
r: 800,
t: 0,
},
}; };
}, },
mounted() { mounted() {
@ -231,6 +237,9 @@ export default {
}); });
} }
console.log("devices source:", source); console.log("devices source:", source);
if (source.length < 2 && window.deviceInfo.device === "PC") {
this.pcDevice = true;
}
return source[source.length - 1].deviceId; return source[source.length - 1].deviceId;
}, },
// video // video
@ -284,7 +293,7 @@ export default {
this.videoRealSize = handledSize; this.videoRealSize = handledSize;
// three // three
this.initTHREELayer(); this.initTHREELayer(streamSize);
console.log("Got stream with constraints:", constraints); console.log("Got stream with constraints:", constraints);
console.log(`Using video device: ${videoTracks[0].label}`); console.log(`Using video device: ${videoTracks[0].label}`);
@ -369,20 +378,20 @@ export default {
this.progressText = "引擎准备中…"; this.progressText = "引擎准备中…";
this.engine.Configure({ this.engine.Configure({
// Webcam video is usually flipped so we want the coordinates to be flipped as well. // Webcam video is usually flipped so we want the coordinates to be flipped as well.
flipX: false, flipX: this.pcDevice ? true : false,
// Crop away a small area at the border to prevent the user to move out of view // Crop away a small area at the border to prevent the user to move out of view
// when reaching for border areas on the canvas. // when reaching for border areas on the canvas.
padding: this.BORDER_PADDING_FACTOR, padding: this.BORDER_PADDING_FACTOR,
}); });
this.engine.SetUpCustomTrackSource(this.video);
// await this.engine.SetUpCameraTrackSource();
// ios // ios
if (window.deviceInfo.system === "IOS") { if (window.deviceInfo.system === "IOS") {
await this.engine.Warmup(); await this.engine.Warmup();
} }
this.engine.SetUpCustomTrackSource(this.video);
// await this.engine.SetUpCameraTrackSource();
this.progressText = "引擎已就绪"; this.progressText = "引擎已就绪";
gsap.to([this.$refs.progress, this.$refs.progressBar], { gsap.to([this.$refs.progress, this.$refs.progressBar], {
autoAlpha: 0, autoAlpha: 0,
@ -413,18 +422,33 @@ export default {
// console.log(e.type); // console.log(e.type);
if (e.type === EventEnum.RESULT) { if (e.type === EventEnum.RESULT) {
this.drawPoint(cursorPos[0], cursorPos[1]);
this.Render();
if (e.poses.fist) { if (e.poses.fist) {
this.handStatus = "握拳"; this.handStatus = "握拳";
this.hideAll();
if (this.handStep == 1) { if (this.handStep == 1) {
this.handStep++; this.handStep++;
} }
// document.getElementById("status").innerText = ""; // document.getElementById("status").innerText = "";
} else if (e.poses.pinch) { } else if (e.poses.pinch) {
this.handStatus = "捏合"; this.handStatus = "捏合";
if (this.handStep > 2) {
this.updatePosition(
cursorPos[0],
cursorPos[1],
0.1
);
this.Render();
}
} else { } else {
this.handStatus = "张开"; this.handStatus = "张开";
if (this.handStep > 2) {
this.updatePosition(
cursorPos[0],
cursorPos[1],
cursorPos[3]
);
this.Render();
}
if (this.handStep == 2) { if (this.handStep == 2) {
this.handStep++; this.handStep++;
} }
@ -432,6 +456,8 @@ export default {
} }
} else if (e.type === EventEnum.LOST) { } else if (e.type === EventEnum.LOST) {
this.handStatus = "未检测到手"; this.handStatus = "未检测到手";
this.hideAll();
// document.getElementById("status").innerText = ""; // document.getElementById("status").innerText = "";
} }
}); });
@ -479,32 +505,36 @@ export default {
} }
}, },
// init THREELayer // init THREELayer
initTHREELayer() { initTHREELayer(size) {
this.container = this.$refs.container; this.container = this.$refs.container;
this.camera_ = new THREE.PerspectiveCamera( this.camera_ = new THREE.PerspectiveCamera(
45, 45,
window.innerWidth / window.innerHeight, this.video.width / this.video.height,
0.1, 0.1,
1000 1500
); );
// const distance = const distance =
// this.videoRealSize.height / this.video.width /
// (2 * Math.tan((this.camera_.fov * Math.PI) / 360)); (2 * Math.tan((this.camera_.fov * Math.PI) / 360));
// this.camera_.up.set(0, -1, 0); this.camera_.up.set(0, -1, 0);
// this.camera_.position.set( this.camera_.position.set(
// this.videoRealSize.width / 2, this.video.width / 2,
// this.videoRealSize.height / 2, this.video.height / 2,
// -distance -distance
// ); );
// this.camera_.lookAt( this.camera_.lookAt(this.video.width / 2, this.video.height / 2, 0);
// this.videoRealSize.width,
// this.videoRealSize.height / 2, // this.camera_ = new THREE.OrthographicCamera(
// 0 // 0,
// this.video.width,
// 0,
// this.video.height,
// 0,
// 1500
// ); // );
this.camera_.position.set(0, 0, 1000); this.clock = new THREE.Clock();
this.camera_.lookAt(0, 0, 0);
this.scene_ = new THREE.Scene(); this.scene_ = new THREE.Scene();
@ -514,28 +544,24 @@ export default {
failIfMajorPerformanceCaveat: false, failIfMajorPerformanceCaveat: false,
antialias: true, antialias: true,
}); });
// console.log(this.video.width, this.video.height);
this.renderer_.debug.checkShaderErrors = false; this.renderer_.debug.checkShaderErrors = false;
this.renderer_.setSize( this.renderer_.setSize(window.innerWidth, window.innerHeight);
this.videoRealSize.width,
this.videoRealSize.height
);
this.renderer_.setPixelRatio(window.devicePixelRatio); this.renderer_.setPixelRatio(window.devicePixelRatio);
this.renderer_.setClearColor(0x000000, 0.0); this.renderer_.setClearColor(0x000000, 0.0);
this.renderer_.autoClear = true; this.renderer_.autoClear = true;
this.renderer_.domElement.id = "canvas"; this.renderer_.domElement.id = "canvas";
this.container.appendChild(this.renderer_.domElement); this.container.appendChild(this.renderer_.domElement);
this.renderer_.domElement.width = this.video.width; // this.renderer_.domElement.width = this.video.width;
this.renderer_.domElement.height = this.video.height; // this.renderer_.domElement.height = this.video.height;
// add stats dom
this.stats = new Stats();
this.container.appendChild(this.stats.dom);
// this.animate(); // this.animate();
this.onWindowResize(); // this.drawPoint();
window.addEventListener( this.addLineBox();
"resize",
() => {
this.onWindowResize();
},
false
);
}, },
onWindowResize(event) { onWindowResize(event) {
this.camera_.aspect = window.innerWidth / window.innerHeight; this.camera_.aspect = window.innerWidth / window.innerHeight;
@ -544,13 +570,24 @@ export default {
}, },
addTestBall() { addTestBall() {
//light //light
let mainLight = new THREE.HemisphereLight(0xffffff, 0x444444);
mainLight.position.set(0, 200, 0);
var light = new THREE.DirectionalLight(0xffffff); var light = new THREE.DirectionalLight(0xffffff);
var light2 = new THREE.DirectionalLight(0xffffff); var light2 = new THREE.DirectionalLight(0xffffff);
light.position.set(200, 0, 200); light.position.set(200, 0, 200);
light2.position.set(-200, 0, 200); light2.position.set(-200, 0, 200);
this.scene_.add(light); this.scene_.add(light);
this.scene_.add(light2);
this.scene_.add(mainLight);
this.group3D = new THREE.Object3D(); this.group3D = new THREE.Object3D();
gsap.to(this.group3D.rotation, {
y: Math.PI * 2,
repeat: -1,
duration: 5,
ease: "none",
});
//mesh //mesh
var material = new THREE.MeshLambertMaterial({ var material = new THREE.MeshLambertMaterial({
color: "#2194ce", color: "#2194ce",
@ -569,26 +606,114 @@ export default {
this.scene_.add(this.group3D); this.scene_.add(this.group3D);
this.ambientLight = new THREE.AmbientLight(0x8a7e7e, 0.2); this.ambientLight = new THREE.AmbientLight(0x8a7e7e, 0.5);
this.scene_.add(this.ambientLight); this.scene_.add(this.ambientLight);
}, },
// draw pointer addLineBox() {
drawPoint(x, y) { const geometry = new THREE.BufferGeometry();
if (!this.meshAdded) { const material = new THREE.LineBasicMaterial({
this.geo_ = new THREE.RingGeometry(0, 10, 32); vertexColors: true,
this.mat_ = new THREE.MeshBasicMaterial({ opacity: 0.5,
color: "red", });
side: THREE.DoubleSide,
}); const positions = [];
this.mesh_ = new THREE.Mesh(this.geo_, this.mat_); const colors = [];
this.scene_.add(this.mesh_); const r = this.lineConfig.r;
this.meshAdded = true;
console.log("three mesh added"); for (let i = 0; i < this.lineConfig.segments; i++) {
const x = Math.random() * r - r / 2;
const y = Math.random() * r - r / 2;
const z = Math.random() * r - r / 2;
// positions
positions.push(x, y, z);
// colors
colors.push(x / r + 0.5);
colors.push(y / r + 0.5);
colors.push(z / r + 0.5);
} }
this.mesh_.position.x = x * this.videoRealSize.width; geometry.setAttribute(
this.mesh_.position.y = y * this.videoRealSize.height; "position",
this.mesh_.position.z = 0; new THREE.Float32BufferAttribute(positions, 3)
);
geometry.setAttribute(
"color",
new THREE.Float32BufferAttribute(colors, 3)
);
this.generateMorphTargets(geometry);
geometry.computeBoundingSphere();
this.lineBox = new THREE.Line(geometry, material);
this.scene_.add(this.lineBox);
this.lineBox.scale.x = 0.15;
this.lineBox.scale.y = 0.15;
this.lineBox.scale.z = 0.15;
},
generateMorphTargets(geometry) {
const data = [];
const r = this.lineConfig.r;
for (let i = 0; i < this.lineConfig.segments; i++) {
const x = Math.random() * r - r / 2;
const y = Math.random() * r - r / 2;
const z = Math.random() * r - r / 2;
data.push(x, y, z);
}
const morphTarget = new THREE.Float32BufferAttribute(data, 3);
morphTarget.name = "target1";
geometry.morphAttributes.position = [morphTarget];
},
// draw pointer
drawPoint() {
this.geo_ = new THREE.RingGeometry(0, 10, 32);
this.mat_ = new THREE.MeshBasicMaterial({
color: "red",
side: THREE.DoubleSide,
});
this.mesh_ = new THREE.Mesh(this.geo_, this.mat_);
this.scene_.add(this.mesh_);
this.meshAdded = true;
console.log("three mesh added");
},
//
updatePosition(x, y, scale) {
gsap.to(this.renderer_.domElement, { autoAlpha: 1 });
if (this.mesh_) {
this.mesh_.position.x = x * this.video.width;
this.mesh_.position.y = y * this.video.height;
this.mesh_.position.z = 0;
}
if (this.group3D) {
this.group3D.position.x = x * this.video.width;
this.group3D.position.y = y * this.video.height;
this.group3D.position.z = 0;
}
if (this.lineBox) {
this.lineBox.position.x = x * this.video.width;
this.lineBox.position.y = y * this.video.height;
this.lineBox.position.z = 100;
}
if (scale) {
gsap.to(this.lineBox.scale, { x: scale, y: scale, z: scale });
} else {
gsap.to(this.lineBox.scale, { x: 0.15, y: 0.15, z: 0.15 });
}
},
// hide all
hideAll() {
gsap.to(this.renderer_.domElement, { autoAlpha: 0 });
}, },
animate() { animate() {
requestAnimationFrame(this.animate); requestAnimationFrame(this.animate);
@ -596,7 +721,20 @@ export default {
}, },
// THREE canvas render // THREE canvas render
Render() { Render() {
this.stats.update();
this.renderer_.render(this.scene_, this.camera_); this.renderer_.render(this.scene_, this.camera_);
if (this.lineBox) {
const delta = this.clock.getDelta();
const time = this.clock.getElapsedTime();
this.lineBox.rotation.x = time * 0.25;
this.lineBox.rotation.y = time * 0.5;
this.lineConfig.t += delta * 0.5;
this.lineBox.morphTargetInfluences[0] = Math.abs(
Math.sin(this.lineConfig.t)
);
}
}, },
}, },
}; };
@ -604,7 +742,7 @@ export default {
<!-- Add "scoped" attribute to limit CSS to this component only --> <!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less"> <style scoped lang="less">
.hand-track { .hand-track {
.prLayout(100%,100vh); .prLayout(100%,100%);
overflow: hidden; overflow: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -614,24 +752,25 @@ export default {
.video { .video {
.paLayout(50%,50%,auto,100%,0); .paLayout(50%,50%,auto,100%,0);
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
// transform: scaleX(-1);
// width: 750px; // width: 750px;
display: none; display: none;
// height: 700px; // height: 700px;
&.flip {
transform: translate(-50%, -50%) scaleX(-1);
}
} }
.info-dialog { .info-dialog {
visibility: hidden; visibility: hidden;
.paLayout(0,0, 50%,130px,2); .paRightLayout(0,0, 50%,130px,2);
background-color: rgba(255, 255, 255, 0.25); background-color: rgba(255, 255, 255, 0.25);
border-radius: 0 0 30px 0; border-radius: 0 0 0 30px;
padding: 20px; padding: 20px;
font-size: 28px; font-size: 28px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-content: center; align-content: center;
align-items: center; align-items: center;
text-align: left; text-align: center;
text-indent: 10px;
justify-content: center; justify-content: center;
div { div {
width: 100%; width: 100%;
@ -675,10 +814,10 @@ export default {
} }
} }
.canvas-container { .canvas-container {
.paLayout(50%,50%,auto,100%,100); .paLayout(50%,50%,auto,auto,100);
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
pointer-events: none; // pointer-events: none;
background-color: rgba(255, 255, 255, 0.1); // background-color: rgba(255, 255, 255, 0.1);
} }
.btn { .btn {
.paCenterBottom(50%,280px,80px,2); .paCenterBottom(50%,280px,80px,2);
@ -687,6 +826,8 @@ export default {
text-align: center; text-align: center;
border-radius: 8px; border-radius: 8px;
font-size: 25px; font-size: 25px;
background-image: linear-gradient(to top, #a18cd1 0%, #fbc2eb 100%);
color: #fff;
} }
.progress-bar { .progress-bar {
visibility: hidden; visibility: hidden;

View File

@ -0,0 +1,5 @@
// stats.js - http://github.com/mrdoob/stats.js
var Stats=function(){function h(a){c.appendChild(a.dom);return a}function k(a){for(var d=0;d<c.children.length;d++)c.children[d].style.display=d===a?"block":"none";l=a}var l=0,c=document.createElement("div");c.style.cssText="position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000";c.addEventListener("click",function(a){a.preventDefault();k(++l%c.children.length)},!1);var g=(performance||Date).now(),e=g,a=0,r=h(new Stats.Panel("FPS","#0ff","#002")),f=h(new Stats.Panel("MS","#0f0","#020"));
if(self.performance&&self.performance.memory)var t=h(new Stats.Panel("MB","#f08","#201"));k(0);return{REVISION:16,dom:c,addPanel:h,showPanel:k,begin:function(){g=(performance||Date).now()},end:function(){a++;var c=(performance||Date).now();f.update(c-g,200);if(c>e+1E3&&(r.update(1E3*a/(c-e),100),e=c,a=0,t)){var d=performance.memory;t.update(d.usedJSHeapSize/1048576,d.jsHeapSizeLimit/1048576)}return c},update:function(){g=this.end()},domElement:c,setMode:k}};
Stats.Panel=function(h,k,l){var c=Infinity,g=0,e=Math.round,a=e(window.devicePixelRatio||1),r=80*a,f=48*a,t=3*a,u=2*a,d=3*a,m=15*a,n=74*a,p=30*a,q=document.createElement("canvas");q.width=r;q.height=f;q.style.cssText="width:80px;height:48px";var b=q.getContext("2d");b.font="bold "+9*a+"px Helvetica,Arial,sans-serif";b.textBaseline="top";b.fillStyle=l;b.fillRect(0,0,r,f);b.fillStyle=k;b.fillText(h,t,u);b.fillRect(d,m,n,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d,m,n,p);return{dom:q,update:function(f,
v){c=Math.min(c,f);g=Math.max(g,f);b.fillStyle=l;b.globalAlpha=1;b.fillRect(0,0,r,m);b.fillStyle=k;b.fillText(e(f)+" "+h+" ("+e(c)+"-"+e(g)+")",t,u);b.drawImage(q,d+a,m,n-a,p,d,m,n-a,p);b.fillRect(d+n-a,m,a,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d+n-a,m,a,e((1-f/v)*p))}}};"object"===typeof module&&(module.exports=Stats);

View File

@ -42,29 +42,29 @@ module.exports = {
config.resolve.alias config.resolve.alias
.set("@", resolve("src")) .set("@", resolve("src"))
.set("weui", resolve("src/utils/plugins/weui.js")) // .set("weui", resolve("src/utils/plugins/weui.js"))
// .set("assets", resolve("src/assets")) .set("stats", resolve("src/utils/plugins/stats-3d.js"))
// .set("components", resolve("src/components")) // .set("components", resolve("src/components"))
// .set("views", resolve("src/views")) // .set("views", resolve("src/views"))
// .set("base", resolve("baseConfig")) // .set("base", resolve("baseConfig"))
// .set("public", resolve("public")); // .set("public", resolve("public"));
// if (process.env.NODE_ENV !== "test") { // if (process.env.NODE_ENV !== "test") {
config.module.rule('compile') // config.module.rule('compile')
.test(/\.js$/) // .test(/\.js$/)
.include // .include
.add(resolve('src')) // .add(resolve('src'))
.add(resolve('node_modules')) // .add(resolve('node_modules'))
.end() // .end()
.use('babel') // .use('babel')
.loader('babel-loader') // .loader('babel-loader')
.options({ // .options({
presets: [ // presets: [
['@babel/preset-env', { // ['@babel/preset-env', {
modules: false // modules: false
}] // }]
] // ]
}); // });
// } // }
}, },