• [技术干货] 点量云流:UE模型网页云推流渲染项目经验
    点量云流近期支撑了一个智慧园区模型网页云推流使用的项目,由于是第一次在项目上使用实时云渲染的技术方案,所以在支撑过程中的很多问题非常典型,今天小编就整个过程的交流做相关分享,希望对有类似问题或需求的伙伴提供一些帮助。项目背景:使用UE5引擎开发的智慧园区模型,最初的计划是配置本地高性能电脑,只需要通过HDMI线连接大屏展示即可。但在验收过程中,业主提出需要在网页上使用,要满足2-3个用户的并发使用。项目上目前已经购买了RTX3080Ti显卡,尝试了webgl和像素流送PixelStreaming的技术路线。但是发现webgl的技术路线,推流后的网页比较糊效果不满意。而像素流送PixelStreaming不稳定,经常会出现崩溃的问题,用户体验非常不好,且目前已有的方案无法满足3个用户的使用。需求分析:基于以上项目背景情况,点量云流认为有以下几个问题需要解决①网页推流使用②需要保证稳定性③在现有硬件配置下尽可能做增加并发用户数 点量云流推流后网页使用,测试过程和疑问分享:在工程师的电脑上安装点量云流渲染服务,工程师的机器是RTX4060显卡,性能比项目现场的机器性能弱。安装了云流渲染服务后,创建了云应用,也就是将要推流的UE应用存储路径告知云流渲染服务。云渲染服务是本地私有化部署,不需要将应用上传到云端。Q1:创建云应用时设置了3个云推流并发,但是电脑上浏览器只能打开一个,其他的链接自动关闭了。这是点量云流渲染系统的一个算力节省策略,同一个电脑同一个浏览器只打开一个推流网页,主要是为了减少GPU和CPU资源占用。如果想要测试多个推流后能支持多少并发,可以用浏览器的无痕模型,或者切换不同的浏览器分别打开。Q2:在现有的GPU和CPU 硬件条件下,如何提升云串流并发数?在点量云流实时渲染系统中,默认的是60帧率,1920*1080的清晰度。在该测试过程中发现,这样的参数设置只能跑2路并发。如果想实现3个并发,在现有硬件基础下,可以通过降低帧率和清晰度的方式,增加并发数。在这个项目中,将帧率降到30以后,就可以实现支持3个并发。测试使用的是RTX4060显卡,而项目上是RTX3080Ti,而3080Ti的性能比4060要高不少,因此在开发环境下测试能实现的化,在生产环境中基本没问题,或者可以将帧率和分辨率在调高一些,在满足3个并发的前提下实现合适的参数组合以实现更好的用户体验。Q3:没用云渲染本地跑1路的时候GPU占用到了90%,实时云渲染推流之后就只能跑1路吗?这个不一定,在本项目中,由于UE模型没有做锁帧,在本地测试的时候确实GPU占用瞬间到了90%,但点量云流系统默认是设置了帧率后自动做锁帧处理。因此该项目中可以支持3并发使用。Q4:如果使用实时渲染推流服务,需要开放哪些端口?还需要30000段开始的一些udp端口,用来传输画面数据,这些是浏览器访问的情况下对浏览器端要用到的。云流服务本地还可能用到一些其他端口,但不一定是浏览器访问的时候用到的,可能是本机服务之间内部通信会用到。具体关于端口的问题可以查看文档:https://doc.dolit.cn/dolitcloudserver/light_portQ5:云推流是否支持https证书设置?点量云流管理平台中可以直接设置https证书,对于自签的私有证书,上传到云流管理平台替换默认的证书文件,还是通过http+ip的形式访问,申请的第三方证书可以用公网域名访问。本项目中是自签的私有证书,上传后无需修改其他的配置,只是原来推流的网页地址是http的,现在换成了https的,不需要考虑端口的切换等问题。直接重新复制要实时渲染应用的推流网址在浏览器重新打开即可。如果需要切换不同的端口也可以自己修改后,用新的端口来访问。Q6:https访问的时候,ip+端口号,后面还要加一串参数,直接ip+端口访问不了,有直接ip+端口能访问的方法么?或者直接用域名访问一定要有参数的,因为实时云渲染系统可以支持多个应用,参数的主要作用就是用来匹配要加载的应用。如果项目中只有一个应用,可以通过调用websdk的方式设置,或者独立做一个网页iframe嵌套实时云渲染网址,或者考虑定制服务。Q7:实时云渲染的前端网页,如果半小时没操作是否能自动下线?这是基础功能,在可视化云流管理平台后台可直接自定义设置,这个机制也是为了节省资源。点量云流官网的测试demo默认设置了3分钟体验时间,也是基于该功能。 Q8:有没有1280*720的分辨率,因为UI要保持16:9的比例在云流管理平台中支持多种分辨率的自定义,可根据项目需要选择适合的分辨率,保存后,重新打开推流网页即可生效。或者在前端页面用户手动修改需要的分辨率和画面比例。在云流管理平台中,点开编辑云应用-高级设置-分辨率,目前支持的分辨率类型有:1280*720、1920*1080、2K、4K、8K等多种。Q9:3个并发,要求带宽要达到多少?如果带宽低,会影响帧率吗?需要的带宽是根据单个并发设置的码率以及并发数来决定的,在本项目中设置的单个并发码率为10M,则3并发总带宽得在30M。如果码率设置的低一些,比如5M,则20M也够用,一般来说在局域网环境中20M的码率非常容易实现。但也不建议码率太低,否则可能会影响画质。以上是智慧园区UE模型实时渲染云推流的交流过程,经过多次反复的比较,最终确定的计划是仍然用1920*1080分辨率推流,同时对UE场景做进一步的优化。在开发环境的RTX4060   8G显存的电脑上能解决3路并发的问题。
  • [技术干货] Easyx图形库应用(和lua结合使用-转载
        lua本身是一门胶水语言。很多项目,在底层逻辑部分,都是用c/c++开发。但是业务代码部分,还是采用lua这种脚本语言来完成。这里面很大一部分原因,就是底层框架偏向于基础和原理、性能,而上层脚本偏向于应用。底层的部分改动不多,甚至来说没什么改动。但是业务不一样,可能这个项目是这么来写,那个项目是那么来处理。所以,就需要看怎么把底层结构和上层区分开来。不过今天我们说的lua和easyx,它是比较简单的。主要就是api级别的适配。 1、nuget安装        本身lua是可以自己安装第三方代码,编译生成静态库、动态库的。不过vs2017本身有nuget工具,可以通过它直接下载lua库。里面包含了头文件、静态库、动态库。   2、设置include目录        下载后的lua不能直接使用,需要手动设置下include目录,这一点稍微注意下。 3、api适配        这里的适配,主要是分成四种。一种是没有参数的api适配,这种是最简单的, static void cleardevice_function(lua_State* L){::cleardevice();}AI写代码        第二种是有输入参数,但是没有返回值的适配, static void initgraph_function(lua_State *L){int width = (int)luaL_checkinteger(L, 1);int height = (int)luaL_checkinteger(L, 2); ::initgraph(width, height);}AI写代码        第三种,就是有返回值的适配,输入参数可以有,也可以没有。返回值1代表一个返回结果的意思。 static int kbhit_function(lua_State* L){lua_pushinteger(L, _kbhit()); return 1; // 1 means 1 parameter}AI写代码        最后一种,就是需要创建对象的适配,这种适配一般需要返回一个指针,在实际场景中用的非常多。 static int getimage_function(lua_State* L){int x = (int)luaL_checkinteger(L, 1);int y = (int)luaL_checkinteger(L, 2);int width = (int)luaL_checkinteger(L, 3);int height = (int)luaL_checkinteger(L, 4); IMAGE* p_image = new IMAGE;::getimage(p_image, x, y, width, height);lua_pushinteger(L, (lua_Integer)(p_image)); return 1; // 1 means 1 parameter}AI写代码 4、适配之后的lua开发        有了api的适配,那么就可以用lua进行程序开发。一般就是exe+lua开发即可。exe是可执行文件,开发之后就不用管了。而lua是程序文件,可以拿过来开发各种业务,比如下面这个简单的lua流程,  -- file:   demo.lua-- author: feixiaoxing-- email:  feixiaoxing@163.com-- date:   2025-10-18 initgraph(640, 480)setfillcolor(0x0000AA)solidcircle(320,240, 100)image = getimage(220, 140, 200, 200)saveimage("./demo.bmp", image)rectangle(100, 100, 200, 50)line(100, 400, 500, 400) direction = 0offset = 0value = 97 -- 'a' BeginBatchDraw()while true doif 1 == _kbhit() thenvalue = _getch() -- update output contentend cleardevice()if offset > 100 then -- no more than 50direction = 1end if offset < 0 then -- no less than 0direction  = 0end if direction == 0 then -- update offset hereoffset = offset + 1elseoffset = offset - 1end putimage(220+offset, 140, image)rectangle(100+offset, 100, 200+offset, 50)line(100+offset, 400, 500+offset, 400)outtextxy(500+offset, 50, value-0x20)outtextxy(500+offset, 75, value-0x20+1)outtextxy(500+offset, 100, value-0x20+2) FlushBatchDraw()sleep(100)end EndBatchDraw()closegraph() AI写代码         实际运行的效果就是这样的,   5、lua开发的一个好处        对于一个公司或者工作室来说,不可能所有人都精通编程。这种情况下,势必会有一部分人需要去做业务开发。如果是业务开发,其实可以用c#、java、lua来做是比较合适的。这里面,lua又是最合适的。不仅适配api非常合适,可以做很多定制开发,而且对使用者的学历没有要求,基本上所见即所得。哪怕是没有基础的同学,也可以短时间内快速上手,这是最大的优势。         lua修改后,快速看到效果,不需要任何编译动作,这就是lua的魅力所在。 6、多线程的处理        lua本身不支持多线程,不过我们可以通过多thread、多vm虚拟机的方法来解决。 7、完整的适配代码        本身api的适配还是不太难的。主要的处理方法,就是创建虚拟机、加载库、加载我们适配的函数,最后运行lua文件即可。这里给出完整的api适配文件,希望对大家有所帮助。一般来说,上层的api并不是很多,一般适配几十个api,就可以让开发业务的同学开始干活了。 // add by feixiaoxing, 2025.10.18 #include <stdio.h>#include <conio.h>#include <graphics.h> #include "lua.hpp" static lua_State* L1 = NULL; // register init function static void initgraph_function(lua_State *L){int width = (int)luaL_checkinteger(L, 1);int height = (int)luaL_checkinteger(L, 2); ::initgraph(width, height);} static int getch_function(lua_State* L){lua_pushinteger(L, _getch()); return 1; // 1 means 1 parameter} static int kbhit_function(lua_State* L){lua_pushinteger(L, _kbhit()); return 1; // 1 means 1 parameter} static void setfillcolor_function(lua_State* L){int value = (int)luaL_checkinteger(L, 1); ::setfillcolor(value);} static void putpixel_function(lua_State* L){int x = (int)luaL_checkinteger(L, 1);int y = (int)luaL_checkinteger(L, 2);int value = (int)luaL_checkinteger(L, 3); ::putpixel(x, y, value);} static void closegraph_function(lua_State* L){::closegraph();} static void cleardevice_function(lua_State* L){::cleardevice();} static void circle_function(lua_State* L){int x = (int)luaL_checkinteger(L, 1);int y = (int)luaL_checkinteger(L, 2);int radius = (int)luaL_checkinteger(L, 3); ::circle(x, y, radius);} static void fillcircle_function(lua_State* L){int x = (int)luaL_checkinteger(L, 1);int y = (int)luaL_checkinteger(L, 2);int radius = (int)luaL_checkinteger(L, 3); ::fillcircle(x, y, radius);} static void solidcircle_function(lua_State* L){int x = (int)luaL_checkinteger(L, 1);int y = (int)luaL_checkinteger(L, 2);int radius = (int)luaL_checkinteger(L, 3); ::solidcircle(x, y, radius);} static void line_function(lua_State* L){int x1 = (int)luaL_checkinteger(L, 1);int y1 = (int)luaL_checkinteger(L, 2);int x2 = (int)luaL_checkinteger(L, 3);int y2 = (int)luaL_checkinteger(L, 4); ::line(x1, y1, x2, y2);} static void rectangle_function(lua_State* L){int x1 = (int)luaL_checkinteger(L, 1);int y1 = (int)luaL_checkinteger(L, 2);int x2 = (int)luaL_checkinteger(L, 3);int y2 = (int)luaL_checkinteger(L, 4); ::rectangle(x1, y1, x2, y2);} static void fillrectangle_function(lua_State* L){int x1 = (int)luaL_checkinteger(L, 1);int y1 = (int)luaL_checkinteger(L, 2);int x2 = (int)luaL_checkinteger(L, 3);int y2 = (int)luaL_checkinteger(L, 4); ::fillrectangle(x1, y1, x2, y2);} static void solidrectangle_function(lua_State* L){int x1 = (int)luaL_checkinteger(L, 1);int y1 = (int)luaL_checkinteger(L, 2);int x2 = (int)luaL_checkinteger(L, 3);int y2 = (int)luaL_checkinteger(L, 4); ::solidrectangle(x1, y1, x2, y2);} static void sleep_function(lua_State* L){int value = (int)luaL_checkinteger(L, 1);::Sleep(value);} static void outextxy_function(lua_State* L){int x = (int)luaL_checkinteger(L, 1);int y = (int)luaL_checkinteger(L, 2);int value = (int)luaL_checkinteger(L, 3); ::outtextxy(x, y, value);} static int inputbox_function(lua_State* L){char s[10] = {0};int speed = 0; InputBox((LPTSTR)s, 10, "Input speed:");speed = atoi(s); lua_pushinteger(L, (lua_Integer)(speed));return 1;} static int getimage_function(lua_State* L){int x = (int)luaL_checkinteger(L, 1);int y = (int)luaL_checkinteger(L, 2);int width = (int)luaL_checkinteger(L, 3);int height = (int)luaL_checkinteger(L, 4); IMAGE* p_image = new IMAGE;::getimage(p_image, x, y, width, height);lua_pushinteger(L, (lua_Integer)(p_image)); return 1; // 1 means 1 parameter} static void putimage_function(lua_State* L){int x = (int)luaL_checkinteger(L, 1);int y = (int)luaL_checkinteger(L, 2);IMAGE* p_image = (IMAGE*)luaL_checkinteger(L, 3); ::putimage(x, y, p_image);} static void saveimage_function(lua_State* L){const char *path = lua_tostring(L, 1);IMAGE* image = (IMAGE*)luaL_checkinteger(L, 2); ::saveimage((LPCTSTR)path, image); // need using multi-bytes encode format} static void rotateimage_function(lua_State* L){IMAGE* dst_image = (IMAGE*)luaL_checkinteger(L, 1);IMAGE* src_image = (IMAGE*)luaL_checkinteger(L, 2);int angle = (int)luaL_checkinteger(L, 3); ::rotateimage(dst_image, src_image, angle/360.0 * 6.28);} static void BeginBatchDraw_function(lua_State* L){::BeginBatchDraw();} static void FlushBatchDraw_function(lua_State* L){::FlushBatchDraw();} static void EndBatchDraw_function(lua_State* L){::EndBatchDraw();} // c read data from lua static int read_lua_value(lua_State* L){if(NULL == L){return -1;} lua_getglobal(L, "max_players"); // this step is very important, 2025.10.19    return (int)luaL_checkinteger(L, -1);} // launch script and execute script here static void* launch_script(void* p_file){int status = 0; // create a virtual machine L1 = luaL_newstate();if (NULL == L1){goto exit;} // register open function here luaL_openlibs(L1); // basic operation lua_register(L1,  "initgraph",       (lua_CFunction)&initgraph_function);lua_register(L1,  "_getch",          (lua_CFunction)&getch_function);lua_register(L1,  "_kbhit",          (lua_CFunction)&kbhit_function);lua_register(L1,  "setfillcolor",    (lua_CFunction)&setfillcolor_function);lua_register(L1,  "putpixel",        (lua_CFunction)&putpixel_function);lua_register(L1,  "circle",          (lua_CFunction)&circle_function);lua_register(L1,  "fillcircle",      (lua_CFunction)&fillcircle_function);lua_register(L1,  "solidcircle",     (lua_CFunction)&solidcircle_function);lua_register(L1,  "line",            (lua_CFunction)&line_function);lua_register(L1,  "rectangle",       (lua_CFunction)&rectangle_function);lua_register(L1,  "fillrectangle",   (lua_CFunction)&fillrectangle_function);lua_register(L1,  "solidrectangle",  (lua_CFunction)&solidrectangle_function);lua_register(L1,  "closegraph",      (lua_CFunction)&closegraph_function);lua_register(L1,  "cleardevice",     (lua_CFunction)&cleardevice_function);lua_register(L1,  "sleep",           (lua_CFunction)&sleep_function);lua_register(L1,  "outtextxy",       (lua_CFunction)&outextxy_function);lua_register(L1,  "inputbox",        (lua_CFunction)&inputbox_function); // about image operation lua_register(L1,  "getimage",        (lua_CFunction)&getimage_function);lua_register(L1,  "putimage",        (lua_CFunction)&putimage_function);lua_register(L1,  "saveimage",       (lua_CFunction)&saveimage_function); //need multi-bytes setting in vs2017lua_register(L1,  "rotateimage",     (lua_CFunction)&rotateimage_function); // about buffer acceleration lua_register(L1,  "BeginBatchDraw",  (lua_CFunction)&BeginBatchDraw_function);lua_register(L1,  "FlushBatchDraw",  (lua_CFunction)&FlushBatchDraw_function);lua_register(L1,  "EndBatchDraw",    (lua_CFunction)&EndBatchDraw_function); // c call lua, if lua needs invoke c, just using lua_pcall & lua_getglobal status = luaL_dofile(L1, (const char*)p_file); // open and execute fileif (status != LUA_OK){printf("failed to open lua file\n");lua_close(L1); goto exit;} // close virtual machine lua_close(L1); exit:return NULL;} int main(int argc, char* argv[]){launch_script((void*)"./demo.lua");return 0;}AI写代码 ————————————————                             版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                        原文链接:https://blog.csdn.net/feixiaoxing/article/details/153558793
  • [技术干货] 【华为开发者布道师】鸿蒙操作系统的图形子系统原理解析——东北大学 张昊阳
    参考书籍资料:《鸿蒙操作系统设计原理与架构》 (李毅,任革林)布道活动地点:东北大学(浑南校区)
  • [热门活动] “AIGC赋能数字内容生产”——用技术提升内容供给侧的丰富度,助力企业降本增效
    HCDG(Huawei Cloud Developer Group 华为云开发者社区组织),是基于城市圈和技术圈,由开发者核心组自发开展的开放、创新、多元的社区技术交流组织。致力于帮助开发者学习提升、互动交流、挖掘合作,推动技术应用与本地产业结合、数智化转型和开发者文化发展。2023年10月27日,由华为云开发者联盟携手华为(北京)虚拟现实创新中心和HCDG北京核心组共同举办的“AIGC赋能数字内容生产”交流会在中关村虚拟现实产业园顺利举办。本次活动邀请到了虚拟现实领域的业界大咖、行业技术专家,以及众多优秀的企业家和开发者一起,共同探讨在AIGC兴起的大背景下,数字内容产业的发展趋势,了解市场的前沿信息,解析行业技术解决方案,共享蓬勃发展的产业生态。▲ 华为(北京)虚拟现实创新中心 张浩华为(北京)虚拟现实创新中心经理张浩介绍到,创新中心成立于2021年年底,由石景山政府与华为组建团队联合运营,依托华为在技术、品牌、产业链资源、人才培养方面的优势,创新中心一是通过联合企业打造标杆项目,推进创新应用场景落地。二是持续开放ICT人才培养能力,协同高校开展专业人才培养工作。三是定期举办峰会论坛等大型活动,构建产业特色品牌。四是联合区内外的虚拟现实企业打造生态聚合体,助力区域的虚拟现实产业发展。重塑内容生产力探索行业前沿技术▲ 华为云媒体服务产品部专家 王菊生本次活动有幸邀请到了华为云媒体服务产品部专家王菊生,他介绍到AIGC被认为是继专业生成内容(PGC)和用户生成内容(UGC)之后,利用人工智能技术自动生成内容的新型生产方式。依托华为云基础设施,通过华为云提供两大类媒体服务,赋能内容开发者和应用开发者。通过AIGC生成视频、生成数字人、数字空间,分钟级快速生成内容,为整个内容生产带来新的变革。其中,AIGC生成视频,通过CG和CV技术的融合,通过CV识别人的动作,驱动CG生成的3D模型,并自动适配周围环境和光线等,通过AIGC生成新的视频。AIGC还可以生成数字空间,基于Nerf的数字空间重建技术,对于100平米空间,1个手机10分钟就可以完成扫描,用普通的电脑和消费级显卡就能实时浏览,还能二次编辑。可以广泛应用于企业展厅、工厂生产车间、博物馆、室外旅游景点等场景,实现物理世界的数字孪生。华为云希望通过打造两个核心产品:华为云MetaEngine图形引擎和盘古数字大模型,以及Studio一站式制作、协同桌面、云渲染、 AIGC等一系列的服务,与伙伴和客户一起共建AIGC时代的媒体基础设施。“一体三核”推动AI大模型与空间计算应用加速落地▲ 北京虚拟动点科技有限公司 贾国耀本次活动特别邀请到企业代表——北京虚拟动点科技有限公司动作捕捉技术主管贾国耀,他表示,虚拟动点作为利亚德集团旗下元宇宙板块领军企业,为元宇宙行业的发展提供了技术驱动作用。虚拟动点以AI赋能,自2017年起,在“空间定位”“空间计算”领域已经深耕多年,沉淀了包括光学计算、惯性计算、无标记点识别计算等多项空间计算的算法技术,实现了包括肢体、手势、表情等多种人体空间计算场景应用,以及体积面积、位置关系、形态大小等物体空间计算场景应用。未来虚拟动点将以“一体三核”作为发展战略,即以空间计算为战略主体,以AI大模型、硬件设备、空间数据为三大核心,构建不断提升的业务能力,推动虚拟动点在AI时代阔步前行。感知虚拟制作推动影视作品完成一站式输出▲ 北京天图万境科技有限公司 图拉古本次活动特别邀请到HCDE专家、企业代表——北京天图万境科技有限公司创始人、导演图拉古亲临现场,他以AI感知虚拟制作为主题进行了分享。他介绍,天图万境发布的感知虚拟制作,是通过人工智能进行空间计算和数字修复,将绿幕拍摄后的内容导入引擎中,通过引擎的空间计算,把绿幕拍摄的二维影像还原成三维图像,并通过数字渲染将三维图像渲染出来。在传统方式中,空间深度的计算,可以使用激光雷达,或用双目摄影机,甚至可以使用连续的单目帧进行后期求解。而天图万境的感知虚拟制作,实现了仅通过一台摄影机,即可在引擎中实现“实时、4K、60帧稳定的空间深度还原”,无需购买任何的深度传感器。这一过程对应的是从物理世界到数字世界的变迁,可以对标胶片到数码时代的跨越。最后环节,华为云专家、HCDG核心组与现场企业开发者们围绕AIGC的技术应用进行热情讨论。现场针对企业及开发者提出的在不同业务场景下面临的创新技术应用困境,华为云专家分别从技术和商业的创新视角进行深入分析与解答。本次活动也安排了中关村虚拟现实展示中心参观体验环节,带领企业家与开发者们体验了石景山区众多虚拟现实企业的产品及解决方案。参会的专家、企业代表们与开发者们结合产业发展现状及未来趋势各抒己见、积极分享观点,为未来共同打造密切合作的产业生态氛围奠定良好基础,携手共创智能化数字内容生产新时代!HCDG城市行活动接下来将在佛山、成都、厦门、大连、天津等开发者核心城市相继落地。华为云也将继续携手各城市HCDG核心组与广大企业及开发者,共建产业新生态,为企业及开发者提供“新技术、新体验、新机会”全方位支撑,欢迎加入HCDG(华为云开发者社区组织)。了解更多本地圈层资讯:微信公众号搜索“华为云开发者联盟”,回复“北京HCDG”