max用ffd晶格ffd修改器器ffd修改器的物体,导入unity不识别怎么办?

补充一个关于这些模型的趣事:&br&在Stanford放出这些模型的网站上,可以看到下面这句话,要求使用者不要滥用其中的某些模型:&br&Inappropriate uses of these models&br&&p&As you browse this repository and think about how you might use our 3D models and range datasets, please remember that several of these artifacts have religious or cultural significance. Aside from the buddha, which is a religious symbol revered by hundreds of millions of people, the dragon is a symbol of Chinese culture, the Thai statue contains elements of religious significance to Hindus, and Lucy is a C statues like her are commonly seen in Italian churches. Keep your renderings and other uses of these particular models in good taste. Don't animate or morph them, don't apply Boolean operators to them, and don't simulate nasty things happening to them (like breaking, exploding, melting, etc.). Choose another model for these sorts of experiments. (You can do anything you want to the Stanford bunny or the armadillo.)&/p&&p&(from &a href=&///?target=https%3A//graphics.stanford.edu/data/3Dscanrep/%23uses& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&The Stanford 3D Scanning Repository&i class=&icon-external&&&/i&&/a&)&br&&/p&&br&&br&大意: &使用这些模型的时候请记住其中的某些有宗教、文化意义,如龙是中国文化的象征,Lucy是基督教的天使等,所以请不要滥用他们,比如说对用恶趣味的方式渲染他们,对他们进行布尔操作,或是用来做爆炸、融化的模拟。请尽情使用其他模型来做这些实验(比如说Bunny或者Armadillo等,你可以随意处置他们。【阴险脸】)&&br&&br&&p&这也解释了为什么受伤的总是Bunny或者Armadillo...&/p&&img src=&/bcec6b430cf8b_b.png& data-rawwidth=&401& data-rawheight=&305& class=&content_image& width=&401&&&br&&img src=&/f79fc1fe4de4faddbb7e_b.png& data-rawwidth=&897& data-rawheight=&666& class=&origin_image zh-lightbox-thumb& width=&897& data-original=&/f79fc1fe4de4faddbb7e_r.png&&&br&&p&(其实Bunny还好,也就是被融化之类的,Armadillo的日子才叫真不好过......)&/p&&img src=&/882599aac706a9eb3a3f9d_b.png& data-rawwidth=&500& data-rawheight=&283& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&/882599aac706a9eb3a3f9d_r.png&&&br&&img src=&/2edbed6ed79_b.png& data-rawwidth=&957& data-rawheight=&711& class=&origin_image zh-lightbox-thumb& width=&957& data-original=&/2edbed6ed79_r.png&&&img src=&/f26f937c6_b.png& data-rawwidth=&957& data-rawheight=&672& class=&origin_image zh-lightbox-thumb& width=&957& data-original=&/f26f937c6_r.png&&&br&&img src=&/16dab0ec21a87be9ecf916b_b.png& data-rawwidth=&579& data-rawheight=&432& class=&origin_image zh-lightbox-thumb& width=&579& data-original=&/16dab0ec21a87be9ecf916b_r.png&&&img src=&/a689209babc6a41f916adf475e51fb10_b.png& data-rawwidth=&576& data-rawheight=&430& class=&origin_image zh-lightbox-thumb& width=&576& data-original=&/a689209babc6a41f916adf475e51fb10_r.png&&&img src=&/5ca2a43efc81ec7841e69f_b.png& data-rawwidth=&840& data-rawheight=&331& class=&origin_image zh-lightbox-thumb& width=&840& data-original=&/5ca2a43efc81ec7841e69f_r.png&&图片来自Prof. Ron Fedkiw和Prof. J. Teran的实验室结果。
补充一个关于这些模型的趣事: 在Stanford放出这些模型的网站上,可以看到下面这句话,要求使用者不要滥用其中的某些模型: Inappropriate uses of these models As you browse this repository and think about how you might use our 3D models and range…
&img src=&/db2cbb69df5ae_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/db2cbb69df5ae_r.jpg&&这个星期我在&a href=&/?target=http%3A///& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&GTC 2016&i class=&icon-external&&&/i&&/a&上给了一个Talk,主题是如何使用OpenGL以及其扩展和Vulkan做高性能的实时渲染。这篇专栏主要内容来自这个Talk中Vulkan的部分,主要讨论一下Vulkan的使用动机,背后的设计哲学,API框架,以及使用时候的一些建议。&p&Talk的视频连接在此:&a href=&/?target=http%3A//on-/gtc/2016/video/S6817.html& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&on-&/span&&span class=&invisible&&/gtc/2016/video/S6817.html&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&p&Vulkan在今年二月份已经正式发布,再加上过去一年内各类科技媒体的宣传以及网上的热烈讨论,关于Vulkan大家想必早已耳熟能详。作为下一代图形API以及OpenGL的继承者,Vulkan也保留了GL跨平台和开发等特性。然而Vulkan诞生的最重要的理由是性能,更具体的说,是&b&优化CPU上图形驱动相关的性能&/b&。下面首先大概谈谈传统图形API,例如OpenGL和D3D11,在设计上面有哪些潜在的不够高效的地方。&/p&&h2&传统图形API的局限&img src=&/90b68eddae82b1ea74ab38e04d9b9fd9_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/90b68eddae82b1ea74ab38e04d9b9fd9_r.jpg&&&/h2&&p&上图是一段比较典型的图形应用程序中的主循环伪代码。循环的最外层,通常每一帧都会有好几个render pass,例如shadow map和gbuffer的渲染,光照以及各种后处理等。每个pass都有需要设定特定的管线状态,例如blending,depth,raster的状态等等。在下面几层循环中通畅需要遍历所有的shader和着色系统需要的材质参数,如纹理,常量等等。在最内层的循环中,则是需要遍历共享材质的几何体,在这里同场需要绑定vertex buffer和index buffer,以及针对物体的常量参数例如矩阵等。&/p&&p&这里潜在的问题是,当渲染的场景非常复杂的时候(集合数量多,材质系统复杂,纹理,参数多,渲染管线复杂等),所有这些每一帧大量的状态更新,资源绑定操作所花费的计算时间就不能被简单的忽略了。而在现在的图形程序中,复杂场景,大量的多边形,材质、shader的组合以及复杂的渲染管线却正是所有高质量渲染所需要的。&/p&&p&在处理这些修改管线状态的操作时,驱动会在后台运行许多工作,包括下载纹理,Mipmap Downsample,资源访问的同步,渲染状态组合正确性验证,以及错误检查等等等等。对于3D App开发者来说,这些工作什么时候发生,是否发生,都是在API层面无法确定的。所以这样的结果就是在CPU端造成卡顿。卡顿也许是你第一次给管线绑定特定的Shader,VBO或者Blend Mode,Render Target的时候。由于不同的硬件厂商的驱动处理这些工作的方式都不一样,所以App在不同显卡上运行的症状以及优化的方式都会有差别,优化也是无从下手。&/p&&img src=&/36bfe8e8de_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/36bfe8e8de_r.jpg&&&p&传统图形API的另一个问题就是,并不多线程友好。现在多核的系统以及不能再更加普及,然而大多数的图形应用和游戏在CPU端并没有将这些放在手边的计算资源利用起来。当在驱动的工作非常费时的情况下,利用CPU端的多线程非常可能有效的提高整个程序的性能。&/p&&p&无论OpenGL还是Direct3D,都包含一个Context的概念。Context包括当前渲染管线中的所有状态,绑定的Shader,Render Target等。在OpenGL中Context和单一线程是绑定的,所以所有需要作用于Context的操作,例如改变渲染状态,绑定Shader,调用Draw Call,都只能在单一线程上进行。NV_CommandList拓展可以让App支持多线程的任务生成,但是所有渲染状态的操作还是只能在主线程进行,此处不展开。在D3D中,多个线程访问Context时需要App显示的做Synchronization,程序写起来比较麻烦,而且也会有一定性能的影响。&/p&&h2&Vulkan的设计哲学及架构&img src=&/9d58f016cc09faf83ed066_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/9d58f016cc09faf83ed066_r.jpg&&&/h2&&p&Vulkan,亦或者Direct3D12的诞生都是为了摆脱以上提到的局限。Vulkan的API在设计上很明显的可以看到以下几个思路:&/p&&ul&&li&&b&更依赖于程序自身的认知&/b&,让程序有更多的权限和责任自主的处理调度和优化,而不依赖于驱动尝试在后台的优化。程序开发者应该程序的最优化行为最为了解,传统图形API则靠驱动分析程序中调用API模式来揣测并且推断所有操作的优化方法。&/li&&li&&b&多线程友好。&/b&让程序尽可能的利用所有CPU计算资源从而提高性能。Vulkan中不再需要依赖于绑定在某个线程上的Context,而是用全新的基于Queue的方式向GPU递交任务,并且提供多种Synchronization的组件让多线程编程更加亲民。&/li&&li&&b&强调复用&/b&,从而减少开销。大多数Vulkan API的组件都可以高效的被复用。&/li&&/ul&&br&下面通过简单介绍Vulkan的API架构来讨论下Vulkan是如何实践这些哲学的。下图是Vulkan中主要的组件以及它们之间的关系。&img src=&/02cf60e58c67ea9e0015fe_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/02cf60e58c67ea9e0015fe_r.jpg&&首先是Device:&p&&img src=&/b62cf79fd_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/b62cf79fd_r.jpg&&Device很好理解,一个Device就代表着一个你系统中的物理GPU。它的功能除了让你可以选择用来渲染(或者计算)的GPU以外,主要功能就是为你提供其他GPU上的资源,例如所有要用到显存的资源,以及接下来会提到的Queue和Synchronization等组件。&/p&&p&&img src=&/3fae35becc54ea7f95d607_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/3fae35becc54ea7f95d607_r.jpg&&第二个主要的组件,比Device复杂很多,就是Pipeline。一个Pipeline包含了传统API中大部分的状态和设定。只不过Pipeline是需要事先创建好的,这样所有的状态组合的验证和编译都可以在初始化的时候完成,运行时不会再因为这些操作有任何性能上的浪费。但正是因为这一点,如果你不同的Pass需要不同的状态,你需要预先创造多个不同的Pipeline。然而我们不能把所有渲染需要的信息全都prebake进pipeline中,一个Pipeline应该是可以通过绑定不同的资源而复用的。接下来介绍的几个组件就可以被动态的绑定给任何Pipeline。&/p&&p&&img src=&/d65bac48206_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/d65bac48206_r.jpg&&接下来是Buffer。Buffer是所有我们所熟悉的Vertex Buffer, Index Buffer, Uniform Buffer等等的统称。而且一个Buffer的用途非常多样。在Vulkan中需要特别注意Buffer是从什么类型的内存中分配的,有的类型CPU可以访问,有的则不行。有的类型会在CPU上被缓存。现在这些内存的类型是重要的功能属性,不再只是对驱动的一个提示了。&/p&&p&&img src=&/f9efc6078fe35_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/f9efc6078fe35_r.jpg&&Image在Vulkan中代表所有具有像素结构的数组,可以用于表示文理,Render Target等等。和其他组件一样,Image也需要在创建的时候指定使用它的模式,例如Vulkan里有参数指定Image的内存Layout,可以是Linear,也可以是Tiled Linear便于纹理Filter。如果把一个Linear layout的Image当做纹理使用,在某些平台上可能导致严重的性能损失。类似传统的API,纹理本身并不直接绑定给Pipeline。需要读取和使用Image则要依赖于ImageView。&/p&&p&&img src=&/d5bb43c83a04c05bc10a6_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/d5bb43c83a04c05bc10a6_r.jpg&&讲了几种不同类型的内存, 但是内存是从什么地方分配的呢?在Vulkan中,所有内存都分配与一个指定的Heap。一个Device也许支持几种不同类型的Heap,有些也许可以分配Mappable的内存,有些不行。具体的类型取决于程序运行的平台。值得注意的是,Vulkan Heap分配的内存和最终的Vulkan组件例如Buffer和Image直接可以不,也不应该是一对一的映射。一段内存可以分配成数段,并且分配给不同的资源使用。某种程度上这样的资源复用也是Vulkan基本的设计哲学之一。&br&&/p&&p&&img src=&/29e9fe965bbb111303faae_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/29e9fe965bbb111303faae_r.jpg&&上面提到,Buffer和Image可以动态的绑定给任意Pipeline。而具体绑定的规则就是由Descriptor指定。和其他组件一样,Descriptor Set也需要在被创建的时候,就由App指定它的固定的Layout,以减少渲染时候的计算量。Descriptor Set Layout可以指定绑定在指定Descriptor Set上的所有资源的种类和数量,以及在Shader中访问它们的索引。App可以定义多个不同的Descriptor Set Layout,所以如何为你的程序或者引擎设计Descriptor Set的Layout将是优化的重要一环。当然,程序也可以拥有多个指定Layout的Descriptor Set。因为Descriptor Set是预先创建并且无法更改的,所以改变一个绑定的资源需要重新创建整个Descriptor Set,但改变一个资源的Offset可以非常快速的在绑定Descriptor Set的时候完成。一会我会讨论如何利用这一点来实现高效的资源更新。&/p&&p&&img src=&/bedab72b85bfda3871d5d_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/bedab72b85bfda3871d5d_r.jpg&&介绍了那么多组件,都是渲染需要的数据。那么Command Buffer就是渲染本身所需要的行为。在Vulkan里,没有任何API允许你直接的,立即的像GPU发出任何命令。所有的命令,包括渲染的Draw Call,计算的调用,甚至内存的操作例如资源的拷贝,都需要通过App自己创建的Command Buffer。Vulkan对于Command Buffer有特有的Flag,让程序制定这些Command只会被调用一次(例如某些资源的初始化),亦或者应该被缓存从而重复调用多次(例如渲染循环中的某个Pass)。另一个值得注意的是,为了让驱动能更加简易的优化这些Command的调用,没有任何渲染状态会在Command Buffer之间继承下来。每一个Command Buffer都需要显式的绑定它所需要的所有渲染状态,Shader,和Descriptor Set等等。这和传统API中,只要你不改某个状态,某个状态就一直不会变,这一点很不一样。&/p&&p&&img src=&/aafb63d65eff5e18fb2d3_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/aafb63d65eff5e18fb2d3_r.jpg&&最后一个关键组件, Queue,是Vulkan中唯一给GPU递交任务的渠道。Vulkan将Queue设计成了完全透明的对象,所以在驱动里没有任何其他的隐藏Queue,也不会有任何的Synchronization发生。在Vulkan中,给GPU递交任务不再依赖于任何所谓的绑定在单一线程上的Context,Queue的API极其简单,你向它递交任务(Command Buffer),然后如果有需要的话,你可以等待当前Queue中的任务完成。这些Synchronization操作是由Vulkan提供的各种同步组件完成的。例如Samaphore可以让你同步Queue内部的任务,程序无法干预。Fence和Event则可以让程序知道某个Queue中指定的任务已经完成。所有这些组件组合起来,使得基于Command Buffer和Queue递交任务的Vulkan非常易于编写多线程程序。后文会简单讨论一些常见的多线程模式。最后,和前面提到的一样,Queue不光接收图形渲染的调用,也接受计算调用和内存操作。&/p&&h2&Vulkan编程模式&/h2&&p&下面讨论一些使用Vulkan时候比较常见的编程模式,这些模式也都各自彰显了前面提到的Vulkan的设计哲学。例如对于内存的管理,Vulkan更加依赖于程序本身对自己资源寿命范围的理解来达到更优化的内存分配和释放。这里提到的许多事情,在传统的API中驱动可能会尝试帮你做一部分,但是在Vulkan中,所有的控制权和责任都在程序本身上。&/p&&p&传统API中,内存的分配,资源的创建以及资源的使用都是一对一的映射,很明显这不是最佳的资源管理模式。在Vulkan中,一次来自Heap的资源分配可以同时创建多个Buffer,每个Buffer又可以用于不同的格式以及用途。这样相对传统的情况已经有不少的优化。Vulkan甚至允许讲一个Buffer对象的不同子区间划分给格式以及用途不同的子Buffer,例如索引和顶点Buffer可以共享同一个Buffer,只要在绑定的时候指定不同的偏移量即可。这也是最优的做法,它既减少了内存分配的频率,也减少了Buffer绑定的频率。&br&&/p&&img src=&/7bdd1cf1b6687deb0345_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/7bdd1cf1b6687deb0345_r.jpg&&&p&正是因为Vulkan在Descriptor Set中绑定资源的时候,不仅需要指定Buffer,也需要指定Buffer的中资源的偏移量,所以我们可以利用这个特性达到高效的更新以及绑定的资源。因为我们可以同时绑定多个不同的资源到同一个大Buffer的不同子区间,然后在需要绑定不同的资源的时候可以重复使用同一个Descriptor Set,指定不同的偏移量即可。&/p&&p&&img src=&/4fca94eba2f03cbee357_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/4fca94eba2f03cbee357_r.jpg&&至于如何组合这些资源和Buffer的组合以及Layout,Vulkan需要程序开发本身找到最佳的资源的分配,绑定以及更新模式。不再依赖于任何驱动的优化。。因为批量资源分配,更新的最佳频率就是资源本身的更新频率。有些资源每次只需要每次运行更新一次,有些则是每个场景更新一次,也有的动态资源每帧都需要更新。然而程序本身这些更新的频率是最清楚明了的,永远都能比驱动分析的结果更加准确。所以将这个任务交给程序本身其实也是非常合理的。&/p&&p&&img src=&/d4b5ef375_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/d4b5ef375_r.jpg&&下面说说另一个非常重要的话题,就是多线程渲染。如前面所说,Vulkan基于Queue的API设计对多线程非常友好,同时也提供了多种Synchronization的方法。常见的并行方法有两种,第一种是在CPU端并行的更新一些Buffer中的数据。这里要注意的是,多线程的情况下更新资源要保证安全。如果你的程序渲染的非常高效,通常在CPU端会同时有好几帧的数据要处理。所以程序会可以Round Robin的方法更新并且使用这些资源。这个时候要是别的线程写的前面某一帧还没有被读取完的数据则会造成错误。Vulkan的Event可以被插入在Command Buffer中,在使用指定资源的调用后面。这样App回一直等到SetEvent被调用之后才会更新指定的资源。&/p&&p&当然在最理想的情况,程序不用真正的等这些Event,因为它早已经被Set过了。当然具体情况要取决于整个系统的性能,以及你的Round robin环有多长。&br&&img src=&/7f394aaba473a0f28695b2_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/7f394aaba473a0f28695b2_r.jpg&&&/p&&p&另一种并行的方式带来的性能提升更加显著,尤其是在渲染非常复杂的场景的时候。这也是Vulkan相比传统API最能体现提高的情况。那就是并行的在不同线程上生成场景不同部分的渲染任务,并且生成自己的Command Buffer,不用任何线程间的Synchronization。最后,不同的线程可以将Command Buffer的Handle传给主线程然后由主线程将它们写入Queue中,也可以直接写入子线程中的per-thread Queue递交给GPU。不过Queue的任务递交时间并不是完全可以忽略的,所以这里还是建议将Command传给主线程一起递交。这样的模式达到了计算资源利用的最大化,多个CPU核都参与了场景的渲染,并且有大量的渲染任务同时递交给GPU最大化了GPU的吞吐量。下图说明了这种模式。&img src=&/a04a3db326adec0ae739_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/a04a3db326adec0ae739_r.jpg&&&/p&&p&和Buffer更新时候的线程安全一样,Command Buffer的更新也需要注意不能直接复盖还未被使用的Command Buffer。Vulkan的Queue写入API接收一个Fence参数,这个Fence会在这个Queue中的任务都被GPU处理完毕后会被Signal。所以程序将Command Buffer递交给Queue后,可以马上接着并行的更新和递交新的任务。直到Fence之前的Fence被Signal之后,才可以安全的覆盖那个Fence所对应Queue中的Command Buffer。&img src=&/81c4bd628efa_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/81c4bd628efa_r.jpg&&&/p&&p&另一个需要主意的多线程相关的组件是Command Buffer Pool。Command Buffer Pool是Command Buffer的父亲组件,负责分配Command Buffer。Command Buffer相关的操作会对其对应的Command Buffer Pool里造成一定的工作,例如内存分配和释放等等。因为多个线程会并行的进行Command Buffer相关的操作,这个时候如果所有的Command Buffer都来自同一个Command Buffer Pool的话,这时Command Buffer Pool内的操作一定要在线程间被同步。所以这里建议每个线程都有自己的Command Buffer Pool,这样每个线程才可以任意的做任何Command Buffer相关的操作。&/p&&img src=&/287a5f42f56c4a7afeb017fa_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/287a5f42f56c4a7afeb017fa_r.jpg&&&p&Command Buffer Pool的另一个性质就是支持非常高效的重置。一旦重置,所有由当前Pool分配的Command Buffer都会被清零,并且不会有任何内存管理上的碎片。所以程序只要为每一个帧和线程的组合分配一个Command Buffer Pool,就可以利用这一点,在更新Round Robin中的Command Buffer时非常快速的将需要的Buffer清零。&/p&&img src=&/b67ee6c8d7_b.jpg& data-rawwidth=&1800& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&1800& data-original=&/b67ee6c8d7_r.jpg&&&p&另一个类似Command Buffer Pool的组件,就是Descriptor Pool。所有Descriptor Set都由Descriptor Pool分配,Descriptor Set操作会导致对应的Descriptor Pool工作而且需要线程间同步,并且Descriptor Pool也支持非常高效的将所有由当前Pool分配的Descriptor Set一次性清零。所以程序应该为每个线程分配一个Descriptor Pool,可以根据Descriptor Set的更新频率,创建不同的Descriptor Pool,例如每帧、每场景等等。&/p&&p&快写完了,说一说Vulkan到底适用于哪些人。如果程序性能的瓶颈在于CPU上和图形相关的部分,并且这部分任务能相对容易的并行化,那么Vulkan很有可能有机会提升它的性能。亦或者对于想要榨干某个计算资源相对有限的平台上的性能,那么Vulkan中允许程序对所有资源直接的分配和管理也可能对性能有一定的帮助。再者,对于非常执着于尽可能的减少程序中的延迟和卡顿,因为Vulkan的驱动不会在背后做太多复杂的工作,那么也许也会有帮助。&/p&&p&但是,如果程序本身的瓶颈是GPU,Vulkan不见得有任何帮助。如果立即需要支持许多平台,并且想要有许多第三方的库,那Vulkan毕竟还非常新。如果程序在CPU端非常难以多线程并行化,那么Vulkan带来的提升也会比较有限。&/p&&p&最后,本文只提供了一些非常概念性的介绍,并不期望读完后会用Vulkan画一个三角形。但是却介绍了Vulkan背后的理念以及一些使用时候的一些模式和注意事项。相信这些东西比Hello World更加有价值。关于Vulkan编程的教程以及资源,请参考下面的链接:&br&&/p&&ol&&li&NVIDIA Developer,包括几个多线程的Sample:&a href=&/?target=https%3A///Vulkan& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&/Vu&/span&&span class=&invisible&&lkan&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&br&&/li&&li&Vulkan Spec:&a href=&/?target=https%3A//www.khronos.org/registry/vulkan/specs/1.0/apispec.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Vulkan API Reference Pages&i class=&icon-external&&&/i&&/a&&br&&/li&&li&Vulkan SDK:&a href=&/?target=http%3A///& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Home Page&i class=&icon-external&&&/i&&/a&&br&&/li&&li&Render Doc的教程:&a href=&/?target=https%3A//renderdoc.org/vulkan-in-30-minutes.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Vulkan in 30 minutes&i class=&icon-external&&&/i&&/a&&br&&/li&&li&Cinder引擎的整合:&a href=&/?target=https%3A//libcinder.org/notes/vulkan& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Vulkan Notes :: Cinder&i class=&icon-external&&&/i&&/a&&/li&&/ol&
这个星期我在上给了一个Talk,主题是如何使用OpenGL以及其扩展和Vulkan做高性能的实时渲染。这篇专栏主要内容来自这个Talk中Vulkan的部分,主要讨论一下Vulkan的使用动机,背后的设计哲学,API框架,以及使用时候的一些建议。Talk的视频连接在此:…
我是问题里文章的作者,也在英伟达直接参与了这一系列VRWorks中多重投影技术(Simultaneous Multi-Projection)的研发,即MultiRes,Lens Matched Shading和Single Pass Stereo。我来稍微解释一下。&br&&br&MultiRes,Lens Matched Shading以及Single Pass Stereo这三个技术都并不能够由显卡驱动单方面完成,而是需要游戏开发者主动的将这些技术集成到他们的引擎里去。所以在驱动菜单里并不会存在这些选项。&br&&br&这三个技术在引擎中的集成其实并非易事。他们首先都依赖于一个NV独有的Fast Geometry Shader技术,用于同时将几何体快速的发送到多个Viewport中去。而且MultiRes改变了原本Viewport的大小,Lens Matched Shading则更进一步直接改变了几何在光栅化前在剪裁空间中的位置, 连你的GBuffer长什么样都改了!所以引擎中所有涉及到屏幕空间的操作,例如从GBuffer读取信息,Deferred光照的计算,又例如所有的Post Process部分都受到了影响。至少是要将线性的UV纹理坐标转化到Warp之后的空间中去。用到Depth Buffer的操作,例如重深度重建世界空间坐标,屏幕空间环境遮挡(SSAO),屏幕空间反射(SSR)等在用Lens Matched Shading的时候还要还原像素原本的线性深度。所有的这些加上引擎内部本身的优化,性能优化等,都不是用一个控制面板的驱动设置能搞定的。&br&&br&可以在这个链接下载我们的SDK:&a href=&///?target=https%3A///vrworks& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&/vr&/span&&span class=&invisible&&works&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&,里面有一个简单的forward renderer sandbox,也简单实现了Temporal AA等算法,可以看一看有个大概的印象。真正的引擎集成当然要复杂很多很多。&br&感兴趣相关内容的可以关注我的专栏:&a href=&/graphics& class=&internal&&Behind the Pixels - 知乎专栏&/a&,等确认允许的时候我会写一篇更详细的介绍。
我是问题里文章的作者,也在英伟达直接参与了这一系列VRWorks中多重投影技术(Simultaneous Multi-Projection)的研发,即MultiRes,Lens Matched Shading和Single Pass Stereo。我来稍微解释一下。 MultiRes,Lens Matched Shading以及Single Pass Stereo…
&a href=&///?target=http%3A///video/av2696202/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&【1080p】《天谕》云垂正史CG首曝 多文明共襄盛世&i class=&icon-external&&&/i&&/a&,这个片子的制作过程简直就是从头到尾在梳理修改替换大量节点.片子除了刚开始入画,后面根本就是一镜到底。&br&&br&因为这个片子的正片是把所有模型贴图导入nuke,然后在nuke里调材质、打灯光、做特效、并直接合成输出的。&br&&br&光片子里的每个画面的模型节点都好几百个,每个篇章都有5~·10个小效果,每个效果都用了几个到几十个不等的节点。做大最后汇成了一个庞大的nuke文件的节点网络。&br&&br&在做之前我们先是按画面先后给节点分区域用Network Box上色从左向右排列,每个区域的贴图动态处理节点按效果从上到下排列再上Network Box..最后所有的连到一个scenes节点上渲染出来,再在渲染出来的后面按镜头从上往下排列添加渲染后的节点,&br&&br&来填坑了&br&&br&&img src=&/7d2e15edaf1fafd02e70f7_b.jpg& data-rawheight=&1080& data-rawwidth=&1717& class=&origin_image zh-lightbox-thumb& width=&1717& data-original=&/7d2e15edaf1fafd02e70f7_r.jpg&&先放一张全景图,摊子铺的实在太大,导致Network Box里面500px大的文字标签也缩的看不清了。&br&&br&&img src=&/0ad9deccc_b.jpg& data-rawheight=&1081& data-rawwidth=&1530& class=&origin_image zh-lightbox-thumb& width=&1530& data-original=&/0ad9deccc_r.jpg&&这个是其中第二三幅画的节点图,首先里面有两条竖着的主干分别是sh02和sh03的模型节点。然后围绕着这两个主干添加效果,每个效果一个Network Box病添加文字注释。最后连到一个scenes节点上。&br&&img src=&/4fd727e96afc92e_b.jpg& data-rawheight=&1068& data-rawwidth=&1571& class=&origin_image zh-lightbox-thumb& width=&1571& data-original=&/4fd727e96afc92e_r.jpg&&&br&然后所有镜头的scenes再连接到一个最终的scenes上。&br&&img src=&/dee268faebde0ce9d78d962_b.jpg& data-rawheight=&852& data-rawwidth=&1752& class=&origin_image zh-lightbox-thumb& width=&1752& data-original=&/dee268faebde0ce9d78d962_r.jpg&&&br&&br&&img src=&/aa7aaa34477_b.jpg& data-rawheight=&941& data-rawwidth=&1635& class=&origin_image zh-lightbox-thumb& width=&1635& data-original=&/aa7aaa34477_r.jpg&&最终的所有的模型贴图在三维视图中的效果。(内存君每次顶多撑半天就爆了,求教怎么释放nuke 的内存)&br&&br&&img src=&/935ec1ee415b9a0aa577dc5d572fa4fa_b.jpg& data-rawheight=&1092& data-rawwidth=&1446& class=&origin_image zh-lightbox-thumb& width=&1446& data-original=&/935ec1ee415b9a0aa577dc5d572fa4fa_r.jpg&&&br&&br&&br&&img src=&/1a82b1e2e5_b.jpg& data-rawheight=&982& data-rawwidth=&1027& class=&origin_image zh-lightbox-thumb& width=&1027& data-original=&/1a82b1e2e5_r.jpg&&&br&这个是sh01的节点图,竖着的那条是给这一镜所有模型依次上材质的节点。这个是从maya中读取信息用python生成的。&br&非商用的源代码可以移步这里查看。&a href=&///?target=http%3A//.cn/s/blog_140a959d50102vltm.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&maya2nuke 传递模型和贴图的关系&i class=&icon-external&&&/i&&/a&&br&&img src=&/d1c8a7cfe50461adea3d7_b.jpg& data-rawheight=&893& data-rawwidth=&947& class=&origin_image zh-lightbox-thumb& width=&947& data-original=&/d1c8a7cfe50461adea3d7_r.jpg&&上面那些黑线三角的尽头就是贴图,比如这个。&br&&br&&img src=&/03bace9e457_b.jpg& data-rawheight=&867& data-rawwidth=&1606& class=&origin_image zh-lightbox-thumb& width=&1606& data-original=&/03bace9e457_r.jpg&&&br&连好后在三维中的效果。&br&&br&&img src=&/77e16dd2a2e7fe3ae101_b.jpg& data-rawheight=&1012& data-rawwidth=&1677& class=&origin_image zh-lightbox-thumb& width=&1677& data-original=&/77e16dd2a2e7fe3ae101_r.jpg&&&br&整个场景的烟雾都集中在左边这个列了。然后再依镜头从上到下排列Network Box&br&每一个烟雾面片的节点都是按照再画面中的上下左右排列的。&br&&br&&br&&img src=&/2b6dd05e698e0f77b2be_b.jpg& data-rawheight=&709& data-rawwidth=&1551& class=&origin_image zh-lightbox-thumb& width=&1551& data-original=&/2b6dd05e698e0f77b2be_r.jpg&&把所有灯光集中在一起。这里的灯光主要是为了给飘动的旗子和披风加上立体感。&br&&img src=&/d4f3b21a8ffca_b.jpg& data-rawheight=&730& data-rawwidth=&1670& class=&origin_image zh-lightbox-thumb& width=&1670& data-original=&/d4f3b21a8ffca_r.jpg&&所有烟雾来个合影(到这里内存君快爆了)&br&&br&&b&最后来个反例&/b&&br&&img src=&/7bf50ea6a73a5cc54c047a_b.jpg& data-rawheight=&874& data-rawwidth=&1482& class=&origin_image zh-lightbox-thumb& width=&1482& data-original=&/7bf50ea6a73a5cc54c047a_r.jpg&&&br&&img src=&/ea31d9bbf_b.jpg& data-rawheight=&1017& data-rawwidth=&1489& class=&origin_image zh-lightbox-thumb& width=&1489& data-original=&/ea31d9bbf_r.jpg&&&br&&br&这个是我同事摆的飞船上喷出来的烟,完全不知道哪个是哪个。迫于周期实在太紧,后来也只能这样了。不过后面动画每次修改,都要他自己来改,哈哈。(基本都是周末或者晚上)&br&&br&之所以这样首先是因为工期实在太短了根本来不及渲染。而且所有环节是并行的。当初在试用katana的时候发现的这种流程感觉非常适合这个项目,但是katana又用不起来只有在nuke里做了。况且也没有人家那么土豪,所以渲染也定在了nuke里。&br&&br&一直到最后几天模型贴图动画还一直在修改中。在传统渲染中一帧要几十分钟到几个小时,况且这个项目里又大量的透明贴图,试了用ar渲染在后来的烟雾和水的合成中各种黑边。&br&&br&这些在nuke中都比较容易解决,而且渲染时间是1到10分钟不等。还能实时更新模型材质动画。
,这个片子的制作过程简直就是从头到尾在梳理修改替换大量节点.片子除了刚开始入画,后面根本就是一镜到底。 因为这个片子的正片是把所有模型贴图导入nuke,然后在nuke里调材质、打灯光、做特效、并直接合成…
&img src=&/129dffb87a69_b.png& data-rawwidth=&1014& data-rawheight=&574& class=&origin_image zh-lightbox-thumb& width=&1014& data-original=&/129dffb87a69_r.png&&&p&散射是一种非常美丽的自然现象,在自然界中光穿过潮湿或者含有杂质的介质时产生散射,散射的光线进入人眼,让这些介质看起来像拢住了光线一样,也就是所谓的体积光。&br&&/p&&p&在游戏中体积光是很常用的一种光照特效,主要用来表现光线照射到遮蔽物体时,在物体透光部分泄露出的光柱。由于视觉上给人很强的体积感,所以称之为体积光。&/p&&p&体积光特效在现今的中高端游戏中非常常见,优秀的体积光特效在烘托游戏氛围,提高画面质感方面发挥了很大的作用。&/p&&img src=&/a4fc949c02b_b.jpg& data-rawwidth=&1024& data-rawheight=&640& class=&origin_image zh-lightbox-thumb& width=&1024& data-original=&/a4fc949c02b_r.jpg&&&p&图中清晨透过树林间隙射出的体积光为游戏增色不少&/p&&p&作为一种常用特效,体积光的制作方式多种多样,不同游戏对于处理性能,画面要求和渲染质量要求各不相同,也会使用不同的体积光表现方式。&/p&&br&&p&早期游戏中由于机能限制经常使用的是BillBoard贴片和径向模糊这两种方式。&br&&/p&&br&在这里简单介绍一下两种方式,重头戏还是后面最新的基于光线追踪的方式。&br&&br&&h2&&b&BillBoard贴片&/b&&/h2&BillBoard贴片很容易理解,用PHOTOSHOP生成一个随机的明暗条文,加上遮罩,让它看起来有光条的感觉。&img src=&/c0f833a57ae29f6a319e18eed9bd8c17_b.png& data-rawwidth=&1230& data-rawheight=&545& class=&origin_image zh-lightbox-thumb& width=&1230& data-original=&/c0f833a57ae29f6a319e18eed9bd8c17_r.png&&&br&&br&将BillBoard放置在场景中光线会泄露出来的区域,这就是最简单的体积光效果。&br&做得精细一点的会再加上UV动画,粒子和远景透明的效果。&img src=&/df7da142fa3f7b2d41d40d_b.png& data-rawwidth=&557& data-rawheight=&554& class=&origin_image zh-lightbox-thumb& width=&557& data-original=&/df7da142fa3f7b2d41d40d_r.png&&&br&&br&&h2&&b&径向模糊&/b&&/h2&径向模糊是一种后处理的方法。主要用来表现天空中日月星光散射的效果。&br&&br&所谓后期处理就是在游戏画面渲染完毕之后,另外加一次渲染,类似于PHOTOSHOP,但处理的对象是每一帧游戏画面,因为速度要求多使用GPU计算。&br&&br&径向模糊体积光主要流程如下:&br&&br&1:渲染出整个画面&br&&br&2:抽取出画面中高亮的部分&br&&br&3:对高亮部分进行径向模糊&br&&br&4:将径向模糊后的高亮层和原图合并&br&&br&这个流程可以在PS中轻松实现,有兴趣的朋友可以打开PS试一试,我很多时候也会先在PS上试验一些效果,然后再用代码实现。&br&&br&这里简单介绍一下径向模糊的GPU实现,实现非常简单,就是从原本像素的位置开始,向画面中心移动坐标,每移动一次就采样一次,将所有采样叠加在一起就可以了。&img src=&/16eecf09f41ce6bacbf3f_b.png& data-rawwidth=&594& data-rawheight=&300& class=&origin_image zh-lightbox-thumb& width=&594& data-original=&/16eecf09f41ce6bacbf3f_r.png&&&br&&br&代码如下:&br&&div class=&highlight&&&pre&&code class=&language-glsl&&&span&&/span&&span class=&k&&vec2&/span& &span class=&n&&position&/span& &span class=&o&&=&/span& &span class=&n&&gl_FragCoord&/span&&span class=&p&&.&/span&&span class=&n&&xy&/span& &span class=&o&&/&/span& &span class=&n&&resolution&/span&&span class=&p&&.&/span&&span class=&n&&xy&/span&&span class=&p&&;&/span&
&span class=&n&&Position&/span& &span class=&o&&=&/span& &span class=&p&&(&/span&&span class=&n&&position&/span&&span class=&o&&-&/span&&span class=&mf&&0.5&/span&&span class=&p&&)&/span&&span class=&o&&*&/span&&span class=&mf&&2.&/span&&span class=&p&&;&/span&
&span class=&n&&position&/span&&span class=&p&&.&/span&&span class=&n&&y&/span& &span class=&o&&=&/span&&span class=&n&&position&/span&&span class=&p&&.&/span&&span class=&n&&y&/span&&span class=&o&&*&/span&&span class=&n&&resolution&/span&&span class=&p&&.&/span&&span class=&n&&y&/span&&span class=&o&&/&/span&&span class=&n&&resolution&/span&&span class=&p&&.&/span&&span class=&n&&x&/span&&span class=&p&&;&/span&
&span class=&k&&vec2&/span& &span class=&n&&uv&/span& &span class=&o&&=&/span& &span class=&n&&position&/span&&span class=&p&&;&/span&
&span class=&c1&&//最后颜色&/span&
&span class=&k&&vec4&/span& &span class=&n&&color&/span& &span class=&o&&=&/span&&span class=&k&&vec4&/span&&span class=&p&&(&/span&&span class=&k&&vec3&/span&&span class=&p&&(&/span&&span class=&mf&&0.&/span&&span class=&p&&),&/span&&span class=&mf&&1.&/span&&span class=&p&&);&/span&
&span class=&c1&&//采样次数&/span&
&span class=&n&&Const&/span& &span class=&k&&int&/span& &span class=&n&&stepNum&/span& &span class=&o&&=&/span&&span class=&mi&&12&/span&&span class=&p&&;&/span&
&span class=&c1&&//每次采样衰减&/span&
&span class=&k&&float&/span& &span class=&n&&decay&/span& &span class=&o&&=&/span& &span class=&mf&&0.9&/span&&span class=&p&&;&/span&
&span class=&c1&&//采样权重&/span&
&span class=&k&&float&/span& &span class=&n&&weight&/span& &span class=&o&&=&/span& &span class=&mf&&1.&/span&&span class=&p&&;&/span&
&span class=&c1&&//坐标移动方向&/span&
&span class=&k&&vec2&/span& &span class=&n&&direction&/span& &span class=&o&&=&/span& &span class=&n&&normalize&/span&&span class=&p&&(&/span&&span class=&n&&uv&/span&&span class=&p&&);&/span&
&span class=&k&&for&/span&&span class=&p&&(&/span&&span class=&k&&int&/span& &span class=&n&&i&/span&&span class=&o&&=&/span&&span class=&mo&&0&/span&&span class=&p&&;&/span& &span class=&n&&i&/span& &span class=&o&&&&/span& &span class=&n&&stepNum&/span& &span class=&p&&;&/span& &span class=&n&&i&/span&&span class=&o&&++&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&c1&&//移动坐标&/span&
&span class=&n&&uv&/span& &span class=&o&&-=&/span& &span class=&n&&direction&/span&&span class=&o&&/&/span&&span class=&k&&float&/span&&span class=&p&&(&/span&&span class=&n&&stepNum&/span&&span class=&p&&)&/span& &span class=&p&&;&/span&
&span class=&k&&vec4&/span& &span class=&n&&sample&/span& &span class=&o&&=&/span& &span class=&n&&texture2D&/span&&span class=&p&&(&/span&&span class=&n&&iChannel0&/span&&span class=&p&&,&/span& &span class=&n&&uv&/span&&span class=&p&&);&/span&
&span class=&n&&sample&/span& &span class=&o&&*=&/span&&span class=&n&&weight&/span& &span class=&p&&;&/span&
&span class=&n&&color&/span& &span class=&o&&+=&/span& &span class=&n&&sample&/span&&span class=&p&&;&/span&
&span class=&n&&weight&/span& &span class=&o&&*=&/span& &span class=&n&&decay&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&这里可以调整移动步幅,采样的数量,移动的方向,会得到不同的效果。&br&&/p&&p&因为实现简单,消耗小,效果也不错,径向模糊在很多游戏中都有广泛应用。&/p&&img src=&/fb42f03ddd7bfe71c16ec_b.jpg& data-rawwidth=&358& data-rawheight=&270& class=&content_image& width=&358&&&br&&p&Webgl径向模糊体积光例子&/p&&br&&p&&a href=&/?target=https%3A///view/MdG3RD& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://www.&/span&&span class=&visible&&/view/MdG3&/span&&span class=&invisible&&RD&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&br&&br&当然径向模糊的缺点十分明显,如果光源不在画面内,显然径向模糊是没办法执行的。&/p&&br&&h2&&b&新的视野&/b&&/h2&&p&上面两种方式在游戏制作中已经使用了很长时间,但这绝对不表示体积光效果只能做到这个程度,其实还有很大的进步空间。&/p&&p&近期伴随着渲染技术的进步,业界已经开始使用基于光线追踪、阴影贴图等更为精细的渲染技术来实现体积光的效果。&/p&&br&尤其是最近几年,Nvidia、寒霜引擎等几家大厂连续在Siggraph和GDC上发表了多篇关于实时体积光渲染的技术论文。这些算法不论是在模型的精密程度还是计算效率,相较于之前都有了质的飞跃,新一代的体积光已经成为3A游戏的标准配置。&p&&img src=&/526b0e88df899aeeb947bb_b.png& data-rawwidth=&1449& data-rawheight=&761& class=&origin_image zh-lightbox-thumb& width=&1449& data-original=&/526b0e88df899aeeb947bb_r.png&&PS:大厂现在做什么都号称是基于物理,实际和真正的物理学、光学比起来精度差得还是千山万水,个人觉得现在实时渲染的技术基本就和古典绘画的光影分析和制作法差不多,非要谈基于物理就有点牵强了。&/p&&p&下面终于要进入正题了,这里会从建模开始一步步深入分析基于光线追踪的体积光算法的各个方面。&/p&&p&我们先从如何描述体积光,也就是为算法建立模型开始。&/p&&p&因为体积光是我们生活中能看到的现象,我们可以从分析自然现象开始,看能不能得到建模的灵感。&/p&&p&首先光自身显然是没有体积的,我们也不可能看见光的形状。那在日常生活中看到的光柱到底是什么,答案很简单其实就空气中的尘埃。&/p&&img src=&/29de26fc64cd631bd0c8_b.jpg& data-rawwidth=&4928& data-rawheight=&3264& class=&origin_image zh-lightbox-thumb& width=&4928& data-original=&/29de26fc64cd631bd0c8_r.jpg&&&p&空气中布满大量微小的尘埃,我们看到的光柱就是光线击中尘埃后散射到我们眼睛中的。&/p&&p&这时候有人就说了,那只要把尘埃模拟出来,放到现在的渲染引擎中就万事大吉了。&/p&&p&这当然是最正确的做法,同时却也是最不切实际的做法,因为实时渲染显然不可能在几毫秒内模拟光线在空气中上亿灰尘分子间发生的各种折射、反射、散射。打个比方这就像是让牛顿只用自己的三大定律去计算大海里每个水分子是怎么碰撞的一样。&/p&&p&渲染尤其是实时渲染,最终要的不是所谓的基于物理,而是找到足够近似于真实情况(骗过人眼)的计算方式。&/p&&p&既然不能用蛮力模拟,就要想一些巧妙的办法了,我们先设计一个近似模型,这个模型可以非常简单,只要先表现出一些我们需要的光学性质就行,其他的可以一步一步加上去。&/p&&p&这里我们要思考一下是否需要尘埃数量这么巨大光学特性又很复杂的东西,因为空气中的尘埃非常小,甚至难以看到,不如用一种匀质的物质代替。当然这种物体要和原来渲染引擎的真空不一样,它要能把光折射到观察者眼中,还要符合光传播随着距离增加而衰减的特性。&/p&&p&&b&光线追踪&/b&&/p&&p&上面只是这个近似模型的特性描述,真实地写出算法就要考虑怎么看见这种物质,也就是怎么渲染这种物质。&/p&&p&这里我们使用光线追踪的算法框架。&/p&&img src=&/6ed06df08ee65b6df02af973f8066a22_b.png& data-rawwidth=&400& data-rawheight=&300& class=&content_image& width=&400&&&p&光线追踪简单说就是设置一个虚拟的眼睛,这个眼睛在三维空间里是在屏幕外的一个点。&/p&&br&屏幕上的每个象素渲染的时候,就是从虚拟的眼睛开始做一条射线通过所要渲染的像素,这条射线和屏幕里面的三维空间交汇在哪里(另一种说法射中哪个位置),像素就渲染那个位置的颜色。&br&&br&注意这里我们不能照搬光线跟踪的定义,要稍微改变一下,因为我们要渲染的这种匀质物体显然不只是表面起作用,而是在射线经过的路径上每个点都会对像素的颜色产生贡献。&br&&br&我们从起点开始,沿着射线每次推进一点,采样每个点的亮度,所有经过的采样点上的亮度求和就是像素的颜色。&img src=&/f2efabbc66_b.png& data-rawwidth=&542& data-rawheight=&406& class=&origin_image zh-lightbox-thumb& width=&542& data-original=&/f2efabbc66_r.png&&&p&在匀质物理内部每个采样点的亮度值怎么算呢。按照上面模型的规则要符合光传播按距离增加而衰减的特性。这里用一个简单的衰减公式,光的亮度和离光源的距离成平方反比。&/p&&p&就得到了一个公式i = l/d^2。&/p&&p&代码如下:&/p&&div class=&highlight&&&pre&&code class=&language-glsl&&&span&&/span&&span class=&c1&&//ro视线起点,rd是视线方向&/span&
&span class=&k&&vec3&/span& &span class=&n&&raymarch&/span&&span class=&p&&(&/span&&span class=&k&&vec3&/span& &span class=&n&&ro&/span&&span class=&p&&,&/span& &span class=&k&&vec3&/span& &span class=&n&&rd&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&k&&const&/span& &span class=&k&&int&/span& &span class=&n&&stepNum&/span&&span class=&o&&=&/span& &span class=&mi&&100&/span&&span class=&p&&;&/span&
&span class=&c1&&//光源强度&/span&
&span class=&k&&const&/span& &span class=&k&&float&/span& &span class=&n&&lightIntense&/span& &span class=&o&&=&/span& &span class=&mf&&100.&/span&&span class=&p&&;&/span&
&span class=&c1&&//推进步幅&/span&
&span class=&k&&float&/span& &span class=&n&&stepSize&/span&&span class=&o&&=&/span& &span class=&mf&&250.&/span&&span class=&o&&/&/span&&span class=&n&&stepNum&/span&&span class=&p&&;&/span&
&span class=&k&&vec3&/span& &span class=&n&&light&/span&&span class=&o&&=&/span& &span class=&k&&vec3&/span&&span class=&p&&(&/span&&span class=&mf&&0.0&/span&&span class=&p&&,&/span& &span class=&mf&&0.0&/span&&span class=&p&&,&/span& &span class=&mf&&0.0&/span&&span class=&p&&);&/span&
&span class=&c1&&//光源位置&/span&
&span class=&k&&vec3&/span& &span class=&n&&lightPos&/span& &span class=&o&&=&/span& &span class=&k&&vec3&/span&&span class=&p&&(&/span&&span class=&mf&&2.&/span&&span class=&p&&,&/span&&span class=&mf&&2.&/span&&span class=&p&&,&/span&&span class=&mf&&.5&/span&&span class=&p&&);&/span&
&span class=&k&&float&/span& &span class=&n&&t&/span& &span class=&o&&=&/span& &span class=&mf&&1.0&/span&&span class=&p&&;&/span&
&span class=&k&&vec3&/span& &span class=&n&&p&/span& &span class=&o&&=&/span& &span class=&k&&vec3&/span&&span class=&p&&(&/span&&span class=&mf&&0.0&/span&&span class=&p&&,&/span& &span class=&mf&&0.0&/span&&span class=&p&&,&/span& &span class=&mf&&0.0&/span&&span class=&p&&);&/span&
&span class=&k&&for&/span&&span class=&p&&(&/span&&span class=&k&&int&/span& &span class=&n&&i&/span&&span class=&o&&=&/span&&span class=&mo&&0&/span&&span class=&p&&;&/span& &span class=&n&&i&/span&&span class=&o&&&&/span&&span class=&n&&stepNum&/span&&span class=&p&&;&/span&&span class=&n&&i&/span&&span class=&o&&++&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&k&&vec3&/span& &span class=&n&&p&/span& &span class=&o&&=&/span& &span class=&n&&ro&/span& &span class=&o&&+&/span& &span class=&n&&t&/span&&span class=&o&&*&/span&&span class=&n&&rd&/span&&span class=&p&&;&/span&
&span class=&c1&&//采样点光照亮度&/span&
&span class=&k&&float&/span& &span class=&n&&vLight&/span& &span class=&o&&=&/span& &span class=&n&&lightIntense&/span& &span class=&o&&/&/span&&span class=&n&&dot&/span&&span class=&p&&(&/span&&span class=&n&&p&/span&&span class=&o&&-&/span&&span class=&n&&lightPos&/span&&span class=&p&&,&/span&&span class=&n&&p&/span&&span class=&o&&-&/span&&span class=&n&&lightPos&/span&&span class=&p&&);&/span&
&span class=&n&&light&/span& &span class=&o&&+&/span& &span class=&o&&=&/span&&span class=&n&&vLight&/span&&span class=&p&&;&/span&
&span class=&c1&&//继续推进&/span&
&span class=&n&&t&/span&&span class=&o&&+=&/span&&span class=&n&&stepSize&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&k&&return&/span& &span class=&n&&light&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&这里要循环那么多次显得很浪费,有没有方法用一个公式算出来呢。&/p&&p&眼尖朋友一定看出来了,这不就是对一个函数求线积分吗。&/p&&p&没错,亮度值是一个空间函数,而光线追踪做的事情其实是求这个函数在视线线段上的线积分。&/p&&p&我们设计的这个简单的模型完全可以用积分的解析解代替光线追踪。&/p&&p&下面是推导过程,当然不考高数,答案可以直接抄。&/p&&p&设函数L(t)=I/r^2&/p&函数x(t)为视线的积分曲线&p&x(t) = ro +t*&/p&s 为光源&p&改写L(t)为L(t) = I/|x(t)-s|^2&/p&要求L(t)从0到介质深度d的积分&p&L(t) = I/dot(p+t*rd-s,p+t*rd-s)&/p&&p&L(t) = I/t^2*dot(rd,rd)+2*dot(p-s,d)t+dot(p-s,p-s)&/p&&p&设dot(p-s,p-s)=c,dot(p-s,d) = b,而且dot(rd,rd)一定等于1&/p&&p&就得到L(t) = I/t^2+2bt+c^2&/p&&p&在裂项L(t) = I/(t^2+2bt+b^2)+(c-b^2)&/p&&p&接着替换u = (t+b),v=(c-b^2)^1/2&/p&&p&积分转换为求 du/u^2+v^2从b到b+d的积分&/p&&p&最后得到I/v*arctan(u/v)从b到b+d&/p&&p&积分解析公式为 I/v*(arctan(b+d/v)-arctan(b/v))&/p&&br&&br&写成代码如下:&br&&br&&div class=&highlight&&&pre&&code class=&language-glsl&&&span&&/span&&span class=&k&&float&/span& &span class=&n&&InScatter&/span&&span class=&p&&(&/span&&span class=&k&&vec3&/span& &span class=&n&&start&/span&&span class=&p&&,&/span& &span class=&k&&vec3&/span& &span class=&n&&rd&/span&&span class=&p&&,&/span& &span class=&k&&vec3&/span& &span class=&n&&lightPos&/span&&span class=&p&&,&/span& &span class=&k&&float&/span& &span class=&n&&d&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&k&&vec3&/span& &span class=&n&&q&/span& &span class=&o&&=&/span& &span class=&n&&start&/span& &span class=&o&&-&/span& &span class=&n&&lightPos&/span&&span class=&p&&;&/span&
&span class=&k&&float&/span& &span class=&n&&b&/span& &span class=&o&&=&/span& &span class=&n&&dot&/span&&span class=&p&&(&/span&&span class=&n&&rd&/span&&span class=&p&&,&/span& &span class=&n&&q&/span&&span class=&p&&);&/span&
&span class=&k&&float&/span& &span class=&n&&c&/span& &span class=&o&&=&/span& &span class=&n&&dot&/span&&span class=&p&&(&/span&&span class=&n&&q&/span&&span class=&p&&,&/span& &span class=&n&&q&/span&&span class=&p&&);&/span&
&span class=&k&&float&/span& &span class=&n&&iv&/span& &span class=&o&&=&/span& &span class=&mf&&1.0&/span&&span class=&n&&f&/span& &span class=&o&&/&/span& &span class=&n&&sqrt&/span&&span class=&p&&(&/span&&span class=&n&&c&/span& &span class=&o&&-&/span& &span class=&n&&b&/span&&span class=&o&&*&/span&&span class=&n&&b&/span&&span class=&p&&);&/span&
&span class=&k&&float&/span& &span class=&n&&l&/span& &span class=&o&&=&/span& &span class=&n&&iv&/span& &span class=&o&&*&/span& &span class=&p&&(&/span&&span class=&n&&atan&/span&&span class=&p&&(&/span& &span class=&p&&(&/span&&span class=&n&&d&/span& &span class=&o&&+&/span& &span class=&n&&b&/span&&span class=&p&&)&/span& &span class=&o&&*&/span& &span class=&n&&iv&/span&&span class=&p&&)&/span& &span class=&o&&-&/span& &span class=&n&&atan&/span&&span class=&p&&(&/span& &span class=&n&&b&/span&&span class=&o&&*&/span&&span class=&n&&iv&/span& &span class=&p&&));&/span&
&span class=&k&&return&/span& &span class=&n&&l&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&&br&现在我们的模型还很简陋,如果单独使用会略显单薄。&br&这时候就是发挥艺术想象力的时候了,可以结合别的画面元素一起达到漂亮的结果。&br&下面是两个用例&/p&一个是用作低消耗的光晕,在气球周围的就是球状的体积光&img src=&/aecdfc5804c_b.png& data-rawwidth=&800& data-rawheight=&456& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&/aecdfc5804c_r.png&&&img src=&/129dffb87a69_b.png& data-rawwidth=&1014& data-rawheight=&574& class=&origin_image zh-lightbox-thumb& width=&1014& data-original=&/129dffb87a69_r.png&&&p&另一个是把体积光框定在圆锥体内部,制造出探照灯的效果。&/p&&p&水下部分的光带是结合了上面提到的贴片制作的。&/p&&p&&b&散射函数&/b&&/p&&p&现在再回想一下我们刚才的模型,为什么视线上的所有采样点的亮度之和能用来表示光线在介质中散射的效果呢。&/p&&p&其实我们在假设每个点上都有个尘埃,光照射到尘埃上时,会把所有光准确都反射到我们的眼睛里。&/p&&p&如果研究得更精细一点就会发现,尘埃并没有自动跟踪系统,是不可能正好把所有光都反射到眼睛里的。&/p&&p&光的散射应该是向四面八方的,在一个以尘埃为球心的球体中几乎所有方向都有可能反射到光线,而且每个方向散射出去的光线亮度应该是不一样的,而这些散射出去的光线亮度总合应该和射到尘埃上的那束光线亮度一样,也就是能量守恒。&/p&这种散射可以用一个公式来表示,称为HG公式。&img src=&/2a739f06461cbb13a21ecb_b.png& data-rawwidth=&272& data-rawheight=&191& class=&content_image& width=&272&&&p&这个公式就是输入指定方向和光线入射方向夹角的cos值,求出指定方向散射光线的亮度。&/p&&p&我们要计算的是朝着眼睛方向的散射光线亮度。&/p&&p&代码如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&
float cosTheta = dot(lightDr,-rd);
float result = 1/(4*3.14)* (1 - g^2)/ pow(1 + g^2 -2*g* cosTheta, 1.5);
&/code&&/pre&&/div&&p&公式中的g值代表了介质散射性质,图中显示了不同g值对散射的影响。&/p&&img src=&/f67bb17c9c7e8fd4c5d168_b.png& data-rawwidth=&819& data-rawheight=&329& class=&origin_image zh-lightbox-thumb& width=&819& data-original=&/f67bb17c9c7e8fd4c5d168_r.png&&&h2&&b&透光比&/b&&/h2&&p&模型另一个要改进的部分是光线透光比例。&/p&&p&透光现象就是说光在介质内内传播,会被吸收一部分,剩下的部分才能透过介质达到观察者眼中。&/p&这里要用一个物理法则Beer–Lambert法则。这个法则描述的是入射光强度和透光强度的比值。&p&这个公式可以简单的写成,Out = In*exp(-c*d)。&/p&&p&c是物质密度,d是距离。&br&&/p&&p&透光强度随着介质的密度光传播的距离的增加成指数下降。&/p&&h2&&b&阴影&/b&&/h2&&p&体积光还有一个重要的元素,阴影,就是它的加入让体积光产生了各种形状。&/p&&p&没有阴影的体积光其实就是雾。&/p&&p&阴影的计算在实时渲染中是比较复杂的,要展开讲内容很多,这里只介绍一下简单的原理。&/p&&p&按照我们的模型图来看,每一次采样的时候如果采样点被阴影遮住了,就直接认为采样结果为0。&/p&&img src=&/beae7f710a_b.png& data-rawwidth=&428& data-rawheight=&370& class=&origin_image zh-lightbox-thumb& width=&428& data-original=&/beae7f710a_r.png&&&p&所以需要知道一个关键信息,就是采样点是否被光照到。&/p&&p&计算过程简单描述起来就是,连接采样点和光源点作一条线段,然后检测场景中这条线段有没有和别的可见物体相交。如果有交点就判定该采样点被阴影遮挡,不记入采样数据,反之正常计算。&/p&&p&线段和几何形状的求交公式翻一下图形学的课本都能很快找到,这里就不再赘述了。&/p&&br&&br&&h2&&b&最终公式&/b&&/h2&&p&将上面三项加入我们的模型后,来重新审视一下我们的模型。&/p&在每个采样点上都需要计算四个参数,来自光源的光照亮度L,采样点到眼睛的透光率T,光线在视线方向上的散射值P,阴影值V。&p&如果是非均匀介质,比如有噪音的雾,还要计算采样点位置的介质密度D。&/p&&p&散射S= L*T*V*P*D&/p&&p&这就是最终的散射公式。&/p&&p&具体代码可以参看这个例子&/p&&img src=&/6faab85a04eaaff230d3d_b.png& data-rawwidth=&513& data-rawheight=&289& class=&origin_image zh-lightbox-thumb& width=&513& data-original=&/6faab85a04eaaff230d3d_r.png&&&p&&a href=&/?target=https%3A///view/XlBSRz& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://www.&/span&&span class=&visible&&/view/XlBS&/span&&span class=&invisible&&Rz&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&br&&p&&b&从算法到现实&/b&&br&&/p&&p&在实际游戏开发的时候,还有很多算法优化的部分。&/p&&p&一、使用3d纹理保存光照和阴影信息&/p&&p&因为游戏渲染的时候同样需要计算阴影和光照,没必要为了渲染体积光重新计算一遍。论文[1]提出的算法是在游戏渲染的同时将光照和阴影保存在一个3d纹理中,之后计算体积光只要对3d纹理中的信息采样就行了。3d纹理的保存方式也有很多种,UE4使用了一种层级式样的3d纹理,因为同时保存了空间层级数据,在3d索引时更快。&/p&&br& 二、使用随即采样减少采样次数&br&今年的独立游戏公司DeadPlay使用了随即采样的方法,很大程度上减少了光线追踪采样的次数,每个象素只采样了3次,使得高端的游戏特效在手机端也可以顺畅运行。&br&&br&三、添加噪点和抗锯齿算法柔滑画面&br&随机采样可以结合TemporalAA算法平滑画面,每一帧的随机采样生成的随机场和TemporalAA每帧的ID关联,这样把采样分散到时间维度上,平滑后同样能达到较高的精度。&br&&br&&p&参考文献&/p&&p&[1]&a href=&/?target=http%3A///s2014/wronski/bwronski_volumetric_fog_siggraph2014.pdf& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Volumetric Fog SIGGRAPH 2014 - Advances in Real-Time Rendering&i class=&icon-external&&&/i&&/a&&/p&&p&[2]&a href=&/?target=https%3A///sites/default/files/akamai/gameworks/downloads/papers/NVVL/Fast_Flexible_Physically-Based_Volumetric_Light_Scattering.pdf& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Fast, Flexible, Physically-Based Volumetric Light Scattering - NVIDIA Developer&i class=&icon-external&&&/i&&/a&&/p&&p&[3]&a href=&/?target=http%3A///2015/08/physically-based-unified-volumetric-rendering-in-frostbite/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Physically-based & Unified Volumetric Rendering in Frostbite&i class=&icon-external&&&/i&&/a&&/p&&p&[4]&a href=&/?target=http%3A///session/low-complexity-high-fidelity-inside-rendering& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Low Complexity, High Fidelity - INSIDE Rendering - GDC &i class=&icon-external&&&/i&&/a&&/p&&p&[5]&a href=&/?target=http%3A///session/low-complexity-high-fidelity-inside-rendering& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Fogshop: Real-Time Design and Rendering of. Inhomogeneous, Single-Scattering Media.&i class=&icon-external&&&/i&&/a&&/p&
散射是一种非常美丽的自然现象,在自然界中光穿过潮湿或者含有杂质的介质时产生散射,散射的光线进入人眼,让这些介质看起来像拢住了光线一样,也就是所谓的体积光。 在游戏中体积光是很常用的一种光照特效,主要用来表现光线照射到遮蔽物体时,在物体透光…
&p&&b&作为一个在中国生活十几年,在游戏行业工作八九年的歪果仁,我从09年就开始使用Unity 3d,并对技术美术做了还算深的研究。由于中文水平不够完美,我还是用英文来和大家分享一下技术美术的经验。PS:&/b&&b&我申请了一个QQ群,有关Shader, Level Design, Game Music都可以加进来&/b&&/p&&br&&p&My name is David and I am a founder of Mechanist Games(梦加网络). Our company made City of Steam(蒸汽之城) and Dream Raiders(盗梦英雄),
and of course have projects in development too.&/p&&br&&p&Shader coding is still a new field in the world, and even
younger in China, so I’ll make this article more about advice, not technical
details. The hardest thing is just knowing how to begin. And that’s all I can
briefly cover here.&/p&&br&&p&&b&Who should do shader
coding, art or coder?&/b&&/p&&br&&p&Last San Francisco GDC at the tech-artist round table, this
same question came up.&/p&&p&Neither. A tech artist should do shader coding. But don’t
worry both artists and coders can become tech artists.&/p&&br&&p&Remember that tech artists in the west are a complete mixed
group, specializing in complex animation rigging, performance optimization,
creating tools for a large art pipeline and coding shaders. In China, the
amount of human resources gaming companies can use usually means that some of
these things be solved by basically just hiring more people or making them work
harder. Western companies have to achieve more with less people, so tech
artists are more than Chinese.&/p&&br&&p&A tech artist needs both sound logic and artistic knowledge
to code good shaders. These are both things you can learn the same way I did
(see below).&/p&&br&&p&&b&How do western
companies do shaders?&/b&&/p&&br&&p&Shaders are a core technology of a game. They have a huge
impact on the visual style of a game. The kind of shaders that need to be used
would be decided very early, when determining the art style of a game.&/p&&br&&p&A tech artist would find out how to achieve the art style,
develop prototype shaders and other assets to realize the desired look and feel
with the art director.&/p&&br&&p&Then, if the game prototype passes a company evaluation, the
tech artist would then begin creating tools and pipeline workflow documents to
teach the other artists in the team how to make the assets required for this
style.&/p&&br&&p&Toward the end of the project, the tech artist would be
mainly focused on optimizing GPU performance and trying to solve compatibility
issues for rendering on different types of target platform.&/p&&br&&p&Even giant European companies like CD Projekt Red only have
a few tech artists, so not all their time can be focused on shaders. Other US
companies like Volition are known to have more tech artists compared to their
size, but these people are still incredibly rare. Many people with tech artist
knowledge go on to develop game engines too.&/p&&br&&p&&b&How do you study how
to write shaders?&/b&&/p&&br&&p&Firstly, have you got what it takes?&/p&&p&There are two core abilities needed to make shaders: Logic
and creativity. If you have these in any amount, it’s possible for you to learn
shader coding.&/p&&br&&p&There is an experience requirement, too. I’d say it’s about
3 years, but it may vary greatly. You really need to know the engine you are
going to use very deeply.&/p&&br&&p&And lastly, there is an English language requirement. If
your English is excellent, you will have access to more study materials and lots
of experienced professionals through the unity asset store.&/p&&br&&p&&b&What language should you learn?&/b&&br&&/p&&br&&p&In China, learn Cg (not CG).
That is Nvidia’s “C for graphics” language. It doesn’t stand for “Computer
Graphics”. It is supposed to be like other C languages, but I don’t really
think it is.&/p&&br&&p&If you learn unity’s shaderlab
or GLSL shader languages, you’re always going to limit yourself. Unity
can compile your Cg shaders for any platform you like, anyway.&/p&&br&&p&Since China is mobile
dominated, Cg is the best choice because it is the most efficient for OpenGL
platforms like Androids or iOS devices. The advantages of getting into GLSL to
benefit from Apple’s Metal API are barely worth it right now, and you’d have to
be doing something really advanced to even need it.&/p&&br&&p&In the future, if mobile
devices become powerful enough that we can start using surface shaders, then
learning will be really easy if you are already a master of Cg shaders.&/p&&br&&p&盗梦英雄’s
shaders are all in Cg, and the performance is excellent. I built all the levels
myself to benefit from the optimized shaders and the results are pretty
satisfactory. I had a good teacher, which helped. Most scenes are ~40k-100k
vertices, which is high for a mobile game. Plus, I learned some good tricks we
are using in our next game too.&/p&&br&&p&&b&What tools do I need?&/b&&br&&/p&&p&You need to be proficient in Photoshop,
3dsMax, and Unity3d. There are many alternative programs like Blender and Maya
too.&br&&/p&&br&&p&There are visual tools like
shaderforge that can be used to create shader code, but unfortunately, these
are useless for mobile devices. The code they produce is likely to be many times
less efficient than Cg, and usually result in thermal issues and poor FPS on
the target device.&/p&&p&Aside from unity MonoDevelop
for writing the shaders, there are lots of choices. And it really is just
preference. Unlike other coding language tools (Visual Studio for example),
shaders don’t have error catching helpers. When there is a mistake in your
code, you won’t get warnings –it will just fail to compile, or compile with
visual errors. There are so many ways to do things in a shader, and different language
syntaxes, so there still isn’t any good solution for this. You just have to
hunt the bugs down with your own eyes, or hope Unity’s console can point you to
the offending line of code (it often does not, though).&/p&&br&&p&One thing you will probably
need soon if you plan to get really serious about doing shader animation or
special effects is a vertex painting tool. Vertices of a model can have color
properties. These colors are not visible in the game (unless you specifically
make them), and are used behind the scenes to do things like moving plants,
wind blowing hair, rolling waves in the sea, and so on. Use the asset store to
get one.&/p&&br&&p&Buy things in the asset store.
The developers of these little tools and plugins are so helpful. This is how
you get introduced to pros who are really experienced and specialized. All of
my teachers were people I purchased tools and plug-ins and assets from. And
many are now my friends too.&/p&&br&&p&&b&Shader theory&/b&&br&&/p&&p&You need to understand the
basic theory first, otherwise you won’t make it very far. Let me make a really
simple breakdown. For Unity3d vertex+fragment shaders (Cg, like you asked for)…&br&&/p&&br&&br&&img src=&/fe0c67daa17ce232ebd156_b.jpg& data-rawwidth=&895& data-rawheight=&392& class=&origin_image zh-lightbox-thumb& width=&895& data-original=&/fe0c67daa17ce232ebd156_r.jpg&&&br&&br&&p&Of course, this is not a
complete shader yet. But it’s how every shader you write will be structured,
and understanding how it works is fundamentally important. You need to know how
these bits work. And you can just read the internet to work this out.&/p&&br&&p&&b&Copy some shaders&/b&&br&&/p&&p&When collecting shader code,
the best thing to do is buy shader packages on the unity asset store. Make sure
you get Cg and not surface shaders –one or two reviews will tell you if it’s
worth buying. If somebody says it’s slow on mobile, don’t buy it.&/p&&br&&p&Look at the shader code. Start
with really simple ones, 50 lines or less. Edit things, change things, see the
results. Experiment.&/p&&br&&p&Make small goals for yourself. For
example, add a slider that can make the object lighter or darker. Add a tint
color. Remove a property. Change small things to build up your confidence.&/p&&br&&p&&b&Write your own shaders&/b&&br&&/p&&br&&p&Trying to write your own
shaders needs to start with small steps. The best guides I had were these, in
English. I can understand these might be daunting to a Chinese native. Does
anyone know a Chinese translation of them?&br&&/p&&br&&p&&a href=&///?target=https%3A///watch%3Fv%3DhDJQXzajiPg& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://www.&/span&&span class=&visible&&/watch?&/span&&span class=&invisible&&v=hDJQXzajiPg&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&p&&a href=&///?target=http%3A//en.wikibooks.org/wiki/Cg_Programming/Unity/Minimal_Shader& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Cg Programming/Unity/Minimal Shader&i class=&icon-external&&&/i&&/a&&/p&&br&&p&Aside from these two, my
knowledge came from a hundred tiny details and facts that I found all over the
net, and as advice from friends and teachers. Everything you can read will be
useful, as long as you can apply it to practice.&/p&&br&&p&When you start, avoid copy
pasting stuff. It will be harder to remember how to do it if you copy paste.
You’re going to copy paste lots of your own code later, so it’s good to know
what you’re actually reproducing. If you’re in the habit of assembling stuff
from different sources online, you are going to have a very hard time making
all the pieces fit.&/p&&br&&p&Once you can write your own
basic shader, your journey to become a shader coder has begun.&/p&&p&Honestly, the rest is downhill,
and gets increasingly more interesting and cooler as you go. You can start
adding more and more things to shaders like lighting, shadows, bumpmaps,
outlines, colors and other things.&/p&&br&&p&Training under someone skilled
is certainly the best situation. That’s how I learned. If you are lucky enough
to have a tech artist in your company, then try to apprentice yourself. If not,
you either have to change company or try to learn on your own. It’s very
uncommon for companies to give people time during work to study new tools or
skills, so you will have to be creative about it or very busy after hours.&/p&&br&&p&I hope that China GDC can also
have a tech artist round table some time soon.&/p&&br&&p&By the way, I’m hiring both
experienced and intern positions to study under me right now in Xiamen, so if
you are interested you can add
我申请了一个QQ群,有关Shader, Level Design, Game Music都可以加进来&/p&
作为一个在中国生活十几年,在游戏行业工作八九年的歪果仁,我从09年就开始使用Unity 3d,并对技术美术做了还算深的研究。由于中文水平不够完美,我还是用英文来和大家分享一下技术美术的经验。PS:我申请了一个QQ群,有关Shader, Level Design, Game Music…
&img src=&/021fd3de3765faa06010_b.png& data-rawwidth=&841& data-rawheight=&442& class=&origin_image zh-lightbox-thumb& width=&841& data-original=&/021fd3de3765faa06010_r.png&&&h2&写在前面&/h2&&p&开始学习图形学的人,尤其是计算机专业而不是数学专业,可能都有一个感觉,很多图形学公式让人完全摸不着头脑。这些公式看起来是微积分公式,但和高数里的不太一样,也不能套用高数课本上的方法推导。不少图形学教科书写得过于简略,导致初学者看这些公式如同看天书一样。教课书里莫名其妙写了几个公式,又莫名其妙地推导出了代码,代码里面还有一些完全不知道怎么来的参数。很多人因为这样的原因在图形学的门口就跪倒了。。。&/p&&p&由此还产生了一些自暴自弃的想法&/p&&p&&b&一、我高数是不是白学了?&/b&&/p&&p&比如经典的渲染等式&/p&&img src=&/equation?tex=L_o+%3D+%5Cint_%5COmega+%5Crho%28%5Comega_o%2C%5Comega_i%29+L_i+cos%28%5Ctheta_i%29+d%5Comega_i& alt=&L_o = \int_\Omega \rho(\omega_o,\omega_i) L_i cos(\theta_i) d\omega_i& eeimg=&1&&&p&虽然大学都学过基本的微积分,但这个积分怎么要算啊&br&&/p&&p&&b&二、积分其实能用乘法代替吧?!&br&&/b&&/p&&p&因为这样的积分公式&img src=&/equation?tex=L_%7B%5Comega_o%7D+%3D+%5Cfrac%7B1%7D%7Bcos%28%5Ctheta_i%29%7D%5Cint_%5COmega+L%28%5Comega_o%2C%5Comega_n%29G_1%28%5Comega_o%2C%5Comega_n%29%28%5Comega_o%5Ccdot+%5Comega_n%29D%28%5Comega_n%29d%5Comega_n+& alt=&L_{\omega_o} = \frac{1}{cos(\theta_i)}\int_\Omega L(\omega_o,\omega_n)G_1(\omega_o,\omega_n)(\omega_o\cdot \omega_n)D(\omega_n)d\omega_n & eeimg=&1&&&br&&/p&&p&推倒完成后写在代码里面是这样&/p&&div class=&highlight&&&pre&&code class=&language-glsl&&&span&&/span&
&span class=&k&&float&/span& &span class=&n&&Dr&/span& &span class=&o&&=&/span& &span class=&n&&GTR1&/span&&span class=&p&&(&/span&&span class=&n&&NdotH&/span&&span class=&p&&,&/span& &span class=&n&&mix&/span&&span class=&p&&(&/span&&span class=&mf&&.1&/span&&span class=&p&&,&/span&&span class=&mf&&.001&/span&&span class=&p&&,&/span&&span class=&n&&clearcoatGloss&/span&&span class=&p&&));&/span&
&span class=&k&&float&/span& &span class=&n&&Fr&/span& &span class=&o&&=&/span& &span class=&n&&mix&/span&&span class=&p&&(&/span&&span class=&mf&&.04&/span&&span class=&p&&,&/span& &span class=&mi&&1&/span&&span class=&p&&,&/span& &span class=&n&&FH&/span&&span class=&p&&);&/span&
&span class=&k&&float&/span& &span class=&n&&Gr&/span& &span class=&o&&=&/span& &span class=&n&&smithG_GGX&/span&&span class=&p&&(&/span&&span class=&n&&NdotL&/span&&span class=&p&&,&/span& &span class=&mf&&.25&/span&&span class=&p&&)&/span& &span class=&o&&*&/span& &span class=&n&&smithG_GGX&/span&&span class=&p&&(&/span&&span class=&n&&NdotV&/span&&span class=&p&&,&/span& &span class=&mf&&.25&/span&&span class=&p&&);&/span&
&span class=&k&&return&/span& &span class=&p&&((&/span&&span class=&mi&&1&/span&&span class=&o&&/&/span&&span class=&n&&PI&/span&&span class=&p&&)&/span& &span class=&o&&*&/span& &span class=&n&&mix&/span&&span class=&p&&(&/span&&span class=&n&&Fd&/span&&span class=&p&&,&/span& &span class=}

我要回帖

更多关于 unity修改器 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信