上周末CBD有个社招,收到社保退款如何做账了有个叫风潮美股的宣传页,听招聘人员讲解了一下,有人了解这个公司吗?

邵莉:济南要规划设计最先进的CBD
16年前的泉城路改造,她是首席设计师;如今济南中央商务区建设如火如荼,她是CBD规划设计管理平台负责人和技术总负责。济南几次大的CBD规划设计发布会,一名靓丽的女士在媒体的聚焦下,向全社会通报CBD规划设计的最新进展,引起全社会关注,她就是济南规划设计研究院副总工程师邵莉。
1991年,邵莉从山东建筑大学毕业,进入济南市规划设计研究院工作,“当初选择学城市规划,是对里面的课程挺感兴趣,包括摄影、美术等,关注美的东西。后来认识到,规划可以说是城市发展的上层建筑,涉及政治、经济、社会、文化、历史、地理等方方面面,既需要逻辑思维又需要形象思维,能培养人的系统思考和整体把握能力,很适合我。”
位于高新区的雅居园,是邵莉职业生涯的第一个代表作品,曾获过国家级金奖等许多规划奖项,被认定为国家康居示范工程。2001年,备受全民关注的泉城路商业街改造环境设计全国方案征集,在时任市规划设计院院长吕杰的主导下,邵莉及其团队夜以继日做出设计方案,最终从包括香港、北京等设计机构在内的众多方案中脱颖而出,拔得头筹,由此承担了泉城路改造的全实施设计,极大地鼓舞了邵莉及其团队。
2004年,邵莉攻读完成清华大学硕士学位归来,正值代表城市最上位法定规划、引领城市发展全局的2020版济南市城市总体规划开始,与泉城路微观设计不同,城市总体规划宏观整体、系统复杂、编制审批周期长,邵莉从技术核心组成员,到市区中心城组组长,再到项目负责人,最终成果于2016年获得国务院批复。
日,是邵莉一直铭记的日子。这一天,市委市政府决策实施“聚焦”战略,选址文博片区,举全市之力规划建设济南唯一的中央商务区。拍板当天,在规划局领导下,邵莉和同事们立刻开始工作,规划系统内部成立团队,她被确定为技术总负责人。邵莉说,与国际和国内一些城市相比,济南CBD算晚的,但优势是起点高,汲取经验不足,规划全球最先进的CBD。
市委市政府对CBD的高点定位,让负责和参与CBD的规划人都倍感压力。重任在肩,为了拿出最先进的规划,邵莉和团队马不停蹄探访先进城市CBD,从伦敦金融城、金丝雀码头,到东京六本木、新宿,再到天津于家堡、武汉王家墩、广州天河等,以及到芝加哥与SOM团队共同工作,辗转十余个各具特色的CBD,开拓国际化视野,吸收先进理念。
“曼哈顿芝加哥的密路网小街区、金丝雀码头人性化的交流空间、六本木的地上地下一体化,还有各地不同的地标建筑,都为济南CBD规划提供了参。”邵莉说,济南CBD要开展20多项规划设计,先后引入海内外十几家顶尖的专业团队加入到CBD规划设计队伍中来。在市建设领导小组统筹调度、规划策划组精密组织下,规划设计管理平台全方位管理,各团队通力协作,CBD成为济南规划理念最先进、落地实施最高效的项目。
2017年,邵莉被授予“山东省城市规划大师”称号,这是我省城市规划业界最高荣誉。邵莉说,能参与CBD的规划十分自豪,不仅学习到了很多先进的理念和工作方法,尤其是系统性大项目的技术管理,也提升了自己的综合协调能力,“城市规划得好才有竞争力,我也在一次次地突破自己,不断学习,努力让自己的眼界和规划水平保持领先。”
责任编辑:
声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。
今日搜狐热点分析师推荐
分析师排行榜
机构排行榜
周榜 | 月榜
周榜 | 月榜
最新入驻:今日头条、网易号、360快传、凤凰新闻、中视金融、汇金网、汇通网、交易商、叽歪网、投顾网、挖财网、财动向、爱投资、金商网、邮币通、析金法、中视金融
微信:caizhidao
财知道微博
电话:021-
媒体合作:
市场商务:
自媒体平台
投资有风险 入市需谨慎既然一个人,就必须待自己如女王(1)_网易新闻
既然一个人,就必须待自己如女王(1)
用微信扫码二维码
分享至好友和朋友圈
(原标题:既然一个人,就必须待自己如女王(1))
CBD空巢青年的座右铭:日常的吃穿用住,最能够体现CBD空巢青年精致的生活方式和对自己的珍爱。对于这群空巢青年来说,城市中央丰富的物质以及高于生活之上的精神享受,两者缺一不可。他们更追崇“生活由自己主宰”的真谛,因此,他们更愿意去消费,也更愿意去追求更好的享受——吃的、穿的、住的,通通都要最好的!所谓的奢侈和任性,只不过是能够真正实现主宰自己人生的表现形式而已。正如多名受访白领所言,既然一个人,就必须要吃好穿好用好,待自己如女王!新快报记者 罗韵/文 廖木兴/图吃在家做饭是什么鬼?偶尔为之,也是为了拍照发朋友圈在CBD里面,叫外卖和下馆子是解决吃饭问题的最主要方式。对于广州CBD的空巢青年来说,他们可能会很不屑地抛出一句:“在家做饭是什么鬼?即使偶尔为之,也是为了拍照发朋友圈好吗?”Louisa,80后, 某证券公司职员单人用餐,仪式感非常重要大多数的日子里,她都可以准时下班,有充足的时间可以去逛超市挑选食材,给自己做一顿晚饭,但是她偏不,理由是“首先,要想好买什么食材,做什么菜,接着,还要拎回家、洗、切、炒,弄得自己满身油烟味,厨房也脏脏乱乱的,以上种种就为了吃顿饭,这样实在太不划算了?”“为什么不出去吃?”Louisa向新快报记者反问道。在天气晴好的日子里,下班以后,Louisa会在公司脱下工装,换上自己好看的衣服,头发披散下来,补好妆才离开办公室。写字楼附近有好几家她最爱的西餐厅,性价比宜人,兼具食物品质和优雅环境,从装修到摆盘都是绝佳的自拍背景。“一个人点餐,只要胃口比较好,仪式感非常重要,要有汤、前菜、主食和甜点,再加一杯佐餐酒。”Louisa说。在这样的餐馆里面,独自用餐的白领男女并不少,他们各自安安静静、理直气壮占有一张桌子,享受食物和服务,唯一的饭搭也许是iPad或手机,并不是可怜到没有人陪着吃饭,只是一个人感觉比较自在。除了西餐,日本料理也是Louisa经常光顾的品类,一大杯生啤搭配拉面、刺身加烤串,也是消暑的好办法。这些在菜单和分量上都适合一人食、独自用餐也不显得突兀的场所,比起动辄要点上N菜一汤然后每样都吃不完的中餐厅,要更受到这群CBD空巢青年的欢迎。周末叫个火锅外卖,老家父母才信你过得好Ben,90后,某互联网公司职员跟大多数餐厅营业时间错开的上班节奏,让外卖成为Ben美食生活的主角。“其实,外卖并不意味着廉价至上的劣质食品”,Ben强调说,目前珠江新城里,大多数白领喜欢的餐馆都在外卖平台上开通了服务,面条、煲仔饭、比萨、沙拉应有尽有,连大闸蟹、小龙虾、海鲜火锅和烤鸡都可以送到家。他购置的房产在珠江新城某公寓楼内,开放式的厨房被改成了储物间使用,两个电磁炉也基本废置。每每一个人半夜下班回家,他会习惯性地在便利店扫一堆啤酒薯片,再叫上一份麻辣小龙虾外卖,“看着堆满桌的各种食物,想翻哪个牌子就翻哪个牌子,吃不完就剩着——感觉特别有满足感,特别治愈。”鸡胸肉沙拉也是Ben常吃的外卖,为了健康和维持身材,“规定自己每周一是沙拉日,再馋嘴也不吃其他的食物”。Ben笑着说。到了周末,几个同事或同学到他家里来聚餐,就会叫上一客火锅到会,有鱼有肉,拍下吃着大餐一脸大满足的合照,发给老家的父母看看,“他们以为是我周末自己在家做火锅,就会相信我过得很好。虽然我挺享受一个人的生活,但是老一辈人还是希望看到自己的孩子有朋友,有人陪,家里有热饭热菜吃,总是下馆子吃外卖,他们会担心你过得不好。”Ben说。穿在家要穿得舒适,出门要穿得美,这些都要靠买买买空巢青年的确会舍得花费在家居服上面,但这并不意味着他们在外出服上就会节省,在家要穿得舒适,出门要穿得美,这些都要靠买买买。一句话:尽管有些奢侈,但是这样才能有种主宰自己生活的感觉!宅家时光, 靠换家居服来切换场景Alicia,70后,某家具品牌职员Alicia说,自己衣柜里的家居服比有些姑娘的高跟鞋数量还多,冬天夏天加起来有几十套,真丝、纯棉、棉麻的套装裙装和外袍,根据季节和心情替换。“我是个比较宅的人,下班以后基本上就待在家里不出门,买什么都是送到家,看电影、画画、做手工、吃饭、喝咖啡和运动几乎所有的活动都是在家进行的,所以家里并不只是休息的地方,还能够切换成好多临时的其他场景,所以就需要多功能的家居服。”她举例说,真丝吊带睡裙只是被窝专用,在厨房做早餐的时候应该穿套头的纯棉大T恤,看书画画的时候就该换上套装了,如果是刚洗完澡,还得罩一件睡袍。朋友上门来探望,也要换上其他体面的家居服。“当你整整一个周末都足不出户的时候,基本上是靠换衣服来换场景、换节目的,也是一种自由自在、自娱自乐的生活方式。你不是为了任何人,任何场合而着装,只是为了自己的感觉的需求更换造型,有种主宰自己生活的感觉,好奢侈哦。”Alicia说。正因为家里没人管, 才要打扮漂亮出门去Rocky,90后,某影视制作公司职员夏天,Rocky跟大多数独居直男一样,只穿一条内裤在家裸奔,冬天的时候就靠两套无印良品的长袖长裤睡衣,但是他却拥有一整个步入式衣帽间,里面装满了行头和各色配件。
日常,Rocky会穿着合身的衬衫西裤提着公文包出现在街角的咖啡馆,也会穿着T恤和牛仔裤跟狗在公园的草地上打滚。更多时候,他会根据不同的节目,穿不同的造型出门。“空巢就应该宅家,就不用买出门的衣服,这是一种肤浅的偏见。正因为家里没人管、没节目,所以我们这样的人更应该打扮得漂漂亮亮出门去玩耍。”Rocky说。闲来无事的时候,他不喜欢待在家里面,即使是大太阳或者雨天,也要抱着电脑或书本到外面去。“去咖啡馆、去户外,有工作就去工作,没工作就带本书去读,带上狗出去草地上玩,在外面玩手机也好过在家里 葛优瘫 。一个人外出并不需要遮遮掩掩,没有一条道理说没人约你出门,就该躲在家里面。一个人同样有权利享受户外的阳光微风。”
(原标题:既然一个人,就必须待自己如女王(1))
本文来源:金羊网
责任编辑:王晓易_NE0011
用微信扫码二维码
分享至好友和朋友圈
加载更多新闻
热门产品:   
:        
:         
热门影院:
阅读下一篇
用微信扫描二维码
分享至好友和朋友圈&figure&&img src=&https://pic3.zhimg.com/v2-dcfbe9c310725_b.jpg& data-rawwidth=&3552& data-rawheight=&1558& class=&origin_image zh-lightbox-thumb& width=&3552& data-original=&https://pic3.zhimg.com/v2-dcfbe9c310725_r.jpg&&&/figure&&p&是时候熟悉ASIO了。&/p&&p&C++20标准库的网络部分将是基于ASIO,已无疑义,目前唯一的变数只是时间的问题。networking的提案依赖executor的提案,所以只有在executor进C++20的前提下,networking才能同一期进入。WG21(c++标准委员会)将在今年9月22至23两天内(&a href=&http://link.zhihu.com/?target=https%3A//isocpp.org/std/meetings-and-participation/upcoming-meetings& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&会议排期看这里&/a&),加班加点专门讨论executor的提案,肯定是希望executor进入C++20,所以对于网络部分进入C++20,也是可以期待的了。&/p&&p&对于ASIO进标准,是好是坏我觉得不重要,重要的是标准库中终于有网络相关的库了。大家都用一样的库,才能便于交流。如果每个人都自己造轮子,含有张三牌轮子的代码给李四用,使用起来成本会很高。标准库本身的意义不就是这样么?&/p&&p&关于ASIO的长短优劣众说纷纭,抱着小马过河的态度,我决定亲自体验一下,断断续续看了部分例子和源码,把之前做的某些网络工具也用ASIO重新实现了一遍,有一些体会。ASIO的异步接口非常易用,配合上C++11的lambda函数,用起来相当简单,写一个像ss那样的fan墙工具也只需要一天的时间,和go语言的同步+协程模型开发效率相当。后续我打算发一系列的学习笔记。&/p&&p&ASIO树大招风,业界有不少偏见,亦假亦真,作为开篇,我今天想针对这些偏见,一一发表一下我自己的看法。&/p&&p&据我所知,偏见主要有这几条:&/p&&p&&b&1、ASIO依赖boost,讨厌boost的人也无条件讨厌ASIO;&/b&&/p&&p&&b&2、ASIO内部有锁,导致牺牲了很多性能;&/b&&/p&&p&&b&3、ASIO为了跨平台,将Linux/Darwin系统的reactor模式,封装成了windows系统的proactor模式,对于Linux/Darwin牺牲了性能;&/b&&/p&&p&&b&4、ASIO很慢,不如自己裸写epoll;&/b&&/p&&p&&b&5、不喜欢ASIO的代码;&/b&&/p&&p&&br&&/p&&p&&b&先看1,ASIO依赖boost,讨厌boost的人也无条件讨厌ASIO;&/b&&/p&&p&我也讨厌boost,如果ASIO还未脱离boost就不会有这篇文章,现在C++11的编译器使用ASIO已经不需要boost了,编译的时候定义宏ASIO_STANDALONE即可,比如说用g++编译,采用g++ -DASIO_STANDALONE ….&/p&&p&&b&再看2,ASIO内部有锁,导致牺牲了很多性能;&/b&&/p&&p&有锁这个是事实,以后是多核的天下,多线程本来就是主流,作为高性能的库,以多线程为默认场景这个是可以接受的。但是在使用ASIO时,如果确定只用单线程,只要在构造io_context时传入参数1,即可。像这样:&/p&&p&asio::io_context context(1); // one thread&/p&&p&这是我看源码时发现的,只要设置了参数1,所有lock和unlock都会被if语句跳过,相当于是无锁。&/p&&p&顺便说一下,不要小看了单线程哦,异步编程的单线程性能那可是相当之高。&/p&&p&&b&再看3,ASIO为了跨平台,将Linux/Darwin系统的reactor模式,封装成了windows系统的proactor模式,对于Linux/Darwin牺牲了性能;&/b&&/p&&p&ASIO采用proactor没错。但是有两点我想说,首先是使用ASIO时,你依然可以使用reactor模式,其次是我不觉得将linux/darwin的epoll/kqueue实现成proactor让性能下降了。&/p&&p&程序员不缺乏偏执狂,linux程序员可能讨厌proactor,觉得asio居然让linux去迎合windows的套路,无法接受;而windows程序员觉得linux/mac的reactor只是半成品、觉得linux程序员好可怜,总之就是鄙视链无处不在。&/p&&p&采用ASIO如何使用reactor模式来编程呢?调用socket类的async_wait方法用来等待读或者写事件,在这个函数的回调函数里面去读或者写,就是reactor模式了!当然我相信应该还有其它方法也能使用reactor,毕竟源码里面相关的类名字就叫epoll_reactor、kqueue_reactor等等,直接拿来用也不是不可能。&/p&&p&那再来看性能问题,是不是linux的epoll封装成proactor就会变慢。reactor的epoll使用姿势一般是这样:程序监听读、写事件,事件到来以后系统会通知程序去调用read/write等函数进行读写数据;ASIO封装成proactor以后变成了这样:程序需要读数据时,将收取数据的buffer传给ASIO,ASIO监听读事件,在事件到来时帮你调用read函数,存到你的buffer里面,然后再告诉你:读到数据了,需要写数据时情况类似。&/p&&p&可见,reactor和proactor唯一的区别是read/write这些系统调用到底是由ASIO库去调用、还是自己写的函数调用,根本没有本质的区别,所以说牺牲性能也是无中生有。&/p&&p&(proactor对于每个scoket一般需要2个buffer,一万个socket就需要2w个buffer,比较浪费内存。reactor在这一点上面有一些些优势。对于某些场合的应用,不管多少socket,可能只需要少许几个buffer即可。但是在实战中,因为需要考虑tcp分包的问题,每次socket都需要将不满一个数据包的内容暂存起来,每次发送也不见得能一次发完同样需要将剩余数据存起来下次再发,所以依然需要2个buffer,和proactor没区别。)&/p&&p&&b&再看4:ASIO很慢,不如自己裸写epoll&/b&&/p&&p&这个我承认,因为我自己就喜欢裸写epoll。但是ASIO集成了很多特性,如果你自己想去用epoll实现类似的功能,肯定是要增加更多的类、每个类增加更多的成员变量、更多的成员函数,对象拷贝代价变大、函数调用次数变多,这样一来,性能有所下降不是正常的事情么?&/p&&p&以后有空我会去压测一下,看看ASIO比裸写的epoll到底慢多少,到时候回来更新内容,我觉得慢10%以内我都能接受。至少会压到几十万连接吧,暂时手头上没足够机器。&/p&&p&&b&继续看5:不喜欢ASIO的代码&/b&&/p&&p&其实C++标准只是规定了接口(名字空间、类名、函数名、参数个数及其类型等等)以及相关算法的复杂度,每个编译器到时候都会有自己的实现。可以这么认为:标准库中的实现代码质量应该优于(至少等于)ASIO、性能高于(至少等于)ASIO。而且,标准库中也看不到ASIO这个单词,到时候只有std::net名字空间,不存在asio名字空间,所以,不管是什么原因对于ASIO无法接受的,可以休矣。&/p&&p&只有抛弃偏见、拥抱变化,才能愉快地学习、提高。关于ASIO学习笔记的第一篇,内容就到这里了。快来一起愉快地学习ASIO吧。&/p&&p&&i&希望以后能够持续更新。因为是学习笔记,所以难免会有纰漏,欢迎大家批评指正, 对于确认之后的内容我会更新到文章的正文中。&/i&&/p&&p&&br&&/p&&p&附:&/p&&p&&i&1、c++标准进度在哪里看?&/i&&/p&&p&&a href=&http://link.zhihu.com/?target=http%3A//open-std.org/JTC1/SC22/WG21/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&可以看这里&/a&,&a href=&http://link.zhihu.com/?target=https%3A//isocpp.org/std/status& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&还有这里&/a&&/p&&p&&i&2、ASIO所有功能都会进入C++20标准么?&/i&&/p&&p&主要只有socket和socket编程需要的定时器功能会进标准(标准也是循序渐进,第一个版本就只有这点,http之类的,可能会在后续版本加入),asio本身包含的文件、串口、unix域套接字等都会删掉。进入C++20的功能大概只占ASIO的25%。&/p&&p&&i&3、在支持C++20的编译器里面还可以用ASIO吗?&/i&&/p&&p&可以,因为名字空间不同。再说标准中的功能没有ASIO多,比如说想用ssl,可以用ASIO。&/p&&p&&i&4、proactor和reactor有啥区别?&/i&&/p&&p&知乎上面就有~~&/p&&p&&/p&&p&&/p&&p&&/p&
是时候熟悉ASIO了。C++20标准库的网络部分将是基于ASIO,已无疑义,目前唯一的变数只是时间的问题。networking的提案依赖executor的提案,所以只有在executor进C++20的前提下,networking才能同一期进入。WG21(c++标准委员会)将在今年9月22至23两天内(
&figure&&img src=&https://pic3.zhimg.com/v2-2a3d34dee07e12d85eda_b.jpg& data-rawwidth=&960& data-rawheight=&536& class=&origin_image zh-lightbox-thumb& width=&960& data-original=&https://pic3.zhimg.com/v2-2a3d34dee07e12d85eda_r.jpg&&&/figure&&p&GTA5的渲染优化向来为人称道。这篇文章是基于&/p&&a href=&http://link.zhihu.com/?target=http%3A//www.adriancourreges.com/blog//gta-v-graphics-study/& data-draft-node=&block& data-draft-type=&link-card& data-image=&https://pic4.zhimg.com/v2-60e35f537abac2bccdeaf9ff_180x120.jpg& data-image-width=&420& data-image-height=&236& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&GTA V - Graphics Study&/a&&p&所写的对渲染过程的图形学分析,并大量引用了里面的内容。之前也有前辈做了翻译,这里我介绍了更多的细节与优化。读者需要一定的图形学基础去理解其中的概念,而我也在其中加入了一些自己的理解,若有错误希望不吝指正。&/p&&h2&&b&剖析GTA5(侠盗猎车手5)中一帧的渲染流程&/b&&/h2&&p&开始之前首先要指出,GTA5里许多渲染所用的缓冲都是&b&HDR缓冲&/b&(每个channel大于8位,无法在显示器上正常显示),直到每帧结尾才转化为常见的RGB图像。原作者&i&Adrian Courrèges&/i&为了演示方便,已经将这些HDR缓冲都转化为8位的正常图像。所以下文你看到的图片都是经过HDR处理过的。文末附上了原作者使用的HDR处理方式的简介以供参考。&/p&&p&&br&&/p&&p&好了,下面进入正题,开始详细的渲染流程介绍!&/p&&hr&&h2&&b&Step 1. 生成环境贴图&/b&&/h2&&p&每一帧开始时,GTA5首先渲染出一张&b&环境立方体贴图&/b&(Cubemap)。这张Cubemap用于后面计算池塘,河流等物体的反射,并不需要很高的精度, 只有地形,天空,摩天大楼等大型景致的低精度模型被渲染到Cubemap中,并且每一面的贴图大小只有128*128,只要约30个&b&draw call&/b&(绘图指令)即可完成。由下图可以发现,只有那些大型模型在水面里有倒影,无论行人、载具还是装饰的静态物体都是没有倒影的,这就是提高渲染效率的代价。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-b1bf5a8ec434e3c6585f57fdaf523a50_b.jpg& data-size=&normal& data-rawwidth=&482& data-rawheight=&339& class=&origin_image zh-lightbox-thumb& width=&482& data-original=&https://pic1.zhimg.com/v2-b1bf5a8ec434e3c6585f57fdaf523a50_r.jpg&&&figcaption&人物,围栏,树木都是没有倒影的&/figcaption&&/figure&&p&&b&环境立方体贴图(Cubemap)&/b&:一个由6张纹理组成各个面的立方体,渲染方式为将摄像机置于你所在的位置,朝着6个方向各进行一次渲染(每次将摄像头角度旋转90°)。Cubemap也可以用于实现简单的天空盒,使远景一直与人物保持相对静止,实现天空的效果。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-89dc271d7ceeabe1a21f_b.jpg& data-size=&normal& data-rawwidth=&205& data-rawheight=&205& class=&content_image& width=&205&&&figcaption&环境立方体贴图(引用自Adrian)&/figcaption&&/figure&&p&然而GTA5在这里还加上了一个优化:将立方体贴图(Cubemap,6个面,128*128)转化为双抛物面贴图(Dual-Paraboloid Map,2个面,128*128),转化后的结果如下所示:&/p&&figure&&img src=&https://pic2.zhimg.com/v2-4d7a0b1bd6d5f3d9eafd_b.jpg& data-size=&normal& data-rawwidth=&256& data-rawheight=&128& class=&content_image& width=&256&&&figcaption&GTA5使用的Dual-Paraboloid Map(引用自Adrian)&/figcaption&&/figure&&p&这两张图可以看成把原来的立方体贴图压缩成球体,然后映射到上半球和下半球上。这样做有什么好处呢?首先,它降低了显存开销(从6张到2张);其次,Cubemap是用于后续的反射计算的,考虑到多数情况中反射体正面朝上(水池,车窗等),只有那些高处的景物会被反射进我们眼中,所以大部分都是对上半球的贴图(上图右边部分)进行采样;再次,使用立方体贴图的话,我们在使用mipmap技术采样贴图时很容易在不同的面之间产生seams,而使用双抛物面贴图则会减轻这个问题的影响。&/p&&p&至此,用于后面计算反射的贴图已经生成好了。&/p&&hr&&h2&&b&Step 2. 背面剔除和LOD技术&/b&&/h2&&p&这一步的操作都由计算着色器(Compute Shader,CS)完成。原文没有提及很多细节,我稍作一些补充。&/p&&p&剔除(Culling)是一种很常见的优化技术,旨在提前去掉不需要渲染的面,比如背面消隐,视锥体剔除等。何为&b&&a href=&http://link.zhihu.com/?target=https%3A//en.wikipedia.org/wiki/Back-face_culling& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&背面消隐&/a&&/b&?比如将镜头对准一张纸板,只有纸板的正面需要渲染,背面则不需要(因为你也看不到背面的内容),这样可以减轻渲染的压力。一般都是通过传入的网格面片的顶点顺序来定义哪一面是正面。何为&b&&a href=&http://link.zhihu.com/?target=https%3A//en.wikipedia.org/wiki/Viewing_frustum& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&视锥体裁剪&/a&&/b&?摄像头同我们眼睛一样,能看到的通常都在一个平截头体中(太近、太远或太偏的物体都看不到),所以我们只要渲染在平截头体内的物体。&/p&&p&LOD技术(Level of Detail)则是为了处理模型的精度问题,精度越高的话,模型的表现越精细。但对于离摄像机很远的物体,用高精度模型渲染没有明显的画质提升,反而会消耗大量的显存(因为远处往往包含更多的物体,使用高精度模型的话,需要的模型面片数量将十分巨大)。常用的解决方案是 对同一个物体预先生成不同精度的网格,当物体离摄像机近时就用高精度的网格,离的远就用低精度的网格(后面介绍的Cascaded Shadow Map技术也是基于类似的原理,来保证近处的阴影质量更高),这样能尽量保证玩家感受高质量的模型。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-e8c6ae79e48d79f6eb7d1c3a_b.jpg& data-size=&normal& data-rawwidth=&437& data-rawheight=&256& class=&origin_image zh-lightbox-thumb& width=&437& data-original=&https://pic4.zhimg.com/v2-e8c6ae79e48d79f6eb7d1c3a_r.jpg&&&figcaption&LOD:根据距离选择不同的模型精度&/figcaption&&/figure&&p&可以观察GTA5对于植被运用的LOD技术处理,当植被与摄像机超过一定距离后不再渲染就不再渲染,即模型精度为0。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-0bdbfc3458edd0f081234_b.jpg& data-size=&normal& data-rawwidth=&741& data-rawheight=&252& class=&origin_image zh-lightbox-thumb& width=&741& data-original=&https://pic1.zhimg.com/v2-0bdbfc3458edd0f081234_r.jpg&&&figcaption&用人物手机拍照,拉近镜头时更多植被被渲染&/figcaption&&/figure&&p&GTA5对LOD技术的运用十分高超。我们知道GTA5的场景庞大而复杂,每一帧都需要对大模型进行处理,但它却能在显存只有256MB的PS3上渲染很多大型场景,并且将细节表现得十分逼真,足见它在LOD上下了很大的功夫。&/p&&hr&&h2&&b&Step 3. G-Buffer Generation&/b&&/h2&&p&主要的渲染过程就是在这一步进行,因此该步骤将做重点介绍。GTA5运用了&b&延迟渲染&/b&技术(与之相对的是:之前绘制cubemap时运用的是&b&前向渲染&/b&),利用MRT技术将同时渲染到5张渲染对象(贴图)。这里首先对名词稍作解释。&/p&&p&&b&前向渲染(Forward Rendering):&/b&在每一帧中轮流渲染各个物体,渲染每个物体时调用它的顶点着色器(VS)和像素着色器(PS),在PS中结合光源信息利用光照模型进行计算,将物体最终绘制在纹理上。该渲染方式存在一些不足:&b&首先&/b&,如果很多物体在摄像机方向前后重叠,则很可能同一个像素有多个物体调用各自的PS进行光照计算(最终只保留一个),这就造成PS运算的浪费,即over-drawing问题(采用由近及远的渲染方式可以减轻这个问题);&b&其次&/b&,当场景中具有大量光源时(如下图所示),每个物体需要向PS传入所有临近光源的信息并一一计算光照,考虑到庞大的物体数目和光源数量,这将严重影响PS的效率,形成GPU Bound。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-b5e0d9a39d4b8b9462f36e_b.jpg& data-size=&normal& data-rawwidth=&960& data-rawheight=&536& class=&origin_image zh-lightbox-thumb& width=&960& data-original=&https://pic3.zhimg.com/v2-b5e0d9a39d4b8b9462f36e_r.jpg&&&figcaption&GTA5夜间场景存在数以百计的光源和大量的物体&/figcaption&&/figure&&p&综合前面所说,前向渲染存在重要的运算浪费:一个像素可能被渲染了数百次(每次都会进行光照运算)但最终只有深度最低的那次运算结果得以保留,其他光照计算都被浪费掉了。&/p&&p&&b&延迟渲染(Deferred Rendering):&/b&针对前向渲染的缺点,延迟渲染换了一种思路。它利用MRT(multiple rendering target)技术将每个物体的漫反射、法线、镜面反射等信息分开绘制到4~5张主要的纹理(也就是G-Buffer)上,暂不进行光照运算。当所有物体的信息绘制完成,光源 再利用G-Buffers中的数据进行运算(”延迟“的由来)。 由此可见,场景的复杂度对光照计算量的影响大大降低(因为无论一个像素点被物体覆盖多少次,光源只对该点渲染了一次),大量光源的渲染得以实现。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-80e4fa9a50a77e9c267b8bed773b7491_b.jpg& data-size=&normal& data-rawwidth=&795& data-rawheight=&257& class=&origin_image zh-lightbox-thumb& width=&795& data-original=&https://pic2.zhimg.com/v2-80e4fa9a50a77e9c267b8bed773b7491_r.jpg&&&figcaption&GTA5延迟渲染示意图&/figcaption&&/figure&&p&下面将主要介绍一下G-Buffer里面几张贴图的具体内容(需要另外指出,这里所用的缓冲都是LDR而非HDR)。一张贴图有RGBA这四个8位通道,而像深度等信息只占一个通道,颜色也只占三个通道,如果只存一种信息必然会浪费空间,因此GTA5会将不同的信息合同到同一个贴图内以充分利用所有通道)。&/p&&ol&&li&&b&Diffuse Map&/b&:漫反射贴图,保存物体的原本的颜色。但与其他游戏不同在于,GTA5在这里顺便加入了阳光对物体的光照计算。考虑太阳的平行光特性,所有物体都会被照射到,这点代价(相较于在结合G-Buffer时进行阳光计算)还是可以接受的。下图中可以看出引擎盖上的对于阳光的光照计算。&/li&&/ol&&figure&&img src=&https://pic3.zhimg.com/v2-ed2a006e75adb91c286ab2_b.jpg& data-size=&normal& data-rawwidth=&431& data-rawheight=&244& class=&origin_image zh-lightbox-thumb& width=&431& data-original=&https://pic3.zhimg.com/v2-ed2a006e75adb91c286ab2_r.jpg&&&figcaption&引擎盖上的阳光光照计算(引用自Adrian)&/figcaption&&/figure&&p&剩下的alpha通道用于保存”Blending“(混合)信息,标志出需要抖动平滑处理的像素点。我们将在介绍Dither Smoothing的时候介绍它是如何使用的。&/p&&p&&b&2. Normal Map&/b&:&a href=&http://link.zhihu.com/?target=https%3A//en.wikipedia.org/wiki/Normal_mapping& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&法线贴图&/a&,用于存储每个像素点的法线,可以在光照计算时模拟表面的变化,从而提高真实度。&/p&&p&这里做一个比较:&b&延迟渲染&/b&中法线贴图存储的是每一点的法线在世界空间(World Space)的向量坐标,而&b&前向渲染&/b&为了方便在PS中计算光照,一般存储的是每一点法线在自己的切线空间(Tangent Space)的向量坐标。这就解释了前向渲染的法线贴图为何偏蓝色:贴图中每个通道只能存储正数,因此需要将法线向量的每一位从 &img src=&http://www.zhihu.com/equation?tex=%28-1%2C+1%29& alt=&(-1, 1)& eeimg=&1&& 线性映射到 &img src=&http://www.zhihu.com/equation?tex=%280%2C+1%29& alt=&(0, 1)& eeimg=&1&& 中,切线空间中常见法线向量 &img src=&http://www.zhihu.com/equation?tex=%280%2C+0%2C+1%29& alt=&(0, 0, 1)& eeimg=&1&& 将会被映射到 &img src=&http://www.zhihu.com/equation?tex=%280.5%2C+0.5%2C+1%29& alt=&(0.5, 0.5, 1)& eeimg=&1&& ,正好对应蓝色。&/p&&p&GTA5中使用延迟渲染,因此normal map需要存储法线的世界空间坐标,可以看出其色调与前向渲染的法线贴图不太一样。但主要色调仍为蓝色,因为大部分物体都是表面朝上(如地面),法向量的世界空间坐标仍会从&img src=&http://www.zhihu.com/equation?tex=%280%2C+0%2C+1%29& alt=&(0, 0, 1)& eeimg=&1&& 映射到 &img src=&http://www.zhihu.com/equation?tex=%280.5%2C+0.5%2C+1%29& alt=&(0.5, 0.5, 1)& eeimg=&1&&。&/p&&p&注意:这里要注意两张贴图的来源区别:延迟渲染的法线贴图是G-Buffer的中间产物;
而前向渲染中的法线贴图则是预先提供的美术素材。这里比较是为了更清晰地展示两张贴图的取值空间的不同。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-966189cfdf8a4cadbe5ce_b.jpg& data-size=&normal& data-rawwidth=&720& data-rawheight=&243& class=&origin_image zh-lightbox-thumb& width=&720& data-original=&https://pic3.zhimg.com/v2-966189cfdf8a4cadbe5ce_r.jpg&&&figcaption&GTA5(延迟渲染)的法线贴图 切线空间下的法线贴图&/figcaption&&/figure&&p&法线贴图的alpha可能是用于判断植被与摄像机的距离(决定是否渲染),原文不甚详细。&/p&&p&&b&3. Specular Map&/b&:&a href=&http://link.zhihu.com/?target=https%3A//en.wikipedia.org/wiki/Specularity& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&高光贴图&/a&,但这里并不直接存储每个像素的镜面反射光颜色,而是存储每个像素点最上面的物体的材质信息以供后续计算,原因在于:GTA5采用的延迟渲染,所以得在G buffer渲染结束后才会进行光照计算(否则光源很多时,你需要对一个物体进行上百次镜面光计算)。这里各个通道存储的信息为:&/p&&p&Red:高光强度(specular intensity)&/p&&p&Green:光泽度(glossiness)&/p&&p&Blue:菲涅尔系数(Fresnel Intensity,同一材质所渲染的像素点都采用相同的菲涅尔系数)&/p&&figure&&img src=&https://pic3.zhimg.com/v2-a76addc83bea_b.jpg& data-size=&normal& data-rawwidth=&431& data-rawheight=&242& class=&origin_image zh-lightbox-thumb& width=&431& data-original=&https://pic3.zhimg.com/v2-a76addc83bea_r.jpg&&&figcaption&高光贴图并非直接存储高光颜色(引用自Adrian)&/figcaption&&/figure&&p&&b&4. Irradiance Map&/b&:辐照贴图(这个我不是很了解),主要用于保存每个点的辐照度。各个通道存储的信息为:&/p&&p&Red:每个像素点接收到的来自太阳的辐照度(基于该像素的位置,法线与阳光角度进行计算)。&/p&&p&Green:原作者猜测该通道记录了来自另一个光源的辐照度&/p&&p&Blue:记录物体的自发光属性(emissive),对于霓虹灯等物体占据的像素,该通道值非0。&/p&&p&Alpha:该通道主要用于标记皮肤、植被等需要特殊处理的像素点(后续将介绍如何处理)。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-3c618f95a6fe65bee0ca_b.jpg& data-size=&normal& data-rawwidth=&450& data-rawheight=&253& class=&origin_image zh-lightbox-thumb& width=&450& data-original=&https://pic3.zhimg.com/v2-3c618f95a6fe65bee0ca_r.jpg&&&figcaption&辐照贴图(引用自Adrian)&/figcaption&&/figure&&p&由上面容易看出,在白天时,Red通道基本都被填充,Blue和Alpha通道大部分为0。所以该贴图明显偏红。城市部分对应的绿色可能是做了特殊处理(我猜测是考虑了热岛效应?)&/p&&p&&b&5. Depth & Stencil Map&/b&:&a href=&http://link.zhihu.com/?target=https%3A//open.gl/depthstencils& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&深度贴图和模板贴图&/a&,用于存储每个像素点的深度和模板值。存储深度时GTA5做了一个优化:存储对数深度缓冲。公式为: &img src=&http://www.zhihu.com/equation?tex=d%3D%5Cfrac%7Blog%28C%2Aw%2B1%29%7D%7Blog%28C%2Afar%2B1%29%7D%2Aw& alt=&d=\frac{log(C*w+1)}{log(C*far+1)}*w& eeimg=&1&& , &img src=&http://www.zhihu.com/equation?tex=C& alt=&C& eeimg=&1&& 通常取小于1的常数, &img src=&http://www.zhihu.com/equation?tex=w& alt=&w& eeimg=&1&& 是观察空间下的深度, &img src=&http://www.zhihu.com/equation?tex=d& alt=&d& eeimg=&1&& 是实际被写入缓冲的深度。&/p&&p&&b&为什么要做这个优化?&/b&正常情况下写入的深度与实际深度的关系为 &img src=&http://www.zhihu.com/equation?tex=d%3Da%5Cfrac%7B1%7D%7Bz%7D%2Bb& alt=&d=a\frac{1}{z}+b& eeimg=&1&& ,可以看出在近平面时 &img src=&http://www.zhihu.com/equation?tex=d& alt=&d& eeimg=&1&&
的精度可以对应大部分深度;而当 &img src=&http://www.zhihu.com/equation?tex=z& alt=&z& eeimg=&1&& 很远时,需要距离较大改变,才能被 &img src=&http://www.zhihu.com/equation?tex=d& alt=&d& eeimg=&1&& 感受到(因为 &img src=&http://www.zhihu.com/equation?tex=d& alt=&d& eeimg=&1&& 的精度是有限的),因此在远处相近的两个物体深度会重叠,这就是Z Fighting现象。GTA5由于地图庞大,经常出现far plane很远的情况,因此必须要解决这一问题。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-9ce450ecdc_b.jpg& data-size=&small& data-rawwidth=&830& data-rawheight=&430& class=&origin_image zh-lightbox-thumb& width=&830& data-original=&https://pic1.zhimg.com/v2-9ce450ecdc_r.jpg&&&figcaption&1/z中远平面的变化无法准确反馈给d(图片来自NVIDIA)&/figcaption&&/figure&&p&我们可以在存储d时使用一个&a href=&http://link.zhihu.com/?target=https%3A//developer.nvidia.com/content/depth-precision-visualized& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&拟对数分布&/a&来保证far部分能对应到大量的d值,near部分也能获得足够的精度,实际上正好可以用对数去模拟这样的分布,这就是公式的由来。我利用Mathematics简单绘制了对数缓冲公式的图像,并举例进行计算:下图右边可见,当far plane为 &img src=&http://www.zhihu.com/equation?tex=10%5E%7B7%7Dm& alt=&10^{7}m& eeimg=&1&& 时,假设物体从 &img src=&http://www.zhihu.com/equation?tex=10%5E%7B5%7Dm& alt=&10^{5}m& eeimg=&1&& 移动到 &img src=&http://www.zhihu.com/equation?tex=10%5E%7B5%7D%2B10m& alt=&10^{5}+10m& eeimg=&1&& ,对数缓冲的变化仍在精度范围内,而倒数函数的变化十分小,已经无法在24位精度下被计算机准确反应了。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-278a4b2dd3a589f55bfa4_b.jpg& data-size=&normal& data-rawwidth=&761& data-rawheight=&302& class=&origin_image zh-lightbox-thumb& width=&761& data-original=&https://pic1.zhimg.com/v2-278a4b2dd3a589f55bfa4_r.jpg&&&figcaption&对数缓冲函数能更精确地记录远处物体的深度数据&/figcaption&&/figure&&p&模板值被用来标志每个像素点渲染的网格类型,同一种网格所渲染的像素都用同一个值进行标志。之所以需要标志出来,是因为GTA5将对一些网格做后续处理(如皮肤,载具等),读取模板值可以决定应该用哪种方式进行处理。一些典型的模板值如下:&/p&&ul&&li&0x89:玩家所控制的人物&/li&&li&0x82:玩家驾驶的载具&/li&&li&0x01:游戏中的NPC&/li&&li&0x07:天空&/li&&/ul&&p&&br&&/p&&p&至此,G buffer中所有的buffer都被介绍完了,这些buffer一共调用了约1900个draw call。但是注意该part还没有结束,下面将列出一些渲染G-Buffer的其他细节;在这些操作完成之后,才能进行G-Buffer的合并计算。&/p&&p&&b&I. &/b&场景中物体都是”从前往后“渲染的,这样做的好处在于:像素被近处的物体先渲染了,后面的物体渲染这些像素时就无法通过深度测试,在调用PS渲染之前就被GPU丢弃了,从而提高了渲染效率。“从前往后”渲染能节省大量不必要的PS运算开销。从下图可以看出,人和车会占据屏幕的一大部分,而后面的物体则省去了渲染的开销。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-a679b25a3f69fd66bb4a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&960& data-rawheight=&536& class=&origin_image zh-lightbox-thumb& width=&960& data-original=&https://pic3.zhimg.com/v2-a679b25a3f69fd66bb4a_r.jpg&&&/figure&&p&&b&II. &/b&前面介绍diffuse map时提到diffuse map的alpha通道,被用来标志出需要抖动平滑处理的像素点。如果你将diffuse map放大查看,会发现远处的树占据的像素只有一半被渲染,就像国际象棋的棋盘一样。GTA5这样做并非因为渲染效率的限制,而是有意为之(在像素着色器中读取屏幕坐标判断是否渲染)。&/p&&p&这种抖动的渲染方式可以使得物体在不同的LOD之间的转化更为自然。在原文后面的Dither Smoothing介绍了后续操作:利用一个后处理像素着色器,读取alpha通道来确定被抖动处理的像素(远处的树木),然后对周围进行采样混合,计算出该像素点平滑处理后的颜色值。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-81b6bf39129eecd1f5a31_b.jpg& data-size=&normal& data-rawwidth=&899& data-rawheight=&243& class=&origin_image zh-lightbox-thumb& width=&899& data-original=&https://pic2.zhimg.com/v2-81b6bf39129eecd1f5a31_r.jpg&&&figcaption&处理前后图像对比(图像经过放大,引用自Adrian)&/figcaption&&/figure&&p&这里有一篇关于该技术(Alpha Stippling)的&a href=&http://link.zhihu.com/?target=https%3A//gamedev.stackexchange.com/questions/47844/why-are-some-games-using-some-dithering-pattern-instead-of-traditional-alpha-for& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&问答&/a&。GTA4也使用了该技术。&/p&&p&&b&III. &/b&阴影技术:GTA5采用了级联式阴影贴图(CSM,Cascaded Shadow Map),这也是大型游戏中常用的技术。对于普通的shadow map,很容易出现摄像机近处多个点对应到同一深度的情况,为了解决这一问题,通常会根据距离生成不同精度的shadow map,来保证近处的阴影的准确性。可以看到左图中大块区域会在shadow map中对应到同一深度,而右图中则可以保证近处的深度划分更加细致。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-a4efff902cdca3d2bb0a34997daa753e_b.jpg& data-size=&normal& data-rawwidth=&742& data-rawheight=&313& class=&origin_image zh-lightbox-thumb& width=&742& data-original=&https://pic3.zhimg.com/v2-a4efff902cdca3d2bb0a34997daa753e_r.jpg&&&figcaption&基本的阴影贴图和级联式阴影贴图&/figcaption&&/figure&&p&GTA5将4张阴影贴图合成了一张的贴图,每个阴影贴图都对应了不同的视锥体(从光源出发)。注意到阴影贴图是从太阳发射的平行光的角度进行渲染的,结合下图和Diffuse Map可以看出,从左到右视锥体覆盖的范围越来越大,并且离摄像机越来越远(即不同距离生成不同精度的贴图)。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-27daa7a4fa33e31787a0cdd_b.jpg& data-size=&normal& data-rawwidth=&923& data-rawheight=&232& class=&origin_image zh-lightbox-thumb& width=&923& data-original=&https://pic2.zhimg.com/v2-27daa7a4fa33e31787a0cdd_r.jpg&&&figcaption&GTA5中的阴影贴图(引用自Adrian)&/figcaption&&/figure&&p&渲染这几张贴图可能会带来很大的开销,因此同一场景被渲染了四次。但值得庆幸的是渲染每张贴图时都可以用视锥裁剪裁去大部分不需要的物体,因此CSM只需要约1000个draw call就可以生成。最后太阳光和云投射的阴影经过平滑处理后进行模糊混合,存储到Specular Map的alpha通道内。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-d2aaf7eaef8_b.jpg& data-size=&small& data-rawwidth=&903& data-rawheight=&508& class=&origin_image zh-lightbox-thumb& width=&903& data-original=&https://pic1.zhimg.com/v2-d2aaf7eaef8_r.jpg&&&figcaption&混合后的阴影贴图(引用自Adrian)&/figcaption&&/figure&&p&由于模糊操作需要从多个纹理中采样进行计算,代价十分高昂。GTA5在模糊混合之前还进行了一个优化:它首先生成原贴图1/8大小的贴图并进行低质量的近似模糊处理,通过结果估计出哪些像素点将会被完全点亮;然后对原大小的贴图进行高质量模糊处理时,如果该点被估计为完全点亮,则跳过模糊计算直接输出1(完全点亮的情况)。由上图可以看出,白天场景中大部分像素都会被完全点亮,因此这个优化可以节省大量昂贵的模糊计算。&/p&&p&&b&IV.&/b& SSAO(Screen Space Ambient Occlusion,屏幕空间环境光遮蔽):在渲染场景时,墙角等地方往往会由于周围建筑的遮挡而显得比较暗。这是因为光线在角落里有更小的概率反射出去,因此墙角的光更难射入人眼中。用Path Tracing可以较好地去感受光线的传播路径,这里放一张我写的路径追踪的渲染结果(噪点太多,请忽视。。):&/p&&figure&&img src=&https://pic3.zhimg.com/v2-bdb7bbe9ea3a93b309692a_b.jpg& data-size=&normal& data-rawwidth=&454& data-rawheight=&271& class=&origin_image zh-lightbox-thumb& width=&454& data-original=&https://pic3.zhimg.com/v2-bdb7bbe9ea3a93b309692a_r.jpg&&&figcaption&Path Tracing的生成结果&/figcaption&&/figure&&p&可以明显看出墙角和墙沿的光线更加昏暗。而只计算太阳光是无法模拟出这样的效果的,因此提出了SSAO技术。考虑到G-Buffer中已经存有Normal Map, Depth Map等信息,我们可以在每个像素点对其法线方向的上半球进行采样,然后根据深度贴图确定周围有多少部分是高于当前深度,即确定有多少光会被遮蔽。计算结果会生成一张SSAO贴图,然而这个贴图一般噪音很强,因此还需要两个pass去做水平和竖直方向的平滑,更新SSAO贴图。&/p&&p&另外需要注意:为了提高效率,SSAO Map都是以原图一半的分辨率去实现的。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-b8fe6e8fe1a06_b.jpg& data-size=&normal& data-rawwidth=&640& data-rawheight=&360& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic3.zhimg.com/v2-b8fe6e8fe1a06_r.jpg&&&figcaption&未平滑的SSAO Map噪音十分严重(引用自Adrian)&/figcaption&&/figure&&p&&br&&/p&&p&细节介绍完了,终于是时候把这些G-Buffer的信息合并计算了!&/p&&p&一个像素着色器将从这些缓冲收集信息并最终计算出HDR下的渲染结果。对于夜间场景则会在这里额外渲染那些灯光,不做赘述。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-bd4d5396bcc07ffa1f097b5f53a2e5bd_b.jpg& data-size=&normal& data-rawwidth=&941& data-rawheight=&287& class=&origin_image zh-lightbox-thumb& width=&941& data-original=&https://pic2.zhimg.com/v2-bd4d5396bcc07ffa1f097b5f53a2e5bd_r.jpg&&&figcaption&G-Buffer集合(引用自Adrian)&/figcaption&&/figure&&p&这是合并后的结果:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-bb240f1daaa038_b.jpg& data-size=&normal& data-rawwidth=&922& data-rawheight=&518& class=&origin_image zh-lightbox-thumb& width=&922& data-original=&https://pic1.zhimg.com/v2-bb240f1daaa038_r.jpg&&&figcaption&G-Buffer Combination(引用自Adrian)&/figcaption&&/figure&&p&至此,渲染结果已经大致成型!&/p&&p&&br&&/p&&p&这篇文章大概介绍了原文四分之一的内容,由于中间加入了很多自己的理解,因此篇幅增长了不少,工作量也比较重。当然如果大家很感兴趣的话,我会继续翻译原文后面的内容。&/p&&p&&br&&/p&&p&&br&&/p&&p&此处附上HDR处理简介,感兴趣的读者可以了解一二:&/p&&p&这里简单介绍一下Adrian使用的HDR处理方式(详情参考Reinhard&i&的&/i&论文&i&Photographic Tone Reproduction for Digital Images&/i&):&/p&&ol&&li&对于每个像素点都根据RGB值计算出其亮度(符号为L,L=0.27R+0.67G+0.06B),然后利用以下方程更新该点的 &img src=&http://www.zhihu.com/equation?tex=L& alt=&L& eeimg=&1&& :&img src=&http://www.zhihu.com/equation?tex=L_%7Bd%7D%28x%2C+y%29%3D%5Cfrac%7BL%28x%2Cy%29%281%2B%5Cfrac%7BL%28x%2C+y%29%7D%7BL_%7Bwhite%7D%5E%7B2%7D%7D%29%7D%7B1%2BL%28x%2C+y%29%7D& alt=&L_{d}(x, y)=\frac{L(x,y)(1+\frac{L(x, y)}{L_{white}^{2}})}{1+L(x, y)}& eeimg=&1&&&/li&&/ol&&p&&img src=&http://www.zhihu.com/equation?tex=L_%7Bwhite%7D& alt=&L_{white}& eeimg=&1&& 代表能映射到纯白色的最小亮度。这一步可以将 &img src=&http://www.zhihu.com/equation?tex=L& alt=&L& eeimg=&1&& 全部映射到 &img src=&http://www.zhihu.com/equation?tex=%5B0%2C+1%5D& alt=&[0, 1]& eeimg=&1&& 区间内,但对于色彩范围极大的图片效果仍然不好,因此需要进一步处理。&/p&&p&2. 对于图片中的每个像素,找出适当的尺寸 &img src=&http://www.zhihu.com/equation?tex=s& alt=&s& eeimg=&1&& 使得:以该点为中心, &img src=&http://www.zhihu.com/equation?tex=s& alt=&s& eeimg=&1&& 为半径的区域与周围对比足够强(利用他定义的公式计算对比)。然后结合 &img src=&http://www.zhihu.com/equation?tex=s& alt=&s& eeimg=&1&& 利用公式&/p&&p&&img src=&http://www.zhihu.com/equation?tex=L_%7Bd%7D%28x%2C+y%29%3D%5Cfrac%7BL%28x%2C+y%29%7D%7B1%2BV_%7B1%7D%28x%2C+y%2C+s_%7Bm%7D%28x%2C+y%29%29%7D& alt=&L_{d}(x, y)=\frac{L(x, y)}{1+V_{1}(x, y, s_{m}(x, y))}& eeimg=&1&&&/p&&p&重新计算 &img src=&http://www.zhihu.com/equation?tex=L& alt=&L& eeimg=&1&& 。 这里&img src=&http://www.zhihu.com/equation?tex=V_%7B1%7D& alt=&V_{1}& eeimg=&1&& 是基于每个像素的 &img src=&http://www.zhihu.com/equation?tex=s& alt=&s& eeimg=&1&& 利用卷积计算出平均亮度。&/p&&p&然后按照色彩空间中的定义进行 &img src=&http://www.zhihu.com/equation?tex=L& alt=&L& eeimg=&1&& 与 &img src=&http://www.zhihu.com/equation?tex=RGB& alt=&RGB& eeimg=&1&& 的转化(这一块不太了解),即可得到处理后的图片,此时每个通道都可以用8位存储。处理结束。&/p&
GTA5的渲染优化向来为人称道。这篇文章是基于所写的对渲染过程的图形学分析,并大量引用了里面的内容。之前也有前辈做了翻译,这里我介绍了更多的细节与优化。读者需要一定的图形学基础去理解其中的概念,而我也在其中加入了一些自己…
&figure&&img src=&https://pic2.zhimg.com/v2-d5babfbd7bab_b.jpg& data-rawwidth=&1200& data-rawheight=&675& class=&origin_image zh-lightbox-thumb& width=&1200& data-original=&https://pic2.zhimg.com/v2-d5babfbd7bab_r.jpg&&&/figure&&p&周末没事找来DXR SDK随便看看,一边看就一边做了点笔记,但是这不是原始SDK文档的直接翻译,而是加了很多帮助理解的解释性的内容。由于我还在忙《全局光照技术:从离线到实时渲染》最后的出版工作,所以这个可能会拖后一点时间,但是1-4章帮助整体了解DXR而不是API细节的部分会在这两天弄完,这样大家可以先了解DXR的基本思路和流程,而不必纠结于细节,后面会慢慢更新整个文档。&/p&&p&&br&&/p&&p&打个广告,《全局光照技术》包含300多页关于光线追踪的内容,是目前市面上较少的同时关注离线和实时渲染,并将两者置于同等重要地位的图书,通过从离线到实时系统的学习,我们可以更好地构建整个渲染的知识结构体系。&/p&&p&&br&&/p&&p&&b&1. Intro &/b&&/p&&p&DXR在D3D 12中成为和compute并行计算与rasterization一样的一等公民,与rasterization管线类似,DXR管线的目标也是保持可编程能力和固定管线之间的平衡,可编程能力要最大化应用程序的表达能力,而固定管线的目的是最大化执行和实现的效率。&/p&&p&&br&&/p&&p&&b&2.Overview&/b&&/p&&p&整个系统的设计是要保持光线(ray)的独立处理,所有与ray有关的shader,ray都是作为一个输入,在每个shader里面不能和其它ray进行交互,也不能依赖于其它线程中的ray,尽管有些ray的运行结果会产生其它的ray,但是运行中的任何ray之间都是不能有依赖关系的。这样就会并行性提供了可能性。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-e3e0babe6aa0ed5be8e4a0f986cbd177_b.jpg& data-size=&normal& data-rawwidth=&1622& data-rawheight=&414& class=&origin_image zh-lightbox-thumb& width=&1622& data-original=&https://pic4.zhimg.com/v2-e3e0babe6aa0ed5be8e4a0f986cbd177_r.jpg&&&figcaption&这个图仅仅是一个实现的近似,看看就好,不要当真&/figcaption&&/figure&&p&众所周知,光线追踪算法在对(尤其是GPU中的)缓存结构是非常不友好的,为了尽可能地提高光线的计算效率,一般的光线追踪实现都需要对光线排序,以使其算法对内存访问具有一定的连贯性。为了最大化这种光线处理(例如对场景的遍历和着色计算)的效率,这样的工作显然作为固定管线会更加合适,上面说过固定管线的目标就是提供最大效率。上图演示了光线在一次追踪中的大概流程,其中一个shader用于生产rays,然后就提交给固定管线,固定管线最后给出ray与表面的交点,交回给一个表面着色shader。在这中间发生了光线对整个几何场景加速数据结构的遍历以及求交计算,这个过程是完全基础与固定管线的,这个做法其为了在遍历和求交计算中尽可能对针对GPU进行优化,例如上图中包含两个调度阶段,其中第一个调度发生在遍历场景之前,第二个发生在着色之前,这里面的调度工作就可能包含对光线的排序,例如针对特定数据结构的优化,这样遍历的算法具有更好的并行性和对内存的连贯性,然后遍历完之后会再经历一次排序,比如为了让后面的着色计算具有更好的并行性,其可能将碰撞点根据表面来排序,让同一个表面或材质对应的光线交点放到一起计算,这样效率就会更高,这方面的算法可以参见Disney的论文(2013 Sorted Deferred Shading for Production Path Tracing)。&/p&&p&我们看到,这个固定管线实际上帮助开发者省了很多事情,这也是整个DXR最重要和最大的固定管线,这也是整个实时光线追踪的重要基础。&/p&&p&基于三角形网格的加速结构只是DXR内建的一种建构,这种内建结构甚至不需要我们知道任何具体数据结构的细节,我们只需要传给他一个双层结构,其中底层包含所有的三角形网格,这个数据就类似于在光栅化中一样传给他一些基本几何体的顶点数据,然后上层的是一个关于整个场景中的物体结构,例如每个物体的多个instance,变换矩阵等,我们甚至不知道DXR到底怎么存储它,实际上出于效率考虑,可能存储的远远不是简单的Octree之类的,会有很多硬件方面的优化。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-cc2ab4e1f233aae2de92faf_b.jpg& data-size=&normal& data-rawwidth=&2502& data-rawheight=&1038& class=&origin_image zh-lightbox-thumb& width=&2502& data-original=&https://pic2.zhimg.com/v2-cc2ab4e1f233aae2de92faf_r.jpg&&&figcaption&对于光线求交计算,DXR内建的加速结构是基于三角形网格的,但是也支持其它自定义体积数据结构&/figcaption&&/figure&&p&DXR也允许提供自定义的加速结构或者其它场景表述的体积结构,例如距离场,稀疏的阶层体素结构等,但是如果想要使用这些结构,就需要手动构建自己的遍历算法,求交计算等,这在后面的内容中可能会包含吧。&/p&&p&DXR相关的shaders主要包括以下几个方面:&/p&&ul&&li&产生和生成光线,也就是生成光线的定义&/li&&li&决定光线的相交计算,这主要是针对自定义的数据结构,比如使用像距离场这样的隐式表述,如果直接对三角形基元进行相交测试,则这个是DXR内建的。&/li&&li&产生和生成光线,也就是生成光线的定义。光线与几何体相交时,你应该做什么,比如通常的操作是对表面执行着色计算,或者计算阴影之类的,当然有时候也可以就是单纯的对一个三角形求交,不涉及着色之类的。。&/li&&/ul&&p&&br&&/p&&p&&b&4. Walkthrough &/b&&/p&&p&本节介绍使用DXR的一些基本流程和概念,以提供一个直观的理解,具体的API和shader相关的细节,例如一些函数接口,将在后面介绍。&/p&&p&&br&&/p&&p&&b&4.1. Initiating raytracing &/b&&/p&&p&和其它两种一等公民的流程类似,例如光栅化通过宿主程序构建一个命令列表,然后通过Draw()接口开始GPU的管线流程,compute并行计算通过Dispatch()接口来发起并行计算,DXR通过DispatchRays()发起光线追踪的管线,DispatchRays()在GPU中会激发多个ray generation shader的调用。所以DispatchRays()也该也需要指定示例的数量,这样才能便于向ray generation shader中传递参数。&/p&&p&&br&&/p&&p&&b&4.2. Ray generation shaders &/b&&/p&&p&和其它两种一等公民的流程类似,例如光栅化通过宿主程序构建一个命令列表,然后通过Draw()接口开始GPU的管线流程,compute并行计算通过Dispatch()接口来发起并行计算,DXR通过DispatchRays()发起光线追踪的管线,DispatchRays()在GPU中会激发多个ray generation shader的调用,。够根据自身位置读取对应的数据,ray generation shader的输出是一条或多条光线,这通过调用DXR API TraceRay()来实现。&/p&&p&&br&&/p&&p&&b&4.3. Rays&/b&&/p&&p&这里说明一下ray的定义,ray是一个包含一个原点origin,一个方向direction,以及一个距离间隔[TMin, TMax]的数据结构,如下图所示,这个间隔定义了ray与表面相交可能发生的范围,例如太远的距离可能直接被忽略掉。光线上的点可以表述为:origin + T*direction,这里的direction不必是归一化的。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-a80f0aa35d3a53eba9bcf_b.jpg& data-size=&small& data-rawwidth=&668& data-rawheight=&189& class=&origin_image zh-lightbox-thumb& width=&668& data-original=&https://pic1.zhimg.com/v2-a80f0aa35d3a53eba9bcf_r.jpg&&&figcaption&ray的定义&/figcaption&&/figure&&p&一个ray实例变量是由用户定义的,这个数据结构是可以被修改的,例如光线求交的结果就会修改其中的较大位置信息,此外它对TraceRay的调用者也是可见的。&/p&&p&TMin在整个求交过程中是不会发生改变的,但是随着场景中与该光线的一些交点被发现,系统会修改TMax的值以反映当目前为止最近的交点,当所有交点计算完毕时,TMax保存的就是距离原点最近的交点。&/p&&p&&br&&/p&&p&&b&4.4. Raytracing output &/b&&/p&&p&DXR的shader输出的结果,可能包括图像的颜色样本,这主要通过UAV(unordered access view)的形成存储,例如texture,buffer等。 &/p&&p&&br&&/p&&p&&b&4.5. Ray-geometry interaction diagram&/b& &/p&&p&下图涉及的内容会被后面的很多小节介绍,可以先看个大概,随时回来看细节部分。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-cb83e3397eb_b.jpg& data-size=&normal& data-rawwidth=&774& data-rawheight=&956& class=&origin_image zh-lightbox-thumb& width=&774& data-original=&https://pic1.zhimg.com/v2-cb83e3397eb_r.jpg&&&figcaption&光线与几何物体求交计算的流程示意图&/figcaption&&/figure&&p&&br&&/p&&p&&b&4.6. Geometry and acceleration structures&/b& &/p&&p&DXR需要知道整个场景的几何数据,一般称为一个加速数据结构,因为这些数据结构的构建通常是为了加速场景的遍历和光线求交计算。DXR使用一个双层的加速数据结构(two levels of acceleration structures),底层的(bottom-level)的加速数据结构有一些列几何体组成,而上层的(top-level)的加速数据结构则表示这些底层加速数据结构的实例化,例如下层包含一把椅子,在上层可以包含这个椅子几何体的多个实例。&/p&&p&在一个底层加速结构中,它可以包含任意数量的三角形网格或程序化的基元(后面介绍)类型,我们通过一个数组D3D12_RAYTRACING_GEOMETRY_DESC来给定这些几何体的数组,然后系统通过命令列表中的BuildRaytracingAccelerationStructure() 命令在GPU中为应用程序构建一个黑盒的加速数据结构,这个结构会被后面的阶段用于光线与几何体的求交计算。&/p&&p&给定一个底层加速架构的集合,应用程序就可以定义这些几何体的实例,这通过指向结构体D3D12_RAYTRACING_INSTANCE_DESC来定义,每个实例指向一个特定的底层加速结构,并且包含一些特定于这个实例的数据信息,例如变换矩阵,或者用于指定的InstanceID用于shader中使用。我们又称这种实例为几何实例(geometry instances)。注意前面的BuildRaytracingAccelerationStructure() 其实是包含建立整个双层的加速数据结构的。&/p&&p&一个应用程序是可以同时使用多个上层加速数据结构的,它们可以分别被绑定到相关的shader中作为shader的输入资源,然后每个shader就可以使用特定的加速数据结构,例如有些shader只需要对场景的部分几何体执行求交计算。&/p&&p&这种双层的数据结构也可以给开发者一种在性能和灵活性之间的渲染,例如为了较好的性能,我们可以最大程度地使用更大的底层加速架构,而为了灵活性,我们可以使用更小的底层加速结构,而在上层加速结构中使用更多的示例。&/p&&p&&br&&/p&&p&&b&4.7. Acceleration structure updates&/b& &/p&&p&我们可以在构建加速结构的时候(BuildRaytracingAccelerationStructure())通过D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAGS来指示一个加速结构可以被动态修改,但是可修改的加速结构在光线追踪的计算过程中性能并不如原始构建的静态的加速结构,但是加速结构的动态修改会比静态的构建要快。&/p&&p&加速结构的更新有一些限制,例如对于下层加速结构中的三角形几何体,只有其顶点位置是可以修改的,而上层的加速架构的修改会有更大的自由度,更多信息见后面内容。&/p&&p&&br&&/p&&p&&b&4.8. Built-in ray-triangle intersection (triangle mesh geometry)&/b& &/p&&p&如前面所述,对三角形几何体的加速结构和求交计算都是DXR内建的,后续的shader可以直接拿到三角形中交点在其质心坐标系下的位置。&/p&&p&&br&&/p&&p&&b&4.9. Intersection shaders (procedural primitive geometry) &/b&&/p&&p&双层加速结构可以使用的另一种几何基元是程序化的几何体,这种程序化基元包含一个轴对齐的AABB包围盒,其中包含着一个程序化的基元,这个基元的表面需要通过一个程序定义的intersection shader来定义,当光线与这种类型的基元相交时,这个shader会被调用,该shader需要决定光线与该基元是否相交。&/p&&p&使用intersection shader来进行相交计算会比DXR内建的ray-triangle相交计算要低效一些,但是提供了更大的灵活性。&/p&&p&&br&&/p&&p&&b&4.9.1. Minor intersection shader details &/b&&/p&&p&需要注意的是,使用intersection shader执行相交计算会出现一定的冗余,因为DXR只能根据一个AABB包围盒而不是具体的表面进行判断,所以一些穿过AABB包围盒但是实际上与程序化的表面并不相交的光线,会被额外执行一次,intersection shader可以通过设置D3D12_RAYTRACING_GEOMETRY_FLAG_NO_DUPLICATE_ANYHIT_INVOCATION来指示后续Any hit shader的行为。&/p&&p&&br&&/p&&p&&b&4.10. Any hit shaders&/b& &/p&&p&Any hit shader可以保证每次光线在[TMin, TMax]范围内与一个几何实例的相交都会被唯一执行一次,Any hit shader可以读取交点的一些属性,修改光线的属性,指示该次相交是否可以被忽略(IgnoreHit()),接受该相交并继续该光线的相交计算,或者接受该相交并终止继续搜索更多交点(AcceptHitAndEndSearch())。&/p&&p&由于沿一条光线上的交点的执行顺序是没有定义的,所以它可以以任意顺序发生,一旦一个Any hit shader接受了一次相交,那么该光线的T值就会变成新的TMax值,所以取决于交点的顺序不同,Any hit shader线程实例被执行的数量是不固定的。&/p&&p&Any hit shader是非常有用的,特别是当几何体是透明的时,一个特别的例子是在阴影计算中的透明度处理,如果一个any hit shader发现一个不透明物体,则它可以接受这个相交并停止继续搜索。但是在大多数时候,any hit shader是不必要的。当Any hit shader不存在的时候,系统实现就是简单地接受该交点并设置TMax的值为当前交点的T值。&/p&&p&不像其它类型的一些shader,Any hit shader不能产生新的光线,因为这可能会导致光线数量暴增。&/p&&p&&br&&/p&&p&&b&4.11. Closest hit shaders&/b& &/p&&p&Closest hit shader针对每个几何体,如果一个几何体与光线相交且是最近的交点,则该几何体的Closest hit shader被唯一执行一次。&/p&&p&Closest hit shader可以读取相交数据,修改光线数据,也可以产生新的光线。&/p&&p&Closest hit shader的一个典型的运用场景是用于表面着色计算,我们可以将计算的结果直接存储在光线的数据定义中,或者通过UAV写入到内存。&/p&&p&Closest hit shader的执行总是在Any hit shader之后(如果包含的话)。&/p&&p&&br&&/p&&p&&b&4.12. Miss shaders&/b& &/p&&p&如果一条光线没有与场景中的任何几何体相交,那么Miss shader就会被执行,它可以修改光线数据和产生新的光线,但是由于没有交点,所以无法读取交点数据。&/p&&p&&br&&/p&&p&&b&4.13. Hit groups&/b& &/p&&p&一个Hit group由{0个或1个intersection shader,0个或1个any hit shader,0个或1个closest hit shader}组成,每个独立的几何实例可以被引用一个hit group,以提供整体的shader code,将这些shader组合在一起的目的是让整个shader的编译和执行更加高效。&/p&&p&ray generation shader和miss shader并不是hit group的一部分,因为它们并不是针对特定几何实例的,例如miss shader是针对光线的,而ray generation shader完全与几何体无关。&/p&&p&如果一个hit group包含intersection shader,则它只能被用于程序化基元,反之,如果hit group不包含intersection shader,则它只能被用于三角形几何体。&/p&&p&一个hit group完全不包含任何shader代码也是可以的,这可以简单地将shader identifier设置为NULL即可,这种空的hit group有时候也是很有用的,例如,如果应用程序不希望做任何事情,仅仅是关心miss以检测是否没有被任何物体遮挡。&/p&&p&&br&&/p&&p&4.14. TraceRay() control flow&/p&&p&当调用TraceRay()函数后,即生成一条新的光线,其整个光线追踪的流程如下图所示,这也是整个DXR的核心管线,它包含了前面介绍的所有几种类似的着色器,以及一些DXR内建的固定管线函数。在深入了解这些不同类型的shader的时候,要注意区分它们作用的对象是什么,这有助于我们更好地理解它们,这就好比要知道vertex shader是针对每个顶点,而pixel shader是针对表面的每个像素一样。在DXR中,有些shader是针对几何基元的,有些是针对光线的,还有的并没有明确目标,就像一个compute shader一样。 &/p&&figure&&img src=&https://pic4.zhimg.com/v2-cce9b0116c9ddcbfe474f_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&768& data-rawheight=&862& class=&origin_image zh-lightbox-thumb& width=&768& data-original=&https://pic4.zhimg.com/v2-cce9b0116c9ddcbfe474f_r.jpg&&&/figure&&p&&b&[1].&/b& 这个阶段首先对加速结构进行保守地搜索,找出那些可能会与光线相交并处于[TMin, TMax]范围内的几何实例,这些被选择的几何实例是最终会被执行相交计算的。注意这个阶段并不执行真正的相交计算,只是找出潜在的相交几何体,所以TMax值也不会在这里被更新。而所谓的保守搜索,大概也就是粗略地执行光线与几何实例的AABB包围盒的相交测试,这个思路可以从该过程同时包含程序化基元可以看出,因为程序化基元此时只有AABB包围盒信息,而没有具体的表面信息。&/p&&p&这一步的并行性主要来源于多条光线同时对同一个加速结构的遍历,例如每个节点同时被所有ray的线程访问。而之所以不立即对几何体执行具体表面的相交计算,是因为这会破话并行性,因为针对同一个几何示例,有些ray与之表面相交,有些则不,所以需要将这些相交计算延迟到后面,以提取新的并行性。例如那个时候重新对光线进行排序,将对同一几何示例相交的ray实例放到一起,提高并行计算的效率。&/p&&p&&b&[2]. &/b&如果光线检测的基元类型是程序化基元,则intersection shader会被执行,intersection shader会调用ReportHit()函数,该函数会要求等待最后的相交结果,所以该intersection shader会暂时被挂起,光线追踪管线继续执行后面的管线,直到第[5]步结束后唤醒intersection shader线程实例,进行后续处理。&/p&&p&&b&[3]. &/b&包含对透明物体的处理,这可以直接通过几何实例的一些标志位以及ray flags来判断,如果是不透明物体,则直接执行相交测试,否则需要交由any hit shader来决定是否需要执行相交测试,any hit shader可以放弃相交测试,如果应用程序并没有提供any hit shader,则物体仍然被当做不透明处理,即直接执行相交计算。&/p&&p&&b&[4]. &/b&执行相交计算,并更新TMax值之后,如果RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH标识为被设置,或者前面的Any hit shader 调用了AcceptHitAndEndSearch()函数,则该光线的后续相交计算被放弃。因为至少当前这次相交计算被执行,所以光线始终会包含一个最近的交点,这不会影响整个光线追踪管线。&/p&&p&&b&[5]. &/b&如果相交的基元不是一个三角形基元,则一个intersection shader仍然处于激活状态,此时唤醒该线程,并传递相关参数进行程序化基元的处理。&/p&&p&&br&&/p&&p&&b&4.15. Ray flags&/b& &/p&&p&TraceRay()函数支持一些可选的Ray flags,这些flags用于重写系统对transparency,culling,以及early-out行为的处理。&/p&&p&为了描述ray flags的用途,考虑它们可以怎样被用于辅助渲染阴影,假设一个应用程序想要向远处的光源发射光线以尾递归(tail recursion)的方式累积这些光源的直接光照,这些光线必须要不与场景中的任何其它几何物体发生碰撞。&/p&&p&这时可以在ray generation shader中调用TraceRay()时同时设置flag:RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER,然后退出shader不做任何其它事情,如果任何几何体包含有any hit shader,可以用来剔除掉透明物体,即让光线穿过该物体,或者也可以设置RAY_FLAG_FORCE_OPAQUE直接忽略这些shader,让所有物体保持不透明。&/p&&p&如果有任何几何物体与光线发生碰撞(当然不必是最近的碰撞),那么光线的处理就会停止,这是因为设置了flag:RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH,虽然光线的一个交点被发现,但是由于设置了flag:RAY_FLAG_SKIP_CLOSEST_HIT_SHADER,所以不存在任何closest hit shader实例的执行,所以这样的光线处理被终止,并且后续不包含任何动作,这也是我们所期望的,即这些光线被遮挡。&/p&&p&如果光线没有与任何物体发生碰撞,那么就会触发miss shader,这即表示光源可以直接照射到光线的原点位置,在这里我们就可以计算光源的光照贡献然后将结果写入到UAV中。所以在这样的场景中,加速结构中的几何物体的作用是用来剔除那些miss shader(即被遮挡的光线)的实例,并忽略任何其它类型的shader执行,除非处于透明度计算的需要。&/p&&p&忽略一个shader的功能也可以通过将shader绑定到NULL来实现(后面将讨论关于绑定的内容),但是在这样的示例中使用flags会更加高效,系统甚至不需要去查询一个shader的绑定结果,同时也以为着应用程序不需要去为shader配置一些NULL的绑定。&/p&&p&&br&&/p&&p&&b&4.16. Instance masking &/b&&/p&&p&前面已经说过,同一个应用程序可以支持多个加速结构,每个shader实例可以选择对不同的加速结构进行遍历和执行相交计算,这些独立的加速结构往往表现为对场景物体的不同分组方式,除了使用这样独立的加速架构,我们还可以使用几何体实例的masking特性来实现这样的分组功能。&/p&&p&在上层加速结构中,每个几何实例都包含一个8-位的用户定义属性:即InstanceMask,然后TraceRay()方法也包含一个8-位的输入参数,当DXR在选择候选的相交几何体实例时,首先会将几何实例的InstanceMask属性与TraceRay()方法的输入参数执行逻辑与(AND)操作,如果其执行的结果为0,则该几何实例会被忽略。&/p&&p&这个特性就允许应用程序在单个加速结构中将不同子集的几何体实例表述为一个组,而不是单独将这些分组的实例分别表示为独立的加速结构。这样应用程序可以在遍历性能和效率之间作出权衡。&/p&&p&其中一个例子是,应用程序可以选择剔除一些物体使其不参见阴影的计算,但是这些被剔除的物体对于其它的光照相关的计算则要保持可见性。&/p&&p&另一种看待这种masking特性的方式是:InstanceMask的bits定义的是一个几何实例属于哪个“组”,例如一个实例设置为0,则表示它始终会被忽略;而光线的InstanceInclusionMask属性则定义在遍历的时候应该包含哪些组。&/p&&p&&br&&/p&&p&4.17. Callable shaders 14&/p&&p&4.18. Resource binding 14&/p&&p&4.18.1. Local root signatures vs root signatures 15&/p&&p&4.19. Shader identifier 15&/p&&p&4.20. Shader record 15&/p&&p&4.21. Shader tables 15&/p&&p&4.22. Indexing into shader tables 16&/p&&p&4.22.1. Shader record stride 16&/p&&p&4.22.2. Shader table memory initialization 16&/p&&p&&br&&/p&&p&&br&&/p&&p&&br&&/p&&p&Shader management 16&/p&&p&Problem space 16&/p&&p&Implementations juggle many shaders 17&/p&&p&Applications control shader compilation 17&/p&&p&State objects 17&/p&&p&Subobjects 17&/p&&p&State object types 18&/p&&p&DXIL libraries and state objects example 20&/p&&p&Subobject association behavior 21&/p&&p&State object caching 23&/p&&p&System limits and fixed function behaviors 24&/p&&p&Addressing calculations within shader tables 24&/p&&p&Hit group table indexing 24&/p&&p&Miss shader table indexing 24&/p&&p&Callable shader table indexing 24&/p&&p&Out of bounds shader table indexing 25&/p&&p&Acceleration structure properties 25&/p&&p&Data rules 25&/p&&p&Determinism based on fixed acceleration structure build input 25&/p&&p&Determinism based varying acceleration structure build input 26&/p&&p&Preservation of triangle set 26&/p&&p&AABB volume 27&/p&&p&Fixed function ray-triangle intersection specification 27&/p&&p&Acceleration structure update constraints 28&/p&&p&Acceleration structure memory restrictions 29&/p&&p&Ray recursion limit 30&/p&&p&Pipeline stack 30&/p&&p&Optimal pipeline stack size calculation 30&/p&&p&Default pipeline stack size 31&/p&&p&Pipeline stack limit behavior 32&/p&&p&Shader limitations resulting from independence 32&/p&&p&Wave Intrinsics 32&/p&&p&Raytracing emulation 34&/p&&p&Tools support 35&/p&&p&Buffer bounds tracking 35&/p&&p&Acceleration structure processing 35&/p&&p&Shader Cycle Counter 36&/p&&p&Basic Semantics 36&/p&&p&Interpreting Cycle Counts 36&/p&&p&Shader Compiler Constraints 37&/p&&p&DXIL Details 37&/p&&p&API 38&/p&&p&Experimental feature exposure 38&/p&&p&Device methods 38&/p&&p&CreateStateObject() 38&/p&&p&GetShaderIdentifierSize() 43&/p&&p&GetRaytracingAccelerationStructurePrebuildInfo() 43&/p&&p&Command list methods 47&/p&&p&BuildRaytracingAccelerationStructure() 47&/p&&p&EmitRaytracingAccelerationStructurePostBuildInfo() 58&/p&&p&CopyRaytracingAccelerationStructure() 61&/p&&p&DispatchRays() 64&/p&&p&StateObjectConfig methods 66&/p&&p&GetShaderIdentifier() 66&/p&&p&GetShaderStackSize() 67&/p&&p&GetPipelineStackSize() 67&/p&&p&SetPipelineStackSize() 67&/p&&p&Additional resource states 68&/p&&p&Additional root signature flags 68&/p&&p&D3D12_ROOT_SIGNATURE_FLAG_LOCAL_ROOT_SIGNATURE 68&/p&&p&Note on shader visibility 69&/p&&p&Additional SRV type 69&/p&&p&Constants 70&/p&&p&HLSL 72&/p&&p&Types, enums, subobjects and concepts 72&/p&&p&Ray flags 72&/p&&p&Ray description structure 74&/p&&p&RaytracingAccelerationStructure 74&/p&&p&Subobject definitions 74&/p&&p&Intersection attributes structure 75&/p&&p&Ray payload structure 75&/p&&p&Call parameter structure 75&/p&&p&Shaders 76&/p&&p&Ray generation shader 76&/p&&p&Intersection shader 77&/p&&p&Any hit shader 78&/p&&p&Closest hit shader 79&/p&&p&Miss shader 80&/p&&p&Callable shader 80&/p&&p&Intrinsics 80&/p&&p&CallShader() 81&/p&&p&TraceRay() 81&/p&&p&ReportHit() 82&/p&&p&IgnoreHit() 82&/p&&p&AcceptHitAndEndSearch() 83&/p&&p&System value intrinsics 84&/p&&p&Ray dispatch system values 84&/p&&p&Ray system values 85&/p&&p&Primitive/object space system values 86&/p&&p&Hit specific system values 86&/p&
周末没事找来DXR SDK随便看看,一边看就一边做了点笔记,但是这不是原始SDK文档的直接翻译,而是加了很多帮助理解的解释性的内容。由于我还在忙《全局光照技术:从离线到实时渲染》最后的出版工作,所以这个可能会拖后一点时间,但是1-4章帮助整体了解DXR而…
&figure&&img src=&https://pic3.zhimg.com/v2-c36fffecee47_b.jpg& data-rawwidth=&500& data-rawheight=&301& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&https://pic3.zhimg.com/v2-c36fffecee47_r.jpg&&&/figure&&p&&b&编者按:&/b&电量如同温暖的血液,裹挟着0和1,从一个终端涌进广阔的赛博空间,抵达另一个终端。低电量,无信号,连接不到服务器,这是数字时代的末日恐惧。&/p&&p&上月底在安卓和苹果商店上线的聊天应用Die With Me,只能在手机电量低于5%的时候使用。匿名聊天室、线上树洞、临终关怀小组,你可以在这里享受陌生人陪伴的最后几分钟,然后纵身跃下悬崖,插上充电宝,一切重新开始。&/p&&p&在数字时代,只要有电,每天都是复活节。&/p&&p&&br&&/p&&p&电量是新世界的宗教,低电量是现代社会的末日恐惧。电子化的浪潮中,个体看起来是那么不堪一击。&/p&&p&即时通讯、点外卖、看视频、手机支付、骑共享单车,衣食住行和社交娱乐全部集中在一个小小的显示屏上。所有人每天都在盯着手机和笔记本电脑右上角那个长方形的图标,看着它依次从绿色、黄色变为红色,数值越变越小,内心的焦虑和不安全感如同暗潮一般愈来愈汹涌。&/p&&p&电源!我的电源哪儿去了?!&/p&&figure&&img src=&https://pic2.zhimg.com/v2-117bb4cabcc6e1dda7a4ba05_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&970& data-rawheight=&546& class=&origin_image zh-lightbox-thumb& width=&970& data-original=&https://pic2.zhimg.com/v2-117bb4cabcc6e1dda7a4ba05_r.jpg&&&/figure&&p&寻找到充电宝的人如释重负,温暖的电流带来了新的生命力,象征着重新入网的机会。如果没找到,抱歉,你在数字世界里走到了生命的尽头。&/p&&p&&br&&/p&&p&&b&赛博时代的死亡仪式 &/b&&/p&&p&前不久,比利时手机应用开发者Dries Depoorter在安卓和苹果商店同时上线了手机聊天应用Die With Me(“和我一起死”),让所有手机重度依存症患者感受了一次“濒死体验”:&/p&&p&你只能在手机电量低于5%的时候进入这个聊天室,和一群陌生人闲聊上几句,然后不知道哪一刻就被强制下线。&/p&&p&这是最初的搭讪,也是最后的遗言。&/p&&p&忍不住好奇,我花3块钱购买了这个app,调高屏幕亮度,打开蓝牙,播放视频,以前所未有的残忍方式把手机电量耗到了4%。然后戳了一下这个绝望的图标,在“至暗时刻”进入聊天室。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-b3e7e6163aba4bf3df7e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&702& data-rawheight=&681& class=&origin_image zh-lightbox-thumb& width=&702& data-original=&https://pic3.zhimg.com/v2-b3e7e6163aba4bf3df7e_r.jpg&&&/figure&&p&UI设计相当令人不安。黑色的背景下,提示电量的红色字母尤为刺眼。&/p&&p&聊天界面下,你可以设置自己的昵称——但其实根本没有人在乎你叫啥,几分钟之后你就“死”了,那个暂时的代号也不会在赛博世界留下任何你来过的痕迹,所以没必要花太多心思包装自己。而且,想多了费电。&/p&&p&聊天记录旁边,显示着每一位聊天者的当前电量,最顶上的红色数字是我自己的,如同一把达摩克利斯之剑,随时可能落下。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-ed8fe72fef0e27c_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&927& data-rawheight=&132& class=&origin_image zh-lightbox-thumb& width=&927& data-original=&https://pic1.zhimg.com/v2-ed8fe72fef0e27c_r.jpg&&&/figure&&figure&&img src=&https://pic4.zhimg.com/v2-a9c52a3f293cfea4af02664f_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&939& data-rawheight=&178& class=&origin_image zh-lightbox-thumb& width=&939& data-original=&https://pic4.zhimg.com/v2-a9c52a3f293cfea4af02664f_r.jpg&&&/figure&&figure&&img src=&https://pic1.zhimg.com/v2-9fc716ae3ca_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&939& data-rawheight=&900& class=&origin_image zh-lightbox-thumb& width=&939& data-original=&https://pic1.zhimg.com/v2-9fc716ae3ca_r.jpg&&&/figure&&p&当电量一点一点减少,你不会再担心邮件有没有查收,微信微博上又收到了什么新的提醒,而是心甘情愿地留在这片黑暗中,和一些完全不认识的家伙分享一些绝望、平静或奇奇怪怪的哲学思考。&/p&&p&你站在悬崖边,亲眼目睹生命以一种前所未有的形式消散。有人抵达0%,即将彻底沉默,还“活着”的人便会委婉地表示:“他开悟了。”这是这个独特世界里独特的悼念方式。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-8f9a70efc4c13bc8d55051_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&939& data-rawheight=&170& class=&origin_image zh-lightbox-thumb& width=&939& data-original=&https://pic2.zhimg.com/v2-8f9a70efc4c13bc8d55051_r.jpg&&&/figure&&p&当然,也有那么些人拒绝“死亡”,他们在手机电量即将变成0%的时候火速插上充电线,希望能在聊天室里多待一分钟,和某个电量还剩4%的家伙多聊上一句。&/p&&p&这种作弊的方法会被所有人看到。在你的名字旁边,会出现一个?符号,就像这样:&/p&&figure&&img src=&https://pic3.zhimg.com/v2-65dd6a8ef451a6f3da7c47c6f50dcfaa_b.jpg& dat

我要回帖

更多关于 收到社保生育津贴 的文章

 

随机推荐