基于three.js的牛逼轰轰的3D编辑器nunuStudio!

8,612 阅读6分钟

这是一款基于Three.js的3D编辑器,我之前一直喊错,叫人家"牛牛",因为我觉得它真的好牛,其实人家正确拼音喊“努努”!

官网地址:https://www.nunustudio.org/,里面内容很详细。我也是刚用不久,在这里写个入门教程。 推荐下载他的electron的版本的安装包,本地运行快点!并且可以发布web的运行包,直接可以网页端二次开发,真的不要太方便了!

1.布局介绍

image.png 配置一下场景,然后点击上方的run就可以看到预览界面效果了

2.工具栏介绍

选中对象,当然也可以直接在右侧的对象列表里点击选中

image.png

移动对象,对应position属性x,y,z,也可以在配置板面直接修改对应数值

image.png

缩放对象,对应scale属性x,y,z,也可以在配置板面直接修改对应数值

image.png

旋转对象,对应rotation属性的x,y,z,也可以在配置板面直接修改对应数值

image.png

其实我感觉这个操作对象,分开一个个点有点鸡肋,很不习惯!

1.2 对象栏

添加形状物体,不同物体的配置板面属性有所不同

image.png

添加字体

image.png

添加光源,常用的环境光,平行光,点光源等都有,像点光源之类的默认是勾选了产生投影的

image.png

添加镜头,有透视镜头和正交镜头,添加后可以看到镜头视角的预览画面,如果需要把这个镜头的画面作为运行画面,需要勾选use camera

image.png

添加控制器,camera记得要勾选use camera

image.png

3.添加脚本(运行时可用,编辑时效果不可见)

可用参数

● self 对象属性 (position, rotation, scale, children等).

● program 当前程序包含当前场景scene,里面的资源resources,可通过(getMaterialByName, getTextureByName等方法获取)

● scene运行场景.

● Keyboard 键盘输入

● Mouse鼠标操作

函数

● initialize在加载场景时调用,它通常用于以编程方式创建新对象、从场景中获取对象、初始化变量等.

● update在将场景绘制到屏幕之前调用每个帧,它可以用于控制对象、获取输入值、更改对象属性等.

● dispose当程序关闭时调用,应用于清理对象、断开与数据流的连接等.

● onMouseOver(objects)当鼠标位于脚本对象的子对象上时调用,可以与鼠标函数组合检查对象是否被单击。接收到对象数组作为参数.

● onResize(x, y) 每次调整程序窗口的大小时调用.

● onAppData(data) 用于从主机页接收数据,数据作为参数接收,用于消息传递

1.操作对象,绕脚本中心点旋转

function initialize()
{

self.position.x += 2;

}
function update()
{

self.rotation.y += 0.01;

}

image.png

image.png

2.鼠标键盘控制

function update()
{
	//鼠标控制旋转
self.rotation.y += Mouse.delta.x * 0.01;

	//键盘控制移动
if(Keyboard.keyPressed(Keyboard.LEFT))
{
	self.position.x -= 0.1;
}
if(Keyboard.keyPressed(Keyboard.RIGHT))
{
	self.position.x += 0.1;
}
}

3.获取场景中对象

var object;
var count=0
function initialize()
{
object = scene.getObjectByName("box");
	object.position.y=2;
}

function update()
{
//使用该材质的对象随机变色
	if(count%60==0){
	
object.material.color=new THREE.Color(`rgb(${parseInt(Math.random()*200)},${parseInt(Math.random()*200)},${parseInt(Math.random()*200)})`);
	}
	count++;
	}

4.获取子对象

image.png

var object1,object2;

function initialize()

{

object1 = self.children[0];//cylinder
object2 = self.children[1];//box

}

function update()

{

object1.rotation.x += 0.01;
	object2.rotation.y += 0.01;

}

5.右击顶层program,新增一个scene场景,双击那个场景进行编辑,可通过脚本切换场景

image.png

//在场景1添加脚本
function update()
{
	//按w切换到场景2
if(Keyboard.keyPressed(Keyboard.W))
{//获取程序里的场景2
	program.setScene("scene2");
}
}

6.添加html,注意这个不是CSS2DRender,不会跟着场景元素进行变换

image.png

var element = document.createElement("div");

element.style.width = "100px";

element.style.height = "100px";

element.style.position = "absolute";

element.style.top = "100px";
element.style.left = "100px";

element.style.backgroundColor = "#0000FF";

function initialize()

{
//添加html
program.division.appendChild(element);

}

function dispose()

{
//销毁时记得删除对应元素
program.division.removeChild(element);

}

