在Blender中,當頂點法向錯誤時,模型的外觀可能會受到影響。修復這個問題是確保渲染和顯示正確的關鍵。以下是修正頂點法向錯誤的步驟:
步驟一:選擇模型
首先,選擇包含法向錯誤的模型。在編輯模式下,你可以通過按Tab鍵切換到編輯模式來進行選擇。
步驟二:顯示法向
在3D視圖中,按N鍵打開側邊欄。在側邊欄的"View"選項卡下,找到"Mesh Display",啟用"Face Normals"選項。這樣,你將能夠看到每個面的法向。
步驟三:檢查法向方向
通過顯示法向,檢查模型的法向方向。正常情況下,法向應該垂直于面。如果法向看起來異常,可能存在法向錯誤。
步驟四:重新計算法向
在編輯模式下,選擇所有頂點(按A鍵),然后按Ctrl+N鍵重新計算法向。這將嘗試修復法向錯誤并使其符合模型表面的朝向。
步驟五:手動調整法向
如果自動計算無法解決問題,你可以手動調整法向。選擇問題面,按Shift+N鍵翻轉法向,或者使用Alt+N鍵手動調整法向。
步驟六:檢查模型
在修正法向后,仔細檢查模型以確保外觀正常。渲染或實時查看模型,以確認法向錯誤是否已成功修復。
通過遵循這些步驟,你可以在Blender中修復頂點法向錯誤,確保模型呈現正常且法向正確。這對于模型的正常顯示和渲染非常重要。
你知道可以在 Blender 中通過編程來自動創建 3D 對象和動畫嗎?
Blender 是一款用于 3D 建模、雕刻、紋理、動畫等方面的知名軟件!隨著版本的不斷推出,這個工具已經慢慢在 CGI 行業中占據了一席之地,以至于現在有一些完全使用 Blender 制作的長篇電影,并且有像 Blender Guru 這樣的 Youtube 頻道完全專注于學習這個軟件的來龍去.....而且它是完全免費和開源的!
這在 3D 世界中引入了思維框架的深刻變化,因為它向人們展示了任何人都可以嘗試并嘗試這種藝術形式,并且可以取得令人難以置信的結果。
所以,是的——起初,CGI 軟件主要針對藝術家。但是 Blender 和它的競爭對手一樣,也為開發人員提供了另一面:一種對 3D 場景進行編程的方法。
今天,我想展示 Blender 的這種編程方法如何讓你立即創建一個像這樣的基本太陽系:
那么Blender 如何讓我們“編程場景”?通過其 Python API。
您可能想知道為什么擁有一個用于 3D 軟件的 Python API 很有趣。我的意思是:為什么要 為編碼人員嵌入一個創意工具?
事實上,在許多用例中, 自動化任務可能很有用:無論你是想快速隨機化場景填充算法、計算對象并獲取場景中的自定義統計信息,還是甚至從頭開始創建一個可以用種子準確復制……有一種方法可以將 程序生成或定制工具 直接集成到 CG 上下文中,這真是一個了不起的機會!
此外,它是用 Python 編寫的,這是一種以易于學習 而 聞名的語言 ,網上有無數的教程 ,這使得初學者可以很容易地深入(不用擔心舊的 C/在我看來,基于 C++ 的 API 需要更高的編碼技能……)。
順便說一句:這個 Python API 不僅僅是對于想要成為開發者的閃亮玩具:它實際上 是 Blender 軟件本身的一部分, 它 在程序內部 用作核心工具,盡管用戶輸入和結果都 包含在用戶中-友好的用戶界面
API 本身,bpy(代表“Blender Python”),可以在 Blender 的特定文檔中瀏覽;它被細分為幾個子模塊,其中 3 個最重要/最常用的是:
今天,由于有了這個 API,我們可以專注于生成對象,因此我們將進行一些 過程生成。我已經在 其他 文章中討論過這種方法的好處;粗略地說,程序生成是關于定義 一組規則 和 使用這些規則 自動創建有效實例的機器。最大的優勢是,一旦你完成了引擎的制作,你可以創建任意數量的實例!此外,這一代可以非常快速、動態地取決于給定的條件和無限(例如對于像跑步者這樣永無止境的游戲) )。但是,當然,這通常比手工設計要難一些,因為需要教程序正確和錯誤的模式(即“規則”)。
程序生成是一個非常廣泛和復雜的話題。生成引擎有很多工具——老實說,其中很大一部分依賴于 隨機性。這個想法是從隨機值開始,然后以某種方式“控制瘋狂”,因此它是有效的。
要了解 Blender 的 Python API 有多么強大以及如何使用它,讓我們來看一個基本示例:實例化圍繞太陽的一堆行星(都是簡單的球體),大小、速度和顏色隨機,并讓行星圍繞旋轉太陽沿著圓形軌道。
現在,讓我們明確一點:這 不是 物理仿真。我們將隨機選擇所有值,到太陽的距離、半徑和行星的表面顏色之間沒有任何邏輯關系。這里的目標只是嘗試 Blender 的 Python API,看看如何進行一些程序生成并享受發光球體的樂趣
首先要做的事情:讓我們看看如何為 Python 設置 Blender 并學習我們可以使用的不同工具。
注意:本教程使用 Blender 2.93 的屏幕截圖。此外,我將使用 Blender 2.8+ 中的 Python API,因此請確保您的版本匹配
當你想在 Blender 中使用 Python 時,非常有用的方法是在附加控制臺的情況下 啟動 Blender。如果你的 Python 腳本錯誤,這將允許您查看一些日志,這對于正確調試非常重要!
基本上,這個想法是從你的終端而不是從您的應用程序快捷方式啟動 Blender。Blender 可執行文件的確切路徑取決于你的操作系統;我使用的是 Mac,所以我所要做的就是打開一個終端,然后運行如下內容:
/Applications/Blender.app/Contents/MacOS/Blender &
Python 腳本的任何輸出都會顯示在這個控制臺中
現在,要準備好編碼,只需轉到 Blender 窗口頂部的“腳本”選項卡:
這將在工作區中提供一組新面板,即:
要檢查一切是否正常,只需單擊文本編輯器的“+ New”按鈕,在 Python 腳本中編寫基本的 print('hello world') 行,然后單擊右側的“運行”圖標(或使用快捷鍵 <Alt + P>;確保您的鼠標懸停在文本編輯器面板上以使其工作)。
如果一切設置正確,您應該會在連接的終端中看到“hello world”!
好吧!是時候在我們的場景中使用 Python 實例化一些對象了
首先,刪除所有初始對象:立方體、相機和燈光。我們不會使用這些。
現在,讓我們編輯 Python 腳本來創建一個新的球體對象。這是使用 bpy.ops 子包完成的,更準確地說 是使用 mesh.primitive_uv_sphere_add() 方法:
import bpy
bpy.ops.mesh.primitive_uv_sphere_add(
radius=3,
location=(0, 0, 0),
scale=(1, 1, 1)
)
參數是不言自明的:我們在原點創建一個半徑為 3 的球體,具有標準化的比例。(請隨意瀏覽文檔以獲取有關可用選項的更多詳細信息)
再次,通過單擊“運行”圖標或使用 <Alt + P> 短代碼來運行您的代碼。Nice!我們剛剛通過腳本在 3D 場景中創建了一個簡單的 UV 球體!
我們現在能夠生成一個球體——讓我們看看生成多個球體是多么容易!
我們要做的是:
制作 N_PLANETS 實例很簡單:我們只需將primitive_uv_sphere_add() 調用包裝在一個for循環中并多次運行它。為了保持腳本的可讀性,我們實際上將這個實例化過程提取到一個 util 函數 create_sphere() 中,并且我們將以隨機半徑和距離傳遞它。
要獲得半徑和距離的隨機值,我們可以依賴 Python 內置的 random 模塊。
此外,我們應該 正確命名我們的對象 :這對于保持層次結構清晰是很有趣的,并且當我們稍后在腳本初始化時自動清理場景時,這將是必不可少的。對于 N_PLANETS 球體,我將簡單地將每個對象稱為“Planet-00”、“Planet-01”等等。
這是更新的腳本:
from random import random
import bpy
def create_sphere(radius, distance_to_sun, obj_name):
# instantiate a UV sphere with a given
# radius, at a given distance from the
# world origin point
obj=bpy.ops.mesh.primitive_uv_sphere_add(
radius=radius,
location=(distance_to_sun, 0, 0),
scale=(1, 1, 1)
)
# rename the object
bpy.context.object.name=obj_name
# return the object reference
return bpy.context.object
N_PLANETS=6
for n in range(N_PLANETS):
# get a random radius (a float in [1, 5])
r=1 + random() * 4
# get a random distace to the origin point:
# - an initial offset of 30 to get out of the sun's sphere
# - a shift depending on the index of the planet
# - a little "noise" with a random float
d=30 + n * 12 + (random() * 4 - 2)
# instantiate the planet with these parameters
# and a custom object name
create_sphere(r, d, "Planet-{:02d}".format(n))
如果你再次運行它,應該會得到一組大小各異的排列良好的行星,它們都以正確的格式命名:
以類似的方式,我們可以重用我們的 create_sphere() 方法并制作另一個方法 create_torus() ,為太陽和 一些圓環物體添加一個球體 以顯示行星軌道:
from random import random
import bpy
def create_sphere(radius, distance_to_sun, obj_name):
# ...
def create_torus(radius, obj_name):
# (same as the create_sphere method)
obj=bpy.ops.mesh.primitive_torus_add(
location=(0, 0, 0),
major_radius=radius,
minor_radius=0.1,
major_segments=60
)
bpy.context.object.name=obj_name
return bpy.context.object
N_PLANETS=6
for n in range(N_PLANETS):
# ...
# add the radius ring display
create_torus(d, "Radius-{:02d}".format(n))
# add the sun sphere
sun=create_sphere(12, 0, "Sun")
太陽當然比行星大,并且環只是與行星球體同時放置,使用到原點的距離作為 torusradius 參數。
這很好,但那些物體有點沉悶,都是灰色和塊狀的。是時候研究兩個重要的 3D 概念了: 對象的陰影和材質 !
一般來說, 陰影 是 指對象如何對光做出反應并在 3D 視圖或渲染中繪制。然而,在這里,我關注的是 “平滑”與“平坦”的 陰影,它決定 了物體的多面性。它基本上是第二層,進一步影響對象的渲染方式,但不會改變其實際幾何形狀:它只是看起來更平滑。
將對象設置為使用“平滑”著色真的很快:在 bpy.ops 中有一個專門的 shade_smooth() 方法:
from random import random
import bpy
def create_sphere(radius, distance_to_sun, obj_name):
# ...
# apply smooth shading
bpy.ops.object.shade_smooth()
# return the object reference
return bpy.context.object
def create_torus(radius, obj_name):
# ...
# apply smooth shading
bpy.ops.object.shade_smooth()
# return the object reference
return bpy.context.object
# ...
這里真正的問題是:我們怎樣才能給我們的行星一些 顏色 ,我們怎樣才能讓我們的太陽 發光?
答案是:感謝 材質和著色器!
這兩個術語都可以用來談論“定義對象如何渲染的屬性調色板”。
粗略地說, 著色器 賦予 3D 對象顏色、光澤度、光澤度、粗糙度……想想看:在現實世界中,是什么讓你說“嗯,這是木頭”只是一整套線索:物體有點棕色,它會反射一點光,但不會像金屬那樣帶有尖銳的光點,它不會像玻璃那樣折射光線,也不會像鏡子那樣反射你的圖像……嗯——隨著不斷改進的 CGI技術,所有這些屬性現在都可以在我們的 3D 場景中建模和再現!
注意:當你真正的目標是逼真的渲染時,可能不得不深入研究 基于物理的渲染或 PBR。這就是 Blender 的 Cycles 引擎用于創建“感覺”真實的令人驚嘆的逼真圖片的原因,這主要歸功于一堆復雜且經過精心調整的著色器。
在 Blender 中,著色器通常通過基于節點的圖形編輯器(在“著色”選項卡中可用)進行編輯,它允許你鏈接和組合盡可能多的內置節點,以構建或多或少復雜的著色流。然而,在本教程中,我們將制作一個只有一個節點的超級簡單著色器,因此我們將在 Python 腳本中完成所有操作
然后材質 使用這些著色器并將它們應用到你的 3D 幾何體。一個對象可能有多個材質槽,即它可以對幾何體的不同部分使用不同的著色器,但我們今天不會深入討論,我們將堅持每個對象使用一個材質槽。
好的,那么——我們應該創建什么著色器?
對于這個項目, 我正在使用 EEVEE 引擎 ,它也可以與著色器節點一起使用,即使它沒有與 Cycles 引擎相同的節點類型。
但這很好,因為在這里,我想要的是兩者中都存在的一個: Emission shader。你可以把它想象成一個有強度(“強度”參數)和顏色的大燈泡。它將使你的 3D 對象發光(因此你的對象將成為場景中的光源,我們將與其余網格進行交互!)并從本質上使其“發光”
要完全通過腳本創建和分配著色器,我們必須:
讓我們一步一步來。我們將從創建一個名為的新函數開始:
create_emission_shader()
它將接收一些強度和顏色參數,并將使用這些參數來設置具有發射節點和輸出節點的基本 2 節點圖。
思路是從基本節點模板入手,清除所有starter節點;然后,我們可以添加我們的發射和輸出節點,通過更新其輸入字段的值來配置發射節點,并在兩個節點之間創建一個鏈接:
def create_emission_shader(color, strength, mat_name):
# create a new material resource (with its
# associated shader)
mat=bpy.data.materials.new(mat_name)
# enable the node-graph edition mode
mat.use_nodes=True
# clear all starter nodes
nodes=mat.node_tree.nodes
nodes.clear()
# add the Emission node
node_emission=nodes.new(type="ShaderNodeEmission")
# (input[0] is the color)
node_emission.inputs[0].default_value=color
# (input[1] is the strength)
node_emission.inputs[1].default_value=strength
# add the Output node
node_output=nodes.new(type="ShaderNodeOutputMaterial")
# link the two nodes
links=mat.node_tree.links
link=links.new(node_emission.outputs[0], node_output.inputs[0])
# return the material reference
return mat
現在很容易使用這種方法來創建我們的材質資源并將它們應用于我們的對象。我們需要一種白色發光材料用于環,一種黃色發光材料用于太陽,每個行星需要一種顏色隨機的發光材料(盡管我會添加更多藍色以獲得更好的整體色彩平衡):
# ...
N_PLANETS=6
ring_mat=create_emission_shader(
(1, 1, 1, 1), 1, "RingMat"
)
for n in range(N_PLANETS):
# ...
planet=create_sphere(r, d, "Planet-{:02d}".format(n))
planet.data.materials.append(
create_emission_shader(
(random(), random(), 1, 1),
2,
"PlanetMat-{:02d}".format(n)
)
)
# add the radius ring display
ring=create_torus(d, "Radius-{:02d}".format(n))
ring.data.materials.append(ring_mat)
# add the sun sphere
sun=create_sphere(12, 0, "Sun")
sun.data.materials.append(
create_emission_shader(
(1, 0.66, 0.08, 1), 10, "SunMat"
)
)
如果你將 3D 視圖中的著色模式更改為“渲染”,刪除當前場景中的所有對象并再次運行腳本,會看到它們現在擁有漂亮的發光材質!
請注意,運行腳本后,你甚至可以轉到“著色”選項卡,選擇帶有著色器的對象并查看著色器圖:
它看起來符合預期:兩個節點,一個發射和一個輸出,兩者之間的鏈接,以及“強度”和“顏色”屬性的一些自定義值。
注意:當你最初打開面板時,節點將在彼此頂部的中間全部打包在一起。在這里,我為演示手動移動了節點,但實際上也可以在代碼中使用 .location 屬性來執行此操作。
我們即將擁有最終的“太陽系發電機”!需要注意的最后一件事是為行星設置動畫,以便它們隨著時間的推移圍繞太陽旋轉。
為此,我們將使用 Blender 的 動畫曲線 系統(F-curves)。它是關鍵幀動畫的更 高級 版本 ,你可以在其中 指定對象的一個?或多個屬性隨時間變化的關鍵值 (例如,在給定幀強制其位置、旋轉、縮放……)以及它們之間的 插值 。
這是具有三種可能插值的同一組關鍵點的示例(從左到右:常數、線性和貝塞爾曲線):
你會看到插值如何影響中間自動計算值(我們手動定義的關鍵幀之間的所有片段)以及它如何影響對象此屬性的整體演變。假設這條線代表一架小直升機的高度:
“最佳插值”取決于你想要的動畫類型。在我們的例子中,我們應該選擇線性插值,以便行星沿著它們的軌道以規則的速度移動。
要為我們的行星創建一些動畫,只需要使用animation_data_create() 方法和我們對象的animation_data 字段來創建然后編輯Z-rotation 屬性的F 曲線。這個屬性實際上是對象的 3D 向量屬性的一部分,稱為“歐拉旋轉”(更多關于 Blender 旋轉模式 here),其中 Z 軸是第三個分量,即索引 2 處的那個(因為分量是 0 -索引)。
一旦我們抓住了這個屬性,將簡單地添加兩個關鍵幀:一個用于 起始幀 (我們當前的旋轉為 0),另一個用于 結束幀 ( 一個或多個圍繞太陽的半圓隨機旋轉) ,因此行星具有 不同的速度)。我們將確保這些關鍵幀使用線性插值模式
注意:所有旋轉都必須使用 弧度寫入。
from math import pi
# ...
N_PLANETS=6
START_FRAME=1
END_FRAME=200
for n in range(N_PLANETS):
# ...
# setup the planet animation data
planet.animation_data_create()
planet.animation_data.action=bpy.data.actions.new(name="RotationAction")
fcurve=planet.animation_data.action.fcurves.new(
data_path="rotation_euler", index=2
)
k1=fcurve.keyframe_points.insert(
frame=START_FRAME,
value=0
)
k1.interpolation="LINEAR"
k2=fcurve.keyframe_points.insert(
frame=END_FRAME,
value=(2 + random() * 2) * pi
)
k2.interpolation="LINEAR"
如果你清理場景并重新運行腳本,就會看到……什么都沒有發生!即使你播放動畫,行星也不會移動!
那是因為,事實上,它們是在旋轉……但不是圍繞正確的支點!目前,這顆行星只是在旋轉,它們圍繞著它們的局部 Z 軸旋轉。
要解決這個問題,我們只需要更改對象的軸心點并將其捕捉回位于世界原點(與太陽相同的位置)的光標:
# ...
for n in range(N_PLANETS):
# ...
# set planet as active object
bpy.context.view_layer.objects.active=planet
planet.select_set(True)
# set object origin at world origin
bpy.ops.object.origin_set(type="ORIGIN_CURSOR", center="MEDIAN")
# setup the planet animation data
# ...
現在,如果你將鼠標懸停在 3D 視圖上并按下空格鍵,時間將開始流動,動畫將開始播放,讓我們的行星圍繞太陽旋轉!
運行腳本后,我們在場景中創建了兩種類型的 資源 :行星、太陽和軌道環的 3D 對象;以及行星的動態材料。
擺脫它非常簡單:我們只需要使用 bpy.data 子模塊來檢查我們的對象和材料,檢查名稱并刪除我們在運行腳本時創建的名稱:
# ...
def delete_object(name):
# try to find the object by name
if name in bpy.data.objects:
# if it exists, select it and delete it
obj=bpy.data.objects[name]
obj.select_set(True)
bpy.ops.object.delete(use_global=False)
N_PLANETS=6
START_FRAME=1
END_FRAME=200
# clean scene + planet materials
delete_object("Sun")
for n in range(N_PLANETS):
delete_object("Planet-{:02d}".format(n))
delete_object("Radius-{:02d}".format(n))
for m in bpy.data.materials:
bpy.data.materials.remove(m)
# ...
現在,你可以根據需要多次運行腳本:每次首先 清理場景, 以便只獲得我們的“太陽能系統”的一個實例
為了獲得更好的視覺效果,帶有一點點 綻放效果、深色背景和沒有網格或 X/Y 軸,甚至可以在腳本的開頭添加以下代碼片段來設置具有良好設置的場景:
# ...
def find_3dview_space():
# find the 3D view panel and its screen space
area=None
for a in bpy.data.window_managers[0].windows[0].screen.areas:
if a.type=="VIEW_3D":
area=a
break
return area.spaces[0] if area else bpy.context.space_data
def setup_scene():
# (set a black background)
bpy.data.worlds["World"].node_tree.nodes["Background"].inputs[0].default_value=(0, 0, 0, 1)
# (make sure we use the EEVEE render engine + enable bloom effect)
scene=bpy.context.scene
scene.render.engine="BLENDER_EEVEE"
scene.eevee.use_bloom=True
# (set the animation start/end/current frames)
scene.frame_start=START_FRAME
scene.frame_end=END_FRAME
scene.frame_current=START_FRAME
# get the current 3D view (among all visible windows
# in the workspace)
space=find_3dview_space()
# apply a "rendered" shading mode + hide all
# additional markers, grids, cursors...
space.shading.type='RENDERED'
space.overlay.show_floor=False
space.overlay.show_axis_x=False
space.overlay.show_axis_y=False
space.overlay.show_cursor=False
space.overlay.show_object_origins=False
N_PLANETS=6
START_FRAME=1
END_FRAME=200
# setup scene settings
setup_scene()
# ...
# deselect all objects
bpy.ops.object.select_all(action='DESELECT')
Blender 不是唯一允許你對場景進行編程和自動執行任務的 3D 軟件;但它不負眾望,隨著每個新版本的推出,Blender 逐漸成為一個 可靠的一體化 CG 制作解決方案,從使用油性鉛筆的故事板到基于節點的合成。
你可以使用 Python 腳本和幾個額外的包來批量生成對象實例化程序,設置渲染設置甚至獲取當前項目的自定義統計信息,這一點非常棒!對我來說,這是一種減輕繁瑣任務負擔的方式,同時也讓開發人員參加聚會,將這個創意工具社區擴展到藝術家之外。
在這篇文章中,我們已經看到,用不到一百行 Python 代碼,我們就可以創建一個具有動態和隨機網格、材質和動畫的基本程序太陽系!
原文鏈接:http://www.bimant.com/blog/blender-procedural-content-generation/