因资金不足,还666元贷款需要延迟一个月,.爱会不会重来影响到信誉,延迟一个月还贷款会扣加多少利息呢?

有贷款问题,就到武汉贷款网问答。
有无抵押:
贷款金额:
联系电话:
无需注册,快速申请
有无抵押:
贷款金额:
联系电话:
无需注册,快速申请
社科院专家:延迟退休对我国并无实际意义
发表时间: 查看次数:85
&&中国社会科学院研究员、社会政策研究中心秘书长。主要论著有:《博文中的社会政策》、《中国社会》、《中国城市居民贫困线研究》、《中国社会福利》等。
  2007年我国城乡低保已经全覆盖,2009年确立了机关公务员公费医疗、企业职工医疗保险(行情&专区)、农村新农合、城市居民养老保险四项制度,基本达到全覆盖的目标。今年三四月份,养老覆盖面已经达到了80%,目标是&十二五&期末能够覆盖全民。当然也有标准的问题,农村所谓的&养老保险&,每月只有55元,不过一场感冒的花费,但毕竟是从无到有。改善养老标准是今后的目标,但又面临许多新问题,养老保险制度争议很多,特别是延迟退休年龄这一热点问题。2004年人社部第一次提出此概念,我就表示反对,坚持了近十年。
  中共十八大报告提出:&统筹推进城乡社会保障体系建设。社会保障是保障人民生活、调节社会分配的一项基本制度。要坚持全覆盖、保基本、多层次、可持续方针,以增强公平性、适应流动性、保证可持续性为重点,全面建成覆盖城乡居民的社会保障体系。&这里突出了覆盖城乡居民,可持续、适应流动性,增强公平性,都是此前未有的。十八届三中全会《决定》当中说,要&研究制订渐进式延迟退休年龄政策&。延迟退休年龄已是必然,加了&渐进式&的定语。既是研究,我当然可以提不同意见,即使反对无效,我还是要将道理说清楚。
  发达国家延迟退休是为增加劳动力
  2004年,学者首先提出延迟退休年龄,而后人社部的领导出来呼应,从2012年起至今,越讲越多。起初说相应推迟退休年龄已是必然趋势,但媒体进行了若干次调查,基本上70%以上的老百姓反对,反对者最多达90%。一反对,就说该政策非常慎重,不会在近期立即实施。如此反复,不断政策试探,可能是想等大家折腾得麻木了就实施。
  深化改革,老百姓肯定赞成,但我觉得新一轮改革,首先要给百姓切实好处。改革开放从大包干开始,使农民获利,到上世纪80年代广大人民群众逐渐得益。然后到90年代,朱镕基才有可能实行下岗分流,因为此前大家都得到了好处,对党非常信任。但如今,一些改革政策可能会伤害百姓,而之前的一些基础已经薄弱,改革要顺利,信任要维持,对未来要有预期,那深化改革首先要给老百姓好处。即或看不到明显的好处,至少一开始不能对老百姓不利。在本世纪初转制并轨失业的人,当时如果40岁,现在也就50多岁,还没到退休年龄,他们的日子非常不轻松,一旦拿到退休金就可以喘口气。如果推迟退休年龄,对他们打击最大,哪怕推迟一个月,压力也很大。这个问题一定要非常慎重地考虑,至少等这一代人退休吧。
  延迟退休,官方的核心理由是发达国家都如此,即是国际趋势。发达国家确实从上世纪90年代开始,但并非因为养老金问题,而是社会老龄化。这其实是两方面促成的,一是人的寿命越来越长,老年人越来越多,二是人口出生率下降,新生劳动力越来越少。所有的发达国家都面临这样的问题。主要发达国家人口多为几千万,少的不过几百万,美国是发达国家中人口最多的,3亿,只相当于中国的零头。一个小的国家,老年人增多,劳动年龄人口减少,马上捉襟见肘。最早延迟退休年龄的动议,其实就是为了增加劳动力供给。
  法国中央银行(行情&专区)行长诺瓦耶跟中国社科院人力资源与社会保障研究所所长蔡昉说:&我们延迟真实目的是增加劳动力供应、提高劳动参与率,但公众误认为我们是为了减少养老金的支出。&大部分国家从60岁推迟到65岁。养老保险制度最早是1873年德国开始的,当时退休年龄定为70岁,但彼时德国人平均寿命只有50岁,二战后大部分退休年龄变成60岁。所以提高到65岁,相对来说欧美还是比较容易接受的,以前想工作没有工作,现在多干几年也很愿意,并没有引起特别大的风波。
  本世纪以来发达国家经济都不太景气,尤其是2007年以后美国的金融危机影响到全世界,欧盟出现了债务危机,这时就采取了很多紧缩政策。在此前提下,延迟退休也跟养老金挂钩。但没有明显的效果,至少我还未见到这方面的研究成果。而另一方面,现在所有发达国家的失业率都非常高,一般失业率在10%左右,而大部分欧洲国家的青年失业率已超过20%。老年人若不退休,为青年人创造新的工作岗位比较难,这是全世界的普遍现象。
  延迟退休对中国并无实际意义
  一般来说,延迟退休,办公室人员比较好接受,但是一线的蓝领工人就比较难接受。发达国家和发展中国家的阶层结构不同。国外中产阶级占到60&%以上,蓝领工人一般只占20%,甚至更少。在这样的情况下,政府不怕得罪蓝领工人,所以可以强行通过。但中国的蓝领工人占到60%甚至70%,中产阶级还很少。而且外企职员,据我所知都不愿意延迟退休,因为工作劳动强度和压力非常大。如前所述,去年至今,媒体各种调查结论都是70%以上的人反对,正好与阶层结构接近。
  此外,职业环境与工作条件不同。欧洲发达国家一般实行6小时工作制,职业歧视较少,有强大的工会支持。外资公司如果要减员,一般辞退新员工,保留老员工。但在中国,一天10-12个小时的工作时间,甚至更长,还有大量年龄性别歧视,蓝领工人女性40岁以上、男性50岁以上就很难找工作,如果延期退休,找工作就更困难,不但拿不到养老金,还要继续交养老保险。
  按照人社部的数据,中国每年大概新增劳动力两千万,而每年新增工作岗位一千万到一千二百万,其中30%是自然更替,有人退休、有人补进来。经济学教科书讲失业有四种状态;周期性失业、季节性失业、摩擦性失业、结构性失业,但中国还有总量性失业,即劳动力太多,怎么安排也有失业。
  我们一直说中国老龄化最高峰时有4亿老人,只有8亿劳动力,两个人养一个人。但这只是从保险的框架里来看问题,也就是8亿劳动力缴费来养4亿不缴费的。养老问题实际上是社会分配问题,本质上取决于两个条件。首先,退休时社会经济能够创造多少财富,其次,这些财富怎么分配。老龄化高峰,政策对老年人有更多优惠的话,就可以多一些财富进行分配,问题可以解决。除了单位和个人缴费,社会保险法也规定政府要承担责任。
  现在常说养老保险有缺口,意指单位和个人缴费不够发养老金,其实将社会保险看做商业保险了。现在拿不到养老金的都是制度外的人,参加社会养老的人应该拿到了养老金。当然也有个案,但是一般来说不会,如果有缺口,政府财政补上。社会保险法规定,养老保险的资金来源于三方面:单位缴费、个人缴费和财政补贴。老龄化趋势下,交社会保险的人越来越少、领保险的越来越多,所以单纯靠缴费不够,还需要财政补贴以及其他很多手段。《决定》里也说,国有资产收益要拿来补这一块。应该说,养老保险基金并没有像很多人认为的那样支持不下去,至少五年到十年内不会出现问题。
  养老并非简单的几个人养几个人的问题。同样是一家三口,父母能赚钱的和父母不能赚钱的,两家过的日子一样吗?这里就有一个劳动生产率的问题。中国现在的劳动生产率非常低,只有美国的1/11,日本的1/12。中国经济要继续发展,只能提高劳动力素质,提高劳动生产率。
  转变经济增长方式,做2.0版的&中国制造&,意味着自动化、高科技、信息化,结果就是少用人工。这是一个比人口自然减少更重要的影响因素。发达国家失业率高很大程度上是劳动密集型产业转移的直接结果。中国人口多,回旋余地大,如果充分就业,经济发展,要度过老龄化高峰比他国容易。现在我最担心的是整体经济设计跟着发达国家的路子走,以它们的现状定义我们的将来。劳动力是分层次的,有些人可能只适合劳动密集型产业,是不可能转轨的。所以我们更应该担心中国的就业问题。
  老龄化出现之后,中国劳动年龄人口的年纪会逐渐偏大,顶峰时平均会达到30多岁。如果延迟退休年龄,实际无助于解决结构性问题,也无新生劳动力。我们离老龄化最高峰还有22年,要真正解决和缓和中国老龄化高峰,是调整计划生育政策。这次《决定》中已经推出了&单独二胎&政策,虽然调整已经晚一些,但是中国人的生育意愿还在,可能大城市弱一些,二三线城市的人还是想生孩子的。
  还有一个问题要考虑,就是现行的退休年龄有没有好处。北京电视台有一个节目拍到各个小区里老人家带孩子。0-3岁的孩子,在中国是没有任何公共服务的,如果父母上班,必须要老人帮忙带。其中采访了一个正在哺育期的女性,说生孩子是算好的,因为妈妈今年退休。退休带孩子其实是社会劳动,在为社会做贡献,尤其中国的生育率已经低于警戒线了。等孩子到学龄,老人家可能就又将服务方向转向比自己更老的老人。中国现在说居家养老,全靠老人自己。现在女性55岁退休、男性60岁退休,他们还有余力,真要延迟退休,恐怕就没有这样的能力了。
  中国的平均退休金只有1800元,如果把此看做老人带孩子所取的社会工资,可能就更清楚些。如果都65岁退休,孩子就没有人带了,要为0-3岁的孩子和许多老人提供公共服务,恐怕远远超过1800元退休金,要全面考虑这些问题才行。
  保证公平才是中国社会养老的关键
  《决定》还提到要&推进机关事业单位养老保险制度改革&,这是针对双轨制的改革。目前企业职工的退休金少于2000元,而机关事业单位的退休金基本高于3000元。计划经济时代是企业自己发养老金,但改革开放之后不可持续了。上世纪九十年代初,提出向市场经济转轨,很多国有企业、集体企业不复存在或不能正常生产了,出现了很多退休人员领不到养老金的情况,所以进行了改革。改革初期财政能力非常差,现在财政是11万亿元人民币,当时只有几千亿元。改革更多是解决国有企业的问题,改变了企业职工的养老金计算方法,都跟社会平均工资挂钩,最高是70%,病退的是40%-50%。
  改革时平均养老金只有700元,企业养老金至今也比较低。但机关事业单位的养老金还是按照70%来计算的。根据工龄,30年以上工龄可以拿到80&%,40年以上就可以拿到90%,差距产生。中国的养老金除了支持老人的基本生活费用外,还有大部分支付自费的医药(行情&专区)费。每个老人都得准备一笔钱就医,而不是国际通常意义的养老金就是管老年生活。所以从这个角度来说,事业单位的养老金也并不高,企业职工的养老金更低。
  简单的并轨,并不能解决企业职工养老金偏低的问题。那么把机关企事业单位的养老金往下拉行不行?事业单位有3000多万教师、1000万医护人员,将他们的养老金下拉,这显然是不可接受的。所以我们的目标是要将企业职工的养老金往上提,而不是将其他人的养老金往下拉。
  还有学者说,之所以不能延迟退休年龄,是因为双轨制不公平,只要大家养老金差不多,延迟退休年龄没有问题。其实这是两回事,但确实有人支持先并轨再延迟退休年龄。目前养老金改革要么小改,就是将企业退休人员的养老金上涨,不动其他制度。如果要大改,要将中国的养老金分为两部分,一是基本养老金,追求公平,大家都以中华人民共和国公民的身份交养老金,根据自己收入一定比例来交。领养老金的时候,从国家领导人到普通人都在一定的水平上领基本养老金,随着物价上涨每年调整,能够保障基本生活需求;二是补充养老金,跟效率挂钩,即收入多就可以多缴,将来可以多得,这应该由市场操纵。
  养老保险真正的问题何在?在于流动问题,包括农民工的流动,也包括城市间的人口流动。他们缴了养老保险,但一旦流动起来,将养老金转移是很难的,到领养老金时就可能根本无处可领。这是中国养老保险制度迫在眉睫要解决的问题,有关部门对此掉以轻心,而将未来几十年的事情算了又算,我觉得比较搞笑。
  要考量一个政策好或不好,首先要从经济理性角度考察,在有限的资源条件下追求效率。但这不是唯一尺度,所有政策都不是算出来的,都是政治决策,要考虑多方因素。公共政策还要考虑人文关怀,即在社会分配过程中,以无数个具有平等权利的个体的人为本的公平分配,其次指在整个社会经济发展中,以无数个具有平等权利的群体的人为本的共享与参与。延迟退休年龄,实际上是注重经济理性,忽视人文关怀;双轨制和并轨问题,注重了人文关怀但又忽视了经济理性;在流动人口的转移接续方面,既忽视了经济理性又忽视了人文关怀,这是我们当前迫切需要解决的政策。
