通过MaxScript烘焙顶点色

大家好,我是阿赵。之前写了一些MaxScript的学习笔记。但那些内容比较的抽象,这次写一个应用的例子。

一、为什么要烘焙顶点色

在做很多模型效果的时候,我们首先想到的是贴图,我们可以通过对模型展UV,然后绘制贴图,达到对模型颜色的修改。
如果模型需要多张不同效果的颜色贴图作为遮罩,我们一般是通过添加多张贴图,或者通过一张贴图的rgba四个通道的颜色去制作素材。
这里有一个问题,如果使用贴图作为颜色的拾取对象,那么他一般是会在片元着色器去进行,他的计算量会比较大,可以理解成是逐个像素去采样。然后,我们使用贴图去存储颜色,实际上一张512512的贴图,也会产生26万个像素颜色,每个像素记录一个rgb颜色。
刚才举例一张512
512的贴图需要记录26万多个颜色,而且其实512级别的贴图并不算很精细,美术经常会用到1024或者2048级别的贴图,那样存储的颜色数量更是多到惊人。
假如需要计算的效果精度并不需要这么高,我们其实可以使用顶点色去记录需要的颜色。如果我们使用顶点颜色去记录,一个几万顶点的模型,已经算是面数比较高了,也就只需要记录几万个颜色值而已,然后rgba通道,可以分别记录不同的遮罩信息,对于制作游戏的人来说,这里可以省不少的资源。而且顶点色的计算,可以在顶点着色器进行,这样无需在片元里面逐像素去计算,这个计算量又会减少了很多。
3DsMax本身是提供了刷顶点颜色的工具,但对于一个模型来说,逐个顶点去指定颜色,会比较的麻烦。如果用脚本去指定顶点颜色,将会比较的方便。

二、根据顶点高度坐标烘焙顶点色渐变

首先,我们要看到顶点颜色,需要在设置里面把显示顶点颜色打开

在这里插入图片描述
在这里插入图片描述

这里写一个很简单的例子,可能没太大的实用性,但可以简单的知道,怎样去根据模型的数据来指定顶点颜色。
这个例子的目的是,根据模型的每个顶点的坐标,找出最高点的坐标和最低点的坐标,然后以最低点为0,最高点为255,设置顶点颜色的R通道,代码如下:

function GetVertFun = 
(
	local selObjs = getcurrentSelection()
	if (selObjs == undefined )or(selObjs.count == 0) then
	(
		print "no obj"

	)
	else
	(
		local selObj = selObjs[1]
		local vertNum = selObj.numverts
		print vertNum
		local minH = 1000000
		local maxH = -100000
		for i in 1 to vertNum do
		(
			local tempPos = getVert selObj.mesh i;
			if tempPos.z>maxH then
				maxH = tempPos.z
			if tempPos.z < minH then
				minH = tempPos.z			
		)
		local offsetH = maxH - minH;
		for i in 1 to vertNum do
		(
			local tempPos = getVert selObj.mesh i;
			local hVal = (tempPos.z-minH) / offsetH*255
			local tempCol = color hval 0 0
			polyop.setVertColor selObj 0 #(i) tempCol
		)
	)
)
rollout testUIWin "Untitled" width:400 height:300
(
	button 'btn1' "Button" pos:[131,123] width:99 height:31 align:#left
	on btn1 pressed do
	(
		GetVertFun()
	)
)
createDialog testUIWin

运行后,选择一个物体,然后点击按钮,会看到模型变成这样:
在这里插入图片描述

为什么我只指定红色通道呢?这是因为,一般这种顶点色,我们都是用来做遮罩用的,所以只需要一个值就够了,其他的bga3个通道,可以根据我们的需要,继续存储其他类型的遮罩数据。
把这个模型导出fbx,然后放到Unity里面。为了简单显示,我就不写shader代码了,直接用ASE连线,获取了模型顶点色的r通道作为漫反射颜色,可以看到,在这个Unlit不受光照的shader效果下,我们的模型产生了很好看的渐变。而且这种渐变不需要采样贴图,也不需要展UV,完全是根据物体的形状(顶点位置)就能显示的。
在这里插入图片描述

刚才说这个例子实用性不大,是因为我们在写Shader的时候,也完全可以直接获取到顶点坐标来实现这个效果。接下来的例子,就会稍微有实用性了。

三、把物体的AO贴图烘焙到顶点色

这里首先要知道,AO是什么。
环境光遮蔽(Ambient Occlusion)“AO”为Ambient Occlusion的缩写。
说得简单一点,就是模拟模型在只受到环境光的情况下的阴影情况。
下面先来给一个模型做一个AO的效果,比如创建一个茶壶,给他一个纯白的材质球,然后打开渲染设置,给一个光线追踪,或者你本地有其他高级渲染器,也可以,反正打开光线最终。然后在创建里面创建一个天光(sky light)。
在这里插入图片描述
在这里插入图片描述

