方案一、图形学渲染技术

这种方法的优点是驱动人物快速,不需要合成视频。这种方式又可以分为两种,一种是把3d模型放到客户端,一种是把模型放到服务器端。

1.把模型放到客户端(不对应口型)

(1)实现方式

这种方式还有一种更简单的方法,比如让设计师设计几个gif图片,或者矢量图,开口讲话的时候一张图片,不开口讲话的时候另一张图片。

把模型放到客户端的方式,可以用3d模型工具,例如:Blender、Maya或3ds Max,ue5等,生成一个比较小的3d模型,用户每次加载的时候,放到客户端(h5),模型大小大概10多M,可以通过前端js,three.js来控制模型。

(2)实现的样例

1)中国电信:
微信图片_20231115175419.jpg

https://szr.ai.chinatelecom.cn:8093/
2)一个demo
微信截图_20231115175510.png
https://manghe151.sxqichuangkeji.com/h5/Digital1.html
到我的文件夹下看视频,以及数字人模型
在线数字人转换工具:https://3dconvert.nsdt.cloud/conv/to/glb

(3)相关技术文档

模型实例参考:https://manghe151.sxqichuangkeji.com/h5/Digital1.html
参考实现方式:https://blog.csdn.net/weixin_48594833/article/details/132598537
参考代码:

<!DOCTYPE html>
<!-- saved from url=(0053)https://manghe151.sxqichuangkeji.com/h5/Digital1.html -->
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        
        <title></title>
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <script async="" src="./manghe151.sxqichuangkeji.com_h5_Digital1_files/es-module-shims.js"></script>
        <style>
            .dbox {
                width: 100vw;
                height: 100vh;
                box-sizing: border-box;
            }
            * {
                margin: 0px;
            }
        </style>
    </head>
    <body>
        <div class="dbox" id="dbox">

        <canvas data-engine="three.js r148" width="1914" height="936" style="display: block; width: 1914px; height: 936px;"></canvas></div>
    
    <script src="./manghe151.sxqichuangkeji.com_h5_Digital1_files/uni.webview.1.5.2.js"></script> 
     <script>
//         {type="importmap"
//             "imports": {
//                 "three": "./three/build/three.module.js"
//             }
//         }
     </script>
    <script type="module">        
         import * as THREE from "./three/build/three.module.js";
        
        import {
            GLTFLoader
        } from './three/jsm/loaders/GLTFLoader.js';
        import { DRACOLoader    } from './three/jsm/loaders/DRACOLoader.js';

        
        let h = document.getElementById("dbox").clientHeight;
        let w = document.getElementById("dbox").clientWidth;
        //创建一个三维场景
        const scene = new THREE.Scene();
        //创建一个长方体


        //创建相机
        const camera = new THREE.PerspectiveCamera(45, w / h, 0.1, 4000);
        camera.position.set(0, 0.6, 3.8);
        camera.lookAt(0, 0, 0);

        let ambientLight = new THREE.AmbientLight(0xffffff, 1); //设置环境光
        scene.add(ambientLight); //将环境光添加到场景中
//         let pointLight = new THREE.PointLight(0xffffff, 0.5, 1);
//         pointLight.position.set(0, 30, 10); //设置点光源位置
//         scene.add(pointLight); //将点光源添加至场景

        const directionalLight = new THREE.DirectionalLight(0xffffff, 0.1);
        directionalLight.position.set(0, 30, 10);
        scene.add(directionalLight);

        //创建渲染器
        const renderer = new THREE.WebGLRenderer({
            antialias: true,
            alpha: true,
            logarithmicDepthBuffer: true
        });
        renderer.setSize(w, h);
