WebSocket을 통한 3D 캐릭터 모션 제어

며칠 전 제 친구가 저에게 연락하여 HTML5 환경에서 3D 인간 캐릭터 시뮬레이션을 위한 기술 솔루션의 실현 가능성을 물었습니다. 그는 Three.js**로 인터랙티브한 3d 캐릭터를 만드는 방법을 제시하는 this article 저를 보냈습니다. 그는 조이스틱과 같은 하드웨어를 통해 캐릭터의 실시간 모션(전신 모션)을 제어할 것으로 기대하고 있다.

매우 흥미로운 작업이며 상당히 쉬워 보입니다. 따라서 나는 그것이 작동하도록 노력하는 약간의 개발 작업을 수행했습니다.


Index.html 파일에서 websocket 서버와 해당 메시지 파서를 정의했습니다. 그런 다음 이 파일은 Electron 창에 래핑되어 데스크톱 소프트웨어로 실행됩니다.
Index.html의 핵심 부분은 다음과 같은 websocket 통신 부분입니다.

<script  type="text/javascript" >
    var angle1 = 0.0;
    var angle2 = 0.0

    const qEvent = new Event('qiu');

    /* for debug */    
    function output(s)
    {
        var out = document.getElementById("debug-area");
        out.innerText += s;
    }

    output("Start running")


    var msg_ready = false;
    var msg_reading = false;  // True: package head 0xAA is received, but 0x7f has not yet been received
    var msg_data_buffer = [];
    var msg_lenth = 0;


    function processMsg(v)
    {       
    if (v[0] == 170) // detect the beginning byte of a message: 0xAA
    {
        // data are sent in little endian, 
        // v.buffer is a byte-array and Int16Array(v.buffer, 8, 1) means that it parses from the 8th byte on to get ONE Int16 number

        if ( (v[1] == 0x01) && (v[2] == 0x53) ) // 01 52
        {
            angle1 = new Int16Array(v.buffer, 8, 1)[0];
            angle2 = new Int16Array(v.buffer, 10, 1)[0];
            var temp3 = new Int16Array(v.buffer, 12, 1)[0];
            document.dispatchEvent(qEvent);

        }
        else 
        {
        }
     }
    }


    var ws = require("nodejs-websocket");
    var clients =  new Array();
    output("开始建立连接... ");
    var count = 0;
    var data = new Buffer.alloc(0);
    var server = ws.createServer(function(conn){

        conn.id = count;
        count += 1;
        clients["conn"+count]  = conn;

        conn.on("text", function (str) {
            output("Received " + str + "! " )
            var typeId = str.charAt(0);         
            conn.sendText('Success!');
        })
        conn.on("close", function (code, reason) {
            output("Connection closed!")
            //clients.delete(conn);
        });

        conn.on("binary", function (inStream) {

            inStream.on("readable", function () {
                var newData = inStream.read();

                if (newData)
                    data = Buffer.concat([data, newData], data.length + newData.length);
            });

            inStream.on("end", function () {

                if(data){
                var t = '', v = new Uint8Array(data);

                for (var i = 0; i < v.length; i++)
                {
                    // packet head 0xAA reached, now start reading the data flow
                    if  ((!msg_reading ) &&(v[i] == 0xaa)){
                        msg_reading = true;
                    }


                    if(msg_reading){

                        if (msg_data_buffer.length == 8) {
                            msg_lenth =  msg_data_buffer[5]*16 + msg_data_buffer[4]; // parsing the data length (bytes size)                            
                        }

                        // received the end of packet, and the length is correct 
                        if ((v[i] == 127 ) && (msg_data_buffer.length == (msg_lenth + 10)))  // 10 extra bytes contained in this package for : length, scope, checksum, msg-id 
                        {                           
                            var msg = new Uint8Array(msg_data_buffer);
                            processMsg(msg);
                            msg_data_buffer = [];
                            msg_reading = false;
                            msg_lenth = 0;
                        } else if (msg_data_buffer.length == (msg_lenth + 10))
                        {
                            msg_data_buffer = [];
                            msg_reading = false;
                            msg_lenth = 0;
                            output("Message length error!");
                        }
                        else{
                            msg_data_buffer.push(v[i]);
                        }
                    }
                }

            }else{

            };
            data = new Buffer.alloc(0);
            conn.sendText('Binary Received!');
            });


        });
        conn.on("message", function (code, reason) {
            output("message! " )
        });
        conn.on("error", function (code, reason) {
            output("Error occurs!")
        });
    }).listen(9999)
    output("Server is ready! ");
