3Delight是应用于高端电影级别渲染的软件渲染器,迄今为止已经参与了无数的电影制作,具体可以参见链接。

如果你对3Delight的印象就依然是RenderMan的替代品,那就显然已经和时代发展脱节了。现在的3Delight是一个完全PBR Unbiased的渲染器,而且完全为了交互式渲染以及云端渲染设计,所以你对它的固有印象可以从看到这篇文章开始彻底改变了。

渲染=数据操作

其实“渲染”这个动作的本身,就是数据处理,你可以用任何流行的思路来对照,比如MapReduce。但是归根结底,可以认为只有3个概念。

  • 数据填充
  • 数据修改
  • 数据计算

这3个概念可以直接展开,把你所知道所有的计算机图形学相关的概念和技术都丢入,但是这里不展开。

本文会结合这3个概念,来仔细的阐述3Delight NSI的优点和思路,以及解决的问题。

一切从过程开始

计算机,其实是过程性设备。所谓面向对象,只是软件设计领域的一个对过程和数据的合并抽象而已,本质上,最后的“执行”这个本身依然是个过程。

那么回顾一下RenderMan API(以下简称RI)的设计。

RenderMan

一个完整RI可渲染的场景一般结构如下,来自这里

 ##RenderMan RIB-Structure 1.1
##Scene Bouncing Ball
##Creator /usr/ucb/vi
##CreationDate :30pm //
##For RenderMan Jones
##Frames
##Shaders PIXARmarble, PIXARwood, MyUserShader
##CapabilitiesNeeded ShadingLanguage Displacements
version 3.03
Declare "d" "uniform point"
Declare "squish" "uniform float"
Option "limits" "bucketsize" [ ] #renderer specific
Option "limits" "gridsize" [] #renderer specific
Format #mandatory resolution
Projection "perspective"
Clipping 1000.0
FrameBegin
##Shaders MyUserShader, PIXARmarble, PIXARwood
##CameraOrientation 10.0 10.0 10.0 0.0 0.0 0.0
Transform [. -. -.
. -.
-. -. -.
17.3205 ]
WorldBegin
AttributeBegin
Attribute "identifier" "name" "myball"
Displacement "MyUserShader" "squish"
AttributeBegin
Attribute "identifier" "shadinggroup" ["tophalf"]
Surface "PIXARmarble"
Sphere . .
AttributeEnd
AttributeBegin
Attribute "identifier" "shadinggroup" ["bothalf"]
Surface "plastic"
Sphere . -. .
AttributeEnd
AttributeEnd
AttributeBegin
Attribute "identifier" "name" ["floor"]
Surface "PIXARwood" "roughness" [.] "d" []
# geometry for floor
Polygon "P" [-. . -. -. . . . . . 10.0 . -.]
AttributeEnd
WorldEnd
FrameEnd
FrameBegin
##Shaders PIXARwood, PIXARmarble
##CameraOrientation 10.0 20.0 10.0 0.0 0.0 0.0
Transform [. -. -.
.
-.
-. -. -.
24.4949 ]
WorldBegin
AttributeBegin
Attribute "identifier" "name" ["myball"]
AttributeBegin
Attribute "identifier" "shadinggroup" ["tophalf"]
Surface "PIXARmarble"
ShadingRate .
Sphere . .
AttributeEnd
AttributeBegin
Attribute "identifier" "shadinggroup" ["bothalf"]
Surface "plastic"
Sphere . -.
AttributeEnd
AttributeEnd
AttributeBegin
Attribute "identifier" "name" ["floor"]
Surface "PIXARwood" "roughness" [.] "d" []
# geometry for floor
AttributeEnd
WorldEnd
FrameEnd

聪明的你告诉我,你觉得这个场景描述有什么限制?这个问题可能很难回答,但是我们先来提几个看似简单的需求。

  • 流式更新
  • 几何体数据的修改
  • 几何体属性的修改
  • 材质数据的修改
  • 材质和几何体关系的修改
  • 多屏幕计算
  • 多屏幕不同分辨率的计算
  • 多屏幕不同分辨率不同数据的计算

但是告诉我,如果你想修改这个Mesh的几何数据,你会如何做?这个答案在RI内,使用负责场景数据,范例如下。

 RiEditBegin("attribute", "string editlights", "light1", RI_NULL);
// specify the coordinate system for light1
RiTransform( ... );
RiLightsource( "spotlight", RI_HANDLEID, "light1", "color lightcolor", (RtPointer)&color );
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.

