3Delight NSI: A Streamable Render API

3Delight是應用於高端電影級別渲染的軟件渲染器,迄今爲止已經參與了無數的電影製做,具體能夠參見連接。html

若是你對3Delight的印象就依然是RenderMan的替代品,那就顯然已經和時代發展脫節了。如今的3Delight是一個徹底PBR Unbiased的渲染器,並且徹底爲了交互式渲染以及雲端渲染設計,因此你對它的固有印象能夠從看到這篇文章開始完全改變了。git

渲染=數據操做github

其實「渲染」這個動做的自己,就是數據處理,你能夠用任何流行的思路來對照,好比MapReduce。可是歸根結底,能夠認爲只有3個概念。數組

  • 數據填充
  • 數據修改
  • 數據計算

這3個概念能夠直接展開,把你所知道全部的計算機圖形學相關的概念和技術都丟入,可是這裏不展開。ide

本文會結合這3個概念,來仔細的闡述3Delight NSI的優勢和思路,以及解決的問題。函數

一切從過程開始

計算機,實際上是過程性設備。所謂面向對象,只是軟件設計領域的一個對過程和數據的合併抽象而已,本質上,最後的「執行」這個自己依然是個過程。性能

那麼回顧一下RenderMan API(如下簡稱RI)的設計。ui

RenderMan

一個完整RI可渲染的場景通常結構以下,來自這裏lua

 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

聰明的你告訴我,你以爲這個場景描述有什麼限制?這個問題可能很難回答,可是咱們先來提幾個看似簡單的需求。spa

  • 流式更新
  • 幾何體數據的修改
  • 幾何體屬性的修改
  • 材質數據的修改
  • 材質和幾何體關係的修改
  • 多屏幕計算
  • 多屏幕不一樣分辨率的計算
  • 多屏幕不一樣分辨率不一樣數據的計算

可是告訴我,若是你想修改這個Mesh的幾何數據,你會如何作?這個答案在RI內,使用負責場景數據,範例以下。

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

這套系統只支持很是有限的場景元素的修改,也就是你只能改改Shader參數,移動一下位置如此,也就是咱們如今看到常見IPR的全部的操做。

固然這一套系統的限制呢,也是寫的明明白白。

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。
  • 不支持多攝影機渲染。
  • 不支持CSG幾何體。
  • 須要新的Display Driver支持。
  • 不支持變長的Shader數組參數。

那麼顯然,這一套系統的缺陷是

  • 前後順序存在依賴
  • API太多太瑣碎每次都得學新的函數
  • 可操做的對象和數據類型受限
  • 不支持複雜操做,好比刪除幾何體
  • 不支持修改分辨率、攝影機參數等必須參數

來到Nodel Scene API

顯然到了現在,再遵循RenderMan標準,顯然已經沒有意義。現在RenderMan渲染器自己就沒有絲毫優點,你們的渲染已經更多,已經不是當年那個缺乏靠譜的解決方案的時代了。因此,爲了克服RenderMan的全部缺點和限制,3Delight從新引入了NSI這麼一套API。下面是全部函數列表,對,你沒有看錯,全部的函數。

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);

以上就是全部的函數。

其實從函數名字就能夠看到背後的設計思路,雖然仍是填充場景對象的數據,可是因爲這個不存在任何的依賴關係,因此克服了RI的那幾個重要的缺點,一切的一切只要在調用NSIRenderControl以前便可。用戶能夠用這一套API以本身喜歡的順序組織場景,構造節點和節點之間的鏈接便可。下面來具體用例子解釋如何構造場景。

一個NSI場景

首先從構造一個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);

對於一個mesh來講,它具有以下幾個內置的屬性

  • P
  • nvertices
  • nholes
  • clockwisewinding
  • subdivision.scheme
  • subdivision.cornervertices
  • subdivision.cornersharpness
  • subdivision.creasevertices
  • subdivision.creasesharpness

顧名思義,這些屬性定義了這個mesh的全部幾何數據,每個屬性的數據就是一個數組,如同範例C++代碼所展現的同樣。

光有mesh固然不行,還須要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");

其實很是簡單,這裏使用了SetAttributeAtTime,用來定義多個matrix實現運動模糊。末了,直接調用Connect,這樣就把先前構造的mesh放入了transform的objects這個屬性之下,今後這個mesh能夠被transform所變換。固然transform是能夠包含transform,構形成了層次化的變換。

下面固然是須要附上材質了,咱們就用最簡單的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");

這裏須要先構造attributes,而後把這個attributes和以前創造的transform節點的geometryattributes鏈接,這樣全部attributes都會被全部transform的objects所繼承,今後那個mesh就會附上了這個lambert材質。固然此shader實例能夠用一樣的方式共享給其餘的幾何體。

還有更多的代碼能夠從nsi-example這個開源項目看到完整的源代碼。

感興趣的用戶能夠直接到3Delight Download下載試用版體驗最新3Delight,體驗其卓越的性能和全部功能特點。

相關文章
相關標籤/搜索