日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

CreatarGlobe实现多机立体显示方案(初稿)

發(fā)布時(shí)間:2023/12/29 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 CreatarGlobe实现多机立体显示方案(初稿) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

?CreatarGlobe實(shí)現(xiàn)多機(jī)立體顯示方案(初稿)

關(guān)鍵字 : 集群渲染 立體顯示 大屏幕 邊緣融合 多機(jī)同步

?

多機(jī)同步顯示

關(guān)鍵字: 大屏幕投影融合系統(tǒng)解決方案 集群渲染

?

多機(jī)3D同步顯示又稱“集群渲染”

目標(biāo)

實(shí)現(xiàn)如下圖的效果:

這個(gè) 3*2 的一個(gè)投影墻:(渲染節(jié)點(diǎn))

?

下面是對(duì)應(yīng)的主節(jié)點(diǎn)(master 節(jié)點(diǎn))

?

?

下面是CAVE模式:(三面CAVE)- 環(huán)幕

?

?

?

網(wǎng)絡(luò)編程相關(guān)知識(shí)

可使用的參考文檔 :

C++_Socket網(wǎng)絡(luò)編程大全

http://wenku.baidu.com/view/3f5ce1d5360cba1aa811da2b.html

Socket服務(wù)器與客戶端雙向通信實(shí)例

http://wenku.baidu.com/view/47a7877101f69e31433294a4.html?re=view

?

?

基礎(chǔ)知識(shí)1

●? winsock APIs

網(wǎng)絡(luò)連接函數(shù)
socket 創(chuàng)建套接字
bind 綁定本機(jī)端口
connect 建立連接
listen 監(jiān)聽端口
accept 接受連接
recv, recvfrom 數(shù)據(jù)接收
send, sendto 數(shù)據(jù)發(fā)送
close, shutdown 關(guān)閉套接字
轉(zhuǎn)換函數(shù)
inet_addr() 點(diǎn)分十進(jìn)制數(shù)表示的IP地址轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序的IP地址
inet_ntoa() 網(wǎng)絡(luò)字節(jié)序的IP地址轉(zhuǎn)換為點(diǎn)分十進(jìn)制數(shù)表示的IP地址
字節(jié)順序轉(zhuǎn)換函數(shù)
htonl 4字節(jié)主機(jī)字節(jié)序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序
ntohl  4字節(jié)網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換為主機(jī)字節(jié)序
htons 2字節(jié)主機(jī)字節(jié)序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序
ntohs 2字節(jié)網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換為主機(jī)字節(jié)序
網(wǎng)絡(luò)信息檢索函數(shù)
gethostname 獲得主機(jī)名
getpeername 獲得與套接口相連的遠(yuǎn)程協(xié)議地址
getsockname 獲得套接口本地協(xié)議地址
gethostbyname 根據(jù)主機(jī)名取得主機(jī)信息
gethostbyaddr 根據(jù)主機(jī)地址取得主機(jī)信息
getprotobyname 根據(jù)協(xié)議名取得主機(jī)協(xié)議信息
getprotobynumber 根據(jù)協(xié)議號(hào)取得主機(jī)協(xié)議信息
getservbyname 根據(jù)服務(wù)名取得相關(guān)服務(wù)信息
getservbyport 根據(jù)端口號(hào)取得相關(guān)服務(wù)信息
getsockopt/setsockopt 獲取/設(shè)置一個(gè)套接口選項(xiàng)
ioctlsocket 設(shè)置套接口的工作方式

?

參考 http://www.doc88.com/p-5929803397139.html

?

?

?

?

?

?

?

基礎(chǔ)知識(shí)2

參考 http://www.cnblogs.com/uvsjoh/archive/2012/12/23/2830299.html

Windows網(wǎng)絡(luò)編程使用winsock。Winsock是一個(gè)基于Socket模型的API,在Windows系統(tǒng)中廣泛使用。
使用Winsock進(jìn)行網(wǎng)絡(luò)編程需要包含頭文件Winsock2.h,需要使用庫(kù)ws2_32.lib,包含方法:可以使用語(yǔ)句來(lái)告訴編譯器連接該庫(kù)
#pragma comment(lib, “ws2_32.lib”);
如果使用VS,可以通過(guò)“項(xiàng)目” --> “XX屬性”--> “連接器”-->“輸入”--> “附加依賴項(xiàng)”添加ws2_32.lib。 (XX為當(dāng)前工程名)

面向連接的C/S程序工程流程圖


使用Winsock API編制的網(wǎng)絡(luò)應(yīng)用程序中,在調(diào)用任何一個(gè)Winsock函數(shù)之前都必須檢查協(xié)議棧安裝情況,使用函數(shù)WSAStartup()完成操作。
一個(gè)服務(wù)端的例子