限制有

  • 仅仅是支持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的片段开始。

 #include <nsi.hpp>

 // Set mesh data.
//
int plane_shape_nvertices_data[] =
{ }; int plane_shape_indices_data[] =
{
, , ,
}; float plane_shape_P_data[] = // 3 * 4
{
-, , ,
, , ,
- , , - ,
, , -
}; int plane_shape_N_data[] = // 3 * 4
{
, , ,
, , ,
, , ,
, ,
}; NSI::ArgumentList plane_shape_attrs; plane_shape_attrs.push(NSI::Argument::New("nvertices")
->SetType(NSITypeInteger)
->SetCount()
->SetValuePointer(plane_shape_nvertices_data)); plane_shape_attrs.push(NSI::Argument::New("P")
->SetType(NSITypePoint)
->SetCount()
->SetFlags(NSIParamInterpolateLinear)
->SetValuePointer(plane_shape_P_data)); plane_shape_attrs.push(NSI::Argument::New("P.indices")
->SetType(NSITypeInteger)
->SetCount()
->SetValuePointer(plane_shape_indices_data)); plane_shape_attrs.push(NSI::Argument::New("N")
->SetType(NSITypeNormal)
->SetCount()
->SetFlags(NSIParamInterpolateLinear)
->SetValuePointer(plane_shape_N_data)); plane_shape_attrs.push(NSI::Argument::New("N.indices")
->SetType(NSITypeInteger)
->SetCount()
->SetValuePointer(plane_shape_indices_data)); 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

 #include <nsi.hpp>

 // Set transform data, which is identity.
//
double plane_xform_matrix_data[] =
{
, , , ,
, , , ,
, , , ,
, , ,
}; NSI::ArgumentList plane_xform_attrs;
plane_xform_attrs.push(NSI::Argument::New("transformationmatrix")
->SetType(NSITypeDoubleMatrix)
->SetCount()
->SetValuePointer(plane_xform_matrix_data)); nsi.SetAttributeAtTime(plane_xform_handle, 0.0, plane_xform_attrs); // Create plane's mesh and connect it to the last transform.
//
const std::string plane_shape_handle("planeShape1"); nsi.Create(plane_shape_handle, "mesh");
nsi.Connect(plane_shape_handle, "", plane_xform_handle, "objects");

其实非常简单,这里使用了SetAttributeAtTime,用来定义多个matrix实现运动模糊。末了,直接调用Connect,这样就把先前构造的mesh放入了transform的objects这个属性之下,从此这个mesh可以被transform所变换。当然transform是可以包含transform,构造成了层次化的变换。

下面当然是需要附上材质了,我们就用最简单的lambert。

 #include <nsi.hpp>

 // Assign lambert shader to the plane.
//
const std::string plane_xform_attrs_handle = plane_xform_handle + "Attrs"; nsi.Create(plane_xform_attrs_handle, "attributes");
nsi.Connect(plane_xform_attrs_handle, "", plane_xform_handle, "geometryattributes"); const std::string lambert_shader_handle("lambert1"); nsi.Create(lambert_shader_handle, "shader"); char lambert_shader_name[];
sprintf(lambert_shader_name, "%s/maya/osl/lambert", delight_dir); nsi.SetAttribute(lambert_shader_handle, (NSI::StringArg("shaderfilename", lambert_shader_name),
NSI::FloatArg("i_diffuse", 0.8))); 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,体验其卓越的性能和所有功能特色。