渲染一下,可以看到模型有一个淡淡的阴影,这个阴影并没有方向性,只是在附近有其他面遮挡的情况下,会有个影子的轮廓。

接下来,要把这个AO烘焙到贴图上。首先要对模型展UV,我就比较随便的展一下了。

在这里插入图片描述
在这里插入图片描述

然后按0快捷键,进入到烘焙贴图界面,添加一张完整贴图,分辨率选择512*512,然后烘焙。
在这里插入图片描述

这样,就可以把AO烘焙到一张图片上面。
在这里插入图片描述

接下来,我把这张贴图赋予给刚才茶壶的材质球,取消高级渲染器的光线最终,再次渲染,发现现在就算没有了高级灯光的光线追踪,模型上面也显示出刚才的影子了。
最后,我写一个脚本,根据模型顶点的UV坐标,来采样这张AO贴图,并把采样到的颜色赋予给顶点色的R通道。

function GetVertAOFun = 
(
	local selObjs = getcurrentSelection()
	if (selObjs == undefined )or(selObjs.count == 0) then
	(
		print "no obj"

	)
	else
	(
		local selObj = selObjs[1]
		print selObj.material
		local tex = selObj.material.diffusemap.bitmap
		local w = tex.width
		local h = tex.height
		local faceNum = getnumfaces selObj.mesh
		local colDict = #()
		for i in 1 to faceNum do
		(
			local posVertArr = getface selObj.mesh i
			local uvVertArr = gettvface selObj.mesh i
			for j in 1 to posVertArr.count do
			(
				local uvPos = getTVert selObj.mesh uvVertArr[j]
				local tx = uvPos.x*w
				local ty = (1-uvPos.y)*h
				local tempCol = getpixels tex [tx,ty] 1
				local vertInd = posVertArr[j]
				if colDict[vertInd] == undefined then
				(
					colDict[vertInd] = #()
				)
				append colDict[vertInd] tempCol[1]				
			)
		)
		for i in 1 to colDict.count do
		(
			if colDict[i] != undefined then
			(
				local tr = 0
				local tg = 0
				local tb = 0
				local tCount = colDict[i].count
				for j in 1 to tCount do
				(					
						tr =tr+colDict[i][j].r
						tg = tg+colDict[i][j].g
						tb =tb+colDict[i][j].b
				)
				local tempCol = color ((tr+tg+tb)/3/tCount) 0 0 
				polyop.setVertColor selObj 0 #(i) tempCol
			)
				
		)
	)
)

rollout testUIWin "Untitled" width:400 height:300
(
	button 'btn1' "Button" pos:[131,123] width:99 height:31 align:#left
	on btn1 pressed do
	(
		GetVertAOFun()
	)
)
createDialog testUIWin

在这里插入图片描述

这个时候,显示顶点色的茶壶,会变成这个样子。
把这个茶壶导出fbx,并放入unity里面。
在这里插入图片描述

可以看到,还是刚才那个只用顶点色的r通道的shader,会看到两个茶壶呈现出完全不一样的效果。

四、总结

这里需要说明几点:
1、由于一个顶点在展UV之后,会变成多个点,对应多个UV坐标,所以我上面的脚本里面,是通过面来取顶点,并把属于面的顶点的UV点索引映射回坐标顶点的index,并把同一个坐标点的多个UV点采样到的颜色值取平均值
2、会看到采样的结果,有些地方黑得不是很自然,那是因为我的茶壶面数很少,导致了采样间隔很大,有些接缝的地方,是由于我展UV展得比较的粗糙,所以在颜色取平均值的时候,会产生比较大的误差。这些细节都需要在面数和UV上面去解决。由于我这里只是简单做个例子,就没有花很多时间去做了。
3、由于是为了些例子,所以没有考虑很多兼容性的问题,比如我里面用到了polyop的方法,那么对应的模型应该是需要塌陷成可编辑多面体的,比如如果获取到的材质球不是standard,可能在获取diffusemap的时候会报错,等等。其实我只是为了说明一个原理,所以这些细节可以自己去修改,比如加上模型是否editablePoly的判断,比如判断材质球类型来获取贴图,等等。

上面也说过了,顶点色很多时候是作为遮罩来使用,比如可以通过指定模型某些部位的顶点色,控制模型的阴影深度、高光强度和范围、渐变过渡、法线强弱等等。由于这些顶点色是不依赖于UV的,所以在同一个模型换不同贴图的时候,这些遮罩依然会生效。
至于其他方面的用途,各位也可以发挥想象力去思考一下。

猜你喜欢

转载自blog.csdn.net/liweizhao/article/details/129373139
今日推荐