</script>

기존 파일 script.js에서 functionmoveOneJoint()을 정의했습니다. 이벤트 'qiu'가 발송될 때마다 호출됩니다.


  document.addEventListener('qiu', function (e) {

    if (neck && waist) {
       moveOneJoint(neck, angle1, angle2);
    }
  });

  function moveOneJoint(joint, x, y) {

    joint.rotation.y = THREE.Math.degToRad(x);
    joint.rotation.x = THREE.Math.degToRad(y);

  }

전체 코드가 github 저장소로 푸시되었습니다.


치우짜오펑 / 3d_character_simulation






3d_character_simulation


실행


다음 cmd를 실행합니다.
cd Interactive3DCharacter
npm install
npm start

제어 신호 전송


websockt를 통해 각도를 보내는 자신의 프로그램을 작성할 수 있습니다. msg_send_posture[8:9]와 msg_send_posture[10:11]에 전송할 각도 데이터(두 개의 int16)를 작성해야 합니다.
예제 코드:
var wsUrl = "ws://localhost:9999"
websocket = new WebSocket(wsUrl)
var msg_send_posture  =  new Uint8Array([0xAA,  0x01,0x53,  0x01,  0x04,0x00,0x00,0x00,   0x01,0x00, 0x00,0x00,  0x00,0x00,   0x7F]
websocket.send(msg_send_posture);

원본 프로젝트: Three.js를 사용한 대화형 3D 캐릭터


웹 사이트에 대화형 3D 캐릭터를 추가하는 방법에 대한 튜토리얼 데모입니다.

Article on Codrops
Demo

학점


  • three.js

  • 특허


    이 리소스는 판매용 웹 사이트, 웹 앱 및 웹 템플릿과 같은 개인 또는 상업 프로젝트에 통합되거나 구축되는 경우 자유롭게 사용할 수 있습니다. 리소스를 "있는 그대로"가져와서 판매하거나, 재배포하거나, 다시 게시하거나, "플러그인화된"버전을 판매하는 것은 허용되지 않습니다. 이 리소스를 사용하여 구축된 무료 플러그인은 눈에 보이는…

    View on GitHub


    나는 조이스틱이 없기 때문에 다른 웹 앱(MUI과 함께 HBuilder 프레임워크를 사용하여 개발)에서 여러 범위 슬라이더로 시뮬레이션합니다. 슬라이더를 슬라이드하면 websocket를 통해 각도 데이터를 위에서 언급한 3d 캐릭터 시뮬레이터로 보낼 수 있습니다. 보낼 데이터 마사지는 다음과 같은 데이터 배열이어야 합니다. 내 코드).

    아래는 제가 녹음한 데모입니다. 슬라이더를 사용하여 시뮬레이션된 3D 캐릭터 머리의 움직임을 제어하고 있습니다.


    또 다른 시도에서는 Android 플랫폼에서 장치 시뮬레이터 앱을 실행하고 전체 화면에서 실행합니다[0xAA, 0x01,0x53, 0x01, 0x04,0x00,0x00,0x00, 0xMM,0xNN, 0xSS,0xTT, 0xYY,0xZZ, 0x7F]. 데모를 확인하십시오.

    좋은 웹페이지 즐겨찾기