7.引入外部脚本,先把js导入项目,再用include来引入库,进行使用

image.png

include("moment.js");
 var text;
function initialize()
{
text=program.getObjectByName('text');

console.log(moment)
}
function update(){
text.setText(moment().format('LTS'));
}

image.png

网页端和electron有所不同,网页端应该是最新的,有些奇怪的bug已经修复,比如在electron里面是脚本一旦运行错误,你就得重新打开nunuStudio才能再run,网页端就不会这样禁止运行

3.材质与贴图

可以创建需要的材质和贴图,双击对应的资源就可以进入配置界面

image.png image.png

image.png

贴图可以拖入材质的对应texture属性里面,也可以直接上传图片作为新的贴图

image.png

材质可以通过拖拽赋值到对应的对象里面,鼠标悬浮对应材质,对应材质会变色,就知道这个对象用什么材质了。

image.png

这个材质和贴图管理有点麻烦,添加贴图和材质莫名生成一堆重复的,一旦材质和贴图资源太多,就会导致很难辨别对象使用的是谁,而且你删除对象的时候,要是对应材质和贴图没有引用,它也不会主动删除,还是会留在资源库里,得手动删除。

4.raycaster拾取对象

先添加一个camera镜头作为射线拾取的视图,然后添加模型,根据点击设置对应的材质

var red, blue;

function initialize()
{
	red = program.getMaterialByName("red");
	blue = program.getMaterialByName("blue");
}

function update()
{
	 //获取射线拾取到的对象
	var intersects = scene.raycaster.intersectObjects(scene.children);

	 
	for(var i = 0; i < intersects.length; i++)
	{//左点击设置为red的材质
		if(Mouse.buttonPressed(Mouse.LEFT))
		{
			intersects[i].object.material = red;
		}
		//右点击设置为blue的材质
		else if(Mouse.buttonPressed(Mouse.RIGHT))
		{
			intersects[i].object.material = blue;
		}
	}
}

image.png

5.发布web网页和导出模型

调整好在nunuStudio的效果,可以导出一个web网页,就是对应的预览界面了

image.png

image.png

index.html里面的代码里有Nunu加载的代码,可进行二次开发,里面是直接可以用THREE的,但是控制器推荐用nunu的,估计它里面用改写过controls,自己添加的THREE的control会报错。里面有些bug,比如飞线之类的动态效果,在导出的预览文件无法复现效果,而在编辑器可以正常运行。我也不知道为什么!

var app;

			//Initialize app
			function run()
			{	
				//Create app object
				app = new NunuApp();

				//Onload enable the vr and fullscreen buttons
				var logo = document.getElementById("logo");
                                //加载成功后
				var onLoad = function()
				{
					var button = document.getElementById("fullscreen");
					button.style.visibility = "visible";

					//Check if VR is available
					if(app.vrAvailable())
					{
						//If there are displays available add button
						Nunu.getVRDisplays(function(display)
						{
							button = document.getElementById("vr");
							button.style.visibility = "visible";
						});
					}

					//Remove logo and loading bar
					document.body.removeChild(logo);
				};

				//下载进度条
				var bar = document.getElementById("bar");
				var onProgress = function(event)
				{
					if(event.lengthComputable)
					{
					 var progress = event.loaded / event.total * 100;
						bar.style.width = progress + "%";
					}
				};

				//Load and run nunu app
				app.loadRunProgram("app.nsp", onLoad, onProgress);
			}

			//window resize时调整大小
			function resize()
			{
				app.resize();
			}

			//全屏
			function toggleFullscreen()
			{
				app.toggleFullscreen(document.body);
			}

			//Toggle VR mode
			function toggleVR()
			{
				app.toggleVR();
			}

调好效果可以导出模型,这个也有点bug,有些格式不能完全导出所有的对象和效果,gltf格式,比如hemisphere光源没跟着导出来,材质里面的emissveMap,alphaMap也没跟着导出来,因此导入模型后要手动代码搞一下。

image.png

总结

20230426_160803.gif

我这里介绍的都是常用的,官网上还有许多优秀的案例,以及功能介绍,什么动画啊,物理啊,粒子,后期效果啊!还能做游戏!真的太多了!你可以在上面搞得差不多再导出来二次开发,省了很多效果设置的功夫! 总体来说,虽然有些bug和操作的鸡肋,但是真的是很优秀的一款3D编辑器!期待会越来越好!

我基于官方的nunuStudio的github地址fork了一下,根据自己和建模师的使用习惯,修改了一些操作

代码地址:https://github.com/xiaolidan00/nunuStudio/tree/dev-0.9.6