3Delight NSI: A Streamable Render API的更多相关文章

  1. CVE-2018-7600-Drupal远程代码执行漏洞-Render API

    今天学习一下Drupal的另一个漏洞,由于渲染数组不当造成的漏洞 poc: url:http://localhost/drupal-8.5.0/user/register?element_parent ...

  2. 代码审计之CVE-2018-7600-Drupal远程代码执行漏洞-Render API

    今天学习一下Drupal的另一个漏洞,由于渲染数组不当造成的漏洞 poc: url:http://localhost/drupal-8.5.0/user/register?element_parent ...

  3. 何时/如何使用 Vue3 render 函数

    什么是 DOM? 如果我们把这个 HTML 加载到浏览器中,浏览器创建这些节点,用来显示网页.所以这个HTML映射到一系列DOM节点,然后我们可以使用JavaScript进行操作.例如: let it ...

  4. Beginning SDL 2.0(1) SDL功能简介

    原文链接为 http://wiki.libsdl.org/Introduction. 由于近期整理音视频可视化的技术,发现好久不更新的SDL发布了2.0版本,以前也没有过于关注,这里准备尝试下.了解S ...

  5. (转)SDL 1.2 to 2.0 Migration Guide--SDL1.2更新到SDL2.0指南

    SDL 1.2 to 2.0 Migration Guide 目录 SDL 1.2 to 2.0 Migration Guide Translations Introduction Overview ...

  6. tornado大全(甩锅版)

    tornado简介 tornado是Python界中非常出名的一款Web框架,和Flask一样它也属于轻量级的Web框架. 但是从性能而言tornado由于其支持异步非阻塞的特性所以对于一些高并发的场 ...

  7. vue3 到底哪里好?看这一篇就够了

    之前写的关于 vue3 的文章,好多人吐槽:这些API每次使用都要引入一遍,感觉有点麻烦. 今天我们就来看看 vue3 相比 vue2 的优点有些啥? 为啥有些人说:自从写了 ts vue3 再也回不 ...

  8. 【React】学习笔记(一)——React入门、面向组件编程、函数柯里化

    课程原视频:https://www.bilibili.com/video/BV1wy4y1D7JT?p=2&spm_id_from=pageDriver 目录 一.React 概述 1.1.R ...

  9. iView组件添加API中介绍的事件的方式(render方式添加事件)

    iView组件好用,文档齐全,品质可靠稳定.最大的好处是使用了Vue框架,使很多数据绑定和交互问题变的轻松,是难得的开源前端组件.给作者点个赞.用这个组件来学习Vue.js也是不错的选择. 最近用的比 ...

随机推荐

  1. SpringBoot进阶教程(二十八)整合Redis事物

    Redis默认情况下,事务支持被禁用,必须通过设置setEnableTransactionSupport(true)为使用中的每个redistplate显式启用.这样做会强制将当前重新连接绑定到触发m ...

  2. Mysql常用基础操作(备忘录)

    常常忘记mysql的一些命令行操作,甚至于说,比较复杂的sql格式记不住或忘记了,也可能根本不会考虑去记,因此,做一下汇总,当下次出现恍惚时不至于去百度挨个找,有时就是记不起来,但是只要给点药引子,立 ...

  3. 非常贴心的轮子 FreeSql

    FreeSql 项目从2018年11月28日开发至今,版本已发布至 v0.3.12,版本规则:年数-月-日-当日版本号.目前主要包括 FreeSql.FreeSql.Repository 两个项目的维 ...

  4. Asp.Net Core 轻松学-正确使用分布式缓存

    前言     本来昨天应该更新的,但是由于各种原因,抱歉,让追这个系列的朋友久等了.上一篇文章 在.Net Core 使用缓存和配置依赖策略 讲的是如何使用本地缓存,那么本篇文章就来了解一下如何使用分 ...

  5. visual studio code .net 开发

    Visual Studio确实是相当好用,各种简化操作什么的简直不要太舒服.但其容量太大,有时不是很方便,所以今天简单介绍一下另一个工具--Visual Studio Code. 虽然相比于老大哥Vi ...

  6. Java基础练习2(构造方法)

    1.以下关于面向对象概念的描述中,不正确的一项是() A.在构造方法中,this()只能出现在构造方法第一行位置 B.在构造方法中,super()只能出现在构造方法第一行位置 C.this()和sup ...

  7. Win64下编译OSG详细过程(Win10+VS2015+OSG3.6.3)

    目录 1. 数据资源准备 2. 编译第三方库 3. 编译GDAL 4. 编译OSG 1) CMAKE_INSTALL_PREFIX: 2) BUILD_OSG_EXAMPLES/BUILD_MFC_ ...

  8. 第一次JVM分析记录:Out of Memory Error (workgroup.cpp:96), pid=6196, tid=139999645685504

    tomcat的catalina.out日志报错如下: Exception in thread "http-bio-8081-Acceptor-0" java.lang.OutOfM ...

  9. Python之路【第八篇】:Python模块

    阅读目录 一.模块和包 模块(module)的概念: 在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码会越来越长,越来越不容易维护. 为了编写可维护的代码,我们把很多函数分组,分别放到 ...

  10. Windows Server 2016-PS筛选导出用户邮箱属性包含某字段列表

    生产环境中我们往往会遇到以多个邮箱别名结尾的情况,如何快速导出当前域用户邮箱以某字段或后缀结尾的用户列表信息变得尤为重要,本例简单汇总下如何通过Powershell快速筛选出当前邮箱信息包含azure ...