使用AE插件Pixels World 3載入一個不含材質(zhì)的obj模型

Pixels World 3是中梓星音大佬(https://space.bilibili.com/564908)開發(fā)的插件,通過敲代碼實現(xiàn)一些有趣的效果,給程序員邁向AE動態(tài)設計開辟了一條新的道路,希望大家多多支持
最近星音大大關于PW3的演示視頻很有意思,展示了一些PW3的新功能,大家可以看看
https://www.bilibili.com/video/BV1Sh411R7zK?
下面進入正題
警告——
在操作步驟中可能包含以下內(nèi)容:



關于obj
在敲代碼之前,我們先來了解一下obj文件
Obj文件可以使用記事本方式打開
在C4D中創(chuàng)建的正方體導出的obj文件打開后內(nèi)容如下:

其中以v、vn、vt、f開頭的行是我們主要要關注的部分。
v表示的是一個頂點位置信息,v后面的三個參數(shù)依次為頂點的x、y、z坐標位置。
vn表示的是一個頂點法向量,其后三個參數(shù)依次為頂點法向量的x、y、z坐標。
vt表示的是一個紋理坐標,其意義在于繪制模型的三角面片時,每個頂點取像素點時對應的紋理圖片上的坐標。
f表示的是一個面片(一般為三角形面),格式一般為f v/vt/vn v/vt/vn v/vt/vn(f 頂點索引 / 紋理坐標索引 / 頂點法向量索引)。有的f后面跟有四組參數(shù),這是四邊形面的面片表示。
以上參考了:
https://blog.csdn.net/xyh930929/article/details/82260581
https://www.cnblogs.com/Anzhongliu/p/6092048.html

?
關于代碼
因為我還不知道怎么導入紋理材質(zhì),所以只能想辦法把不帶材質(zhì)的obj模型載入PW3。
另外我因為沒有專門學習過Lua語言,所寫代碼的依據(jù)都來自于星音大大制作的PW3網(wǎng)上文檔(https://milai.tech/products/PixelsWorld/docs_CN/)以及網(wǎng)絡上零零散散的相關博客,所以代碼沒能寫的太好,執(zhí)行效率也很低下,權當做拋磚引玉吧。
在忽略了模型的材質(zhì)之后,我們只需要關注的是obj文件里面的以v(頂點)和f(面片)開頭的行(而在PW3的網(wǎng)上文檔里缺少(或者是我沒看懂)關于頂點法向量的描述,因此以vn開頭的行也被我忽略了),而以f開頭的行中也只需要關注表示頂點索引的參數(shù)(它們將表示面片的繪制順序,在PW3的網(wǎng)上文檔里關于poly函數(shù)的解釋里有關于面片繪制順序的說明)。
簡言之,就是要取以v開頭的行的后三個參數(shù),和以f開頭的行的三組(或者四組)參數(shù)中的每組第一個參數(shù)。
代碼如下:
?
--輸入的obj文件路徑
path = utf8ToLocal(projectFolder)..[[cube.obj]]
?
-- 分割字符串
-- 參考:https://blog.csdn.net/forestsenlin/article/details/50590577
function splitStr( str,reps )
????local splitList = {};
????string.gsub(str,'[^'..reps..']+',function(w)
????????table.insert(splitList,w)
????end)
????return splitList
end
?
vertexArray = {}
faceArray = {}
?
-- 讀入文件
local function readOBJ(fileName)
????local f = assert(io.open(fileName,'r'))
????local line = f:read('*line')
????while line do
????????-- println('line content:'..line)
????????if string.sub(line,1,2)=="v "then -- 頂點 vertex
????????????local tmp = splitStr(line,' ')
????????????local p = {-tonumber(tmp[2]),-tonumber(tmp[3]),-tonumber(tmp[4])} -- x,y,z坐標要反向,然而道理我也不明白
????????????table.insert(vertexArray,p)
????????end
????????????if string.sub(line,1,2)=="f "then -- 面 face
????????????????local tmp = splitStr(line,' ')
????????????????local f1 = splitStr(tmp[2],'/')[1]
????????????????local f2 = splitStr(tmp[3],'/')[1]
????????????????local f3 = splitStr(tmp[4],'/')[1]
????????????????local f = {}
????????????????if #tmp==5 then -- 四邊面的情況
????????????????????local f4 = splitStr(tmp[5],'/')[1]
????????????????????f = {tonumber(f1),tonumber(f2),tonumber(f3),tonumber(f4)}
????????????????elseif #tmp==4 then -- 三邊面的情況
????????????????????f = {tonumber(f1),tonumber(f2),tonumber(f3),tonumber(f1)}
????????????????end
????????????????table.insert(faceArray,f)
????????????end
????????????line = f:read('*line')
????????end
????f:close()
end
?
readOBJ(path)
?
根據(jù)PW3網(wǎng)上文檔中poly函數(shù)的描述,其繪制的obj格式應當如下:
?


?
為了滿足這樣的格式,對于含有任意數(shù)量頂點和任意數(shù)量面片繪制順序的obj模型文件,要一行行的將每個頂點位置和每個面片繪制順序手敲下來顯得非常不現(xiàn)實。
我想到的解決辦法是使用Lua語言創(chuàng)建一個具有這種格式的txt文件,然后再讀入這個文件執(zhí)行即可。(這個文件是不會自己刪掉的,但是內(nèi)容會被覆蓋,有興趣的可以打開看看)
創(chuàng)建文件的代碼如下:
?
--中間文件(生成的obj代碼塊)存儲路徑
pathOBJ = utf8ToLocal(projectFolder)..[[objCode.txt]]
?
-- 寫入
local function writeFile(fileName,content)
????local f = assert(io.open(fileName,'w')) -- 如果文件不存在則創(chuàng)建,文件若存在則被覆蓋
????f:write(content)
????f:close()
end
?
-- 追加
local function appendFile(fileName,content)
????local f = assert(io.open(fileName,'a'))
????f:write(content)
????f:close()
end
?
writeFile(pathOBJ,[[
version3()
dim3()
-- normal()
anime()
getLight()
?
mainColor = {0.2,0.4,0.6}
obj = {
????point = {]])
?
-- 輸出頂點信息
contentForVertex = ""
for i=1,#vertexArray do
????contentForVertex = contentForVertex.."{p=vertexArray["..i.."]},\n"
end
appendFile(pathOBJ,contentForVertex)
?
appendFile(pathOBJ,[[},
????prim = {]])
-- 輸出面信息
contentForFace = ""
for i=1,#faceArray do
????contentForFace = contentForFace..[[{type="triangler",]].."pref=faceArray["..i.."],color=mainColor},\n"
end
appendFile(pathOBJ,contentForFace)
?
appendFile(pathOBJ,[[},
????-- my_tex = INPUT
}
poly(obj)
]])
?
對于cube.obj,生成的中間文件如下:
?

?
最后只需要將這個文件(objCode.txt)讀入并執(zhí)行就好了:
?
runFile(localToUtf8(pathOBJ))
?
效果展示
我導入的一些obj模型:
?

(上面這個模型有1w+個頂點,已經(jīng)很卡了,攝像機動不了了)
(這個Miku的模型我忘記是哪里來的了,如果有侵權的嫌疑,我會刪除的)
?
?

(上面這個模型有接近3000個頂點,可以調(diào)整攝像機角度來看看,基本不卡)
?
?

(上面這個模型只有8個頂點,因此就非常流暢了,喜歡麻煩的讀者都建議這樣來創(chuàng)建cube,而不要使用PW3的cube函數(shù))
?
在將完整的代碼羅列出來之前,這里有一些要提醒讀者(程序員)的事情——
代碼文件的后綴為code,是我瞎寫的,其實就是一個txt文件,你可以用記事本或者別的文本文件編輯軟件打開;
默認所有的文件路徑都是在AE工程文件即.aep文件的同級目錄,也就是說請將要導入的obj文件以及這個代碼文件都放在AE工程文件的旁邊。如果想要修改路徑還請參考PW3的網(wǎng)上文檔(https://milai.tech/products/PixelsWorld/docs_CN/);
頂點面片多了就很卡很卡很卡,選擇導入obj模型時需謹慎;
有的obj導入了但是看不見,可能是太小了,這邊建議攝像機推近一點,如果因為很卡導致攝像機推不近,建議用三維軟件載入obj模型然后導出的時候放大點(如果你使用的是C4D,建議導出的大小和C4D自帶的人偶大小相當)(所以為什么不直接用三維軟件呢???);
因為代碼里面是開了anime渲染模式的,所以需要在合成里創(chuàng)建燈光,才能看得見你的模型,詳情參見PW3網(wǎng)上文檔關于anime渲染的描述;
我用C4D導出obj的時候?qū)С鲈O置里面會默認勾選反轉(zhuǎn)Z軸,但是這樣導出的obj在導入PW3之后是左右翻轉(zhuǎn)了的,所以建議導出的時候取消勾選反轉(zhuǎn)Z軸。我還不知道其他的三維軟件導出obj的時候會出現(xiàn)什么問題;
建議在使用前清理緩存,更換obj模型導入后可能短時間無法刷新,請耐性等待,如果還是不成,建議重置插件,如果還是不行,建議重啟AE,如果還是不行,建議……
如果不清楚怎么修改代碼中的一些參數(shù),建議參閱PW3網(wǎng)上文檔(https://milai.tech/products/PixelsWorld/docs_CN/);
在PW3插件編輯面板的代碼欄里,請寫上(內(nèi)容用黃色標出來了,其中test.code就是代碼文件的名字了):

本來是抱著一種嘗試和娛樂的心態(tài)來敲代碼的,因此也許一點都不專業(yè),不要太較真啊。當然有什么好玩的主意歡迎留言。
?
完整代碼
?
version3()
-- background(0.8)
move(width/2,height/2)
?
--輸入的obj文件路徑
path = utf8ToLocal(projectFolder)..[[cube.obj]]
--中間文件(生成的obj代碼塊)存儲路徑
pathOBJ = utf8ToLocal(projectFolder)..[[objCode.txt]]
?
-- 分割字符串
-- 參考:https://blog.csdn.net/forestsenlin/article/details/50590577
function splitStr( str,reps )
????local splitList = {};
????string.gsub(str,'[^'..reps..']+',function(w)
????????table.insert(splitList,w)
????end)
????return splitList
end
?
vertexArray = {}
faceArray = {}
?
-- 讀入文件
local function readOBJ(fileName)
????local f = assert(io.open(fileName,'r'))
????local line = f:read('*line')
????while line do
????????-- println('line content:'..line)
????????if string.sub(line,1,2)=="v "then -- 頂點 vertex
????????????local tmp = splitStr(line,' ')
????????????local p = {-tonumber(tmp[2]),-tonumber(tmp[3]),-tonumber(tmp[4])} -- x,y,z坐標要反向,然而道理我也不明白
????????????table.insert(vertexArray,p)
????????end
????????if string.sub(line,1,2)=="f "then -- 面 face
????????????local tmp = splitStr(line,' ')
????????????local f1 = splitStr(tmp[2],'/')[1]
????????????local f2 = splitStr(tmp[3],'/')[1]
????????????local f3 = splitStr(tmp[4],'/')[1]
????????????local f = {}
????????????if #tmp==5 then -- 四邊面的情況
????????????????local f4 = splitStr(tmp[5],'/')[1]
????????????????f = {tonumber(f1),tonumber(f2),tonumber(f3),tonumber(f4)}
????????????elseif #tmp==4 then -- 三邊面的情況
????????????????f = {tonumber(f1),tonumber(f2),tonumber(f3),tonumber(f1)}
????????????end
????????????table.insert(faceArray,f)
????????end
????????line = f:read('*line')
????end
????f:close()
end
?
readOBJ(path)
?
-- 寫入
local function writeFile(fileName,content)
????local f = assert(io.open(fileName,'w')) -- 如果文件不存在則創(chuàng)建,文件若存在則被覆蓋
????f:write(content)
????f:close()
end
?
-- 追加
local function appendFile(fileName,content)
????local f = assert(io.open(fileName,'a'))
????f:write(content)
????f:close()
end
?
writeFile(pathOBJ,[[
version3()
dim3()
-- normal()
anime()
getLight()
?
mainColor = {0.2,0.4,0.6}
obj = {
????point = {]])
?
-- 輸出頂點信息
contentForVertex = ""
for i=1,#vertexArray do
????contentForVertex = contentForVertex.."{p=vertexArray["..i.."]},\n"
end
appendFile(pathOBJ,contentForVertex)
?
appendFile(pathOBJ,[[},
????prim = {]])
-- 輸出面信息
contentForFace = ""
for i=1,#faceArray do
????contentForFace = contentForFace..[[{type="triangler",]].."pref=faceArray["..i.."],color=mainColor},\n"
end
appendFile(pathOBJ,contentForFace)
?
appendFile(pathOBJ,[[},
????-- my_tex = INPUT
}
poly(obj)
]])
?
runFile(localToUtf8(pathOBJ))
?
臨時寫的,沒有仔細打草稿,可能會有些疏漏,歡迎吐槽~~
另外不知道應該投到哪個分區(qū)好 :(
上傳了代碼文件和obj模型(不含Miku的那個)
鏈接:https://pan.baidu.com/s/1nit4zYfB5JR2B10WY2Y7ig?
提取碼:5glb

完?