void server::startServer() { ??? WORD wVersionRequested; ??? WSADATA wsaData; ??? wVersionRequested=MAKEWORD(2,2); ??? if(WSAStartup(wVersionRequested,&wsaData)!=0) ??? { ??????? //Winsock初始化錯(cuò)誤??????? msgBox.exec(); ??????? return; ??? } ??? if(wsaData.wVersion!=wVersionRequested) ??? { ??????? //Winsock版本不匹配??????? ????????WSACleanup(); ??????? return; ??? } ??? if ((m_sk = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR) ??? {??????? ????????WSACleanup(); ??????? return; ??? } ??? bool ok; ??? unsigned short port = ui.portLineEdit->text().toInt(&ok, 10); ??? if (ok == false) ??? { ??????? //端口輸入錯(cuò)誤??????? closesocket(m_sk); ??????? m_sk = -1; ??????? WSACleanup(); ??????? return; ??? } ??? sockaddr_in addr; ??? addr.sin_family = AF_INET; //使用互聯(lián)網(wǎng)際協(xié)議,即IP協(xié)議 ??? addr.sin_port = htons(port); ??? addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); ??? if (bind(m_sk, (sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) ??? { ??????? //綁定端口失敗 ??????? closesocket(m_sk); ??????? m_sk = -1; ??????? WSACleanup(); ??????? return; ??? } ??? if (listen(m_sk, 10) == SOCKET_ERROR) ??? { ??????? //監(jiān)聽端口失敗??????? closesocket(m_sk); ??????? m_sk = -1; ??????? WSACleanup(); ??????? return; ??? } ??? updateMsgRecs(tr("等待連接...")); ??? //創(chuàng)建線程去等待連接 ??? HANDLE h = CreateThread(NULL, 0, &server::acceptProc, (LPVOID)this, 0, 0); ??? if (h == NULL) ??? {??????? ????????closesocket(m_sk); ??????? m_sk = -1; ??????? WSACleanup(); ??????? return;??????? ????} ??? CloseHandle(h); }

監(jiān)聽線程函數(shù)

DWORD WINAPI server::acceptProc(LPVOID lpParamter) { ??? /*server *p_server = (server *)lpParamter; ??? sockaddr_in client_addr; ??? int len = sizeof(client_addr); ??? char msgBuff[256]; ??? int sk; ??? while (1) ??? { ??????? if ((sk = ::accept(p_server->sk(), (sockaddr*)&client_addr, &len) )== SOCKET_ERROR) ??????? { ??????????? emit p_server->haveNewMsg(QObject::tr("accept 出錯(cuò)")); ??????????? break; ??????? } ??????? ????????sprintf(msgBuff, "新連接來(lái)自%s", inet_ntoa(client_addr.sin_addr)); ??????? emit p_server->haveNewMsg(msgBuff); ??????? emit p_server->newClient(inet_ntoa(client_addr.sin_addr), sk); ?? ?}*/ ??? server *p_server = (server *)lpParamter; ??? int client[FD_SETSIZE]; ??? fd_set allset, rset; ??? sockaddr_in client_addr; ??? int len; ??? int clientfd; ??? int sockfd; ??? int i; ??? for (i=0; i<FD_SETSIZE; i++) ??????? client[i] = -1; ??? FD_ZERO(&allset); ??? int listenfd = p_server->sk(); ??? FD_SET(listenfd, &allset); ??? int nready; ??? int maxfd = listenfd; ??? int maxi = -1; ??? char buff[MAX_LEN+1]; ??? while (1) ??? { ??????? rset = allset; ??????? nready = select(maxfd+1, &rset, NULL, NULL, NULL); ??????? if (FD_ISSET(listenfd, &rset)) //new connection??????? { ??????????? len = sizeof(client_addr); ??????????? if ( (clientfd = ::accept(p_server->sk(), (sockaddr*)&client_addr, &len)) == SOCKET_ERROR) ??????????? { ??????????????? emit p_server->haveNewMsg(QObject::tr("accept 出錯(cuò)")); ??????????????? break; ??????????? } ??????????? //找出client數(shù)組中第一個(gè)為-1的單元存放已經(jīng)連接的socket ??????????? for (i=0; i<FD_SETSIZE; i++) ??????????? { ??????????????? if (client[i] <0) ??????????????? { ??????????????????? client[i] = clientfd; ?????? ?????????????break; ??????????????? } ??????????? } ??????????? if (i == FD_SETSIZE) ??????????? { ??????????????? emit p_server->haveNewMsg(QObject::tr("error: too many clients!")); ??????????????? break; ??????????? } ??????????? //sprintf(buff, "新連接來(lái)自%s", inet_ntoa(client_addr.sin_addr)); ??????????? //emit p_server->haveNewMsg(buff); ??????????? emit p_server->newClient(inet_ntoa(client_addr.sin_addr), clientfd); ??????????? FD_SET(clientfd, &allset); ??????????? if (clientfd > maxfd) ??????????????? maxfd = clientfd; ??????????? if (i>maxi) ??????????????? maxi = i; ??????????? if (--nready <= 0) ??????????????? continue; ??????? } ??????? for (i=0; i<=maxi; i++) ??????? { ??????????? if ( (sockfd = client[i]) < 0) ??????????????? continue; ??????????? if (FD_ISSET(sockfd, &rset)) ??????????? { ??????????????? int n; ??????????????? //客戶端已經(jīng)關(guān)閉連接 ??????????????? if ( (n = recv(sockfd, buff, MAX_LEN, 0 )) <= 0) ??????????????? { ??????????????????? closesocket(sockfd); ??????????????????? FD_CLR(sockfd, &allset); ??????????????????? client[i] = -1; ??????????????????? emit p_server->haveNewMsg(QObject::tr("client closed")); ??????????????????? emit p_server->sClientClose(sockfd); ??????????????? } ??????????????? else //收到數(shù)據(jù)??????????????? { ??????????????? ????buff[n] = 0; ??????????????????? emit p_server->haveNewMsg( buff, sockfd); ??????????????????? if (--nready <= 0) ??????????????????????? break; ??????????????? } ??????????? } ??????? } //for (i=0; i<=maxi; i++)??? } ??? return 0; }

?

?一個(gè)客戶端的例子

void client::connectServer() { ??? WORD wVersionRequested; ??? WSADATA wsaData; ??? wVersionRequested=MAKEWORD(2,2); ??? if(WSAStartup(wVersionRequested,&wsaData)!=0) ??? { ??????? //Winsock初始化錯(cuò)誤 ??????? QMessageBox msgBox(QMessageBox::Warning, tr("錯(cuò)誤"), tr("Winsock初始化錯(cuò)誤"), QMessageBox::Ok, 0); ??????? msgBox.exec(); ??????? return; ??? } ??? if(wsaData.wVersion!=wVersionRequested) ??? { ??????? //Winsock版本不匹配 ??????? QMessageBox msgBox(QMessageBox::Warning, tr("錯(cuò)誤"), tr("Winsock版本不匹配"), QMessageBox::Ok, 0); ??????? msgBox.exec(); ??????? WSACleanup(); ??????? return; ??? } ??? if ((m_sk = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR) ??? { ??????? QMessageBox msgBox(QMessageBox::Warning, tr("錯(cuò)誤"), tr("創(chuàng)建socket失敗"), QMessageBox::Ok, 0); ??????? msgBox.exec(); ??????? WSACleanup(); ??????? return; ??? } ??? sockaddr_in addr; ??? addr.sin_family = AF_INET; ??? bool ok; ??? unsigned short port = ui.portLineEdit->text().toInt(&ok, 10); ??? if (ok == false) ??? { ??????? QMessageBox msgBox(QMessageBox::Warning, tr("錯(cuò)誤"), tr("端口輸入錯(cuò)誤"), QMessageBox::Ok, 0); ??????? msgBox.exec(); ??????? closesocket(m_sk); ??????? m_sk = -1; ??????? WSACleanup(); ??????? return; ??? } ?? ?addr.sin_port? = htons(port); ??? addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); ??? if (0 == ::connect(m_sk, (sockaddr *)&addr, sizeof(addr))) ??? { ??????? updateMsgRecs(tr("連接成功...")); ??????? HANDLE h = CreateThread(NULL, 0, &client::recvProc, (LPVOID)this, 0, 0); ??????? CloseHandle(h); ??? } ??? else ??? { ??????? QMessageBox msgBox(QMessageBox::Warning, tr("錯(cuò)誤"), tr("連接出錯(cuò)"), QMessageBox::Ok, 0); ??????? msgBox.exec(); ??????? closesocket(m_sk); ??????? m_sk = -1; ??????? WSACleanup(); ??????? ????} }

●? winsock APIs

網(wǎng)絡(luò)連接函數(shù)
socket 創(chuàng)建套接字
bind 綁定本機(jī)端口
connect 建立連接
listen 監(jiān)聽端口
accept 接受連接
recv, recvfrom 數(shù)據(jù)接收
send, sendto 數(shù)據(jù)發(fā)送
close, shutdown 關(guān)閉套接字
轉(zhuǎn)換函數(shù)
inet_addr() 點(diǎn)分十進(jìn)制數(shù)表示的IP地址轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序的IP地址
inet_ntoa() 網(wǎng)絡(luò)字節(jié)序的IP地址轉(zhuǎn)換為點(diǎn)分十進(jìn)制數(shù)表示的IP地址
字節(jié)順序轉(zhuǎn)換函數(shù)
htonl 4字節(jié)主機(jī)字節(jié)序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序
ntohl  4字節(jié)網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換為主機(jī)字節(jié)序
htons 2字節(jié)主機(jī)字節(jié)序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序
ntohs 2字節(jié)網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換為主機(jī)字節(jié)序
網(wǎng)絡(luò)信息檢索函數(shù)
gethostname 獲得主機(jī)名
getpeername 獲得與套接口相連的遠(yuǎn)程協(xié)議地址
getsockname 獲得套接口本地協(xié)議地址
gethostbyname 根據(jù)主機(jī)名取得主機(jī)信息
gethostbyaddr 根據(jù)主機(jī)地址取得主機(jī)信息
getprotobyname 根據(jù)協(xié)議名取得主機(jī)協(xié)議信息
getprotobynumber 根據(jù)協(xié)議號(hào)取得主機(jī)協(xié)議信息
getservbyname 根據(jù)服務(wù)名取得相關(guān)服務(wù)信息
getservbyport 根據(jù)端口號(hào)取得相關(guān)服務(wù)信息
getsockopt/setsockopt 獲取/設(shè)置一個(gè)套接口選項(xiàng)
ioctlsocket 設(shè)置套接口的工作方式

winsock client & server

分類: winsock 2013-05-07 21:36 489人閱讀 評(píng)論(0) 收藏 舉報(bào)

winsock

// winsock client.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)。
//
客戶端實(shí)例

//vs2010編譯通過(guò)

#include "stdafx.h"


#include <winsock2.h>
#include <stdio.h>

#define SERVPORT??? 5050????????????? // 端口為5050
#define MAXDATASIZE 100
#define SERVIP????? "127.0.0.1"????? //
服務(wù)器IP地址為"127.0.0.1",注意使用inet_addrIP地址轉(zhuǎn)換為網(wǎng)絡(luò)格式
#pragma comment(lib,"ws2_32.lib")

void main(int argc, char *argv[])
{
?WSADATA????????????? wsaData;
?SOCKET?????????????? sConnect;
?SOCKADDR_IN????????? serverAddr;?
?int?????recvbytes;
?char?????buf[MAXDATASIZE];

?//初始化Windows Socket 2.2

?WSAStartup(MAKEWORD(2,2), &wsaData);

?// 創(chuàng)建一個(gè)新的Socket來(lái)連接服務(wù)器

?sConnect = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

?// 填寫連接地址信息

?serverAddr.sin_family = AF_INET;
?serverAddr.sin_port = htons(SERVPORT);???
?serverAddr.sin_addr.s_addr = inet_addr(SERVIP);

?memset(&(serverAddr.sin_zero), 0, sizeof(serverAddr.sin_zero));

?// 向服務(wù)器發(fā)出連接請(qǐng)求

?if (connect(sConnect, (SOCKADDR *)&serverAddr, sizeof(SOCKADDR)) == SOCKET_ERROR)
?{
??printf("connect failed!\n");
??system("pause");
??return;
?}
?//
接受服務(wù)器的回應(yīng)消息
?recvbytes = recv(sConnect, buf, MAXDATASIZE, 0);
?if (recvbytes == SOCKET_ERROR)
?{
??system("pause");
??printf("recv failed!\n");
?}
?else
?{
??buf[recvbytes] = '\0';
??printf("%s\n",buf);
?}

?closesocket(sConnect);

?// 釋放Windows Socket DLL的相關(guān)資源

?system("pause");
?WSACleanup();
}
?

// winsock server.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)。
//
服務(wù)器端實(shí)例

//vs2010端編譯通過(guò)

#include "stdafx.h"


#include <winsock2.h>
#include <stdio.h>

#define SERVPORT??? 5050
#pragma comment(lib,"ws2_32.lib")

void main(void)
{
?WSADATA????????????? wsaData;
?SOCKET?????????????? sListen;??//
監(jiān)聽socket
?SOCKET?????????????? sClient;??//
連接socket
?SOCKADDR_IN????????? serverAddr;??//
本機(jī)地址信息
?SOCKADDR_IN????????? clientAddr;??//
客戶端地址信息
?int????????????? clientAddrLen;?//
地址結(jié)構(gòu)的長(zhǎng)度
?int????????????????? nResult;
?//
初始化Windows Socket 2.2

?WSAStartup(MAKEWORD(2,2), &wsaData);

?// 創(chuàng)建一個(gè)新的Socket來(lái)響應(yīng)客戶端的連接請(qǐng)求

?sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

?// 填寫服務(wù)器綁定的地址信息
?//
端口為5050
?// IP
地址為INADDR_ANY,響應(yīng)每個(gè)網(wǎng)絡(luò)接口的客戶機(jī)活動(dòng)
?//
注意使用htonlIP地址轉(zhuǎn)換為網(wǎng)絡(luò)格式

?serverAddr.sin_family = AF_INET;
?serverAddr.sin_port = htons(SERVPORT);???
?serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
?memset(&(serverAddr.sin_zero), 0, sizeof(serverAddr.sin_zero));??
?//
綁定監(jiān)聽端口

?nResult = bind(sListen, (SOCKADDR *)&serverAddr, sizeof(SOCKADDR));
?if (nResult == SOCKET_ERROR)
?{
??printf("bind failed!\n");

??return;
?}

?// 開始監(jiān)聽,指定最大接受隊(duì)列長(zhǎng)度5,不是連接數(shù)的上限

?listen(sListen, 5);

?// 接受新的連接
?while(1)
?{
??clientAddrLen = sizeof (SOCKADDR);
??sClient = accept(sListen, (SOCKADDR *)&clientAddr, &clientAddrLen);
??if(sClient == INVALID_SOCKET)
??{
???printf("Accept failed!");
??}
??else
??{
???printf("Accepted client: %s : %d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
???//
向客戶端發(fā)送信息
???nResult = send(sClient, "Connect success!", 16, 0);
???if (nResult == SOCKET_ERROR)
???{
????printf("send failed!");
???}
??}
??//
我們直接關(guān)閉連接,
??closesocket(sClient);
?}?


?// 并關(guān)閉監(jiān)聽Socket,然后退出應(yīng)用程序

?closesocket(sListen);

?// 釋放Windows Socket DLL的相關(guān)資源

?WSACleanup();
}

?

多機(jī)同步(多線程同步)

采用Event事件和等待waitfor...機(jī)制實(shí)現(xiàn)跨機(jī)同步。

WaitForSingleObject

等待函數(shù)可使線程自愿進(jìn)入等待狀態(tài),直到一個(gè)特定的內(nèi)核對(duì)象變?yōu)橐淹ㄖ獱顟B(tài)為止。這些等待函數(shù)中最常用的是WaitForSingleObject:

DWORD WaitForSingleObject(HANDLE hObject, DWORD dwMilliseconds);

HANDLE經(jīng)常使用 CEvent對(duì)象,在程序中可以通過(guò)調(diào)用SetEvent/ResetEvent分別將EVENT置為這兩種狀態(tài)分別是發(fā)信號(hào)與不發(fā)信號(hào)。

?

參考 :

WaitForSingleObject的用法

http://blog.sina.com.cn/s/blog_4b88ef57010009js.html

線程中CreateEvent和SetEvent及WaitForSingleObject的用法

http://chinaxyw.iteye.com/blog/548622

?

?

WaitForMultiObjects

用法類似于waitForSingleObjects只不過(guò)是多信號(hào)等待。

?

?

?

原理分析

?

多機(jī)同步顯示示意圖

?

多機(jī)同步的實(shí)現(xiàn)原理實(shí)際是控制master和slaver上的投影機(jī)姿態(tài)矩陣,將二者綁定在一起,就像是架在一根竹棍上的兩個(gè)相機(jī)一樣,一起做相同的運(yùn)動(dòng),這樣集群中的所有相機(jī)的相對(duì)位置相同,姿態(tài)也相對(duì)不變,投影畫面也就能夠始終保持無(wú)縫拼接了。

?

疑問(wèn): 重疊區(qū)域地物的調(diào)度需要怎么才能保證多臺(tái)機(jī)器同時(shí)出現(xiàn)同時(shí)消失?不控制是否可以接受?

??? 如果不控制的話在重疊區(qū)域就很容易出現(xiàn)建筑物的亮度比非重疊區(qū)亮度低的情況(因?yàn)橹丿B區(qū)做了融合處理削弱了亮度),尤其是在邊緣處亮度接近于全黑。

?

流程設(shè)計(jì)

  • 1. ?啟動(dòng)集群所有計(jì)算機(jī)中的creatar軟件,creatar通過(guò)網(wǎng)絡(luò)廣播反饋或者自定義配置文件方式 確定集群中的 主控機(jī)master(一臺(tái))和從屬機(jī)slaver(一臺(tái)或多臺(tái))角色, 建立主控機(jī)master與從屬機(jī)slaver之間的通訊通道(每臺(tái)slaver和master之間都有一個(gè)通訊線程存在)。
  • 主控機(jī)向從屬機(jī)發(fā)送的信息為 “相機(jī)矩陣” 和 “翻屏指令”, 接受的信息為從屬機(jī)發(fā)來(lái)的 “等待翻屏指令”。從屬機(jī)向主控機(jī)發(fā)送的信息為 “等待翻屏指令”,接收的信息是“相機(jī)矩陣”。【細(xì)化】

    ?

    一般這種多機(jī)顯示應(yīng)用都要設(shè)計(jì)一個(gè)獨(dú)立的控制界面,放在主控機(jī)master的集成顯卡顯示區(qū)域。主控機(jī)的專業(yè)3D顯卡和從屬機(jī)3D顯卡顯示3D系統(tǒng)的3D視圖畫面。

    ?

  • 2. ?固定從屬機(jī)slaver的相機(jī)與主控機(jī)master的相機(jī)之間的姿態(tài)差異,這樣只要初始狀態(tài)時(shí)的master和slavers的相機(jī)的投影畫面能無(wú)縫拼合在一起,未來(lái)master的相機(jī)做了怎樣的矩陣變換,slavers的相機(jī)也跟隨做相應(yīng)的矩陣變換就能保證投影畫面能始終無(wú)縫拼合。這樣就需要每幀master都要把它的“相機(jī)矩陣”發(fā)給slaver,或者把其變換矩陣發(fā)給slaver,slaver根據(jù)收到的矩陣做相同的姿態(tài)改變就可以了。
  • ?

    3. 流程圖如下所示

    ?

    ?

    會(huì)用到windows多線程編程的阻塞機(jī)制,master上建議使用WaitForMultiObjects(),slaver上建議使用WaitForSingleObject()來(lái)阻塞渲染線程。

    ?

    實(shí)現(xiàn)細(xì)節(jié)

    winsock廣播

    參考資料 http://www.cnblogs.com/uvsjoh/archive/2012/12/28/2837687.html

    廣播通信是無(wú)連接的通信,通信前不需要建立連接。不需要listen和accept,但需要綁定一個(gè)socket用來(lái)接收廣播。

    ?廣播包的發(fā)送
    創(chuàng)建socket
    設(shè)置socket,例如設(shè)置超時(shí)、允許廣播等
    綁定socket。在使用廣播前必須綁定一個(gè)socket。這一步可有可無(wú),如果沒(méi)有,系統(tǒng)自動(dòng)綁定到一個(gè)未用端口。
    發(fā)送廣播。廣播的端口號(hào)要和接收方綁定的端口號(hào)一致

    #include "stdafx.h" #include <WinSock2.h> #include <Windows.h> #include <string.h> #pragma comment(lib, "ws2_32.lib") void autoCleanup() { ??? WSACleanup(); } int _tmain(int argc, _TCHAR* argv[]) { ??? WORD wVersionRequested; ??? WSADATA wsaData; ??? wVersionRequested=MAKEWORD(2,2); ??? int ret; ??? ret = WSAStartup(wVersionRequested,&wsaData); ??? int sock = socket(AF_INET, SOCK_DGRAM, 0); ??? int bc = 1; ??? //允許發(fā)送廣播消息 ??? int so_broadcast = TRUE; ??? ret = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&so_broadcast, sizeof(so_broadcast)); ??? ????//sockaddr_in addr; ??? //addr.sin_family = AF_INET; //使用互聯(lián)網(wǎng)際協(xié)議,即IP協(xié)議 ??? //addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); ????//addr.sin_port = htons(2526); ??? //如果僅僅是發(fā)送廣播,這一步可有可無(wú)。沒(méi)有綁定也能發(fā)送廣播 ??? //ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); ??? struct sockaddr_in b_addr; ??? b_addr.sin_family = AF_INET; ??? b_addr.sin_addr.S_un.S_addr =htonl(INADDR_BROADCAST); ??? b_addr.sin_port = htons(2527); ??? char buff[50] = "Hello, world!"; ??? while (1) ??? { ??????? ret = sendto(sock, buff, strlen(buff), 0, (struct sockaddr*)&b_addr, sizeof(b_addr)); ??????? printf("send... %d\n", WSAGetLastError()); ??????? Sleep(3000); ??? } ??? closesocket(sock); ??? atexit(autoCleanup); ??? return 0; }

    ?接收廣播包
    接收方一定要知道廣播方的端口號(hào),然后綁定同樣的端口號(hào)才能正確接收。道理很簡(jiǎn)單,如果不綁定到一個(gè)端口,它不知道到哪里接收數(shù)據(jù)。

    // send.cpp : Defines the entry point for the console application.// #include "stdafx.h" #include <WinSock2.h> #include <Windows.h> #include <string.h> #pragma comment(lib, "ws2_32.lib") void autoCleanup() { ??? WSACleanup(); } int _tmain(int argc, _TCHAR* argv[]) { ??? WORD wVersionRequested; ??? WSADATA wsaData; ??? wVersionRequested=MAKEWORD(2,2); ??? WSAStartup(wVersionRequested,&wsaData); ??? SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0); ??? struct sockaddr_in addr; ??? addr.sin_family = AF_INET; ??? addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); ??? //這個(gè)端口要和廣播方廣播端口一致 ??? addr.sin_port = htons(2527); ??? bind(sock, (struct sockaddr *)&addr, sizeof(addr)); ??? struct sockaddr_in from; ??? int len = sizeof(from); ??? int ret; ??? char buff[50]; ??? while (1) ??? { ??????? ret = recvfrom(sock, buff, 49, 0, (struct sockaddr *)&from, &len); ??????? if (ret > 0) ??????? { ??????????? buff[ret] = 0; ??????? ????printf("%s\n", buff); ??????????? printf("%s %d\n", inet_ntoa(from.sin_addr), ntohs(from.sin_port)); ??????? } ??? } ??? closesocket(sock); ??? atexit(autoCleanup); ??? return 0; }

    winsock網(wǎng)絡(luò)通訊

    利用winsock編寫網(wǎng)絡(luò)應(yīng)用程序服務(wù)端的步驟簡(jiǎn)述如下
    WSAStartup 初始化網(wǎng)絡(luò)編程庫(kù)
    socket 創(chuàng)建套接字
    bind 指定地址、端口,綁定套接字
    listen 進(jìn)入監(jiān)聽狀態(tài)
    accept 等待接收新連接
    send/recv 收發(fā)數(shù)據(jù)
    closesocket 關(guān)鍵套接字
    WSAStartup 釋放對(duì)動(dòng)態(tài)庫(kù)的使用

    ?

    參考資料

    C++_Socket網(wǎng)絡(luò)編程大全

    http://wenku.baidu.com/view/3f5ce1d5360cba1aa811da2b.html

    Socket服務(wù)器與客戶端雙向通信實(shí)例

    http://wenku.baidu.com/view/47a7877101f69e31433294a4.html?re=view

    Windows網(wǎng)絡(luò)編程

    http://www.cnblogs.com/uvsjoh/archive/2012/12/23/2830299.html

    MSDN上的客戶端代碼(Winsock Client Source Code)

    http://www.cnblogs.com/wwping/archive/2012/04/18/2454979.html

    ?

    ?

    ?

    面向連接的C/S程序工程流程圖

    ?

    字節(jié)序轉(zhuǎn)換函數(shù)
    htons 把 unsigned short 類型從主機(jī)序轉(zhuǎn)換到網(wǎng)絡(luò)序
    htonl 把 unsigned long 類型從主機(jī)序轉(zhuǎn)換到網(wǎng)絡(luò)序
    ntohs 把 unsigned short 類型從網(wǎng)絡(luò)序轉(zhuǎn)換到主機(jī)序
    ntohl 把 unsigned long 類型從網(wǎng)絡(luò)序轉(zhuǎn)換到主機(jī)序
    這幾個(gè)函數(shù)很好記,比如htons中hton代表host to network, s代表unsigned short
    char FAR * inet_ntoa( struct in_addr in);
    將一個(gè)IP轉(zhuǎn)換成一個(gè)互聯(lián)網(wǎng)標(biāo)準(zhǔn)點(diǎn)分格式的字符串。
    in_addr_t inet_addr(const char *cp);
    將一個(gè)點(diǎn)分十進(jìn)制的IP轉(zhuǎn)換成一個(gè)長(zhǎng)整數(shù)型數(shù)(u_long類型)。返回值已是網(wǎng)絡(luò)字節(jié)順序,可以直接作為internet 地址

    ?

    Openscenegraph單機(jī)立體顯示

    參考資料

    osg中立體顯示的設(shè)置“最長(zhǎng)的一幀”第三日

    osg::DisplaySettings 這個(gè)類在OSG 的窗口顯示中扮演了重要的地位:它保存了OSG 目前用到的,與圖形顯示,尤其是立體顯示有關(guān)的所有信息,

    主要包括:

    _displayType:顯示器類型,默認(rèn)為MONITOR(監(jiān)視器),此外還支持POWERWALL

    (威力墻),REALITY_CENTER(虛擬實(shí)境中心)和HEAD_MOUNTED_DISPLAY(頭盔

    顯示器)。

    _stereoMode : 立體顯示模式, 默認(rèn)為ANAGLYPHIC ( 互補(bǔ)色), 此外還支持

    QUAD_BUFFER(四路緩沖),HORIZONTAL_SPLIT(水平分割),VERTICAL_SPLIT(垂直分割),LEFT_EYE(左眼用),RIGHT_EYE(右眼用),HORIZONTAL_INTERLACE(水平交錯(cuò)),VERTICAL_INTERLACE(垂直交錯(cuò)),CHECKERBOARD(棋盤式交錯(cuò),用于DLP 顯示器)。

    _eyeSeparation:雙眼的物理距離,默認(rèn)為0.05。

    _screenWidth,_screenHeight:屏幕的實(shí)際寬度和高度,分別默認(rèn)設(shè)置為0.325 和0.26,

    目前它們影響的僅僅是視圖采用透視投影時(shí)的寬高比。

    _screenDistance:人眼到屏幕的距離,默認(rèn)為0.5。

    _splitStereoHorizontalEyeMapping:默認(rèn)為L(zhǎng)EFT_EYE_LEFT_VIEWPORT(左眼渲染左

    視口),也可設(shè)為L(zhǎng)EFT_EYE_RIGHT_VIEWPORT(左眼渲染右視口)。

    _splitStereoHorizontalSeparation:左視口和右視口之間的距離(像素?cái)?shù)),默認(rèn)為0。

    _splitStereoVerticalEyeMapping:默認(rèn)為L(zhǎng)EFT_EYE_TOP_VIEWPORT(左眼渲染頂視

    口),也可設(shè)為L(zhǎng)EFT_EYE_BOTTOM_VIEWPORT(左眼渲染底視口)。

    _splitStereoVerticalSeparation:頂視口和底視口之間的距離(像素?cái)?shù)),默認(rèn)為0。

    _splitStereoAutoAdjustAspectRatio:默認(rèn)為true,用于屏幕分割之后對(duì)其寬高比進(jìn)行補(bǔ)

    償。

    _maxNumOfGraphicsContexts:用戶程序中最多可用的GraphicsContext(圖形設(shè)備上下

    文)數(shù)目,默認(rèn)為32 個(gè)。

    _numMultiSamples:多重采樣的子像素樣本數(shù),默認(rèn)為0。如果顯示卡支持的話,打開

    多重采樣可以大幅改善反走樣(anti-aliasing)的效果。

    此外還有很多可以設(shè)置的類變量,如_minimumNumberStencilBits(模板緩存的最小位

    數(shù))等,其默認(rèn)設(shè)置均在osg::DisplaySettings::setDefaults 函數(shù)中完成,其中有些變量可能還

    沒(méi)有作用。要注意的是,DisplaySettings 的作用僅僅是保存所有可能在系統(tǒng)顯示中用到的數(shù)

    據(jù),這個(gè)類本身并不會(huì)據(jù)此改變?nèi)魏蜗到y(tǒng)設(shè)置和渲染方式。

    值得稱道的是,DisplaySettings 可以很方便地從系統(tǒng)環(huán)境變量或者命令行參數(shù)中獲取用

    戶對(duì)顯示設(shè)備的設(shè)置,詳細(xì)的調(diào)用方法可以參閱DisplaySettings::readEnvironmentalVariables

    和DisplaySettings::readCommandLine 兩個(gè)函數(shù)的內(nèi)容,十分通俗易懂。

    如果希望在用戶程序中更改DisplaySettings 中的顯示設(shè)置,請(qǐng)務(wù)必在執(zhí)行視景器的

    realize 函數(shù)之前,當(dāng)然也就是仿真循環(huán)開始之前。這一點(diǎn)也是要切記的。

    ?

    分析及方案設(shè)計(jì)

    目前通用的立體實(shí)現(xiàn)模式有兩種:主動(dòng)式立體和被動(dòng)式立體

    主動(dòng)式立體顯示

    ???? 主動(dòng)立體顯示方式也稱快門式3D顯示技術(shù),主動(dòng)快門式3D技術(shù)是目前3D投影設(shè)備市場(chǎng)上應(yīng)用比較廣泛的3D顯示技術(shù),其需要配合主動(dòng)快門式眼鏡使用,原理是這樣的:屏幕會(huì)先顯示給左眼看的畫面,這時(shí)眼鏡會(huì)同步將你的右眼遮住,有點(diǎn)像海盜戴的眼罩那樣。接著,屏幕會(huì)快速切換到給右眼看的畫面,這時(shí)眼鏡就會(huì)轉(zhuǎn)成將你的左眼遮住,確保你看到的畫面是正確的。??

    ????? 主動(dòng)快門式3D技術(shù)是通過(guò)交替左眼和右眼看到的圖像以至于你的大腦將兩幅圖像融合成一體來(lái)實(shí)現(xiàn),從而產(chǎn)生了單幅圖像的3D立體感。畫面交替的過(guò)程非常迅速,每秒可以到120次(120Hz刷新率),因此對(duì)人眼來(lái)說(shuō)是無(wú)法看到這個(gè)左右轉(zhuǎn)換的。??

    ????? 主動(dòng)立體的主要技術(shù)特征:同步發(fā)射器及快門式眼鏡、支持120Hz輸出的融合器、支持120Hz投影機(jī)。

    ?

    這種情況下osg::DisplaySettings應(yīng)設(shè)置如下:

    _displayType使用默認(rèn)值 MONITOR(監(jiān)視器),

    _stereoMode 使用默認(rèn)值 ANAGLYPHIC ( 互補(bǔ)色)

    其它參數(shù)使用默認(rèn)值即可。

    評(píng)價(jià) 這種模式對(duì)3D程序的渲染速率要求很高(120幀每秒)一般的3D軟件都難以達(dá)到,所以應(yīng)用比較多的是3D視頻播放或3D電影領(lǐng)域。聽說(shuō)這么高的頻率下人眼也是很容易累的,而且這種3D影片的制作難度也比較大。

    被動(dòng)式立體顯示

    ????? 被動(dòng)立體顯示方式也稱光學(xué)偏振顯示技術(shù),主要實(shí)現(xiàn)方式:通過(guò)兩臺(tái)顯示設(shè)備(投影機(jī)),同時(shí)把兩個(gè)經(jīng)過(guò)特殊處理(立體處理)的圖像或影片同步放映,使這略有差別的兩幅圖像(景深差別)重疊在銀幕上(偏振光學(xué)幕)。這時(shí)如果用眼睛直接觀看,看到的畫面是重影模糊不清的,要看到立體影像,就要在每架投影機(jī)前裝一塊偏振片。從兩架放映機(jī)投射出的光,通過(guò)偏振片后,就成了偏振光。左右兩架投影機(jī)前的偏振片的偏振化方向互相垂直,因而產(chǎn)生的兩束偏振光的偏振方向也互相垂直。??

    ????? 這兩束偏振光投射到銀幕上再反射到觀眾處,偏振光方向不改變。當(dāng)觀眾帶上偏振眼鏡后,左右兩片偏振鏡的偏振軸互相垂直并與放映鏡頭前的偏振軸一致,所以每只眼睛只看到相應(yīng)的偏振光圖象,即左眼只能看到左機(jī)映出的畫面,右眼只能看到右機(jī)映出的畫面,這樣就會(huì)像直接觀看那樣產(chǎn)生立體感覺(jué)。??

    ????? 被動(dòng)立體顯示的主要技術(shù)特征:雙倍的投影機(jī)、配置偏振光片及眼鏡、配置偏振屏幕、左右眼單獨(dú)播放的融合器。

    這種情況下osg::DisplaySettings應(yīng)設(shè)置如下:

    _displayType使用默認(rèn)值 MONITOR(監(jiān)視器),

    _stereoMode 使用默認(rèn)值 HORIZONTAL_SPLIT(水平分割)或者VERTICAL_SPLIT(垂直分割),具體使用哪種方式應(yīng)視具體的設(shè)備環(huán)境來(lái)決定,一般水平分割的情況比較多。

    _splitStereoHorizontalEyeMapping 如果使用水平分割可以設(shè)置此參數(shù)。

    _splitStereoVerticalEyeMapping? 如果使用垂直分割可以設(shè)置此參數(shù)。

    其它參數(shù)默認(rèn)值是否需要修改結(jié)合具體情況決定。

    評(píng)價(jià): 最簡(jiǎn)單的應(yīng)用就是一臺(tái)電腦+兩臺(tái)投影機(jī)的小規(guī)模場(chǎng)景應(yīng)用,不需要融合器。如果是上下2*2或3*3的投影機(jī)布局的話就需要融合器和軟件系統(tǒng)實(shí)現(xiàn)3D同步控制功能,相當(dāng)于多機(jī)立體顯示。

    Openscenegraph多機(jī)同步立體顯示

    ?

    最好是選用支持Overlap邊緣重疊的專業(yè)顯卡 + 支持邊緣融合的投影機(jī) 這樣在程序級(jí)就不需要產(chǎn)生重疊帶及對(duì)邊緣區(qū)域進(jìn)行融合處理。用硬件的解決方案簡(jiǎn)化系統(tǒng)實(shí)現(xiàn)復(fù)雜度。

    對(duì)于單機(jī)輸出多路信號(hào)的需求可以借助專業(yè)級(jí)多屏顯卡來(lái)實(shí)現(xiàn)。

    專業(yè)網(wǎng)站 :中國(guó)投影網(wǎng) http://www.ty360.com/dp/

    ?

    將多機(jī)同步與立體顯示相結(jié)合可以實(shí)現(xiàn)多機(jī)立體顯示。涉及的問(wèn)題有重疊區(qū)對(duì)象調(diào)度!

    ?

    OpenscenGraph中控制swapbuffer的方法(用于多機(jī)大屏幕同步顯示機(jī)制)

    *********************************************************************
    osg多機(jī)同步swapbuffer的實(shí)現(xiàn)方式。

    osg中真正調(diào)用opengl::swapbuffer的地方在 osg::GrapicsContext::swapBuffers()中調(diào)用的。
    如果developer想干預(yù)的話 可以調(diào)用 osg::GrapicsContext::setSwapCallback(SwapCallback* rc)來(lái)設(shè)置自定義的緩存交換回調(diào)。
    自定義的回調(diào)必須調(diào)用GraphicsContext::swapBuffersImplementation()函數(shù).

    typedef std::vector<osg::GraphicsContext*> Contexts;
    osg::Viewer::getContexts(Contexts& contexts, bool onlyValid=true); 可以獲取grapicscontext的數(shù)組
    *********************************************************************

    ?

    可以將同步這部份做成exe程序(精靈程序,隱藏窗口),用消息機(jī)制分發(fā)給目標(biāo)機(jī)器的目標(biāo)窗口。也可以直接集成到creatarglobe系統(tǒng)中做為可選模塊。

    ?

    ?

    方案

    1. 在系統(tǒng)配置文件中寫入角色信息。creatarGlobe系統(tǒng)啟動(dòng)時(shí)通過(guò)讀取配置信息確定自身角色及集群中其它機(jī)器的角色,然后建立集群跨機(jī)通訊機(jī)制(網(wǎng)絡(luò)監(jiān)聽和收發(fā))。

    ?

    2. 調(diào)整初始狀態(tài)時(shí)的master和slaves的相機(jī)位置使其投影畫面在重疊區(qū)能重合在一起。借助投影機(jī)和融合器實(shí)現(xiàn)產(chǎn)生重疊區(qū)和邊緣融合功能。

    ?

    3. 立體顯示設(shè)置。根據(jù)實(shí)際需求設(shè)置osg::DisplaySettings中的參數(shù)實(shí)現(xiàn)立體效果。具體可以參考上面的openscenegragh單機(jī)立體顯示中的描述。

    ?

    4. 進(jìn)入同步機(jī)制。

    對(duì)于master,首先將自己“相機(jī)矩陣”通過(guò)網(wǎng)絡(luò)通訊分發(fā)給每個(gè)slave,然后開始渲染,渲染完畢后等待slaver的“等待翻屏指令”的到來(lái)【waitformultiobjects】,當(dāng)收到所有的“等待翻屏指令”后(每個(gè)指令對(duì)應(yīng)一個(gè)事件Event),master通過(guò)網(wǎng)絡(luò)向所有的slave發(fā)送“翻屏指令”,然后自己翻屏,重置事件信號(hào)ResetEvent。然后就是重復(fù)以上流程。

    對(duì)于slave,首先接收master發(fā)來(lái)的“相機(jī)矩陣”,根據(jù)此矩陣調(diào)整自身相機(jī)使其相對(duì)master的相機(jī)位置姿態(tài)不變。然后開始渲染,渲染完畢后向master發(fā)送 “等待翻屏指令”,然后等待master發(fā)送的“翻屏指令”事件【waitforsingleobject】,當(dāng)收到“翻屏指令”后設(shè)置翻屏事件信號(hào)Event,然后開始翻屏,重置事件信號(hào)ResetEvent。然后就是重復(fù)以上流程。

    這里的翻屏swapbuffer通過(guò)osg::GrapicsContext::setSwapCallback(SwapCallback* rc),自己定義緩存交換回調(diào)函數(shù)來(lái)實(shí)現(xiàn)。

    ?

    備注: 接收的命令最好是帶時(shí)間戳的,這樣可以保證同步的是同一幀的3D畫面,避免畫面撕裂。

    ?

    5. 系統(tǒng)退出時(shí),slave的網(wǎng)絡(luò)線程先退出然后master的網(wǎng)絡(luò)線程再退出以保證線程安全。

    ?

    6. 偽代碼:

    需要實(shí)現(xiàn)的自定義類

    CGlbGlobeSwapBufferCallback : public osg::SwapCallback

    {

    ?????? CGlbGlobeSwapBufferCallback(CGlbGlobeView* view)

    ?????? {

    ????????????? mpr_view = view;

    ?????? }???????????

    ??????

    ?????? virtual void operater ()

    ?????? {

    ????????????? .......

    ????????????? // 從view中獲取自身角色master or slaver

    ????????????? bool isMaster = mpr_view->GetRole();

    ????????????? if (isMaster)

    ????????????? {// 如果自己角色是master,做以下工作: 收集網(wǎng)絡(luò)傳來(lái)的所有slaver繪制完畢消息,然后廣播翻屏指令,翻屏

    ???????????????????? int slaverCnt = mpr_view->GetSlaverCount();

    ???????????????????? WaitForMultiObjects(...);

    ???????????????????? {// 向所有的slaver發(fā)送翻屏指令

    ??????????????????????????? for(int i = 0; i < slaverCnt; i++)

    ??????????????????????????? {

    ?????????????????????????????????? mpr_view->sendMessage(slaverIP,"drawcomplete");

    ??????????????????????????? }

    ???????????????????? }

    ???????????????????? swapBuffer();???????

    ????????????? }

    ????????????? else{// 如果自己角色是slaver,做以下工作: 通過(guò)網(wǎng)絡(luò)向master發(fā)送繪制完畢消息,等待master通過(guò)網(wǎng)絡(luò)傳來(lái)的翻屏指令,翻屏

    ???????????????????? mpr_view->sendMessage(masterIP,"drawcomplete");

    ???????????????????? WaitForSingleObject();

    ???????????????????? swapBuffer();

    ????????????? }

    ?????? }

    ?

    ?????? private:

    ????????????? glbref_ptr<CGlbGlobeView> mpr_view;

    }

    ?

    在view中要有處理TCP/IP網(wǎng)絡(luò)發(fā)送和接收的類或接口。

    ?

    CGlbClient

    {

    }

    CGlbServer

    {

    }

    ?

    ?

    設(shè)置相機(jī)矩陣由gluperspective->glufrustum

    總結(jié)

    以上是生活随笔為你收集整理的CreatarGlobe实现多机立体显示方案(初稿)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。