版权所有:武汉贷款网 技术支持:武汉陂人投资
武汉贷款网专注于为个人和企业提供银行贷款,小额贷款,个人贷款,住房贷款,汽车贷款,抵押贷款,创业贷款,无抵押信用贷款等贷款咨询和融资服务。24h热线:400-626-0995
本网只是供需平台,对一切贷款业务的风险识别和防范都有会员和浏览者自己承担
Copyright(C) 2008 . All Rights Reserved你好,我去年有笔助学贷款银账户里余额不足,少了300左右,导致还款延迟了,不知道是否影响个人征信,可以办理信用卡或者贷款吗,谢谢-金斧子7148人阅读
Deferred Shading,看过《Gems2》&的应该都了解了。无论是Unreal3、Crysis还是星际2,都已经支持或者准备支持这个技术。
不过因为国内这种环境,真正在项目中能用到的可能并不多,不知道这次星际2出来后,情况会不会有所变化。^_^
本文是对Gems2这篇文章的一个补充,小生在做此次外包的时候,由于需要,翻译了这篇文章,不敢独享,遂贴于此,望能抛砖引玉,愿众位前辈不吝赐教。 ^_^
Tabula Rasa中的延迟着色技术
作者:Rusty Koonce (NCSoft)
翻译:noslopforever(天堂里的死神)
本翻译仅用于学术目的。
这篇文章是对GEMS2里《Deferred Shading in S.T.A.L.K.E.R.》(中文译名《S.T.A.L.K.E.R.中的延期着色》,原作者Oles Shishkovtsov)的一个补充。它是在我们耗时两年时间、为游戏Tabula Rasa(Richard Garriott担纲的MMORPG)完成的渲染引擎的基础上形成的。GEMS2的这篇文章覆盖了实现一个Deferred Shading引擎的基本原理,而我们将重点放在了基于Deferred Shading引擎的工作中时可能遇到的更高层面的问题、技术和解决方案上。
1 Introduction
在计算机图形学的词典里,Shading表示&对受光物体的渲染&,这个渲染过程包括下面几步:
1,计算几何多边形(也就是Mesh)。
2,决定表面材质特性,例如法线、双向反射分布函数(bidirectional reflectance distribution function,BRDF)等等。
3,计算入射光照。
4,计算光照对表面的影响,并最终显示。
一般渲染引擎,渲染场景中的物体的时候,是将这四步一次执行完的。延迟着色则将前两步和后两步分开到渲染管道相互独立的两个部分来执行。
我们希望读者在阅读本文前,能先了解一下延迟着色的基本原理。以下的文章都不错,可以读读:,,。
在本文中:Forward Shading(前向渲染)是指4个步骤一齐处理的传统着色方法。Effect就是Direct3D的D3DX Effect,而Technique,Annotation和Pass,与它们在D3DX中的概念一样。
材质着色(Material Shader)是指用来渲染几何图元的Effect(也就是前两步),光着色则是指用来渲染可见光源的Effect。几何体(Body)用来指代那些需要渲染的物体。
在这里我们忽略了显卡相关的优化或实现,所有的解决方案都是普遍适应于SM2和SM3硬件的。我们希望能强调这个技术,而非实现。
2 Some Background
在Tabula Rasa中,我们一开始的渲染引擎是基于最初的DX9而完成的传统前向渲染技术的,使用了HLSL和D3DX Effect。我们的Effect使用了Pass里的Annotation来描述这个Pass所支持的光照。而在CPU这边,引擎可以算出来每个几何体被那些光源所影响&&这个信息连同那些在Pass的Annotation里的信息一起,用于设置光源的参数、以及确定每个Pass该调用多少次。
这种前向着色有多种问题:
1,计算每个几何体受那些光影响耗费了CPU的时间,更坏的是,这是个O(n*m)的操作。
2,Shader经常需要超过一次以上的Pass来渲染光照,渲染n个灯光,对于复杂的Shader,可能需要O(n)次运算。
3,增加新的光照模型和新的光源类型,可能需要改变所有Effect的源文件。
4,Shader很快就将达到或者超出SM2的指令限制。
在MMO里,我们对游戏环境很少会有过于苛求的要求。我们无法控制同屏可见的玩家数量、无法控制同屏会有多少特效和光源。由于传统前向渲染缺乏对环境的控制,且对于光源的复杂度难于估量,因此我们选择了延期着色。这可以让我们的画面更接近于当今顶尖的游戏引擎,并且让光照所耗费的资源独立于场景的几何复杂度。
延期着色提供了下面的好处:
1,光照所耗费的资源独立于场景复杂度,这样就不用再费尽心机去想着处理那些光源影响几何体了。
2,不必要再为几何体的受光提供附加的Pass了,这样就节省了Draw Call和状态切换的数量。
3,在增加新的光源类型和光照模型时,材质的Shader不需要做出任何改变。
4,材质Shader不产生光照,这样就节省了计算额外的几何体的指令数。
延期着色需要显卡提供MRT的支持,且利用了不断增加的存储器的带宽&&这也就意味着我们可能得对玩家所使用的硬件提出更高的要求。因此我们既实现了前向着色,也实现了延期着色。我们优化了前向着色管道,并在此基础上完成了延期着色管道。
有了一个完全基于前向着色的系统作为后盾,我们就可以以更高的硬件标准来完成延期渲染系统了。我们使用了SM2的显卡作为前向着色系统的最低配置,而延期着色系统,则需要支持SM3的显卡。这样就更易于开发一个延期渲染管道,因为我们不必要再顾虑指令数的限制,且能使用动态分支语句。
3前向着色支持
即便是工作在延期着色引擎下,对于半透明物体的渲染依旧需要前向渲染管道的支撑(详见本文第8节)。我们的引擎里保留了对整个前向着色管道的支持,这个管道用来处理半透明物体,以及用于在低端显卡上替代延期着色引擎。
本节讲述了我们是通过什么方法来同时支持前向和延期渲染的。
3.1 受限的特性
我们限制了前向渲染管道的特性,只让它实现延期渲染管道所有特性的一个很小的子集。有些特性因为技术上的原因无法支持,有些是因为工期太紧,但更多的,是为了开发起来方便而被我们丢弃掉了。
我们的前向渲染管道支持球状光源(hemispheric),方向光源和点光源,其中点光源是可选的,其他的所有类型光源都不支持(包括Spot Light,Box Light,它们只由延期着色管道来支持)。在延期渲染管道里构建的阴影和其他特性,在前向渲染管道中都不支持。
最后,前向渲染中的Shader是可以做逐顶点光照和逐像素光照的。在延期渲染管道中,所有的光都是逐像素的。
3.2 一个Effect,多个Technique
我们使用了在Effect中使用了不同的Technique来完成前向着色、延迟着色和Shadow Map,以及更多的东西。我们对每个Technique指定了Annotation来标明这个Technique使用了什么样的渲染方式。这就允许我们将所有的Shader代码放到一个统一的Effect文件里,来实现渲染引擎所需的所有Shader(见表19-1)。这包括前向着色中的静态和骨骼模型,延期着色使用到的&材质着色&(Material Shading)的静态和骨骼模型,以及Shadow Map。
把Effect所能用道德所有的Shader放到一个地方,我们就可以尽可能多地共享一些可以跨越不同渲染技术的代码。当然,我们不会去做一个超长的文件来储存这些代码,而是将这些Shader建立了一个由多个文件组成的Shader库,包含了各个Effect都能用到的共享的顶点和像素代码,以及常用的函数。这减少了Shader代码的复制,使得维护变得容易,减少了Bug,以及增强了各个Shader之间的稳定性(consistency)。
表19-1:材质示例
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& & Code View:
// These are defined in a common header, or definitions
// can be passed in to the effect compiler.
#define RM_FORWARD 1
#define RM_DEFERRED 2
#define TM_STATIC 1
#define TM_SKINNED 2
// Various techniques are defined, each using annotations to describe
// the render mode and the transform mode supported by the technique.
technique ExampleForwardStatic
& int render_mode = RM_FORWARD;
& int transform_mode = TM_STATIC;
technique ExampleForwardSkinned
& int render_mode = RM_FORWARD;
& int transform_mode = TM_SKINNED;
technique ExampleDeferredStatic
& int render_mode = RM_DEFERRED;
& int transform_mode = TM_STATIC;
technique ExampleDeferredSkinned
& int render_mode = RM_DEFERRED;
& int transform_mode = TM_SKINNED;
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& &
3.3&&&&& 光照优先级
我们的前向渲染在对一个集合体使用多个光源时,很容易就需要增加额外的Pass了。增加Pass不仅会产生更多的Draw Call,也会造成更多的状态切换和更多重绘(原文是Overdraw,我感觉这里可能想表达重绘的意思)。我们发现在有很多光源的情况下,我们的前向渲染只绘制一小部分光源,就会比延期渲染慢。因此,为了更好的性能,我们严格限制了前向渲染管道里,对一个集合体受光的最大数量。
延期渲染管道每帧可以处理30个、40个、50个、甚至更多的动态光源,它们消耗的资源与几何体的数量,大小,以及受光程度毫无关系。然而,在前向渲染管道中,当有两个光源影响了一大坨几何体时,瞬间就卡了。由于两个渲染管道存在如此显著的性能差别,使用相同数量的光源几乎是不可能的。
我们为美术和策划提供了对光源优先级的编辑操作,提供了光源用于前向渲染、还是用于延期渲染、还是都用的开关。光源的优先级在两个管道中都有作用&&当心能不足的时候,我们可以知道该关哪些光源;在延期渲染中,为了性能、质量设置,光源可能需要依据优先级关掉因它产生的阴影。
地图通常是按照延期渲染管道进行打光的。我们提供了一个很快的Pass来确认光源在前向渲染管道中是否是可接受的。一般地,在前向渲染管道下唯一的一个额外工作是增加Ambient Light的数量,来补偿相对于延期渲染管道少得多的灯光。
4 高级光照特性
下面的这些技术在前向和延期渲染引擎中都有可能实现。在我们的延期渲染管道中,我们支持了所有这些技术。即便我们不用延期渲染,这些技术仍然可以使用(Even though deferred shading is not required, it made implementation much cleaner.)。在延期渲染中,我们将这些特性的实现与材质Shader分离开,这样我们就可以增加新的光照模型和光源类型,而不必要修改材质属性。这就正如我们可以添加新的、独立于光照模型和光源类型材质。
4.1 Bidirectional Lighting各向异性光照
传统的球面光照(hemispheric lighting),正如DX文档里所说的那样,太普通了。这种光照模型使用了两个颜色,一般标记为Top和Bottom,然后基于表面法线对这两个颜色进行线性插值。标准的球面光照,根据表面法线方向朝正上方和正下方(这也就是为什么叫Top和Bottom),来对颜色进行插值。在Tabula Rasa中,我们支持了这种传统的球面光照,但我们也为方向光源提供了背部颜色(Back Color)。
在延期渲染中,美术可以很简单地增加多盏方向光源。我们发现他们经常使用一盏与另一盏光源恰巧相反的光源来模拟辐射度。他们很喜欢这种方法的结果,因此一个自然而然的优化就是:将这两个光源统一成一个特殊的方向光源&&一个正面颜色和一个背部颜色。这给了他们相同的控制,但少了一半工作量。
对于之后的优化,背部颜色只是一个N&L的运算,或者一个简单的朗伯(Lambertian)光照模型。我们不必要为背部实现Specular,阴影,遮挡,以及更多高级光照技术。这些背部颜色只是对整个场景环境光和辐射度一个简单的近似。我们将正面颜色的N&L存了下来,将它的方向取反以用到背部颜色的计算上。
4.2 Globe Mapping
Globe Map是用来对光照添加颜色的,就像我们生活中的玻璃球(溜溜弹)那样。光线从光源发射出来,穿过玻璃球,然后被玻璃球赋予颜色和遮挡。对于点光源,我们使用一个Cube Map来完成这个功能,而对于聚光灯,我们使用2D纹理。这可以用于高效地模拟彩色玻璃的效果,或者通过一个模板来对光线进行遮挡。我们也为美术提供了旋转和让这些Globe Map动起来的效果。
可能的话,美术可以使用Globe Map来高效模拟Shadow Map,模拟彩色玻璃,迪斯科球(就是一般舞厅里那个旋转的,闪着暧昧和刺眼光芒的球球),以及更多。我们引擎里所有的光源都支持这些。请参考图19-1 和19-3。
图19-1:基本的聚光灯
图19-2:简单的Globe Map。
图19-3:融合了Globe Map的聚光灯。
4.3 Box Lights
在Tabula Rasa中,方向光是影响整个场景的全局光,且用于模拟太阳光和月亮光。我们发现,美术有时候想用方向光影响一小块区域,而不是整个个场景。
我们的解决方案是Box Light。这些光也是方向光,但他们只在一个长方体中起作用。在这个长方体中,我们可以支持类如聚光灯那样的衰减,这样,他们的强度就会随着距离边界越近而衰减得越厉害。Box Light也支持Shadow Map,Globe Map,背面颜色,以及所有其他被我们引擎支持的光源特性。
4.4 Shadow Maps
在Tabula Rasa中,没有预计算的光照。我们只用到了Shadow Map,而没有使用Stencil Shadow和Light Map。美术们可以让任何光源产生阴影(除了球面光照外)。对于Point Light,我们使用了Cube Map来产生Shadow Map,其它的情况下,我们都使用了2D纹理。
Tabula Rasa中的所有Shadow Map都使用了浮点纹理,且使用了抖动采样(Jitter Sampling)来进行柔化。美术可以控制抖动的幅度,以控制软阴影&软&的程度。这个方法允许我们用一个固定的方法,在所有的硬件上实现相同的效果,当然,对于Shadow Map,我们肯定是要使用硬件相关的纹理格式的。硬件相关的纹理格式可以提供诸如更好的精度,更好的硬件过滤。
Global Shadow Maps
很多论文讨论了全局阴影图,或者由一盏方向光的平截台体所产生的单独的阴影图。我们花了两个星期的时间来研究透视阴影图(Perspective shadow Maps)[]和梯形阴影图(trapezoidal shadow maps)[]。这两个方法最大的问题是最后的结果取决于光源方向和眼睛方向。只要摄像机一变化,阴影的质量就会发生改变,最坏的情况下,变成了标准的正交投影。
Tabula Rasa里面是有白天和夜晚的循环的,太阳和月亮持续的在天上划过。在黄昏和拂晓的时候,光源方向与水平面几乎平行,这就增加了摄像机方向与光源方向平行的几率。这是前述两种方法面对的最糟糕的情况。
由于摄像机和光源方向不断移动,阴影质量变得很难把控,我们最终(end up,这里不知道作者是想说最终,还是说不再)使用一张大的2048 X 2048的Shadow Map进行正交投影。这使得最后的结果很统一,而且与光源和摄像机的夹角无关。当然,肯定会有比我们这种方法好得多的方法,例如Cascaded Shadow Map。
我们使用了抖动采样来柔滑阴影边缘,我们对光源的位移进行了离散化,因此他总是指向Shadow Map中固定的位置,我们也对光源的方向离散化了,这样Shadow Map计算时的值不需要每帧都发生变化。最终的结果是,我们获得了一个稳定的阴影,无论摄像机如何移动。
请看表19-2。
表19-2 离散化光源位置以计算Shadow Map投影矩阵。
Code View:
// Assumes a square shadow map and square shadow view volume.
// Compute how "wide" a pixel in the shadow map is in world space.
const float pixelSize = viewSize / shadowMapW
// How much has our light position changed since last frame?
vector3 delta(lightPos - lastLightPos);
// Project the delta onto the basis vectors of the light matrix.
float xProj = dot(delta, lightRight);
float yProj = dot(delta, lightUp);
float zProj = dot(delta, lightDir);
// Quantize the projection to the nearest integral value.
// (How many "pixels" across and up has the light moved?)
const int numStepsX = static_cast&int&(xProj / pixelSize);
const int numStepsY = static_cast&int&(yProj / pixelSize);
// Go ahead and quantize "z" or the light direction.
// This value affects the depth components going into the shadowmap.
// This will stabilize the shadow depth values and minimize
// shadow bias aliasing.
const float zQuantization = 0.5f;
const int numStepsZ = static_cast&int&(zProj / zQuantization);
// Compute the new light position that retains the same subpixel
// location as the last light position.
lightPos = lastLightPos + (pixelSize * numStepsX) * lgtRight +
&&&&&&&&&&&&&&&&&&&&&&&&& (pixelSize * numStepsY) * lgtUp +
&&&&&&&&&&&&&&&&&&&&&&&&& (zQuantization * numStepsZ) * lgtD
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& &
Local Shadow Maps
在我们的引擎中,所有光源都可能产生阴影,而整个地图有上百盏灯。引擎必须提供管理和使用Shadow Map的方法。所有的Shadow Map知道他们需要时才会被创建出来,并且,大多数Shadow Map是静态的,不需要每帧都重新创建。我们为美术提供了控制每个产影灯是使用静态Shadow Map还是动态的Shadow Map。静态的Shadow Map只生成一次,之后就一直使用,而动态的则每帧都会被刷新。
我们同样标定了几何体是静态的还是动态的,也就是运行时是否可动。我们可以根据这个标志来在计算中裁减掉部分几何体。当创建静态Shadow Map时,我们排除了动态几何体部分。这可以防止类如Avatar这样的动态物体产生的动态影&getting &baked& into a static shadow map&(这句话不是特别明白,可能想表达的意思就是,静态Shadow Map产生时仅考虑静态物体,而不考虑场景中当前的动态物体吧?这不废话么?!拍静态物体的Shadow Map当然不应该考虑动态物体了,要不干嘛弄这一套静态Shadow Map?!)。动态物体如同其它静态物体那样,使用静态Shadow Map来对自己打影。例如,沿着楼梯走的Avatar,将会被楼梯投到他身上的影子所影响。(个人感觉这里作者可能想表述的就是他们把静态物体和动态物体的产影分开了,互相独立,不过业界的应该都是这么做的吧?需要这么特别说明一下么?搞不明白!当然,也可能使我理解错了,欢迎大家批评指正!)
这里有很多种自动化和优化的方法。我们并不一开始就生成所有的静态Shadow Map,而是在需要用到的时候再去创建。这就意味着我们并不需要发布这些Shadow Map文件,且减少了Loading时和运行时从磁盘读取数据的数量。为了节省显存和节省纹理创建的开销,我们使用了Shadow Map池。关于这些本文后面会有更多地描述。
动态产影光源是最耗费的,他们需要常时重新生成他们的Shadow Map。如果动态产影光源不移动,或者移动得不那么剧烈,则就有一些方法可以提升一些性能了。最简单的是,除非有动态几何体在光源的影响范围内,否则就不要重新生成这些Shadow Map。另一个选择是将静态模型渲到各自独立的静态Shadow Map上,这样这些Shadow Map就只用创建一次了。每帧都需要将动态物体渲染到独立的动态Shadow Map上,在最后,只需要判断两个Shadow Map中最小的,或者最近的值就可以了。最后的结果就类似于整个场景的所有物体都产生了Shadow Map&&其实我们生成的只有动态物体。
4.5 将来的扩展
由于基于延期着色的引擎已经将光照和几何渲染完全分开, 因此我们就可以很方便的修改或增加光照的特性了。事实上,前面说的Box Light,从会议上的提议到最后编辑器里的完整功能,我们只花了三天时间。
HDR,Bloom,以及其它特效,添加到延期渲染引擎里、与添加到传统渲染引擎的难度相当。延期渲染引擎的架构,使得他更易于扩展。一般的,在延期渲染引擎里增加一个特性,比在前向渲染引擎里增加一个特性显得简单,或者起码不会难太多。限制延期渲染引擎特性的最大问题是能添加到每个象素中的材质属性,可用的显存,以及显存带宽。
5 可读的Depth和Normal Buffer的优势
延期着色的一个前提是,需要创建储存深度和法线信息的纹理。这些信息将被用到光照计算中。然而,他们也可以超越光照的范畴,用于计算雾,深度Blur(应该是指DOF),体积粒子,以及消除半透明物体穿入不透明物体时的硬边。
5.1 高级水和折射
在Tabula Rasa的延期渲染中,我们的水面Shader充分考虑了水的深度信息(视空间下)。当水的被渲染时,我们将拿它的每个象素和我们延期着色中已有的深度进行比较。这就使我们的水面可以具备自动的海岸线,而且,水可以根据视空间的深度来改变颜色和半透明,同时,在水下的物体可以做折射,而水上的物体则不用。我们可以在一个Pass里面做完所有这些工作,而不像传统渲染引擎那样。(译者:但是,我个人认为,这种海岸线的效果真得不怎么样&&!除了过度柔和的边缘之外,对于海浪之类的模拟较差。相比而言,还是Crysis、FarCry这类引擎的海面做得好啊)
我们的前向渲染引擎只支持基本的折射特性,它需要一条独立的Pass来初始化折射纹理的Alpha信息,以分辨那些在水面之上的部分,这些部分不能计算折射。[]给出了这个算法。
在我们的延期渲染引擎中,我们可以采样到当前像素的视空间深度和被折射像素的视空间深度。通过比较这两个深度,我们可以知道究竟被折射像素是高于水面还是低于水面,低于水面的,发生折射,高于水面的,就不再处理了。见图19-4和19-5。
图19-4 前向渲染的水。
折射只在低于水面的地方产生,这里没有可访问的深度信息,只能用多个Pass来处理,不能用视空间深度。
图19-5 使用了前向渲染,但通过延期渲染的Depth Buffer来获取深度。
注意颜色和半透明随着视空间深度变化而变化,没有了水体的硬边,只需要一个Pass。
为了方便美术控制随深度变化的颜色和透明度,我们提供了一个Volume纹理,而非一个1维纹理。1维纹理只是一个从归一化的深度查询到透明度的速查表。而Volume纹理则允许美术模拟水深对半透明的非线性的变化。Volume纹理也用于影响水面的颜色。这可以使一个平板Volume纹理(也就是一张标准的2D纹理),也可以是有2或者4个W分量的Volume纹理。归一化的深度用于对W进行采样,UV则由美术来指定。水面的表面法线有两个相互独立的、UV动画的发现图构成。
5.2 分辨率无关的边缘检测
[](GEMS2里的那篇文章)提出了一个边缘检测方法,用于在帧缓存上模拟反锯齿。这种方法需要一些与分辨率相关的魔数。我们也需要反锯齿,我们修改了一下这个方法,使之可以与分辨率无关。
我们对一个像素邻近的8个像素,进行深度梯度和法线角度的采样,这一点是与Gems2一致的。我们在这个点上判断深度上最大的和最小的变动,来确定边缘有多强。像素之间深度的梯度是与分辨率无关的。通过比较梯度变化率之间的关系,而不是梯度,就可以做到分辨率无关了。
我们的法线处理类似于GEMS2的方法。我们比较了中央像素和其周围、沿与我们检测梯度相同的边缘、的像素角度的余弦的变化(译者:我也没弄明白啥意思,具体就看代码吧&&)。这里我们使用了我们自己的常数。无论如何,法线的变化率也是分辨率无关的,这就达到了我们的要求。
在这个算法中,我们没有做对&右上&或&前&边缘的选择的限制,因此很多边缘会有两个像素宽,不过,当使用了Filter来平滑这些边缘后,看起来也不错。
边缘检测的结果是生成了逐像素的边缘权重,这个值在0~1之间。这个权重反映了会有多少像素在它上面。在最后的渲染前,我们会把这个权重进行四个Bilinear采样。这四个采样是中心像素权重0,四周权重为1的采样。这样的结果就是目标像素的权重是它8个邻居权重的平均值。像素越是一个边缘像素,就会越多与它的邻居混合。请参考表19-3。
表19-3:边缘检测的Shader代码。
Code View:
////////////////////////////
// Neighbor offset table
////////////////////////////
const static float2 offsets[9] = {
& float2( 0.0,& 0.0), //Center&&&&&& 0
& float2(-1.0, -1.0), //Top Left&&&& 1
& float2( 0.0, -1.0), //Top&&&&&&&&& 2
& float2( 1.0, -1.0), //Top Right&&& 3
& float2( 1.0,& 0.0), //Right&&&&&&& 4
& float2( 1.0,& 1.0), //Bottom Right 5
& float2( 0.0,& 1.0), //Bottom&&&&&& 6
& float2(-1.0,& 1.0), //Bottom Left& 7
& float2(-1.0,& 0.0)& //Left&&&&&& &&8
float DL_GetEdgeWeight(in float2 screenPos)
& float Depth[9];
& float3 Normal[9];
& //Retrieve normal and depth data for all neighbors.
& for (int i=0; i&9; ++i)
&&& float2 uv = screenPos + offsets[i] * PixelS
&&& Depth[i] = DL_GetDepth(uv);& //Retrieves depth from MRTs
&&& Normal[i]= DL_GetNormal(uv); //Retrieves normal from MRTs
& //Compute Deltas in Depth.
& float4 Deltas1;
& float4 Deltas2;
& Deltas1.x = Depth[1];
& Deltas1.y = Depth[2];
& Deltas1.z = Depth[3];
& Deltas1.w = Depth[4];
& Deltas2.x = Depth[5];
& Deltas2.y = Depth[6];
& Deltas2.z = Depth[7];
& Deltas2.w = Depth[8];
& //Compute absolute gradients from center.
& Deltas1 = abs(Deltas1 - Depth[0]);
& Deltas2 = abs(Depth[0] - Deltas2);
& //Find min and max gradient, ensuring min != 0
& float4 maxDeltas = max(Deltas1, Deltas2);
& float4 minDeltas = max(min(Deltas1, Deltas2), 0.00001);
& // Compare change in gradients, flagging ones that change
& // significantly.
& // How severe the change must be to get flagged is a function of the
& // minimum gradient. It is not resolution dependent. The constant
& // number here would change based on how the depth values are stored
& // and how sensitive the edge detection should be.
& float4 depthResults = step(minDeltas * 25.0, maxDeltas);
& //Compute change in the cosine of the angle between normals.
& Deltas1.x = dot(Normal[1], Normal[0]);
& Deltas1.y = dot(Normal[2], Normal[0]);
& Deltas1.z = dot(Normal[3], Normal[0]);
& Deltas1.w = dot(Normal[4], Normal[0]);
& Deltas2.x = dot(Normal[5], Normal[0]);
& Deltas2.y = dot(Normal[6], Normal[0]);
& Deltas2.z = dot(Normal[7], Normal[0]);
& Deltas2.w = dot(Normal[8], Normal[0]);
& Deltas1 = abs(Deltas1 - Deltas2);
& // Compare change in the cosine of the angles, flagging changes
& // above some constant threshold. The cosine of the angle is not a
& // linear function of the angle, so to have the flagging be
& // independent of the angles involved, an arccos function would be
& // required.
& float4 normalResults = step(0.4, Deltas1);
& normalResults = max(normalResults, depthResults);
& return (normalResults.x + normalResults.y +
&&&&&&&&& normalResults.z + normalResults.w) * 0.25;
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& &
6.1 材质属性
小心选择属性
Tabula Rasa的延期渲染瞄准的是DX9 平台,SM3的硬件环境。这个阶段的配置有大量的用户群,然而同时,DX10和SM4可以减少许多限制。首要的一点是,SM3最多只支持4个Render Target,且不支持独立的Render Target位深度(也就是,4个RT必须具备同样的Bit数,如果你一个用的是R8G8B8A8,32bit,那么另一个你就不能用FP16,只能用FP32,因为FP16是16Bit的,而FP32是32Bit的)。这就限制了我们可以使用的、用于储存材质信息的数据通道数量。
一般的4个DX9 32Bit MRT纹理,除了深度缓冲(指DX本身的DepthStencil)之外,剩下还有13个数据通道来储存属性信息:3个4通道的RGBA纹理,和一个32Bit的高精度深度纹理。即便我们使用的是64Bit,而非32Bit,除了能提供更高的精度外,其实并不能增加数据通道的数量。
即便所有的数据通道都是按照顺序来储存信息的,但在SM3下,所有对数据的访问都通过浮点寄存器。这就意味着使用Bit Mask或者类似手段来做压缩或者将更多信息存储到一个通道里是不切实际的。到了SM4,才支持真正的整数运算。
必须指出的是,这些通道里存储的信息,直接决定了引擎能支持怎样的光源类型。我们只能尽可能避免存储某一个具体光源类型独特的数据。在通道受限的情况下,每个通道都必须最大程度地利用来存储那些最重要的数据。
这里有一些辅助压缩或者减少通道使用量的方法。存储视矩阵的法线时,可以存储在两个Channel里,而不是三个。在视矩阵里,法线的Z分量只可能具有统一的符号(正负号),因为所有可视的像素都面对摄像机。利用这个信息,同时,利用所有的法线都是单位向量,我们可以通过XY分量构建出Z分量来。另一个方法,是把材质属性存储到一个纹理速查表中,然后把必要的纹理坐标(也就是这个速查表的索引)存储到MRT的数据通道里。
这些材质的属性,就是维系材质和光源之间的胶水。它们是材质Shader的输出,同时是光照Shader的输入。
同时,他们也是材质和光照之间唯一的关联。这样,改变材质的属性数据(应该是指改变数据通道的组织,服了这为大哥了&&到处写这种语焉不详模棱两可的话,官腔!&&改变个数据至于要修改所有Shader吗?那你延期渲染的优势还能体现到哪里呢?!),同时必然需要改变所有的Shader,包括材质和光照。
封装和隐藏MRT数据
我们并不直接把材质属性的数据通道或者数据格式暴露给光照Shader,而是通过一些函数来设置和获取这些信息(拜托,求你了,大哥,我们懂基本的封装,你不是写给小学生看的,OK?!快说重点!)。这样,数据的位置和格式就可以随意改变,而材质和光源则只需要重新编译,而不必修改。
我们也提供了一个在材质里专门初始化所有MRT数据的Shader。这可能增加了不必要的指令开销,但为我们未来扩展新的数据通道提供了便利,也不必要再去修改已经存在的材质Shader了。材质Shader只有在默认值需要发生改变时,才会去修改。请见表19-4:
表19-4:封装和隐藏MRT数据
Code View:
// Put all of the material attribute layout information in its own
// header file and include this header from material and light
// shaders. Provide accessor and mutator functions for each
// material attribute and use those functions exclusively for
// accessing the material attribute data in the MRTs.
// Deferred lighting material shader output
struct DL_PixelOutput
& float4 mrt_0 : COLOR0;
& float4 mrt_1 : COLOR1;
& float4 mrt_2 : COLOR2;
& float4 mrt_3 : COLOR3;
// Function to initialize material output to default values
void DL_Reset(out DL_PixelOutput frag)
& // Set all material attributes to suitable default values
& frag.mrt_0 = 0;
& frag.mrt_1 = 0;
& frag.mrt_2 = 0;
& frag.mrt_3 = 0;
// Mutator/Accessor & Any data conversion/compression should be done
// here to keep it and the exact storage specifics abstracted and
// hidden from shaders
void DL_SetDiffuse(inout DL_PixelOutput frag, in float3 diffuse)
& frag.mrt_0.rgb =
float3 DL_GetDiffuse(in float2 coord)
& return tex2D(MRT_Sampler_0, coord).
// Example material shader
DL_PixelOutput psProgram(INPUT input)
& DL_PixelO
& // Initialize output with default values
& DL_Reset(output);
& // Override default values with properties
& // specific to this shader.
& DL_SetDiffuse(output, input.diffuse);
& DL_SetDepth(output, input.pos);
& DL_SetNormal(output, input.normal);
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& &
延期着色很容易由于丧失了数据的精度而引发问题。最明显的丢失,是由于材质数据被存储到了MRT数据通道里。在Tabula Rasa中,绝大多数数据通道是8Bit或者16Bit的,取决于我们使用了32Bit的Render Target还是64Bit的(一个Render Target有4通道这一点并没有改变)。硬件内部的寄存器与Render Target的内部格式精度并不一致,再读和写的时候均需要数据的转换。例如:我们的法线分量是通过硬件最高精度的运算得出的,但却要被存储到8Bit或者16Bit精度的通道里。在8Bit的情况下,高光看起来很不平滑,而且还会有破碎的情况出现。
在延期着色下,光照系统的性能直接取决于光源需要处理的像素的数量。我们用了下面的技术来减少光照需要计算的象素数量,以提升性能。
早期Z剔除(Early z-rejection),模板缓冲,以及动态分支,它们具备相同的特征:取决于数据的位置。这需要硬件体系结构的支持,不过现在绝大多数硬件都支持了。一般的,如果我们尽可能地使用了早期Z剔除,模板缓冲和动态分支,那么在屏幕上的一个局部区域内,所有的像素的行为都是均匀的。也就是说,他们都经过了Z剔除,模板,或者走入了同一个分支中。
7.1 有效的Light Volume
我们使用了紧密包围着光源影响区域的Light Volume来计算光照。理论上说,如果我们对整个屏幕所有的像素全都用光照计算,那么最后的结果也是一样的,但是,性能就会变得很差(每个灯光对整个GBuffer进行一次全采样,OMG&&)。Light Volume覆盖的屏幕空间的像素越少,光照Pixel Shader需要处理的数据就越少。我们适用锥体来描述聚光灯,球体来描述点光源,长方体来描述Box Light,而对于方向光这样的全局光照,我们适用了整个屏幕空间。
另一个延期渲染的论文都会描述的方法,是通过基于Light Volume和摄像机位置的深度测试和Cull Mode(顺时针逆时针那个),来减少计算量。这种调整最大程度的进行了早期Z剔除。这种方法需要CPU来判断用哪种深度测试和Cull Mode的组合可以最大程度的进行早期Z剔除。
在我们所有的情况下(我们的Light Volume不会被远面剔除掉),我们都使用&Greater&的深度测试和顺时针的绕法(也就是反着绕)。可以通过一些推测,来选出对自己最有效的深度测试和Cull Mode。然而我们遇到的瓶颈在其他地方,因此我们决定不再用这种方法,通过浪费CPU资源来优化性能。
(感觉本段的技术并无实用性,剔除肯定是必须做的,硬件Occlusion,其他的,各种方法其实都很简单,而且实用性较强。不过也有可能是我没理解了作者的意思。)
7.2 模板缓冲
在延期渲染系统中,使用Stencil来屏蔽一些像素,是另一个常用的手段。基本上,就是用Stencil Buffer来指定哪些像素不必要进行光照。当渲染Light Volume的几何体时,可以通过简单的模板测试来取消对这些标定像素的处理。
我们试了一些这个技术的变种。我们发现这个方法所带来的性能提升,还不如增加了Draw Call导致的性能下降。我们试图使用一个&便宜的&Pass来标定所有的像素是否面向光源或者是否在光源之外。这个确实是减少了需要处理光源的像素的数量。在DX9 一般的下,&便宜的&Pass增加的Draw Call抵消掉、甚至远超过了最后光源Pass时提升的性能。
我们利用了Stencil来标定那些之后延期渲染需要处理的场景中的不透明物体。这个方法把那些天空盒和其他正面不需要进行光照的物体排除掉了(主要是那些只有Emissive的物体)。这个方法不需要任何多余的Draw Call,因此会非常&便宜&。光照Pass之需要简单的把这些标定的像素丢弃掉就可以了。当天空盒占了整个屏幕绝大部分的时候,这种方法会带来相当程度的性能提升,而即便不是这样,这种方法起码也不会带来任何损失。
DX10减少了Draw Call的开销。对于那些瞄准了DX10平台的读者,制作一个&便宜的Pass&(第二段描述的方式)应该是个不错的尝试。然而在SM3下使用动态分支,比增加新的Pass要好一些。
7.3 动态分支
SM3一个很关键的特性就是支持动态分支。动态分支不仅增加了GPU的可编程性,在合适的情况下,他也可以用来进行优化。
使用动态分支进行优化需要注意两个原则:
1,制造一个或者两个动态分支,以确保能最大程度的跳过更多数量的代码和那些频率较高的代码。
2,注意数据的位置。如果一个像素走了分支A,那么它邻近的像素最好也能尽可能走分支A。
光照中最好的时机是根据像素距离光源的远近和表面法线来进行分支。如果使用了法线图,则表面法线就会变得不再均匀,优化就会变得比较麻烦。
8 一些问题
在使用延期着色的过程中并非都是一帆风顺的。由于显存带宽和数据通道的限制,延期着色也有它本身不可调和的问题。
8.1 半透明几何体
延期着色最大的问题是在处理半透明物体的时候显得无能为力。不支持半透明不仅仅是硬件的限制问题,同时也是这个技术本身的硬伤:我们所有的工作均受限于&只能知道临近像素的材质信息&。在Tabula Rasa中,我们使用了大家都在用的方法:在延期着色渲染勒索由的不透明物体之后,使用前向着色来渲染所有的半透明物体。
要在延期着色中支持真正的半透明,则可能需要一些更多的帧缓冲来存储一个片断是否被遮挡的信息。这也是解决不排序半透明的一种方式。这种缓冲现在并不被我们的图形显卡支持。
然而,开启MRT时,只要Render Target允许,我们可以支持基于Alpha Test的Additive式的混合(也是一种Alpha混合)。(看了看后面,感觉这个意思可能是想说,由于延期着色中,Alpha是不会被存下来的,因此只能用来做Additive这样的混合,而不能做基于Alpha的混合。)当MRT的Alpha Test开启时,如果COLOR0(也就是第一张Render Target)的Alpha为零,当前片断的Test失败,则不会有任何Render Target被更新(也就是说,存储Alpha是无意义的,因为那些被裁减掉的像素根本就不会写到帧缓存里)。因此这里我们不能使用Alpha Test,而是应该使用clip指令来裁掉一个像素。因为Render Target 0并没有用来存储Diffuse,而是用于存储其它材质信息的。延期渲染管道渲染的东西都应该是完全不透明的,因此,我们不再使用这些通道来存储无意义的Alpha信息。
使用前向渲染来处理半透明几何体可以解决一些问题。我们使用了我们的前向渲染管道来处理水面和其他半透明几何体。水的Shader使用了在延期着色中生成的深度纹理。水的光照计算用的是传统的前向着色技术。这种方案也有一点问题&&让半透明几何体和不透明几何体之间光照统一是比较困难的。而且,我们延期着色支持的很多光照特性,前向渲染管道是不支持的。这就使得两者的结合变得不太现实。
Tabula Rasa中,在两个方面,两种光照系统的不一致成为一个巨大的问题:头发和植被。头发和植被在半透明时看这会比较舒服。然而,当一个角色进入阴影的时候,他的头发没有变色,这点是不可接受的。同样的,当周围所有的东西都被投影的时候,仅有草没有被投影,也是不可接受的。
我们最终决定使用Alpha Test而不是半透明。这样,头发和植被就可以利用延期渲染来处理了。光源的效果在头发和植被上也比较统一。为了减少植被边缘的粗糙,我们尝试使用过一些小技巧。例如进行屏幕空间的半透明排序,或者使用半透明从前向着色过渡到延期着色。但没有一个方案是真正可用的。我们现在的做法是通过让植被变大变小来处理淡入和淡出。
由于硬件带宽的增加,延期着色才成为可能。延期着色需要写入到4个Render Target中,而不是1个,也就是写入量是原来的4倍。在光照Pass里,我们也需要从这所有的缓冲中读取信息,读入量也超过了过去。带宽和填充率,是延期着色最大的性能影响因素。
最大的减少带宽开销的因素是屏幕分辨率。带宽与渲染像素的数量直接相关,只有的66%速度。延期渲染的引擎性能严重受限于分辨率的大小。
进行独立的位深度存取,在舍弃一定精度的前提下,应该可以减少带宽的损耗量。但是这种方法对我们并没有用,因为现在的硬件并不支持这种特性。我们的做法是尽可能减少材质数据的存储量、尽可能减少这些缓冲的使用率。
当渲染光照的时候,我们也使用了MRT。我们使用了两个Render Target,并进行Additive的混合。这些Render Target分别属于Diffuse和Specular的积累缓冲区。乍一眼看,这好像对于节省带宽而言是多余的,因为我们将信息写到了两个Render Target上。然而,这个选择确实可以提高效率。
将Diffuse和Specular加到一起的一办法可能如下面这样:
Fraglit = Fragunlit x Lightdiffuse + Lightspecular&
这个公式是可以分为Diffuse和Specular两部分的。将这两部分分别放到两个Render Target的话,在光照Shader里,我们不用再去取出Unlit的片段(Frag Unlit)。Shader只是产生Light Diffuse和Light Specular项,它们除了表面与光源的关系外,不用承担任何其他的计算量。
如果我们不把Diffuse和Specular分开,那么Light Shader则必须计算出最终的片段颜色。这个计算必须获取Unlit的片段颜色(纹理本身的颜色),以及其他可能影响最终颜色的材质属性(例如自发光)。把这些最终颜色放到光照Shader中,就意味着我们将真正丢掉Diffuse和Specular分量。也就是说,我们无法从Shader的结果中分解出来光源的原始信息了。将Diffuse和Specular分量存储到Render Target中,对于进行HDR和其他需要影响光源的Post Process运算都很有利。
在所有的光照Shader都运行完毕后,我们进行最后一个全屏的Pass,来计算最终的片段颜色。这个最终的Post Process Pass里,我们计算雾、边缘检测和平滑、以及最终的片段颜色。这个方法确保了这些方法对每个象素仅计算一次,减少了绕路的数量,最大化了从MRT里读取信息时纹理Cache的命中率。从MRT中反解材质数据是耗费很高的运算,特别是当大量使用时,导致的纹理Cache的颠簸,会让这个情况变得更加糟糕。
使用这些光照的积累缓冲之后,我们可以很方便的在需要的时候关闭Specular光照,以避免带宽的浪费。这些光照的积累缓冲也可以在光照相关的Post Process中也很有用,例如增加对比度,计算HDR,以及其它类似的特效。
8.3 内存管理
在Tabula Rasa中,即便在最普通的分辨率下,我们也要为延期渲染和反射这所有的Render Target花掉50MB的显存。这还不包括主缓冲,顶点和索引,以及纹理。而这些Render Target在的分辨率下则需要100MB的显存。
我们使用了4个、屏幕大小的Render Target来存储几何体的材质数据。我们的光照Shader使用了两个、屏幕大小的Render Target。这些Render Target可以是32 Bit的或者64Bit的,取决于显卡和显示质量设置。然后,为了全局方向光,还有一个的Shadow Map,以及为了其他光源产生的各种附加Shadow Map。
使用Render Target的一个可能的提议是:减少分辨率,只在最后渲染的时候把它们缩放上去。这有很多好处,但我们发现图像质量变差了,因此就没有继续接下去研究,不过这种方法有可能在一些特殊的应用中是可行的。
Render Target使用的显存只是一个问题。他们的生存周期和位置对整个性能有更为关键的影响。即使这些纹理在显存中,超出了我们的控制范畴,我们仍然可以做一些事情来挽回一些事情。
我们使我们主要的MRT早于其他任何纹理分配,这种分配可以帮助驱动,将他们放到最完整、连续的显存中。我们仍然受制于驱动的实现,但是我们起码可以帮助驱动去让它实现我们希望的结果。
我们使用了Shadow Map池,并允许光源共享这些Shadow Map。在引擎里,我们限制了Shadow Map的最大数量。基于光源的优先级,位置和所需的Shadow Map大小,为这些光源分配少量的Shadow Map。这些Shadow Map永远不会释放,只是不断地重用。这个减少了显存碎片,并且减少了因为创建和销毁资源而带来的性能损失。
基于这一点,我们也限制了每帧渲染(或重生)的Shadow Map的数量。如果有好几盏光同时需要生成他们的Shadow Map,引擎每帧只会创建一到两个,这就将花销平摊到了几帧中。
在Tabula Rasa中,使用延期渲染,使我们达到了预定的目标。我们找到了一条高性能、可度量的方法来实现延期渲染。在一些早期的SM3显卡&&如NV6800Ultra&&在基本的设置和中端分辨率上可以达到30帧。而在最新的DX10显卡,诸如NV8800和ATI2900上,可以在全效果下跑的很好。
Figure 19-6. An Outdoor Scene with a Global Shadow Map
Figure 19-7. An Indoor Scene with Numerous Static Shadow-Casting Lights
Shown are box, spot, and point lights.
Figure 19-8. Fragment Colors and Normals
Left: Unlit diffuse fragment color. Right: Normals
Figure 19-9. Depth and Edge Weight Visualization
Left: The depth of pixels. Right: Edge detection.
Figure 19-10. Light Accumulation
延期着色正在从理论走向现实。很多时候,很多新的技术需要耗费高昂代价,过于抽象,或者无法真正应用于商业。而延期着色则被证明是真实感游戏设计领域一个通用的、强大的、可控的技术。
延期着色还需要克服的主要障碍包括:
较高的显存带宽占用
无硬件反锯齿的支持
对Alpha Blend支持较差
我们发现当前驻留的显卡已经可以在稍低的分辨率下解决贷款问题了,而在当今最高端的机器上,可以在开启全部特性的前提下,适应更高的分辨率。在DX10 即便显卡上,ATI和NVIDIA都增强了MRT的性能。DX10和SM4都提供了GPU支持的整数处理,以及从深度缓冲中读取数据。所有这些都可以减少显存带宽。当提供了新的硬件和特性时,性能自然就会提升。
在合适的Filter作用下,精确的边缘检测可以减少几何体边缘的锯齿。虽然这些方法并不像硬件全场景反锯齿那样精确,但是仍然可以以假乱真。
延期着色最显著的问题是对半透明的支持。我们自觉牺牲了一些半透明方面的图形质量,然而,我们觉得延期着色所带来的优点远远超过了这些问题。
延期着色主要的好处包括:
光照的开销与场景复杂度无关。
Shader可以访问深度和其他像素信息。
每个象素对每个光源仅运行一次。也就是说,那些被遮挡的像素是不会被光照计算到的。
材质和光照的Shader完全分开。
每天都有新的技术和新的硬件出来,由于他们的存在,延期渲染的地位也可能会有浮沉。未来是很难预料的,但我们很高兴当时做出了在当今的显卡上使用延期着色的决定。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:284515次
积分:4984
积分:4984
排名:第2007名
原创:162篇
评论:421条
邮箱请用:noslopdev
或加QQ: 不保证回复频率
(2)(2)(8)(2)(2)(4)(4)(1)(1)(1)(1)(6)(5)(4)(4)(1)(1)(1)(1)(1)(2)(1)(1)(1)(4)(1)(1)(5)(8)(1)(6)(3)(2)(2)(1)(2)(3)(1)(2)(1)(1)(3)(1)(3)(1)(5)(10)(6)(4)(10)(5)(3)(9)(2)(1)(1)(1)(1)(3)(2)(1)(3)(5)}

我要回帖

更多关于 爱会不会重来 的文章

更多推荐

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

点击添加站长微信