3Delight是应用于高端电影级别渲染的软件渲染器,迄今为止已经参与了无数的电影制做,具体能够参见连接。html
若是你对3Delight的印象就依然是RenderMan的替代品,那就显然已经和时代发展脱节了。如今的3Delight是一个彻底PBR Unbiased的渲染器,并且彻底为了交互式渲染以及云端渲染设计,因此你对它的固有印象能够从看到这篇文章开始完全改变了。git
渲染=数据操做github
其实“渲染”这个动做的自己,就是数据处理,你能够用任何流行的思路来对照,好比MapReduce。可是归根结底,能够认为只有3个概念。数组
这3个概念能够直接展开,把你所知道全部的计算机图形学相关的概念和技术都丢入,可是这里不展开。ide
本文会结合这3个概念,来仔细的阐述3Delight NSI的优势和思路,以及解决的问题。函数
计算机,实际上是过程性设备。所谓面向对象,只是软件设计领域的一个对过程和数据的合并抽象而已,本质上,最后的“执行”这个自己依然是个过程。性能
那么回顾一下RenderMan API(如下简称RI)的设计。ui
一个完整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
聪明的你告诉我,你以为这个场景描述有什么限制?这个问题可能很难回答,可是咱们先来提几个看似简单的需求。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();
这套系统只支持很是有限的场景元素的修改,也就是你只能改改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.
限制有
那么显然,这一套系统的缺陷是
显然到了现在,再遵循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以本身喜欢的顺序组织场景,构造节点和节点之间的链接便可。下面来具体用例子解释如何构造场景。
首先从构造一个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来讲,它具有以下几个内置的属性
顾名思义,这些属性定义了这个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,体验其卓越的性能和全部功能特点。