//         renderer.setPixelRatio(window.devicePixelRatio);
        renderer.shadowMap.enabled = true;
        renderer.outputEncoding = THREE.sRGBEncoding;
        renderer.textureEncoding = THREE.sRGBEncoding;
        document.getElementById("dbox").appendChild(renderer.domElement);



        const loader = new GLTFLoader();

        const clock = new THREE.Clock();

        window.IdleAction = null;
        window.walkAction = null;
        window.walkAction1 = null;
         let mixer = null;
        
         const dracoLoader = new DRACOLoader();
              dracoLoader.setDecoderPath('./three/jsm/libs/draco/gltf/');
              loader.setDRACOLoader(dracoLoader);
        loader.load('https://ptcgtext.oss-cn-chengdu.aliyuncs.com/people2.glb', function(gltf) {
//             console.log('人的模型', gltf);

            // 返回的场景对象gltf.scene插入到threejs场景中
            gltf.scene.position.y=-1.6;
            gltf.scene.position.x=0;
            gltf.scene.scale.set(2.6, 2.6, 2.6)
            mixer = new THREE.AnimationMixer(gltf.scene);
            gltf.scene.traverse(function(child) {
                if (child.isMesh) {
                    child.castShadow = true;
                     child.receiveShadow = true;
        
                }
            });
            wait(gltf);
            
        })

        
        function wait(gltf){
            loader.load('./model/glb/movement.glb', function(gltfmovement) {
                console.log('待机模型', gltfmovement);
                // 返回的场景对象gltf.scene插入到threejs场景中    
                window.IdleAction = mixer.clipAction(gltfmovement.animations[0]);
                window.IdleAction.play();
                hand_fn(gltf);
                
            })
        }
        
        
        function hand_fn(gltf){
            loader.load('./model/glb/mouth1.glb', function(gltfmovement) {
                // console.log('这个手动作数据', gltfmovement);
                // 返回的场景对象gltf.scene插入到threejs场景中
                window.walkAction1 = mixer.clipAction(gltfmovement.animations[0]);
                window.walkAction1.stop();
                speak(gltf);
            })
        }
        
        function speak(gltf){
            loader.load('./model/glb/mouth.glb', function(gltfmovement) {
                // console.log('口型模型', gltfmovement);
                // 返回的场景对象gltf.scene插入到threejs场景中
                window.walkAction = mixer.clipAction(gltfmovement.animations[0]);
                window.walkAction.stop();
                scene.add(gltf.scene);
                animate();
            })
        }
        
    
        
        function animate() {

            const delta = clock.getDelta();
            mixer.update(delta);

            renderer.render(scene, camera);
            //转动,可以忽略
            window.requestAnimationFrame(animate);
            // scene.rotation.y += 0.01;
        }
        
        
    </script>
    <script>
            
        function getMessage(result){//应用向网页传递消息

            if(result==1){
              window.walkAction.play();
              window.walkAction1.play();
            }
            if(result==2){
              window.walkAction.stop(); 
              window.walkAction1.stop();
            }
        }
        
    </script>

方案二、视频合成的方式

2D真人视频合成
优点:输入一个真人图片或者真人视频经过训练,生成一个真人视频
缺点:需要GPU较多,需要分段生成。

方案三 .把模型放到服务器端(可以对应上口型,视频推流方式在客户端显示)

互动数字人(对应开放平台的“客服助理”场景)使用阿里云RTC渠道,平台会将数字人渲染的视频流推到RTC服务器,客户可以使用RTC的客户端SDK进行拉流播放对应视频流;
微信截图_20231116164859.png

微信截图_20231116165442.png

微信截图_20231116165550.png
3d建模使用:虚幻引擎(ue5)

1、后端开发接入服务端 API 接入篇
  
2、前端引入视频流接入篇 集成到网页端数字人视频流
  
3、前端接入语音收音能力,通过RTC上传到虚拟数字人开放平台,即可实现与数字人的语音交互

前后端数据流程图:

p505166.png

参考阿里数字人:
https://help.aliyun.com/document_detail/447761.html?spm=a2c4g.451451.0.0.db0c37a7WlBWlm

口型驱动插件

微信截图_20231116150656.png
参考:https://www.yiihuu.com/a_11791.html

模型视频推流

利用像素流送可以在用户不可见的电脑上远程运行虚幻引擎应用程序。举例而言,这台电脑可以是机构中的一台实体电脑,也可以是云端服务提供的虚拟机。虚幻引擎将使用该电脑可用的资源(CPU、GPU、内存等)来运行游戏逻辑并渲染每一帧。它会不断将此渲染输出编码到一个媒体流送中,再通过一个轻量级的网页服务堆栈进行传递。用户即可在其他电脑和移动设备上运行的标准网页浏览器中查看直播流送。

推流组件:
(1)像素流送插件 - 此插件在虚幻引擎中运行。其使用H.264视频压缩对每个渲染帧的最终结果进行编码,将这些视频帧随游戏音频一同打包到媒体流送中,并通过直接点对点连接将该流送发送到一个或多个连线的浏览器上。

(2)信令和Web服务器- 信令和Web服务器负责交涉浏览器和像素流送插件之间的连接,将播放媒体流送的HTML和JavaScript环境提供给浏览器。

参考:
https://docs.unrealengine.com/5.1/zh-CN/customizing-the-player-web-page-in-unreal-engine/
https://blog.csdn.net/H_1512826122/article/details/129400685

标签: none

添加新评论