3Delight NSI: A Streamable Render API
3Delight是應(yīng)用于高端電影級別渲染的軟件渲染器,迄今為止已經(jīng)參與了無數(shù)的電影制作,具體可以參見鏈接。
如果你對3Delight的印象就依然是RenderMan的替代品,那就顯然已經(jīng)和時(shí)代發(fā)展脫節(jié)了。現(xiàn)在的3Delight是一個(gè)完全PBR Unbiased的渲染器,而且完全為了交互式渲染以及云端渲染設(shè)計(jì),所以你對它的固有印象可以從看到這篇文章開始徹底改變了。
渲染=數(shù)據(jù)操作
其實(shí)“渲染”這個(gè)動(dòng)作的本身,就是數(shù)據(jù)處理,你可以用任何流行的思路來對照,比如MapReduce。但是歸根結(jié)底,可以認(rèn)為只有3個(gè)概念。
- 數(shù)據(jù)填充
- 數(shù)據(jù)修改
- 數(shù)據(jù)計(jì)算
這3個(gè)概念可以直接展開,把你所知道所有的計(jì)算機(jī)圖形學(xué)相關(guān)的概念和技術(shù)都丟入,但是這里不展開。
本文會(huì)結(jié)合這3個(gè)概念,來仔細(xì)的闡述3Delight NSI的優(yōu)點(diǎn)和思路,以及解決的問題。
一切從過程開始
計(jì)算機(jī),其實(shí)是過程性設(shè)備。所謂面向?qū)ο?#xff0c;只是軟件設(shè)計(jì)領(lǐng)域的一個(gè)對過程和數(shù)據(jù)的合并抽象而已,本質(zhì)上,最后的“執(zhí)行”這個(gè)本身依然是個(gè)過程。
那么回顧一下RenderMan API(以下簡稱RI)的設(shè)計(jì)。
RenderMan
一個(gè)完整RI可渲染的場景一般結(jié)構(gòu)如下,來自這里。
1 ##RenderMan RIB-Structure 1.1 2 ##Scene Bouncing Ball 3 ##Creator /usr/ucb/vi 4 ##CreationDate 12:30pm 8/24/89 5 ##For RenderMan Jones 6 ##Frames 2 7 ##Shaders PIXARmarble, PIXARwood, MyUserShader 8 ##CapabilitiesNeeded ShadingLanguage Displacements 9 version 3.03 10 Declare "d" "uniform point" 11 Declare "squish" "uniform float" 12 Option "limits" "bucketsize" [6 6] #renderer specific 13 Option "limits" "gridsize" [18] #renderer specific 14 Format 1024 768 1 #mandatory resolution 15 Projection "perspective" 16 Clipping 10 1000.0 17 FrameBegin 1 18 ##Shaders MyUserShader, PIXARmarble, PIXARwood 19 ##CameraOrientation 10.0 10.0 10.0 0.0 0.0 0.0 20 Transform [.707107 -.408248 -.57735 0 21 0 .816497 -.57735 0 22 -.707107 -.408248 -.57735 0 23 0 0 17.3205 1 ] 24 WorldBegin 25 AttributeBegin 26 Attribute "identifier" "name" "myball" 27 Displacement "MyUserShader" "squish" 5 28 AttributeBegin 29 Attribute "identifier" "shadinggroup" ["tophalf"] 30 Surface "PIXARmarble" 31 Sphere .5 0 .5 360 32 AttributeEnd 33 AttributeBegin 34 Attribute "identifier" "shadinggroup" ["bothalf"] 35 Surface "plastic" 36 Sphere .5 -.5 0. 360 37 AttributeEnd 38 AttributeEnd 39 AttributeBegin 40 Attribute "identifier" "name" ["floor"] 41 Surface "PIXARwood" "roughness" [.3] "d" [1] 42 # geometry for floor 43 Polygon "P" [-100. 0. -100. -100. 0. 100. 100. 0. 100. 10.0 0. -100.] 44 AttributeEnd 45 WorldEnd 46 FrameEnd 47 FrameBegin 2 48 ##Shaders PIXARwood, PIXARmarble 49 ##CameraOrientation 10.0 20.0 10.0 0.0 0.0 0.0 50 Transform [.707107 -.57735 -.408248 0 51 0 .57735 52 -.815447 0 53 -.707107 -.57735 -.408248 0 54 0 0 24.4949 1 ] 55 WorldBegin 56 AttributeBegin 57 Attribute "identifier" "name" ["myball"] 58 AttributeBegin 59 Attribute "identifier" "shadinggroup" ["tophalf"] 60 Surface "PIXARmarble" 61 ShadingRate .1 62 Sphere .5 0 .5 360 63 AttributeEnd 64 AttributeBegin 65 Attribute "identifier" "shadinggroup" ["bothalf"] 66 Surface "plastic" 67 Sphere .5 -.5 0 360 68 AttributeEnd 69 AttributeEnd 70 AttributeBegin 71 Attribute "identifier" "name" ["floor"] 72 Surface "PIXARwood" "roughness" [.3] "d" [1] 73 # geometry for floor 74 AttributeEnd 75 WorldEnd 76 FrameEnd View Code聰明的你告訴我,你覺得這個(gè)場景描述有什么限制?這個(gè)問題可能很難回答,但是我們先來提幾個(gè)看似簡單的需求。
- 流式更新
- 幾何體數(shù)據(jù)的修改
- 幾何體屬性的修改
- 材質(zhì)數(shù)據(jù)的修改
- 材質(zhì)和幾何體關(guān)系的修改
- 多屏幕計(jì)算
- 多屏幕不同分辨率的計(jì)算
- 多屏幕不同分辨率不同數(shù)據(jù)的計(jì)算
但是告訴我,如果你想修改這個(gè)Mesh的幾何數(shù)據(jù),你會(huì)如何做?這個(gè)答案在RI內(nèi),使用負(fù)責(zé)場景數(shù)據(jù),范例如下。
1 RiEditBegin("attribute", "string editlights", "light1", RI_NULL); 2 // specify the coordinate system for light1 3 RiTransform( ... ); 4 RiLightsource( "spotlight", RI_HANDLEID, "light1", "color lightcolor", (RtPointer)&color ); 5 RiEditEnd(); View Code這套系統(tǒng)只支持非常有限的場景元素的修改,也就是你只能改改Shader參數(shù),移動(dòng)一下位置如此,也就是我們現(xiàn)在看到常見IPR的所有的操作。
當(dāng)然這一套系統(tǒng)的限制呢,也是寫的明明白白。
Restrictions, Constraints, and Known Issues
Each re-rendering mode has certain restrictions and limitations that should be considered before being incorporated in a production pipeline. It is our intent to address these in future releases. Below is the current list of restrictions, constraints, and known issues:
- Hider restrictions The only hiders supported are stochastic and raytrace. Sigma buffer and stitching are not supported.
- Camera restrictions Multi-camera rendering is not supported.
- Graphics primitives CSG is not supported.
- Display Progressive refinement is critical to making editing interactive. We have provided a new display driver, multires, that can quickly display the multi-resolution images produced by re-rendering. However, existing display drivers can't display multi-resolution images and will cause the re-renderer to disable progressive refinement, rendering only at the highest resolution.
- Resizable Arrays Traditional shaders with resizeable arrays will not be baked properly, leading to a crash during re-rendering. However, shader object-based shaders do support the use of resizeable arrays.
限制有
- 僅僅是支持stochastic和raytrace 2種Hider。
- 不支持多攝影機(jī)渲染。
- 不支持CSG幾何體。
- 需要新的Display Driver支持。
- 不支持變長的Shader數(shù)組參數(shù)。
那么顯然,這一套系統(tǒng)的缺陷是
- 先后順序存在依賴
- API太多太瑣碎每次都得學(xué)新的函數(shù)
- 可操作的對象和數(shù)據(jù)類型受限
- 不支持復(fù)雜操作,比如刪除幾何體
- 不支持修改分辨率、攝影機(jī)參數(shù)等必須參數(shù)
來到Nodel Scene API
顯然到了如今,再遵循RenderMan標(biāo)準(zhǔn),顯然已經(jīng)沒有意義。如今RenderMan渲染器本身就沒有絲毫優(yōu)勢,大家的渲染已經(jīng)更多,已經(jīng)不是當(dāng)年那個(gè)缺少靠譜的解決方案的時(shí)代了。所以,為了克服RenderMan的所有缺點(diǎn)和限制,3Delight重新引入了NSI這么一套API。下面是所有函數(shù)列表,對,你沒有看錯(cuò),所有的函數(shù)。
NSIContext_t NSIBegin(int nparams, const struct NSIParam_t *params );void NSIEnd( NSIContext_t ctx );void NSICreate(NSIContext_t ctx, NSIHandle_t handle, const char *type, int nparams, const struct NSIParam_t *params );void NSIDelete(NSIContext_t ctx, NSIHandle_t handle, int nparams, const struct NSIParam_t *params);void NSISetAttribute(NSIContext_t ctx, NSIHandle_t object, int nparams, const struct NSIParam_t *params );void NSISetAttributeAtTime(NSIContext_t ctx, NSIHandle_t object, double time, int nparams, const struct NSIParam_t *params );void NSIDeleteAttribute(NSIContext_t ctx, NSIHandle_t object, const char *name );void NSIConnect(NSIContext_t ctx, NSIHandle_t from, const char *from_attr, NSIHandle_t to, const char *to_attr, int nparams, const struct NSIParam_t *params );void NSIDisconnect(NSIContext_t ctx, NSIHandle_t from, const char *from_attr, NSIHandle_t to, const char *to_attr);void NSIEvaluate(NSIContext_t ctx, int nparams, const struct NSIParam_t *params);void NSIRenderControl(NSIContext_t ctx, int nparams, const struct NSIParam_t *params);以上就是所有的函數(shù)。
其實(shí)從函數(shù)名字就可以看到背后的設(shè)計(jì)思路,雖然還是填充場景對象的數(shù)據(jù),但是由于這個(gè)不存在任何的依賴關(guān)系,所以克服了RI的那幾個(gè)重要的缺點(diǎn),一切的一切只要在調(diào)用NSIRenderControl之前即可。用戶可以用這一套API以自己喜歡的順序組織場景,構(gòu)造節(jié)點(diǎn)和節(jié)點(diǎn)之間的連接即可。下面來具體用例子解釋如何構(gòu)造場景。
一個(gè)NSI場景
首先從構(gòu)造一個(gè)Plane的片段開始。
1 #include <nsi.hpp> 2 3 4 // Set mesh data. 5 // 6 int plane_shape_nvertices_data[1] = 7 { 8 4 9 }; 10 11 int plane_shape_indices_data[4] = 12 { 13 0, 1, 3, 2 14 }; 15 16 float plane_shape_P_data[12] = // 3 * 4 17 { 18 -50, 0, 50, 19 50, 0, 50, 20 - 50, 0, - 50, 21 50, 0, - 50 22 }; 23 24 int plane_shape_N_data[12] = // 3 * 4 25 { 26 0, 1, 0, 27 0, 1, 0, 28 0, 1, 0, 29 0, 1, 0 30 }; 31 32 NSI::ArgumentList plane_shape_attrs; 33 34 plane_shape_attrs.push(NSI::Argument::New("nvertices") 35 ->SetType(NSITypeInteger) 36 ->SetCount(1) 37 ->SetValuePointer(plane_shape_nvertices_data)); 38 39 plane_shape_attrs.push(NSI::Argument::New("P") 40 ->SetType(NSITypePoint) 41 ->SetCount(4) 42 ->SetFlags(NSIParamInterpolateLinear) 43 ->SetValuePointer(plane_shape_P_data)); 44 45 plane_shape_attrs.push(NSI::Argument::New("P.indices") 46 ->SetType(NSITypeInteger) 47 ->SetCount(4) 48 ->SetValuePointer(plane_shape_indices_data)); 49 50 plane_shape_attrs.push(NSI::Argument::New("N") 51 ->SetType(NSITypeNormal) 52 ->SetCount(4) 53 ->SetFlags(NSIParamInterpolateLinear) 54 ->SetValuePointer(plane_shape_N_data)); 55 56 plane_shape_attrs.push(NSI::Argument::New("N.indices") 57 ->SetType(NSITypeInteger) 58 ->SetCount(4) 59 ->SetValuePointer(plane_shape_indices_data)); 60 61 nsi.SetAttribute(plane_shape_handle, plane_shape_attrs);對于一個(gè)mesh來說,它具備如下幾個(gè)內(nèi)置的屬性
- P
- nvertices
- nholes
- clockwisewinding
- subdivision.scheme
- subdivision.cornervertices
- subdivision.cornersharpness
- subdivision.creasevertices
- subdivision.creasesharpness
顧名思義,這些屬性定義了這個(gè)mesh的所有幾何數(shù)據(jù),每一個(gè)屬性的數(shù)據(jù)就是一個(gè)數(shù)組,如同范例C++代碼所展示的一樣。
光有mesh當(dāng)然不行,還需要transform
1 #include <nsi.hpp> 2 3 // Set transform data, which is identity. 4 // 5 double plane_xform_matrix_data[16] = 6 { 7 1, 0, 0, 0, 8 0, 1, 0, 0, 9 0, 0, 1, 0, 10 0, 0, 0, 1 11 }; 12 13 NSI::ArgumentList plane_xform_attrs; 14 plane_xform_attrs.push(NSI::Argument::New("transformationmatrix") 15 ->SetType(NSITypeDoubleMatrix) 16 ->SetCount(1) 17 ->SetValuePointer(plane_xform_matrix_data)); 18 19 nsi.SetAttributeAtTime(plane_xform_handle, 0.0, plane_xform_attrs); 20 21 // Create plane's mesh and connect it to the last transform. 22 // 23 const std::string plane_shape_handle("planeShape1"); 24 25 nsi.Create(plane_shape_handle, "mesh"); 26 nsi.Connect(plane_shape_handle, "", plane_xform_handle, "objects");其實(shí)非常簡單,這里使用了SetAttributeAtTime,用來定義多個(gè)matrix實(shí)現(xiàn)運(yùn)動(dòng)模糊。末了,直接調(diào)用Connect,這樣就把先前構(gòu)造的mesh放入了transform的objects這個(gè)屬性之下,從此這個(gè)mesh可以被transform所變換。當(dāng)然transform是可以包含transform,構(gòu)造成了層次化的變換。
下面當(dāng)然是需要附上材質(zhì)了,我們就用最簡單的lambert。
1 #include <nsi.hpp> 2 3 // Assign lambert shader to the plane. 4 // 5 const std::string plane_xform_attrs_handle = plane_xform_handle + "Attrs"; 6 7 nsi.Create(plane_xform_attrs_handle, "attributes"); 8 nsi.Connect(plane_xform_attrs_handle, "", plane_xform_handle, "geometryattributes"); 9 10 const std::string lambert_shader_handle("lambert1"); 11 12 nsi.Create(lambert_shader_handle, "shader"); 13 14 char lambert_shader_name[256]; 15 sprintf(lambert_shader_name, "%s/maya/osl/lambert", delight_dir); 16 17 nsi.SetAttribute(lambert_shader_handle, (NSI::StringArg("shaderfilename", lambert_shader_name), 18 NSI::FloatArg("i_diffuse", 0.8))); 19 20 nsi.Connect(lambert_shader_handle, "", plane_xform_attrs_handle, "surfaceshader");這里需要先構(gòu)造attributes,然后把這個(gè)attributes和之前創(chuàng)造的transform節(jié)點(diǎn)的geometryattributes連接,這樣所有attributes都會(huì)被所有transform的objects所繼承,從此那個(gè)mesh就會(huì)附上了這個(gè)lambert材質(zhì)。當(dāng)然此shader實(shí)例可以用同樣的方式共享給其他的幾何體。
還有更多的代碼可以從nsi-example這個(gè)開源項(xiàng)目看到完整的源代碼。
感興趣的用戶可以直接到3Delight Download下載試用版體驗(yàn)最新3Delight,體驗(yàn)其卓越的性能和所有功能特色。
總結(jié)
以上是生活随笔為你收集整理的3Delight NSI: A Streamable Render API的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Swift:用UICollectionV
- 下一篇: 2006世界杯赛程表,不能错过:)