œ] Ćœ€5.5.1f1ţ˙˙˙˙˙Č0ĹňĂôL.`?^Ů0D7€˙˙˙˙€Ś€˛€ЀŚ€Ś€Ś€#Ś€+H€3˙˙˙˙€1€1€˙˙˙˙@ހ€ Q€j€ ™€< Ś€H H€Z˙˙˙˙ €1€1€˙˙˙˙@ހ€Q€j€ЀgŚ€Ś€Ś€#Ś€+v~ € €– €Ÿ €¨ €ą €ş €Ă €Ě €Ő €Ţ  €ç! €ń" €ű# €$ €% €&Ő€#˙˙˙˙'€1€1€˙˙˙˙(€ހ€)H€j€˙˙˙˙*€1€1€˙˙˙˙+@ހ€,Q€j€-™€*.ހ8/AssetMetaDataguiddata[0]data[1]data[2]data[3]pathNametimeCreatedoriginalChangesetoriginalNameoriginalParentHash128originalDigestbytes[0]bytes[1]bytes[2]bytes[3]bytes[4]bytes[5]bytes[6]bytes[7]bytes[8]bytes[9]bytes[10]bytes[11]bytes[12]bytes[13]bytes[14]bytes[15]labelsassetStoreReflicenseTypeď˙˙2ƒ…>”KËĽŁMźHîÁƒ#{7€˙˙˙˙€Ś€˛€Ő€ ހ%.€.†€Ä€ ހ%.€.H€Ť€˙˙˙˙€1€1€˙˙˙˙ @ހ€ Q€j€ Ő€7˙˙˙˙ Ŕ1€1€˙˙˙˙ €ހ€€j€˙˙˙˙€H€›€˙˙˙˙€1€1€˙˙˙˙@ހ€Q€j€°€ € ހ%.€.H€I˙˙˙˙€1€1€˙˙˙˙@ހ€Q€j€H€T˙˙˙˙€1€1€˙˙˙˙@ހ€Q€j€H€f˙˙˙˙€1€1€˙˙˙˙ @ހ€!Q€j€"ShaderImporterPPtrm_FileIDm_PathIDm_DefaultTexturesm_UserDatam_AssetBundleNamem_AssetBundleVariant0˙˙şěŻu`Aă•\á|^FďYŹü 7€˙˙˙˙€Ś€˛€Ő€ ހ.€&†€Ä€ ހ.€&H€Ť€˙˙˙˙€1€1€˙˙˙˙ @ހ€ Q€j€ /@˙˙˙˙ €Mb˙˙˙˙ €Ő€m˙˙˙˙€1€1€˙˙˙˙€ހ€uj€˙˙˙˙€H€Ť€˙˙˙˙€1€1€˙˙˙˙@ހ€Q€j€H€ˆ˙˙˙˙€1€1€˙˙˙˙@ހ€Q€j€Ő€–˙˙˙˙€1€1€˙˙˙˙€ހ€H€j€˙˙˙˙€ 1€1€˙˙˙˙@ ހ€ Q€j€ ހ€!Ś€Ł"Ą€Ť#Ą€š$Ą€Ç%Ą€Ő&ăý˙˙˙˙'€H€ ˙˙˙˙(€1€1€˙˙˙˙)@ ހ€* Q€j€+ހ,Ő€!˙˙˙˙-€1€1€˙˙˙˙.€ހ€/.j€˙˙˙˙0€Ő€B˙˙˙˙1€1€1€˙˙˙˙2€ހ€3Kj€˙˙˙˙4€ń€Z˙˙˙˙5€ 1€1€˙˙˙˙6€ ހ€7 €j€˙˙˙˙8€ H€›€˙˙˙˙9€ 1€1€˙˙˙˙:@ ހ€; Q€j€< ހ €=ހ€>h~˙˙˙˙?€ H€Ť€˙˙˙˙@€ 1€1€˙˙˙˙A@ ހ€B Q€j€C †Ł˙˙˙˙D€ ŹÇ˙˙˙˙E€ Ą€ĐF Š€Ô˙˙˙˙G€ H€Ô˙˙˙˙H€ 1€1€˙˙˙˙I@ހ€JQ€j€K ŹŮ˙˙˙˙L€ Ą€ĐM Š€Ô˙˙˙˙N€ H€Ô˙˙˙˙O€ 1€1€˙˙˙˙P@ހ€QQ€j€R Źă˙˙˙˙S€ Ą€ĐT Š€Ô˙˙˙˙U€ H€Ô˙˙˙˙V€ 1€1€˙˙˙˙W@ހ€XQ€j€Y Źń˙˙˙˙Z€ Ą€Đ[ Š€Ô˙˙˙˙\€ H€Ô˙˙˙˙]€ 1€1€˙˙˙˙^@ހ€_Q€j€` Ź˙˙˙˙a€ Ą€Đb Š€Ô˙˙˙˙c€ H€Ô˙˙˙˙d€ 1€1€˙˙˙˙e@ހ€fQ€j€g Ź˙˙˙˙h€ Ą€Đi Š€Ô˙˙˙˙j€ H€Ô˙˙˙˙k€ 1€1€˙˙˙˙l@ހ€mQ€j€n Ź˙˙˙˙o€ Ą€Đp Š€Ô˙˙˙˙q€ H€Ô˙˙˙˙r€ 1€1€˙˙˙˙s@ހ€tQ€j€u †˙˙˙˙v€ ŹÇ˙˙˙˙w€ Ą€Đx Š€Ô˙˙˙˙y€ H€Ô˙˙˙˙z€ 1€1€˙˙˙˙{@ހ€|Q€j€} ŹŮ˙˙˙˙~€ Ą€Đ Š€Ô˙˙˙˙€€ H€Ô˙˙˙˙€ 1€1€˙˙˙˙‚@ހ€ƒQ€j€„ Źă˙˙˙˙…€ Ą€Đ† Š€Ô˙˙˙˙‡€ H€Ô˙˙˙˙ˆ€ 1€1€˙˙˙˙‰@ހ€ŠQ€j€‹ Źń˙˙˙˙Œ€ Ą€Đ Š€Ô˙˙˙˙Ž€ H€Ô˙˙˙˙€ 1€1€˙˙˙˙@ހ€‘Q€j€’ Ź˙˙˙˙“€ Ą€Đ” Š€Ô˙˙˙˙•€ H€Ô˙˙˙˙–€ 1€1€˙˙˙˙—@ހ€˜Q€j€™ Ź˙˙˙˙š€ Ą€Đ› Š€Ô˙˙˙˙œ€ H€Ô˙˙˙˙€ 1€1€˙˙˙˙ž@ހ€ŸQ€j€  Ź˙˙˙˙Ą€ Ą€Đ˘ Š€Ô˙˙˙˙Ł€ H€Ô˙˙˙˙¤€ 1€1€˙˙˙˙Ľ@ހ€ŚQ€j€§ †&˙˙˙˙¨€ ŹÇ˙˙˙˙Š€ Ą€ĐŞ Š€Ô˙˙˙˙Ť€ H€Ô˙˙˙˙Ź€ 1€1€˙˙˙˙­@ހ€ŽQ€j€Ż ŹŮ˙˙˙˙°€ Ą€Đą Š€Ô˙˙˙˙˛€ H€Ô˙˙˙˙ł€ 1€1€˙˙˙˙´@ހ€ľQ€j€ś Źă˙˙˙˙ˇ€ Ą€Đ¸ Š€Ô˙˙˙˙š€ H€Ô˙˙˙˙ş€ 1€1€˙˙˙˙ť@ހ€źQ€j€˝ Źń˙˙˙˙ž€ Ą€Đż Š€Ô˙˙˙˙Ŕ€ H€Ô˙˙˙˙Á€ 1€1€˙˙˙˙Â@ހ€ĂQ€j€Ä Ź˙˙˙˙ŀ Ą€ĐĆ Š€Ô˙˙˙˙ǀ H€Ô˙˙˙˙Ȁ 1€1€˙˙˙˙É@ހ€ĘQ€j€Ë Ź˙˙˙˙̀ Ą€ĐÍ Š€Ô˙˙˙˙΀ H€Ô˙˙˙˙π 1€1€˙˙˙˙Đ@ހ€ŃQ€j€Ň Ź˙˙˙˙Ӏ Ą€ĐÔ Š€Ô˙˙˙˙Հ H€Ô˙˙˙˙ր 1€1€˙˙˙˙×@ހ€ŘQ€j€Ů †/˙˙˙˙ڀ ŹÇ˙˙˙˙ۀ Ą€ĐÜ Š€Ô˙˙˙˙݀ H€Ô˙˙˙˙ހ 1€1€˙˙˙˙ß@ހ€ŕQ€j€á ŹŮ˙˙˙˙†Ą€Đă Š€Ô˙˙˙˙ä€ H€Ô˙˙˙˙ĺ€ 1€1€˙˙˙˙ć@ހ€çQ€j€č Źă˙˙˙˙é€ Ą€Đę Š€Ô˙˙˙˙ë€ H€Ô˙˙˙˙ě€ 1€1€˙˙˙˙í@ހ€îQ€j€ď Źń˙˙˙˙đ€ Ą€Đń Š€Ô˙˙˙˙ň€ H€Ô˙˙˙˙ó€ 1€1€˙˙˙˙ô@ހ€őQ€j€ö Ź˙˙˙˙÷€ Ą€Đř Š€Ô˙˙˙˙ů€ H€Ô˙˙˙˙ú€ 1€1€˙˙˙˙ű@ހ€üQ€j€ý Ź˙˙˙˙ţ€ Ą€Đ˙ Š€Ô˙˙˙˙€ H€Ô˙˙˙˙€ 1€1€˙˙˙˙@ހ€Q€j€ Ź˙˙˙˙€ Ą€Đ Š€Ô˙˙˙˙€ H€Ô˙˙˙˙€ 1€1€˙˙˙˙ @ހ€ Q€j€  †8˙˙˙˙ € ŹÇ˙˙˙˙ € Ą€Đ Š€Ô˙˙˙˙€ H€Ô˙˙˙˙€ 1€1€˙˙˙˙@ހ€Q€j€ ŹŮ˙˙˙˙€ Ą€Đ Š€Ô˙˙˙˙€ H€Ô˙˙˙˙€ 1€1€˙˙˙˙@ހ€Q€j€ Źă˙˙˙˙€ Ą€Đ Š€Ô˙˙˙˙€ H€Ô˙˙˙˙€ 1€1€˙˙˙˙@ހ€ Q€j€! Źń˙˙˙˙"€ Ą€Đ# Š€Ô˙˙˙˙$€ H€Ô˙˙˙˙%€ 1€1€˙˙˙˙&@ހ€'Q€j€( Ź˙˙˙˙)€ Ą€Đ* Š€Ô˙˙˙˙+€ H€Ô˙˙˙˙,€ 1€1€˙˙˙˙-@ހ€.Q€j€/ Ź˙˙˙˙0€ Ą€Đ1 Š€Ô˙˙˙˙2€ H€Ô˙˙˙˙3€ 1€1€˙˙˙˙4@ހ€5Q€j€6 Ź˙˙˙˙7€ Ą€Đ8 Š€Ô˙˙˙˙9€ H€Ô˙˙˙˙:€ 1€1€˙˙˙˙;@ހ€<Q€j€= †A˙˙˙˙>€ ŹÇ˙˙˙˙?€ Ą€Đ@ Š€Ô˙˙˙˙A€ H€Ô˙˙˙˙B€ 1€1€˙˙˙˙C@ހ€DQ€j€E ŹŮ˙˙˙˙F€ Ą€ĐG Š€Ô˙˙˙˙H€ H€Ô˙˙˙˙I€ 1€1€˙˙˙˙J@ހ€KQ€j€L Źă˙˙˙˙M€ Ą€ĐN Š€Ô˙˙˙˙O€ H€Ô˙˙˙˙P€ 1€1€˙˙˙˙Q@ހ€RQ€j€S Źń˙˙˙˙T€ Ą€ĐU Š€Ô˙˙˙˙V€ H€Ô˙˙˙˙W€ 1€1€˙˙˙˙X@ހ€YQ€j€Z Ź˙˙˙˙[€ Ą€Đ\ Š€Ô˙˙˙˙]€ H€Ô˙˙˙˙^€ 1€1€˙˙˙˙_@ހ€`Q€j€a Ź˙˙˙˙b€ Ą€Đc Š€Ô˙˙˙˙d€ H€Ô˙˙˙˙e€ 1€1€˙˙˙˙f@ހ€gQ€j€h Ź˙˙˙˙i€ Ą€Đj Š€Ô˙˙˙˙k€ H€Ô˙˙˙˙l€ 1€1€˙˙˙˙m@ހ€nQ€j€o †J˙˙˙˙p€ ŹÇ˙˙˙˙q€ Ą€Đr Š€Ô˙˙˙˙s€ H€Ô˙˙˙˙t€ 1€1€˙˙˙˙u@ހ€vQ€j€w ŹŮ˙˙˙˙x€ Ą€Đy Š€Ô˙˙˙˙z€ H€Ô˙˙˙˙{€ 1€1€˙˙˙˙|@ހ€}Q€j€~ Źă˙˙˙˙€ Ą€Đ€ Š€Ô˙˙˙˙€ H€Ô˙˙˙˙‚€ 1€1€˙˙˙˙ƒ@ހ€„Q€j€… Źń˙˙˙˙†€ Ą€Đ‡ Š€Ô˙˙˙˙ˆ€ H€Ô˙˙˙˙‰€ 1€1€˙˙˙˙Š@ހ€‹Q€j€Œ Ź˙˙˙˙€ Ą€ĐŽ Š€Ô˙˙˙˙€ H€Ô˙˙˙˙€ 1€1€˙˙˙˙‘@ހ€’Q€j€“ Ź˙˙˙˙”€ Ą€Đ• Š€Ô˙˙˙˙–€ H€Ô˙˙˙˙—€ 1€1€˙˙˙˙˜@ހ€™Q€j€š Ź˙˙˙˙›€ Ą€Đœ Š€Ô˙˙˙˙€ H€Ô˙˙˙˙ž€ 1€1€˙˙˙˙Ÿ@ހ€ Q€j€Ą †S˙˙˙˙˘€ ŹÇ˙˙˙˙Ł€ Ą€Đ¤ Š€Ô˙˙˙˙Ľ€ H€Ô˙˙˙˙Ś€ 1€1€˙˙˙˙§@ހ€¨Q€j€Š ŹŮ˙˙˙˙Ş€ Ą€ĐŤ Š€Ô˙˙˙˙Ź€ H€Ô˙˙˙˙­€ 1€1€˙˙˙˙Ž@ހ€ŻQ€j€° Źă˙˙˙˙ą€ Ą€Đ˛ Š€Ô˙˙˙˙ł€ H€Ô˙˙˙˙´€ 1€1€˙˙˙˙ľ@ހ€śQ€j€ˇ Źń˙˙˙˙¸€ Ą€Đš Š€Ô˙˙˙˙ş€ H€Ô˙˙˙˙ť€ 1€1€˙˙˙˙ź@ހ€˝Q€j€ž Ź˙˙˙˙ż€ Ą€ĐŔ Š€Ô˙˙˙˙Á€ H€Ô˙˙˙˙€ 1€1€˙˙˙˙Ă@ހ€ÄQ€j€Ĺ Ź˙˙˙˙Ć€ Ą€ĐÇ Š€Ô˙˙˙˙Č€ H€Ô˙˙˙˙É€ 1€1€˙˙˙˙Ę@ހ€ËQ€j€Ě Ź˙˙˙˙Í€ Ą€ĐÎ Š€Ô˙˙˙˙Ď€ H€Ô˙˙˙˙Đ€ 1€1€˙˙˙˙Ń@ހ€ŇQ€j€Ó L€\Ô@ Źl˙˙˙˙Ő€ Ą€ĐÖ Š€Ô˙˙˙˙×€ H€Ô˙˙˙˙Ř€ 1€1€˙˙˙˙Ů@ ހ€Ú Q€j€Ű Źr˙˙˙˙Ü€ Ą€ĐÝ Š€Ô˙˙˙˙Ţ€ H€Ô˙˙˙˙߀ 1€1€˙˙˙˙ŕ@ ހ€á Q€j€â Źy˙˙˙˙〠Ą€Đä Š€Ô˙˙˙˙ĺ€ H€Ô˙˙˙˙ć€ 1€1€˙˙˙˙ç@ ހ€č Q€j€é Ź˙˙˙˙ę€ Ą€Đë Š€Ô˙˙˙˙ě€ H€Ô˙˙˙˙í€ 1€1€˙˙˙˙î@ ހ€ď Q€j€đ ŹŽ˙˙˙˙ń€ Ą€Đň Š€Ô˙˙˙˙ó€ H€Ô˙˙˙˙ô€ 1€1€˙˙˙˙ő@ ހ€ö Q€j€÷ Źš˙˙˙˙ř€ Ą€Đů Š€Ô˙˙˙˙ú€ H€Ô˙˙˙˙ű€ 1€1€˙˙˙˙ü@ ހ€ý Q€j€ţ Śş˙˙˙˙˙€ ŹÄ˙˙˙˙€ Ą€Đ Š€Ô˙˙˙˙€ H€Ô˙˙˙˙€ 1€1€˙˙˙˙@ހ€Q€j€ ŹÉ˙˙˙˙€ Ą€Đ Š€Ô˙˙˙˙ € H€Ô˙˙˙˙ € 1€1€˙˙˙˙ @ހ€ Q€j€  ŹÎ˙˙˙˙€ Ą€Đ Š€Ô˙˙˙˙€ H€Ô˙˙˙˙€ 1€1€˙˙˙˙@ހ€Q€j€ ŹÔ˙˙˙˙€ Ą€Đ Š€Ô˙˙˙˙€ H€Ô˙˙˙˙€ 1€1€˙˙˙˙@ހ€Q€j€ ŚŮ˙˙˙˙€ ŹÄ˙˙˙˙€ Ą€Đ Š€Ô˙˙˙˙€ H€Ô˙˙˙˙ € 1€1€˙˙˙˙!@ހ€"Q€j€# ŹÉ˙˙˙˙$€ Ą€Đ% Š€Ô˙˙˙˙&€ H€Ô˙˙˙˙'€ 1€1€˙˙˙˙(@ހ€)Q€j€* ŹÎ˙˙˙˙+€ Ą€Đ, Š€Ô˙˙˙˙-€ H€Ô˙˙˙˙.€ 1€1€˙˙˙˙/@ހ€0Q€j€1 ŹÔ˙˙˙˙2€ Ą€Đ3 Š€Ô˙˙˙˙4€ H€Ô˙˙˙˙5€ 1€1€˙˙˙˙6@ހ€7Q€j€8 Śč˙˙˙˙9€ ŹÄ˙˙˙˙:€ Ą€Đ; Š€Ô˙˙˙˙<€ H€Ô˙˙˙˙=€ 1€1€˙˙˙˙>@ހ€?Q€j€@ ŹÉ˙˙˙˙A€ Ą€ĐB Š€Ô˙˙˙˙C€ H€Ô˙˙˙˙D€ 1€1€˙˙˙˙E@ހ€FQ€j€G ŹÎ˙˙˙˙H€ Ą€ĐI Š€Ô˙˙˙˙J€ H€Ô˙˙˙˙K€ 1€1€˙˙˙˙L@ހ€MQ€j€N ŹÔ˙˙˙˙O€ Ą€ĐP Š€Ô˙˙˙˙Q€ H€Ô˙˙˙˙R€ 1€1€˙˙˙˙S@ހ€TQ€j€U Źö˙˙˙˙V€ Ą€ĐW Š€Ô˙˙˙˙X€ H€Ô˙˙˙˙Y€ 1€1€˙˙˙˙Z@ ހ€[ Q€j€\ Ź˙˙˙˙]€ Ą€Đ^ Š€Ô˙˙˙˙_€ H€Ô˙˙˙˙`€ 1€1€˙˙˙˙a@ ހ€b Q€j€c Ź˙˙˙˙d€ Ą€Đe Š€Ô˙˙˙˙f€ H€Ô˙˙˙˙g€ 1€1€˙˙˙˙h@ ހ€i Q€j€j Ź"˙˙˙˙k€ Ą€Đl Š€Ô˙˙˙˙m€ H€Ô˙˙˙˙n€ 1€1€˙˙˙˙o@ ހ€p Q€j€q Ź+˙˙˙˙r€ Ą€Đs Š€Ô˙˙˙˙t€ H€Ô˙˙˙˙u€ 1€1€˙˙˙˙v@ ހ€w Q€j€x Ź2˙˙˙˙y€ Ą€Đz Š€Ô˙˙˙˙{€ H€Ô˙˙˙˙|€ 1€1€˙˙˙˙}@ ހ€~ Q€j€ =Y˙˙˙˙€€ Źb˙˙˙˙€ Ą€Đ‚ Š€Ô˙˙˙˙ƒ€ H€Ô˙˙˙˙„€ 1€1€˙˙˙˙…@ހ€†Q€j€‡ Źd˙˙˙˙ˆ€ Ą€Đ‰ Š€Ô˙˙˙˙Š€ H€Ô˙˙˙˙‹€ 1€1€˙˙˙˙Œ@ހ€Q€j€Ž Źf˙˙˙˙€ Ą€Đ Š€Ô˙˙˙˙‘€ H€Ô˙˙˙˙’€ 1€1€˙˙˙˙“@ހ€”Q€j€• Źh˙˙˙˙–€ Ą€Đ— Š€Ô˙˙˙˙˜€ H€Ô˙˙˙˙™€ 1€1€˙˙˙˙š@ހ€›Q€j€œ Š€Ô˙˙˙˙€ H€Ô˙˙˙˙ž€ 1€1€˙˙˙˙Ÿ@ ހ€  Q€j€Ą ހj˘ ހrŁ ˙˙˙˙¤€ ń€—˙˙˙˙Ľ€ 1€1€˙˙˙˙Ś€ ހ€§ €j€˙˙˙˙¨€ H€›€˙˙˙˙Š€1€1€˙˙˙˙Ş@ހ€ŤQ€j€Ź H€ €˙˙˙˙­€1€1€˙˙˙˙Ž@ހ€ŻQ€j€° ހœą L€˘˛@Ś€ŤłšË˙˙˙˙´€ Ő€Ö˙˙˙˙ľ€ 1€1€˙˙˙˙ś€ ހ€ˇ äj€˙˙˙˙¸€ Ś€ůš ˙˙˙˙ş€ Ő€˙˙˙˙ť@1€1€˙˙˙˙źހ€˝#j€ž5€5ż5€<Ŕ Ś€CÁ Ő€O˙˙˙˙ 1€1€˙˙˙˙Ăހ€Ä‹€j€Ĺ 5€`Ć 5€uÇ@ Ő€†˙˙˙˙Č€ 1€1€˙˙˙˙É€ހ€Ę•j€Ë€ހĽĚހƒ€ÍހąÎ5€€Ď5€˝Đ@ Ő€Ă˙˙˙˙Ń€ 1€1€˙˙˙˙Ň€ހ€ÓŇj€Ô€ހĽŐހƒ€Öހą×5€€Ř5€âŮ@ Ő€í˙˙˙˙Ú€ 1€1€˙˙˙˙Ű€ހ€Üýj€ Ý€ހĽŢހƒ€ßހŕ5€˝á@ Ő€˙˙˙˙â 1€1€˙˙˙˙ăހ€ä,j€ĺހĽćހƒ€ç Ő€:˙˙˙˙č€ 1€1€˙˙˙˙é€ހ€ęLj€˙˙˙˙ë€ހĽěŐ€Ă˙˙˙˙í€1€1€˙˙˙˙î€ހ€ďŇj€đ€ހĽńހƒ€ňހąó5€€ô5€âő@Ő€†˙˙˙˙ö€1€1€˙˙˙˙÷€ހ€ř•j€ů€ހĽúހƒ€űހąü5€€ý5€˝ţ@ހ[˙ Ő€b˙˙˙˙ 1€1€˙˙˙˙ހ€,j€ހĽހƒ€ Ő€{˙˙˙˙ 1€1€˙˙˙˙ހ€‡j€ ހĽ ހƒ€ ހ” š¤˙˙˙˙ € Ő€Ö˙˙˙˙€ 1€1€˙˙˙˙€ ހ€ äj€˙˙˙˙€ Ś€ů ˙˙˙˙€ Ő€˙˙˙˙@1€1€˙˙˙˙ހ€#j€5€55€< Ś€C Ő€O˙˙˙˙ 1€1€˙˙˙˙ހ€‹€j€ 5€` 5€u @ Ő€†˙˙˙˙!€ 1€1€˙˙˙˙"€ހ€#•j€$€ހĽ%ހƒ€&ހą'5€€(5€˝)@ Ő€Ă˙˙˙˙*€ 1€1€˙˙˙˙+€ހ€,Ňj€-€ހĽ.ހƒ€/ހą05€€15€â2@ Ő€í˙˙˙˙3€ 1€1€˙˙˙˙4€ހ€5ýj€ 6€ހĽ7ހƒ€8ހ95€˝:@ Ő€˙˙˙˙; 1€1€˙˙˙˙<ހ€=,j€>ހĽ?ހƒ€@ Ő€:˙˙˙˙A€ 1€1€˙˙˙˙B€ހ€CLj€˙˙˙˙D€ހĽEŐ€Ă˙˙˙˙F€1€1€˙˙˙˙G€ހ€HŇj€I€ހĽJހƒ€KހąL5€€M5€âN@Ő€†˙˙˙˙O€1€1€˙˙˙˙P€ހ€Q•j€R€ހĽSހƒ€TހąU5€€V5€˝W@ހ[X Ő€b˙˙˙˙Y 1€1€˙˙˙˙Zހ€[,j€\ހĽ]ހƒ€^ Ő€{˙˙˙˙_ 1€1€˙˙˙˙`ހ€a‡j€ bހĽcހƒ€dހ”ešą˙˙˙˙f€ Ő€Ö˙˙˙˙g€ 1€1€˙˙˙˙h€ ހ€i äj€˙˙˙˙j€ Ś€ůk ˙˙˙˙l€ Ő€˙˙˙˙m@1€1€˙˙˙˙nހ€o#j€p5€5q5€<r Ś€Cs Ő€O˙˙˙˙t 1€1€˙˙˙˙uހ€v‹€j€w 5€`x 5€uy@ Ő€†˙˙˙˙z€ 1€1€˙˙˙˙{€ހ€|•j€}€ހĽ~ހƒ€ހą€5€€5€˝‚@ Ő€Ă˙˙˙˙ƒ€ 1€1€˙˙˙˙„€ހ€…Ňj€†€ހĽ‡ހƒ€ˆހą‰5€€Š5€â‹@ Ő€í˙˙˙˙Œ€ 1€1€˙˙˙˙€ހ€Žýj€ €ހĽހƒ€‘ހ’5€˝“@ Ő€˙˙˙˙” 1€1€˙˙˙˙•ހ€–,j€—ހĽ˜ހƒ€™ Ő€:˙˙˙˙š€ 1€1€˙˙˙˙›€ހ€œLj€˙˙˙˙€ހĽžŐ€Ă˙˙˙˙Ÿ€1€1€˙˙˙˙ €ހ€ĄŇj€˘€ހĽŁހƒ€¤ހąĽ5€€Ś5€â§@Ő€†˙˙˙˙¨€1€1€˙˙˙˙Š€ހ€Ş•j€Ť€ހĽŹހƒ€­ހąŽ5€€Ż5€˝°@ހ[ą Ő€b˙˙˙˙˛ 1€1€˙˙˙˙łހ€´,j€ľހĽśހƒ€ˇ Ő€{˙˙˙˙¸ 1€1€˙˙˙˙šހ€ş‡j€ ťހĽźހƒ€˝ހ”žšž˙˙˙˙ż€ Ő€Ö˙˙˙˙Ŕ€ 1€1€˙˙˙˙Á€ ހ€Â äj€˙˙˙˙Ă€ Ś€ůÄ ˙˙˙˙Ĺ€ Ő€˙˙˙˙Ć@1€1€˙˙˙˙Çހ€Č#j€É5€5Ę5€<Ë Ś€CĚ Ő€O˙˙˙˙Í 1€1€˙˙˙˙Îހ€Ď‹€j€Đ 5€`Ń 5€uŇ@ Ő€†˙˙˙˙Ó€ 1€1€˙˙˙˙Ô€ހ€Ő•j€Ö€ހĽ×ހƒ€ŘހąŮ5€€Ú5€˝Ű@ Ő€Ă˙˙˙˙Ü€ 1€1€˙˙˙˙Ý€ހ€ŢŇj€ß€ހĽŕހƒ€áހąâ5€€ă5€âä@ Ő€í˙˙˙˙ĺ€ 1€1€˙˙˙˙ć€ހ€çýj€ č€ހĽéހƒ€ęހë5€˝ě@ Ő€˙˙˙˙í 1€1€˙˙˙˙îހ€ď,j€đހĽńހƒ€ň Ő€:˙˙˙˙ó€ 1€1€˙˙˙˙ô€ހ€őLj€˙˙˙˙ö€ހĽ÷Ő€Ă˙˙˙˙ř€1€1€˙˙˙˙ů€ހ€úŇj€ű€ހĽüހƒ€ýހąţ5€€˙5€â@Ő€†˙˙˙˙€1€1€˙˙˙˙€ހ€•j€€ހĽހƒ€ހą5€€5€˝ @ހ[  Ő€b˙˙˙˙  1€1€˙˙˙˙ ހ€ ,j€ހĽހƒ€ Ő€{˙˙˙˙ 1€1€˙˙˙˙ހ€‡j€ ހĽހƒ€ހ”šÇ˙˙˙˙€ Ő€Ö˙˙˙˙€ 1€1€˙˙˙˙€ ހ€ äj€˙˙˙˙€ Ś€ů ˙˙˙˙€ Ő€˙˙˙˙@1€1€˙˙˙˙ ހ€!#j€"5€5#5€<$ Ś€C% Ő€O˙˙˙˙& 1€1€˙˙˙˙'ހ€(‹€j€) 5€`* 5€u+@ Ő€†˙˙˙˙,€ 1€1€˙˙˙˙-€ހ€.•j€/€ހĽ0ހƒ€1ހą25€€35€˝4@ Ő€Ă˙˙˙˙5€ 1€1€˙˙˙˙6€ހ€7Ňj€8€ހĽ9ހƒ€:ހą;5€€<5€â=@ Ő€í˙˙˙˙>€ 1€1€˙˙˙˙?€ހ€@ýj€ A€ހĽBހƒ€CހD5€˝E@ Ő€˙˙˙˙F 1€1€˙˙˙˙Gހ€H,j€IހĽJހƒ€K Ő€:˙˙˙˙L€ 1€1€˙˙˙˙M€ހ€NLj€˙˙˙˙O€ހĽPŐ€Ă˙˙˙˙Q€1€1€˙˙˙˙R€ހ€SŇj€T€ހĽUހƒ€VހąW5€€X5€âY@Ő€†˙˙˙˙Z€1€1€˙˙˙˙[€ހ€\•j€]€ހĽ^ހƒ€_ހą`5€€a5€˝b@ހ[c Ő€b˙˙˙˙d 1€1€˙˙˙˙eހ€f,j€gހĽhހƒ€i Ő€{˙˙˙˙j 1€1€˙˙˙˙kހ€l‡j€ mހĽnހƒ€oހ”pL€Ňq@H€é˙˙˙˙r€ 1€1€˙˙˙˙s@ ހ€t Q€j€uH€Ť€˙˙˙˙v€ 1€1€˙˙˙˙w@ ހ€x Q€j€yH€ó˙˙˙˙z€ 1€1€˙˙˙˙{@ ހ€| Q€j€}˙˙˙˙~€ ń€—˙˙˙˙€ 1€1€˙˙˙˙€€ ހ€ €j€˙˙˙˙‚€ H€›€˙˙˙˙ƒ€ 1€1€˙˙˙˙„@ހ€…Q€j€† H€ €˙˙˙˙‡€ 1€1€˙˙˙˙ˆ@ހ€‰Q€j€Š˙˙˙˙‹€ń€—˙˙˙˙Œ€1€1€˙˙˙˙€ހ€Ž€j€˙˙˙˙€ H€›€˙˙˙˙€ 1€1€˙˙˙˙‘@ ހ€’ Q€j€“ H€ €˙˙˙˙”€ 1€1€˙˙˙˙•@ ހ€– Q€j€—ހœ˜H€Ť€˙˙˙˙™€1€1€˙˙˙˙š@ހ€›Q€j€œH€˙˙˙˙€1€1€˙˙˙˙ž@ހ€ŸQ€j€ H€˙˙˙˙Ą€1€1€˙˙˙˙˘@ހ€ŁQ€j€¤Ő€#˙˙˙˙Ľ€1€1€˙˙˙˙Ś€ހ€§2j€˙˙˙˙¨€H€M˙˙˙˙Š€1€1€˙˙˙˙Ş@ހ€ŤQ€j€ŹH€R˙˙˙˙­€1€1€˙˙˙˙Ž@ހ€ŻQ€j€°L€Uą@Ő€r˙˙˙˙˛1€1€˙˙˙˙łހ€´Ś€j€ľŐ€|˙˙˙˙ś1€1€˙˙˙˙ˇހ€¸Ś€j€šŐ€„˙˙˙˙ş1€1€˙˙˙˙ťހ€źŚ€j€˝Ő€–˙˙˙˙ž1€1€˙˙˙˙żހ€ŔŚ€j€ÁŐ€Ş˙˙˙˙€1€1€˙˙˙˙Ă@ހ€Ä €j€ĹŐ€#˙˙˙˙Ć1€1€˙˙˙˙Çހ€Čšj€ ÉހĘ.€&ËL€ĆĚ@ń€Ö˙˙˙˙Í€1€1€˙˙˙˙΀ހ€Ď€j€˙˙˙˙Đ€H€›€˙˙˙˙Ń€1€1€˙˙˙˙Ň@ހ€ÓQ€j€Ô°€ € ŐހÖ.€&×čţ˙˙˙˙Ř€ń€ ˙˙˙˙Ů€1€1€˙˙˙˙Ú€ހ€Ű€j€˙˙˙˙Ü€ހ›€Ý €˙˙˙˙Ţ€H€%˙˙˙˙߀1€1€˙˙˙˙ŕ@ހ€áQ€j€âH€,˙˙˙˙ă€1€1€˙˙˙˙ä@ހ€ĺQ€j€ćŚ€8猀Gčހb錀nęz‚ë €‘ě €ší €Łî €Źď €ľđ €žń €Çň €Đó €Ůô €âő €ëö €ő÷ €˙ř € ů €ú €űL€'ü@ހ3ýŐ€>˙˙˙˙ţ€1€1€˙˙˙˙˙€ހ€Ő€j€˙˙˙˙€ 1€1€˙˙˙˙€ ހ€ H€j€˙˙˙˙€ 1€1€˙˙˙˙@ ހ€ Q€j€Ő€N˙˙˙˙€1€1€˙˙˙˙ €ހ€ Ő€j€˙˙˙˙ € 1€1€˙˙˙˙ € ހ€  H€j€˙˙˙˙€ 1€1€˙˙˙˙@ ހ€ Q€j€Ő€^˙˙˙˙€1€1€˙˙˙˙€ހ€Ő€j€˙˙˙˙€ 1€1€˙˙˙˙€ ހ€ H€j€˙˙˙˙€ 1€1€˙˙˙˙@ ހ€ Q€j€Ő€n˙˙˙˙€1€1€˙˙˙˙€ހ€Ő€j€˙˙˙˙€ 1€1€˙˙˙˙ € ހ€! H€j€˙˙˙˙"€ 1€1€˙˙˙˙#@ ހ€$ Q€j€%Ő€~˙˙˙˙&€1€1€˙˙˙˙'€ހ€(Ő€j€˙˙˙˙)€ 1€1€˙˙˙˙*€ ހ€+ H€j€˙˙˙˙,€ 1€1€˙˙˙˙-@ ހ€. Q€j€/Ő€Ž˙˙˙˙0€1€1€˙˙˙˙1€ހ€2Ő€j€˙˙˙˙3€ 1€1€˙˙˙˙4€ ހ€5 H€j€˙˙˙˙6€ 1€1€˙˙˙˙7@ ހ€8 Q€j€9Ő€ž˙˙˙˙:€1€1€˙˙˙˙;€ހ€<Ő€j€˙˙˙˙=€ 1€1€˙˙˙˙>€ ހ€? H€j€˙˙˙˙@€ 1€1€˙˙˙˙A@ ހ€B Q€j€CŐ€ą˙˙˙˙D€1€1€˙˙˙˙E€ހ€FŐ€j€˙˙˙˙G€ 1€1€˙˙˙˙H€ ހ€I H€j€˙˙˙˙J€ 1€1€˙˙˙˙K@ ހ€L Q€j€MŐ€Ä˙˙˙˙N€1€1€˙˙˙˙O€ހ€PŐ€j€˙˙˙˙Q€ 1€1€˙˙˙˙R€ ހ€S H€j€˙˙˙˙T€ 1€1€˙˙˙˙U@ ހ€V Q€j€WŐ€×˙˙˙˙X€1€1€˙˙˙˙Y€ހ€ZŐ€j€˙˙˙˙[€ 1€1€˙˙˙˙\€ ހ€] H€j€˙˙˙˙^€ 1€1€˙˙˙˙_@ ހ€` Q€j€aŐ€ę˙˙˙˙b€1€1€˙˙˙˙c€ހ€dŐ€j€˙˙˙˙e€ 1€1€˙˙˙˙f€ ހ€g H€j€˙˙˙˙h€ 1€1€˙˙˙˙i@ ހ€j Q€j€kŐ€ý˙˙˙˙l€1€1€˙˙˙˙m€ހ€nŐ€j€˙˙˙˙o€ 1€1€˙˙˙˙p€ ހ€q H€j€˙˙˙˙r€ 1€1€˙˙˙˙s@ ހ€t Q€j€uހ vŐ€ ˙˙˙˙w€1€1€˙˙˙˙x€ހ€yH€j€˙˙˙˙z€ 1€1€˙˙˙˙{@ ހ€| Q€j€}Ő€+ ˙˙˙˙~€1€1€˙˙˙˙€ހ€€H€j€˙˙˙˙€ 1€1€˙˙˙˙‚@ ހ€ƒ Q€j€„Ő€= ˙˙˙˙…€1€1€˙˙˙˙†€ހ€‡H€j€˙˙˙˙ˆ€ 1€1€˙˙˙˙‰@ ހ€Š Q€j€‹Ő€O ˙˙˙˙Œ€1€1€˙˙˙˙€ހ€ŽH€j€˙˙˙˙€ 1€1€˙˙˙˙@ ހ€‘ Q€j€’Ő€a ˙˙˙˙“€1€1€˙˙˙˙”€ހ€•H€j€˙˙˙˙–€ 1€1€˙˙˙˙—@ ހ€˜ Q€j€™Ő€s ˙˙˙˙š€1€1€˙˙˙˙›€ހ€œH€j€˙˙˙˙€ 1€1€˙˙˙˙ž@ ހ€Ÿ Q€j€ H€… ˙˙˙˙Ą€1€1€˙˙˙˙˘@ހ€ŁQ€j€¤H€Ÿ ˙˙˙˙Ľ€1€1€˙˙˙˙Ś@ހ€§Q€j€¨Ś€ą ŠL€Î ŞL€â ŤShaderPPtrm_FileIDm_PathIDSerializedShaderm_ParsedFormSerializedPropertiesm_PropInfom_PropsSerializedPropertym_Descriptionm_Attributesm_Flagsm_DefValue[0]m_DefValue[1]m_DefValue[2]m_DefValue[3]SerializedTexturePropertym_DefTexturem_DefaultNamem_TexDimm_SubShadersSerializedSubShaderm_PassesSerializedPassm_NameIndicesSerializedShaderStatem_StateSerializedShaderRTBlendStatertBlend0SerializedShaderFloatValuesrcBlendvalnamedestBlendsrcBlendAlphadestBlendAlphablendOpblendOpAlphacolMaskrtBlend1rtBlend2rtBlend3rtBlend4rtBlend5rtBlend6rtBlend7rtSeparateBlendzTestzWritecullingoffsetFactoroffsetUnitsalphaToMaskSerializedStencilOpstencilOppassfailzFailcompstencilOpFrontstencilOpBackstencilReadMaskstencilWriteMaskstencilReffogStartfogEndfogDensitySerializedShaderVectorValuefogColorxyzwfogModegpuProgramIDSerializedTagMapm_Tagstagsm_LODlightingm_ProgramMaskSerializedProgramprogVertexm_SubProgramsSerializedSubProgramm_BlobIndexParserBindChannelsm_ChannelsShaderBindChannelsourcetargetm_SourceMapm_KeywordIndicesm_ShaderHardwareTierm_GpuProgramTypem_VectorParamsVectorParameterm_NameIndexm_ArraySizem_Dimm_MatrixParamsMatrixParameterm_RowCountm_TextureParamsTextureParameterm_SamplerIndexm_BufferParamsBufferBindingm_ConstantBuffersConstantBufferm_Sizem_ConstantBufferBindingsm_UAVParamsUAVParameterm_OriginalIndexprogFragmentprogGeometryprogHullprogDomainm_HasInstancingVariantm_UseNamem_TextureNamem_CustomEditorNamem_FallbackNamem_DependenciesSerializedShaderDependencyfromtom_DisableNoSubshadersMessageplatformsoffsetscompressedLengthsdecompressedLengthscompressedBlobPPtrm_ShaderIsBakedm_DefaultTexturesShaderCompilationInfom_CompileInfom_SnippetsShaderSnippetm_Codem_AssetPathm_PlatformMaskm_HardwareTierVariantsMaskm_StartLinem_TypesMaskHash128m_IncludesHashbytes[0]bytes[1]bytes[2]bytes[3]bytes[4]bytes[5]bytes[6]bytes[7]bytes[8]bytes[9]bytes[10]bytes[11]bytes[12]bytes[13]bytes[14]bytes[15]m_FromOtherm_Languagem_VariantsUser0m_VariantsUser1m_VariantsUser2m_VariantsUser3m_VariantsUser4m_VariantsUser5m_VariantsBuiltin0m_VariantsBuiltin1m_VariantsBuiltin2m_VariantsBuiltin3m_VariantsBuiltin4m_VariantsBuiltin5m_Targetm_TargetVariants0m_TargetVariants1m_TargetVariants2m_TargetVariants3m_TargetVariants4m_TargetVariants5m_NonStrippedUserKeywordsm_BuiltinKeywordsm_MeshComponentsFromSnippetsm_HasSurfaceShadersm_HasFixedFunctionShadersČČ0>IřN çňĚ8ú‘~D‡gtΠ\MÂpAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/Shaders/ScreenSpaceReflection.shader%qiV_MainTex Base (RGB)white€? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA A A A A C C unity_FogStart unity_FogEndunity_FogDensity unity_FogColor˙˙˙˙ĄZ€? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA A A A A C C unity_FogStart unity_FogEndunity_FogDensity unity_FogColor˙˙˙˙\€? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA A A A A C C unity_FogStart unity_FogEndunity_FogDensity unity_FogColor˙˙˙˙„€? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA A A A A C C unity_FogStart unity_FogEndunity_FogDensity unity_FogColor˙˙˙˙‘É€? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA A A A A C C unity_FogStart unity_FogEndunity_FogDensity unity_FogColor˙˙˙˙05€? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA A A A A C C unity_FogStart unity_FogEndunity_FogDensity unity_FogColor˙˙˙˙z€? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA A A A A C C unity_FogStart unity_FogEndunity_FogDensity unity_FogColor˙˙˙˙+‘€? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA A A A A C C unity_FogStart unity_FogEndunity_FogDensity unity_FogColor˙˙˙˙ڑ€? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA A A A A C C unity_FogStart unity_FogEndunity_FogDensity unity_FogColor˙˙˙˙ćĚ€? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA A A A A C C unity_FogStart unity_FogEndunity_FogDensity unity_FogColor˙˙˙˙_ €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA A A A A C C unity_FogStart unity_FogEndunity_FogDensity unity_FogColor˙˙˙˙g™ €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA A A A A C C unity_FogStart unity_FogEndunity_FogDensity unity_FogColor˙˙˙˙kĄ €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA A A A A C C unity_FogStart unity_FogEndunity_FogDensity unity_FogColor˙˙˙˙7 €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA A A A A C C unity_FogStart unity_FogEndunity_FogDensity unity_FogColor˙˙˙˙˝B €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA A A A A C C unity_FogStart unity_FogEndunity_FogDensity unity_FogColor˙˙˙˙Üh€? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA A A A A C C unity_FogStart unity_FogEndunity_FogDensity unity_FogColor˙˙˙˙›€? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA €? €? pA A A A A C C unity_FogStart unity_FogEndunity_FogDensity unity_FogColor˙˙˙˙2eHidden/ScreenSpaceReflectionĄZ‡#line 982 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #line 12 #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "ScreenSpaceRaytrace.cginc" float4 _ProjInfo; float4x4 _WorldToCameraMatrix; float4x4 _CameraToWorldMatrix; float4x4 _ProjectToPixelMatrix; float2 _ScreenSize; float2 _ReflectionBufferSize; float2 _InvScreenSize; float3 _CameraClipInfo; sampler2D _CameraGBufferTexture0; sampler2D _CameraGBufferTexture1; sampler2D _CameraGBufferTexture2; sampler2D _CameraGBufferTexture3; sampler2D _CameraReflectionsTexture; float _CurrentMipLevel; float _RayStepSize; float _MaxRayTraceDistance; float _LayerThickness; float _FresnelFade; float _FresnelFadePower; sampler2D _MainTex; int _VisualizeWhereBilateral; int _EnableSSR; int _DebugMode; int _HalfResolution; int _TreatBackfaceHitAsMiss; int _AllowBackwardsRays; int _TraceEverywhere; // RG: SS Hitpoint of ray // B: distance ray travelled, used for mip-selection in the final resolve // A: confidence value sampler2D _HitPointTexture; sampler2D _FinalReflectionTexture; // RGB: camera-space normal (encoded in [0-1]) // A: Roughness sampler2D _NormalAndRoughnessTexture; float4 _MainTex_TexelSize; float4 _SourceToTempUV; int _EnableRefine; int _AdditiveReflection; int _ImproveCorners; float _ScreenEdgeFading; float _MipBias; int _UseOcclusion; int _MaxSteps; int _FullResolutionFiltering; int _BilateralUpsampling; float _MaxRoughness; float _RoughnessFalloffRange; float _SSRMultiplier; float _FadeDistance; float _DistanceBlur; int _TraceBehindObjects; int _FallbackToSky; int _UseEdgeDetector; int _HighlightSuppression; /** The height in pixels of a 1m object if viewed from 1m away. */ float _PixelsPerMeterAtOneMeter; int _UseAverageRayDistance; sampler2D _AverageRayDistanceBuffer; // For temporal filtering: float4x4 _CurrentCameraToPreviousCamera; sampler2D _PreviousReflectionTexture; sampler2D _PreviousCSZBuffer; float _TemporalAlpha; int _UseTemporalConfidence; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy; o.uv2 = v.texcoord.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv2.y = 1-o.uv2.y; #endif return o; } float2 mipToSize(int mip) { return floor(_ReflectionBufferSize * exp2(-mip)); } float3 ReconstructCSPosition(float2 S, float z) { float linEyeZ = -LinearEyeDepth(z); return float3(( (( S.xy * _MainTex_TexelSize.zw) ) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); } /** Read the camera-space position of the point at screen-space pixel ssP */ float3 GetPosition(float2 ssP) { float3 P; P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy); // Offset to pixel center P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); return P; } float square(float x) { return x*x; } float applyEdgeFade(float2 tsP, float fadeStrength) { float maxFade = 0.1; float2 itsP = float2(1.0, 1.0) - tsP; float dist = min(min(itsP.x, itsP.y), min(tsP.x, tsP.x)); float fade = dist / (maxFade*fadeStrength + 0.001); fade = max(min(fade, 1.0), 0.0); fade = pow(fade, 0.2); return fade; } float3 csMirrorVector(float3 csPosition, float3 csN) { float3 csE = -normalize(csPosition.xyz); float cos_o = dot(csN, csE); float3 c_mi = normalize((csN * (2.0 * cos_o)) - csE); return c_mi; } float4 fragRaytrace(v2f i, int stepRate) { float2 ssP = i.uv2.xy * _SourceToTempUV.zw; float3 csPosition = GetPosition(ssP); float smoothness = tex2D(_CameraGBufferTexture1, ssP).a; if (csPosition.z < -100.0 || smoothness == 0.0) { return float4(0.0,0.0,0.0,0.0); } if ((!_TraceEverywhere) && (smoothness < (1.0 - _MaxRoughness)) ) { return float4(0.0, 0.0, 0.0, 0.0); } float3 wsNormal = tex2D(_CameraGBufferTexture2, ssP).rgb * 2.0 - 1.0; int2 ssC = int2(ssP * _ScreenSize); float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 csRayDirection = csMirrorVector(csPosition, csN); if (_AllowBackwardsRays == 0 && csRayDirection.z > 0.0) { return float4(0.0, 0.0, 0.0, 0.0); } float maxRayTraceDistance = _MaxRayTraceDistance; float jitterFraction = 0.0f; float layerThickness = _LayerThickness; int maxSteps = _MaxSteps; // Bump the ray more in world space as it gets farther away (and so each pixel covers more WS distance) float rayBump = max(-0.01*csPosition.z, 0.001); float2 hitPixel; float3 csHitPoint; float stepCount; bool wasHit = castDenseScreenSpaceRay (csPosition + (csN) * rayBump, csRayDirection, _ProjectToPixelMatrix, _ScreenSize, _CameraClipInfo, jitterFraction, maxSteps, layerThickness, maxRayTraceDistance, hitPixel, stepRate, _EnableRefine == 1, _TraceBehindObjects == 1, csHitPoint, stepCount); float2 tsPResult = hitPixel / _ScreenSize; float rayDist = dot(csHitPoint - csPosition, csRayDirection); float confidence = 0.0; if (wasHit) { confidence = square(1.0 - max(2.0*float(stepCount) / float(maxSteps) - 1.0, 0.0)); confidence *= clamp(((_MaxRayTraceDistance - rayDist) / _FadeDistance), 0.0, 1.0); // Fake fresnel fade float3 csE = -normalize(csPosition.xyz); confidence *= max(0.0, lerp(pow(abs(dot(csRayDirection, -csE)), _FresnelFadePower), 1, 1.0 - _FresnelFade)); if (_TreatBackfaceHitAsMiss > 0) { float3 wsHitNormal = tex2Dlod(_CameraGBufferTexture2, float4(tsPResult, 0, 0)).rgb * 2.0 - 1.0; float3 wsRayDirection = mul(_CameraToWorldMatrix, float4(csRayDirection, 0)).xyz; if (dot(wsHitNormal, wsRayDirection) > 0) { confidence = 0.0; } } } else if (_FallbackToSky > 0) { float sceneZ = tex2Dlod(_CameraDepthTexture, float4(tsPResult, 0, 0)).r; sceneZ = -LinearEyeDepth(sceneZ); if (sceneZ < -1000) { // 1km ~= infinity confidence = 1.0; } } // Fade out reflections that hit near edge of screen, to prevent abrupt appearance/disappearance when object go off screen // Fade out reflections that hit near edge of screen, // to prevent abrupt appearance/disappearance when object go off screen float vignette = applyEdgeFade(tsPResult, _ScreenEdgeFading); confidence *= vignette; confidence *= vignette; return float4(tsPResult, rayDist, confidence); } float4 fragComposite(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; // View space point being shaded float3 C = GetPosition(tsP); // Final image before this pass float4 gbuffer3 = tex2D(_MainTex, i.uv); float4 specEmission = float4(0.0,0.0,0.0,0.0); float3 specColor = tex2D(_CameraGBufferTexture1, tsP).rgb; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP).a; float4 reflectionTexel = tex2D(_FinalReflectionTexture, tsP * _SourceToTempUV.xy); float4 gbuffer0 = tex2D(_CameraGBufferTexture0, tsP); // Let core Unity functions do the dirty work of applying the BRDF float3 baseColor = gbuffer0.rgb; float occlusion = gbuffer0.a; float oneMinusReflectivity; baseColor = EnergyConservationBetweenDiffuseAndSpecular(baseColor, specColor, oneMinusReflectivity); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csEyeVec = normalize(C); float3 eyeVec = mul(_CameraToWorldMatrix, float4(csEyeVec, 0)).xyz; float3 worldPos = mul(_CameraToWorldMatrix, float4(C, 1)).xyz; float cos_o = dot(wsNormal, eyeVec); float3 w_mi = -normalize((wsNormal * (2.0 * cos_o)) - eyeVec); float3 incomingRadiance = reflectionTexel.rgb; UnityLight light; light.color = 0; light.dir = 0; light.ndotl = 0; UnityIndirect ind; ind.diffuse = 0; ind.specular = incomingRadiance; float3 ssrResult = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, 1-roughness, wsNormal, -eyeVec, light, ind).rgb * _SSRMultiplier; float confidence = reflectionTexel.a; if(_EnableSSR == 0) { confidence = 0.0; } specEmission.rgb = tex2D(_CameraReflectionsTexture, tsP).rgb; float3 finalGlossyTerm; // Subtract out Unity's glossy result: (we're just applying the delta) if (_AdditiveReflection == 0) { gbuffer3 -= specEmission; // We may have blown out our dynamic range by adding then subtracting the reflection probes. // As a half-measure to fix this, simply clamp to zero gbuffer3 = max(gbuffer3, 0); finalGlossyTerm = lerp(specEmission.rgb, ssrResult, saturate(confidence)); } else { finalGlossyTerm = ssrResult*saturate(confidence); } if (_UseOcclusion) { finalGlossyTerm *= occlusion; } if (_DebugMode == 1) return float4(incomingRadiance,0); if (_DebugMode == 2) return float4(ssrResult,0); if (_DebugMode == 3) return float4(finalGlossyTerm,0); if (_DebugMode == 4) return confidence; if (_DebugMode == 5) return roughness; if (_DebugMode == 6) return float4(baseColor,0); if (_DebugMode == 7) return float4(specColor,0); if (_DebugMode == 8) return 1.0-oneMinusReflectivity; if (_DebugMode == 9) return float4(specEmission.rgb,0); if (_DebugMode == 10) return float4(specEmission.rgb-ssrResult,0); if (_DebugMode == 11) return float4(ssrResult-specEmission.rgb,0); if (_DebugMode == 12) return gbuffer3; if (_DebugMode == 13) return -float4(gbuffer3.rgb, 0.0); if (_DebugMode == 14) // Mip level, stored directly in ssrResult return float4(incomingRadiance, 0); // Additively blend the glossy GI result with the output buffer return gbuffer3 + float4(finalGlossyTerm, 0); } float roughnessWeight(float midpointRoughness, float tapRoughness) { return (1.0 - sqrt(sqrt(abs(midpointRoughness-tapRoughness)))); } float normalWeight(float3 midpointNormal, float3 tapNormal) { return clamp(dot(midpointNormal, tapNormal), 0, 1); } float highlightDecompression(float x) { return x / (1.0 - x); } float3 highlightDecompression(float3 x) { return float3( highlightDecompression(x.x), highlightDecompression(x.y), highlightDecompression(x.z)); } float highlightCompression(float x) { return x / (1.0 + x); } float3 highlightCompression(float3 x) { return float3( highlightCompression(x.x), highlightCompression(x.y), highlightCompression(x.z)); } float4 _Axis; float4 fragGBlur(v2f i) : SV_Target { int radius = 4; // Pixel being shaded float2 tsP = i.uv2.xy; float weightSum = 0.0; float gaussWeights[5] = {0.225, 0.150, 0.110, 0.075, 0.0525};//{0.225, 0.150, 0.110, 0.075, 0.0525}; float4 resultSum = float4(0.0, 0.0, 0.0, 0.0); float4 unweightedResultSum = float4(0.0, 0.0, 0.0, 0.0); float4 nAndRough = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = nAndRough.a; float3 midpointNormal = nAndRough.rgb * 2 - 1; if (_FullResolutionFiltering) { for (int i = -2*radius; i <= 2*radius; ++i) { float4 temp; float tapRoughness = midpointRoughness; float3 tapNormal = midpointNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)); temp = tex2D(_MainTex, tsTap); int gaussWeightIndex; #if defined(UNITY_COMPILER_HLSL) && !defined(SHADER_API_D3D9) gaussWeightIndex = abs(i) >> 1; // We purposefully do integer division, but that does not compile on hlsl2glsl #else gaussWeightIndex = frac(abs(i) / 2); #endif float weight = temp.a * gaussWeights[gaussWeightIndex]*0.5; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; resultSum += temp*weight; unweightedResultSum += temp; } } else { for (int i = -radius; i <= radius; ++i) { float4 temp; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)*2.0); temp = tex2D(_MainTex, tsTap); float weight = temp.a * gaussWeights[abs(i)]; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; if (_HighlightSuppression) { temp.rgb = highlightCompression(temp.rgb); } unweightedResultSum += temp; resultSum += temp*weight; } } if (weightSum > 0.01) { float invWeightSum = (1.0/weightSum); // Adding the sqrt seems to decrease temporal flickering at the expense // of having larger "halos" of fallback on rough surfaces // Subject to change with testing. Sqrt around only half the expression is *intentional*. float confidence = min(resultSum.a * sqrt(max(invWeightSum, 2.0)), 1.0); float3 finalColor = resultSum.rgb * invWeightSum; if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, confidence); } else { float3 finalColor = unweightedResultSum.rgb / (2 * radius + 1); if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, 0.0); } } sampler2D _ReflectionTexture0; sampler2D _ReflectionTexture1; sampler2D _ReflectionTexture2; sampler2D _ReflectionTexture3; sampler2D _ReflectionTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getReflectionValue(float2 tsP, int mip) { float4 coord = float4(tsP,0,0); if (mip == 0) { return tex2Dlod(_ReflectionTexture0, coord); } else if (mip == 1) { return tex2Dlod(_ReflectionTexture1, coord); } else if (mip == 2) { return tex2Dlod(_ReflectionTexture2, coord); } else if (mip == 3) { return tex2Dlod(_ReflectionTexture3, coord); } else { return tex2Dlod(_ReflectionTexture4, coord); } } sampler2D _EdgeTexture0; sampler2D _EdgeTexture1; sampler2D _EdgeTexture2; sampler2D _EdgeTexture3; sampler2D _EdgeTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getEdgeValue(float2 tsP, int mip) { float4 coord = float4(tsP + float2(1.0/(2 * mipToSize(mip))),0,0); if (mip == 0) { return tex2Dlod(_EdgeTexture0, coord); } else if (mip == 1) { return tex2Dlod(_EdgeTexture1, coord); } else if (mip == 2) { return tex2Dlod(_EdgeTexture2, coord); } else if (mip == 3) { return tex2Dlod(_EdgeTexture3, coord); } else { return tex2Dlod(_EdgeTexture4, coord); } } float2 centerPixel(float2 inputP) { return floor(inputP - float2(0.5,0.5)) + float2(0.5,0.5); } float2 snapToTexelCenter(float2 inputP, float2 texSize, float2 texSizeInv) { return centerPixel(inputP * texSize) * texSizeInv; } float4 bilateralUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; float2 fullTexSize = _ReflectionBufferSize; float2 fullTexSizeInv = 1.0 / fullTexSize; float4 hiP0 = float4(snapToTexelCenter(p0, fullTexSize, fullTexSizeInv), 0,0); float4 hiP3 = float4(snapToTexelCenter(p3, fullTexSize, fullTexSizeInv), 0,0); float4 hiP1 = float4(snapToTexelCenter(p1, fullTexSize, fullTexSizeInv), 0,0); float4 hiP2 = float4(snapToTexelCenter(p2, fullTexSize, fullTexSizeInv), 0,0); float4 tempCenter = tex2Dlod(_NormalAndRoughnessTexture, float4(tsP, 0, 0)); float3 n = tempCenter.xyz * 2 - 1; float4 temp0 = tex2Dlod(_NormalAndRoughnessTexture, hiP0); float4 temp1 = tex2Dlod(_NormalAndRoughnessTexture, hiP1); float4 temp2 = tex2Dlod(_NormalAndRoughnessTexture, hiP2); float4 temp3 = tex2Dlod(_NormalAndRoughnessTexture, hiP3); float3 n0 = temp0.xyz * 2 - 1; float3 n1 = temp1.xyz * 2 - 1; float3 n2 = temp2.xyz * 2 - 1; float3 n3 = temp3.xyz * 2 - 1; a0 *= normalWeight(n, n0); a1 *= normalWeight(n, n1); a2 *= normalWeight(n, n2); a3 *= normalWeight(n, n3); float r = tempCenter.a; float r0 = temp0.a; float r1 = temp1.a; float r2 = temp2.a; float r3 = temp3.a; a0 *= roughnessWeight(r, r0); a1 *= roughnessWeight(r, r1); a2 *= roughnessWeight(r, r2); a3 *= roughnessWeight(r, r3); // Slightly offset from zero a0 = max(a0, 0.001); a1 = max(a1, 0.001); a2 = max(a2, 0.001); a3 = max(a3, 0.001); // Nearest neighbor // a0 = a1 = a2 = a3 = 1.0; // Normalize the blending weights (weights were chosen so that // the denominator can never be zero) float norm = 1.0 / (a0 + a1 + a2 + a3); // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3) * norm; //return V0; return value; } /** Explicit bilinear fetches; must be used if the reflection buffer is bound using point sampling */ float4 bilinearUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3); return value; } // Unity's roughness is GGX roughness squared float roughnessToBlinnPhongExponent(float roughness) { float r2 = roughness*roughness; return 2.0f / r2*r2 - 2.0f; } float glossyLobeSlope(float roughness) { return pow(roughness, 4.0/3.0); } // Empirically based on our filter: // Mip | Pixels // -------------- // 0 | 1 no filter, so single pixel // 1 | 17 2r + 1 filter applied once, grabbing from pixels r away in either direction (r=8, four samples times stride of 2) // 2 | 50 2r + 1 filter applied on double size pixels, and each of those pixels had reached another r out to the side 2(2r + 1) + m_1 // 3 | 118 4(2r + 1) + m_2 // 4 | 254 8(2r + 1) + m_3 // // Approximated by pixels = 16*2^mip-15 // rearranging we get mip = log_2((pixels + 15) / 16) // float filterFootprintInPixelsToMip(float footprint) { return log2((footprint + 15) / 16); } float3 ansiGradient(float t) { //return float3(t, t, t); return fmod(floor(t * float3(8.0, 4.0, 2.0)), 2.0); } float4 fragCompositeSSR(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP * _SourceToTempUV.zw).a; float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).z; } // Get the camera space position of the reflection hit float3 csPosition = GetPosition(tsP); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 c_mi = csMirrorVector(csPosition, csN); float3 csHitpoint = c_mi * rayDistance + csPosition; float gatherFootprintInMeters = glossyLobeSlope(roughness) * rayDistance; // We could add a term that incorporates the normal // This approximation assumes reflections happen at a glancing angle float filterFootprintInPixels = gatherFootprintInMeters * _PixelsPerMeterAtOneMeter / csHitpoint.z; if (_HalfResolution == 1) { filterFootprintInPixels *= 0.5; } float mip = filterFootprintInPixelsToMip(filterFootprintInPixels); float nonPhysicalMip = pow(roughness, 3.0 / 4.0) * UNITY_SPECCUBE_LOD_STEPS; if (_HalfResolution == 1) { nonPhysicalMip = nonPhysicalMip * 0.7; } mip = lerp(nonPhysicalMip, mip, _DistanceBlur); mip = max(0, min(4, mip + _MipBias)); if (_DebugMode == 14) { return float4(ansiGradient(mip*0.25), 1.0); } float4 result; { int mipMin = int(mip); int mipMax = min(mipMin + 1, 4); float mipLerp = mip-mipMin; if (_BilateralUpsampling == 1) { if (_UseEdgeDetector == 1) { float nonEdginess = lerp(getEdgeValue(tsP, mipMin), getEdgeValue(tsP, mipMax), mipLerp); if (nonEdginess < 0.9) { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); if (_VisualizeWhereBilateral == 1) { result = float4(1, 0, 0, 0); } } else { result = lerp(bilinearUpsampleReflection(tsP, mipMin), bilinearUpsampleReflection(tsP, mipMax), mipLerp); } } else { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); } } else { float4 minResult = getReflectionValue(tsP, mipMin); float4 maxResult = getReflectionValue(tsP, mipMax); result = lerp(minResult, maxResult, mipLerp); result.a = min(minResult.a, maxResult.a); } } result.a = min(result.a, 1.0); float vignette = applyEdgeFade(tsP, _ScreenEdgeFading); result.a *= vignette; float alphaModifier = 1.0 - clamp((roughness - (_MaxRoughness - _RoughnessFalloffRange)) / _RoughnessFalloffRange, 0.0, 1.0); result.a *= alphaModifier; return result; } float4 fragBlit(v2f i) : SV_Target { // Pixel being shaded float2 ssP = i.uv2.xy; return tex2D(_MainTex, ssP); } float edgeDetector(float2 tsP, float2 axis, float2 texelSize) { float4 temp = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = temp.a; float3 midpointNormal = temp.rgb * 2 - 1; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (axis * _MainTex_TexelSize.xy); temp = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = temp.a; tapNormal = temp.rgb * 2 - 1; float weight = 1.0; weight *= roughnessWeight(midpointRoughness, tapRoughness); weight *= normalWeight(midpointNormal, tapNormal); return weight; } float4 fragEdge(v2f i) : SV_Target { float2 tsP = i.uv2.xy; return edgeDetector(tsP, float2(1,0), _MainTex_TexelSize.xy) * edgeDetector(tsP, float2(0,1), _MainTex_TexelSize.xy); } int _LastMip; float4 fragMin(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float2 lastTexSize = mipToSize(_LastMip); float2 lastTexSizeInv = 1.0 / lastTexSize;//_SourceToTempUV.xy * exp2(mip); float2 p00 = snapToTexelCenter(tsP, lastTexSize, lastTexSizeInv); float2 p11 = p00 + lastTexSizeInv; return min( min(tex2D(_MainTex, p00), tex2D(_MainTex, p11)), min(tex2D(_MainTex, float2(p00.x, p11.y)), tex2D(_MainTex, float2(p11.x, p00.y))) ); } float4 fragResolveHitPoints(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float4 temp = tex2D(_HitPointTexture, tsP); float2 hitPoint = temp.xy; float confidence = temp.w; float3 colorResult = confidence > 0.0 ? tex2D(_MainTex, hitPoint).rgb : tex2D(_CameraReflectionsTexture, tsP).rgb; #ifdef UNITY_COMPILER_HLSL if (any(isnan(colorResult))) colorResult = float3(0.0, 0.0, 0.0); // As of 11/29/2015, on Unity 5.3 on a Windows 8.1 computer with a NVIDIA GeForce 980, // with driver 347.62, the above check does not actually work to get rid of NaNs! // So we add this "redundant" check. if (!all(isfinite(colorResult))) colorResult = float3(0.0, 0.0, 0.0); #endif return float4(colorResult, confidence); } float4 fragBilatKeyPack(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float3 csN = tex2D(_CameraGBufferTexture2, tsP).xyz; float roughness = tex2D(_CameraGBufferTexture1, tsP).a; return float4(csN, roughness); } float4 fragDepthToCSZ(v2f i) : SV_Target { float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv2.xy); return float4(-LinearEyeDepth(depth), 0.0, 0.0, 0.0); } float3 csPositionToOldNormalizedScreenCoordinate(float3 csPosition) { // We could multiply these matrices together on the CPU float4 oldCSPosition = mul(_CurrentCameraToPreviousCamera, float4(csPosition, 1.0)); float4 oldSSCoordinate = mul(_ProjectToPixelMatrix, oldCSPosition); return float3((oldSSCoordinate.xy / oldSSCoordinate.w) * _InvScreenSize, oldCSPosition.z); } float4 fragTemporalFilter(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, tsP); float3 csPosition = GetPosition(tsP); float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).b; } // Virtual is the physics sense, not the other 20+ interpretations in CG float3 virtualReflectionCSPosition = normalize(csPosition) * rayDistance + csPosition; float3 oldTSP = csPositionToOldNormalizedScreenCoordinate(csPosition); float3 oldVirtualReflectionTSP = csPositionToOldNormalizedScreenCoordinate(virtualReflectionCSPosition); float oldCSZ = tex2D(_PreviousCSZBuffer, oldTSP.xy); float4 currentResult = tex2D(_FinalReflectionTexture, tsP); float4 oldResult = tex2D(_PreviousReflectionTexture, oldVirtualReflectionTSP.xy); float zError = abs(oldCSZ - oldTSP.z); // Start knocking down contribution at 5cm error, go to 0 at 5 more cm later float fallbackStart = 0.05; float fallbackEnd = 0.05; float zAlphaModifier = 1.0 - clamp((zError - fallbackStart) * (1.0 / fallbackEnd), 0.0, 1.0); float alpha = _TemporalAlpha; alpha *= zAlphaModifier; if (_UseTemporalConfidence) { alpha *= oldResult.w; } // Always compress currentResult.rgb = highlightCompression(currentResult.rgb); oldResult.rgb = highlightCompression(oldResult.rgb); float4 resultValue = lerp(currentResult, oldResult, alpha); resultValue.rgb = highlightDecompression(resultValue.rgb); return resultValue; } float4 fragAverageRayDistance(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float sumRayDistance = tex2D(_HitPointTexture, tsP).b; float sumWeight = 1.0; float roughness = 1.0 - tex2D(_CameraGBufferTexture1, tsP).a; float step = roughness * 3.0; /* for (float x = -step; x < step; x += 2 * step) { for (float y = -step; y < step; y += 2 * step) { float weight = 0.5; sumRayDistance += weight * tex2D(_HitPointTexture, (float2(x,y) * _InvScreenSize) + tsP).b; sumWeight += weight; } } */ return float4(sumRayDistance / sumWeight, 0.0, 0.0, 0.0); } static const int NUM_POISSON_TAPS = 12; // Same as used in CameraMotionBlur.shader static const float2 poissonSamples[NUM_POISSON_TAPS] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; float4 fragFilterSharpReflections(v2f i) : SV_Target { // Could improve perf by not computing blur when we won't be sampling the highest level anyways float2 tsP = i.uv2.xy; float4 sum = 0.0; float sampleRadius = _MainTex_TexelSize.xy * 2.0; for (int i = 0; i < NUM_POISSON_TAPS; i++) { float2 p = tsP + poissonSamples[i] * sampleRadius; float4 tap = tex2Dlod(_MainTex, float4(p, 0, 0)); if (_HighlightSuppression) { tap.rgb = highlightCompression(tap.rgb); } sum += tap; } float4 result = sum / float(NUM_POISSON_TAPS); if (_HighlightSuppression) { result.rgb = highlightDecompression(result.rgb); } return result; } #line 982 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif /* UNITY: Original start of shader */ #pragma exclude_renderers gles xbox360 ps3 #pragma vertex vert #pragma fragment fragRaytrace1 #pragma target 3.0 float4 fragRaytrace1(v2f i) : SV_Target { return fragRaytrace(i, 1); } SAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/ShadersÓ˙˙˙Řküń'¨ô™!]{Ňô SHADOWS_SOFTDIRLIGHTMAP_COMBINEDDIRLIGHTMAP_SEPARATEDYNAMICLIGHTMAP_ON INSTANCING_ON\‡#line 993 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #line 12 #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "ScreenSpaceRaytrace.cginc" float4 _ProjInfo; float4x4 _WorldToCameraMatrix; float4x4 _CameraToWorldMatrix; float4x4 _ProjectToPixelMatrix; float2 _ScreenSize; float2 _ReflectionBufferSize; float2 _InvScreenSize; float3 _CameraClipInfo; sampler2D _CameraGBufferTexture0; sampler2D _CameraGBufferTexture1; sampler2D _CameraGBufferTexture2; sampler2D _CameraGBufferTexture3; sampler2D _CameraReflectionsTexture; float _CurrentMipLevel; float _RayStepSize; float _MaxRayTraceDistance; float _LayerThickness; float _FresnelFade; float _FresnelFadePower; sampler2D _MainTex; int _VisualizeWhereBilateral; int _EnableSSR; int _DebugMode; int _HalfResolution; int _TreatBackfaceHitAsMiss; int _AllowBackwardsRays; int _TraceEverywhere; // RG: SS Hitpoint of ray // B: distance ray travelled, used for mip-selection in the final resolve // A: confidence value sampler2D _HitPointTexture; sampler2D _FinalReflectionTexture; // RGB: camera-space normal (encoded in [0-1]) // A: Roughness sampler2D _NormalAndRoughnessTexture; float4 _MainTex_TexelSize; float4 _SourceToTempUV; int _EnableRefine; int _AdditiveReflection; int _ImproveCorners; float _ScreenEdgeFading; float _MipBias; int _UseOcclusion; int _MaxSteps; int _FullResolutionFiltering; int _BilateralUpsampling; float _MaxRoughness; float _RoughnessFalloffRange; float _SSRMultiplier; float _FadeDistance; float _DistanceBlur; int _TraceBehindObjects; int _FallbackToSky; int _UseEdgeDetector; int _HighlightSuppression; /** The height in pixels of a 1m object if viewed from 1m away. */ float _PixelsPerMeterAtOneMeter; int _UseAverageRayDistance; sampler2D _AverageRayDistanceBuffer; // For temporal filtering: float4x4 _CurrentCameraToPreviousCamera; sampler2D _PreviousReflectionTexture; sampler2D _PreviousCSZBuffer; float _TemporalAlpha; int _UseTemporalConfidence; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy; o.uv2 = v.texcoord.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv2.y = 1-o.uv2.y; #endif return o; } float2 mipToSize(int mip) { return floor(_ReflectionBufferSize * exp2(-mip)); } float3 ReconstructCSPosition(float2 S, float z) { float linEyeZ = -LinearEyeDepth(z); return float3(( (( S.xy * _MainTex_TexelSize.zw) ) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); } /** Read the camera-space position of the point at screen-space pixel ssP */ float3 GetPosition(float2 ssP) { float3 P; P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy); // Offset to pixel center P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); return P; } float square(float x) { return x*x; } float applyEdgeFade(float2 tsP, float fadeStrength) { float maxFade = 0.1; float2 itsP = float2(1.0, 1.0) - tsP; float dist = min(min(itsP.x, itsP.y), min(tsP.x, tsP.x)); float fade = dist / (maxFade*fadeStrength + 0.001); fade = max(min(fade, 1.0), 0.0); fade = pow(fade, 0.2); return fade; } float3 csMirrorVector(float3 csPosition, float3 csN) { float3 csE = -normalize(csPosition.xyz); float cos_o = dot(csN, csE); float3 c_mi = normalize((csN * (2.0 * cos_o)) - csE); return c_mi; } float4 fragRaytrace(v2f i, int stepRate) { float2 ssP = i.uv2.xy * _SourceToTempUV.zw; float3 csPosition = GetPosition(ssP); float smoothness = tex2D(_CameraGBufferTexture1, ssP).a; if (csPosition.z < -100.0 || smoothness == 0.0) { return float4(0.0,0.0,0.0,0.0); } if ((!_TraceEverywhere) && (smoothness < (1.0 - _MaxRoughness)) ) { return float4(0.0, 0.0, 0.0, 0.0); } float3 wsNormal = tex2D(_CameraGBufferTexture2, ssP).rgb * 2.0 - 1.0; int2 ssC = int2(ssP * _ScreenSize); float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 csRayDirection = csMirrorVector(csPosition, csN); if (_AllowBackwardsRays == 0 && csRayDirection.z > 0.0) { return float4(0.0, 0.0, 0.0, 0.0); } float maxRayTraceDistance = _MaxRayTraceDistance; float jitterFraction = 0.0f; float layerThickness = _LayerThickness; int maxSteps = _MaxSteps; // Bump the ray more in world space as it gets farther away (and so each pixel covers more WS distance) float rayBump = max(-0.01*csPosition.z, 0.001); float2 hitPixel; float3 csHitPoint; float stepCount; bool wasHit = castDenseScreenSpaceRay (csPosition + (csN) * rayBump, csRayDirection, _ProjectToPixelMatrix, _ScreenSize, _CameraClipInfo, jitterFraction, maxSteps, layerThickness, maxRayTraceDistance, hitPixel, stepRate, _EnableRefine == 1, _TraceBehindObjects == 1, csHitPoint, stepCount); float2 tsPResult = hitPixel / _ScreenSize; float rayDist = dot(csHitPoint - csPosition, csRayDirection); float confidence = 0.0; if (wasHit) { confidence = square(1.0 - max(2.0*float(stepCount) / float(maxSteps) - 1.0, 0.0)); confidence *= clamp(((_MaxRayTraceDistance - rayDist) / _FadeDistance), 0.0, 1.0); // Fake fresnel fade float3 csE = -normalize(csPosition.xyz); confidence *= max(0.0, lerp(pow(abs(dot(csRayDirection, -csE)), _FresnelFadePower), 1, 1.0 - _FresnelFade)); if (_TreatBackfaceHitAsMiss > 0) { float3 wsHitNormal = tex2Dlod(_CameraGBufferTexture2, float4(tsPResult, 0, 0)).rgb * 2.0 - 1.0; float3 wsRayDirection = mul(_CameraToWorldMatrix, float4(csRayDirection, 0)).xyz; if (dot(wsHitNormal, wsRayDirection) > 0) { confidence = 0.0; } } } else if (_FallbackToSky > 0) { float sceneZ = tex2Dlod(_CameraDepthTexture, float4(tsPResult, 0, 0)).r; sceneZ = -LinearEyeDepth(sceneZ); if (sceneZ < -1000) { // 1km ~= infinity confidence = 1.0; } } // Fade out reflections that hit near edge of screen, to prevent abrupt appearance/disappearance when object go off screen // Fade out reflections that hit near edge of screen, // to prevent abrupt appearance/disappearance when object go off screen float vignette = applyEdgeFade(tsPResult, _ScreenEdgeFading); confidence *= vignette; confidence *= vignette; return float4(tsPResult, rayDist, confidence); } float4 fragComposite(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; // View space point being shaded float3 C = GetPosition(tsP); // Final image before this pass float4 gbuffer3 = tex2D(_MainTex, i.uv); float4 specEmission = float4(0.0,0.0,0.0,0.0); float3 specColor = tex2D(_CameraGBufferTexture1, tsP).rgb; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP).a; float4 reflectionTexel = tex2D(_FinalReflectionTexture, tsP * _SourceToTempUV.xy); float4 gbuffer0 = tex2D(_CameraGBufferTexture0, tsP); // Let core Unity functions do the dirty work of applying the BRDF float3 baseColor = gbuffer0.rgb; float occlusion = gbuffer0.a; float oneMinusReflectivity; baseColor = EnergyConservationBetweenDiffuseAndSpecular(baseColor, specColor, oneMinusReflectivity); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csEyeVec = normalize(C); float3 eyeVec = mul(_CameraToWorldMatrix, float4(csEyeVec, 0)).xyz; float3 worldPos = mul(_CameraToWorldMatrix, float4(C, 1)).xyz; float cos_o = dot(wsNormal, eyeVec); float3 w_mi = -normalize((wsNormal * (2.0 * cos_o)) - eyeVec); float3 incomingRadiance = reflectionTexel.rgb; UnityLight light; light.color = 0; light.dir = 0; light.ndotl = 0; UnityIndirect ind; ind.diffuse = 0; ind.specular = incomingRadiance; float3 ssrResult = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, 1-roughness, wsNormal, -eyeVec, light, ind).rgb * _SSRMultiplier; float confidence = reflectionTexel.a; if(_EnableSSR == 0) { confidence = 0.0; } specEmission.rgb = tex2D(_CameraReflectionsTexture, tsP).rgb; float3 finalGlossyTerm; // Subtract out Unity's glossy result: (we're just applying the delta) if (_AdditiveReflection == 0) { gbuffer3 -= specEmission; // We may have blown out our dynamic range by adding then subtracting the reflection probes. // As a half-measure to fix this, simply clamp to zero gbuffer3 = max(gbuffer3, 0); finalGlossyTerm = lerp(specEmission.rgb, ssrResult, saturate(confidence)); } else { finalGlossyTerm = ssrResult*saturate(confidence); } if (_UseOcclusion) { finalGlossyTerm *= occlusion; } if (_DebugMode == 1) return float4(incomingRadiance,0); if (_DebugMode == 2) return float4(ssrResult,0); if (_DebugMode == 3) return float4(finalGlossyTerm,0); if (_DebugMode == 4) return confidence; if (_DebugMode == 5) return roughness; if (_DebugMode == 6) return float4(baseColor,0); if (_DebugMode == 7) return float4(specColor,0); if (_DebugMode == 8) return 1.0-oneMinusReflectivity; if (_DebugMode == 9) return float4(specEmission.rgb,0); if (_DebugMode == 10) return float4(specEmission.rgb-ssrResult,0); if (_DebugMode == 11) return float4(ssrResult-specEmission.rgb,0); if (_DebugMode == 12) return gbuffer3; if (_DebugMode == 13) return -float4(gbuffer3.rgb, 0.0); if (_DebugMode == 14) // Mip level, stored directly in ssrResult return float4(incomingRadiance, 0); // Additively blend the glossy GI result with the output buffer return gbuffer3 + float4(finalGlossyTerm, 0); } float roughnessWeight(float midpointRoughness, float tapRoughness) { return (1.0 - sqrt(sqrt(abs(midpointRoughness-tapRoughness)))); } float normalWeight(float3 midpointNormal, float3 tapNormal) { return clamp(dot(midpointNormal, tapNormal), 0, 1); } float highlightDecompression(float x) { return x / (1.0 - x); } float3 highlightDecompression(float3 x) { return float3( highlightDecompression(x.x), highlightDecompression(x.y), highlightDecompression(x.z)); } float highlightCompression(float x) { return x / (1.0 + x); } float3 highlightCompression(float3 x) { return float3( highlightCompression(x.x), highlightCompression(x.y), highlightCompression(x.z)); } float4 _Axis; float4 fragGBlur(v2f i) : SV_Target { int radius = 4; // Pixel being shaded float2 tsP = i.uv2.xy; float weightSum = 0.0; float gaussWeights[5] = {0.225, 0.150, 0.110, 0.075, 0.0525};//{0.225, 0.150, 0.110, 0.075, 0.0525}; float4 resultSum = float4(0.0, 0.0, 0.0, 0.0); float4 unweightedResultSum = float4(0.0, 0.0, 0.0, 0.0); float4 nAndRough = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = nAndRough.a; float3 midpointNormal = nAndRough.rgb * 2 - 1; if (_FullResolutionFiltering) { for (int i = -2*radius; i <= 2*radius; ++i) { float4 temp; float tapRoughness = midpointRoughness; float3 tapNormal = midpointNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)); temp = tex2D(_MainTex, tsTap); int gaussWeightIndex; #if defined(UNITY_COMPILER_HLSL) && !defined(SHADER_API_D3D9) gaussWeightIndex = abs(i) >> 1; // We purposefully do integer division, but that does not compile on hlsl2glsl #else gaussWeightIndex = frac(abs(i) / 2); #endif float weight = temp.a * gaussWeights[gaussWeightIndex]*0.5; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; resultSum += temp*weight; unweightedResultSum += temp; } } else { for (int i = -radius; i <= radius; ++i) { float4 temp; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)*2.0); temp = tex2D(_MainTex, tsTap); float weight = temp.a * gaussWeights[abs(i)]; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; if (_HighlightSuppression) { temp.rgb = highlightCompression(temp.rgb); } unweightedResultSum += temp; resultSum += temp*weight; } } if (weightSum > 0.01) { float invWeightSum = (1.0/weightSum); // Adding the sqrt seems to decrease temporal flickering at the expense // of having larger "halos" of fallback on rough surfaces // Subject to change with testing. Sqrt around only half the expression is *intentional*. float confidence = min(resultSum.a * sqrt(max(invWeightSum, 2.0)), 1.0); float3 finalColor = resultSum.rgb * invWeightSum; if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, confidence); } else { float3 finalColor = unweightedResultSum.rgb / (2 * radius + 1); if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, 0.0); } } sampler2D _ReflectionTexture0; sampler2D _ReflectionTexture1; sampler2D _ReflectionTexture2; sampler2D _ReflectionTexture3; sampler2D _ReflectionTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getReflectionValue(float2 tsP, int mip) { float4 coord = float4(tsP,0,0); if (mip == 0) { return tex2Dlod(_ReflectionTexture0, coord); } else if (mip == 1) { return tex2Dlod(_ReflectionTexture1, coord); } else if (mip == 2) { return tex2Dlod(_ReflectionTexture2, coord); } else if (mip == 3) { return tex2Dlod(_ReflectionTexture3, coord); } else { return tex2Dlod(_ReflectionTexture4, coord); } } sampler2D _EdgeTexture0; sampler2D _EdgeTexture1; sampler2D _EdgeTexture2; sampler2D _EdgeTexture3; sampler2D _EdgeTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getEdgeValue(float2 tsP, int mip) { float4 coord = float4(tsP + float2(1.0/(2 * mipToSize(mip))),0,0); if (mip == 0) { return tex2Dlod(_EdgeTexture0, coord); } else if (mip == 1) { return tex2Dlod(_EdgeTexture1, coord); } else if (mip == 2) { return tex2Dlod(_EdgeTexture2, coord); } else if (mip == 3) { return tex2Dlod(_EdgeTexture3, coord); } else { return tex2Dlod(_EdgeTexture4, coord); } } float2 centerPixel(float2 inputP) { return floor(inputP - float2(0.5,0.5)) + float2(0.5,0.5); } float2 snapToTexelCenter(float2 inputP, float2 texSize, float2 texSizeInv) { return centerPixel(inputP * texSize) * texSizeInv; } float4 bilateralUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; float2 fullTexSize = _ReflectionBufferSize; float2 fullTexSizeInv = 1.0 / fullTexSize; float4 hiP0 = float4(snapToTexelCenter(p0, fullTexSize, fullTexSizeInv), 0,0); float4 hiP3 = float4(snapToTexelCenter(p3, fullTexSize, fullTexSizeInv), 0,0); float4 hiP1 = float4(snapToTexelCenter(p1, fullTexSize, fullTexSizeInv), 0,0); float4 hiP2 = float4(snapToTexelCenter(p2, fullTexSize, fullTexSizeInv), 0,0); float4 tempCenter = tex2Dlod(_NormalAndRoughnessTexture, float4(tsP, 0, 0)); float3 n = tempCenter.xyz * 2 - 1; float4 temp0 = tex2Dlod(_NormalAndRoughnessTexture, hiP0); float4 temp1 = tex2Dlod(_NormalAndRoughnessTexture, hiP1); float4 temp2 = tex2Dlod(_NormalAndRoughnessTexture, hiP2); float4 temp3 = tex2Dlod(_NormalAndRoughnessTexture, hiP3); float3 n0 = temp0.xyz * 2 - 1; float3 n1 = temp1.xyz * 2 - 1; float3 n2 = temp2.xyz * 2 - 1; float3 n3 = temp3.xyz * 2 - 1; a0 *= normalWeight(n, n0); a1 *= normalWeight(n, n1); a2 *= normalWeight(n, n2); a3 *= normalWeight(n, n3); float r = tempCenter.a; float r0 = temp0.a; float r1 = temp1.a; float r2 = temp2.a; float r3 = temp3.a; a0 *= roughnessWeight(r, r0); a1 *= roughnessWeight(r, r1); a2 *= roughnessWeight(r, r2); a3 *= roughnessWeight(r, r3); // Slightly offset from zero a0 = max(a0, 0.001); a1 = max(a1, 0.001); a2 = max(a2, 0.001); a3 = max(a3, 0.001); // Nearest neighbor // a0 = a1 = a2 = a3 = 1.0; // Normalize the blending weights (weights were chosen so that // the denominator can never be zero) float norm = 1.0 / (a0 + a1 + a2 + a3); // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3) * norm; //return V0; return value; } /** Explicit bilinear fetches; must be used if the reflection buffer is bound using point sampling */ float4 bilinearUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3); return value; } // Unity's roughness is GGX roughness squared float roughnessToBlinnPhongExponent(float roughness) { float r2 = roughness*roughness; return 2.0f / r2*r2 - 2.0f; } float glossyLobeSlope(float roughness) { return pow(roughness, 4.0/3.0); } // Empirically based on our filter: // Mip | Pixels // -------------- // 0 | 1 no filter, so single pixel // 1 | 17 2r + 1 filter applied once, grabbing from pixels r away in either direction (r=8, four samples times stride of 2) // 2 | 50 2r + 1 filter applied on double size pixels, and each of those pixels had reached another r out to the side 2(2r + 1) + m_1 // 3 | 118 4(2r + 1) + m_2 // 4 | 254 8(2r + 1) + m_3 // // Approximated by pixels = 16*2^mip-15 // rearranging we get mip = log_2((pixels + 15) / 16) // float filterFootprintInPixelsToMip(float footprint) { return log2((footprint + 15) / 16); } float3 ansiGradient(float t) { //return float3(t, t, t); return fmod(floor(t * float3(8.0, 4.0, 2.0)), 2.0); } float4 fragCompositeSSR(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP * _SourceToTempUV.zw).a; float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).z; } // Get the camera space position of the reflection hit float3 csPosition = GetPosition(tsP); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 c_mi = csMirrorVector(csPosition, csN); float3 csHitpoint = c_mi * rayDistance + csPosition; float gatherFootprintInMeters = glossyLobeSlope(roughness) * rayDistance; // We could add a term that incorporates the normal // This approximation assumes reflections happen at a glancing angle float filterFootprintInPixels = gatherFootprintInMeters * _PixelsPerMeterAtOneMeter / csHitpoint.z; if (_HalfResolution == 1) { filterFootprintInPixels *= 0.5; } float mip = filterFootprintInPixelsToMip(filterFootprintInPixels); float nonPhysicalMip = pow(roughness, 3.0 / 4.0) * UNITY_SPECCUBE_LOD_STEPS; if (_HalfResolution == 1) { nonPhysicalMip = nonPhysicalMip * 0.7; } mip = lerp(nonPhysicalMip, mip, _DistanceBlur); mip = max(0, min(4, mip + _MipBias)); if (_DebugMode == 14) { return float4(ansiGradient(mip*0.25), 1.0); } float4 result; { int mipMin = int(mip); int mipMax = min(mipMin + 1, 4); float mipLerp = mip-mipMin; if (_BilateralUpsampling == 1) { if (_UseEdgeDetector == 1) { float nonEdginess = lerp(getEdgeValue(tsP, mipMin), getEdgeValue(tsP, mipMax), mipLerp); if (nonEdginess < 0.9) { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); if (_VisualizeWhereBilateral == 1) { result = float4(1, 0, 0, 0); } } else { result = lerp(bilinearUpsampleReflection(tsP, mipMin), bilinearUpsampleReflection(tsP, mipMax), mipLerp); } } else { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); } } else { float4 minResult = getReflectionValue(tsP, mipMin); float4 maxResult = getReflectionValue(tsP, mipMax); result = lerp(minResult, maxResult, mipLerp); result.a = min(minResult.a, maxResult.a); } } result.a = min(result.a, 1.0); float vignette = applyEdgeFade(tsP, _ScreenEdgeFading); result.a *= vignette; float alphaModifier = 1.0 - clamp((roughness - (_MaxRoughness - _RoughnessFalloffRange)) / _RoughnessFalloffRange, 0.0, 1.0); result.a *= alphaModifier; return result; } float4 fragBlit(v2f i) : SV_Target { // Pixel being shaded float2 ssP = i.uv2.xy; return tex2D(_MainTex, ssP); } float edgeDetector(float2 tsP, float2 axis, float2 texelSize) { float4 temp = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = temp.a; float3 midpointNormal = temp.rgb * 2 - 1; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (axis * _MainTex_TexelSize.xy); temp = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = temp.a; tapNormal = temp.rgb * 2 - 1; float weight = 1.0; weight *= roughnessWeight(midpointRoughness, tapRoughness); weight *= normalWeight(midpointNormal, tapNormal); return weight; } float4 fragEdge(v2f i) : SV_Target { float2 tsP = i.uv2.xy; return edgeDetector(tsP, float2(1,0), _MainTex_TexelSize.xy) * edgeDetector(tsP, float2(0,1), _MainTex_TexelSize.xy); } int _LastMip; float4 fragMin(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float2 lastTexSize = mipToSize(_LastMip); float2 lastTexSizeInv = 1.0 / lastTexSize;//_SourceToTempUV.xy * exp2(mip); float2 p00 = snapToTexelCenter(tsP, lastTexSize, lastTexSizeInv); float2 p11 = p00 + lastTexSizeInv; return min( min(tex2D(_MainTex, p00), tex2D(_MainTex, p11)), min(tex2D(_MainTex, float2(p00.x, p11.y)), tex2D(_MainTex, float2(p11.x, p00.y))) ); } float4 fragResolveHitPoints(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float4 temp = tex2D(_HitPointTexture, tsP); float2 hitPoint = temp.xy; float confidence = temp.w; float3 colorResult = confidence > 0.0 ? tex2D(_MainTex, hitPoint).rgb : tex2D(_CameraReflectionsTexture, tsP).rgb; #ifdef UNITY_COMPILER_HLSL if (any(isnan(colorResult))) colorResult = float3(0.0, 0.0, 0.0); // As of 11/29/2015, on Unity 5.3 on a Windows 8.1 computer with a NVIDIA GeForce 980, // with driver 347.62, the above check does not actually work to get rid of NaNs! // So we add this "redundant" check. if (!all(isfinite(colorResult))) colorResult = float3(0.0, 0.0, 0.0); #endif return float4(colorResult, confidence); } float4 fragBilatKeyPack(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float3 csN = tex2D(_CameraGBufferTexture2, tsP).xyz; float roughness = tex2D(_CameraGBufferTexture1, tsP).a; return float4(csN, roughness); } float4 fragDepthToCSZ(v2f i) : SV_Target { float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv2.xy); return float4(-LinearEyeDepth(depth), 0.0, 0.0, 0.0); } float3 csPositionToOldNormalizedScreenCoordinate(float3 csPosition) { // We could multiply these matrices together on the CPU float4 oldCSPosition = mul(_CurrentCameraToPreviousCamera, float4(csPosition, 1.0)); float4 oldSSCoordinate = mul(_ProjectToPixelMatrix, oldCSPosition); return float3((oldSSCoordinate.xy / oldSSCoordinate.w) * _InvScreenSize, oldCSPosition.z); } float4 fragTemporalFilter(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, tsP); float3 csPosition = GetPosition(tsP); float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).b; } // Virtual is the physics sense, not the other 20+ interpretations in CG float3 virtualReflectionCSPosition = normalize(csPosition) * rayDistance + csPosition; float3 oldTSP = csPositionToOldNormalizedScreenCoordinate(csPosition); float3 oldVirtualReflectionTSP = csPositionToOldNormalizedScreenCoordinate(virtualReflectionCSPosition); float oldCSZ = tex2D(_PreviousCSZBuffer, oldTSP.xy); float4 currentResult = tex2D(_FinalReflectionTexture, tsP); float4 oldResult = tex2D(_PreviousReflectionTexture, oldVirtualReflectionTSP.xy); float zError = abs(oldCSZ - oldTSP.z); // Start knocking down contribution at 5cm error, go to 0 at 5 more cm later float fallbackStart = 0.05; float fallbackEnd = 0.05; float zAlphaModifier = 1.0 - clamp((zError - fallbackStart) * (1.0 / fallbackEnd), 0.0, 1.0); float alpha = _TemporalAlpha; alpha *= zAlphaModifier; if (_UseTemporalConfidence) { alpha *= oldResult.w; } // Always compress currentResult.rgb = highlightCompression(currentResult.rgb); oldResult.rgb = highlightCompression(oldResult.rgb); float4 resultValue = lerp(currentResult, oldResult, alpha); resultValue.rgb = highlightDecompression(resultValue.rgb); return resultValue; } float4 fragAverageRayDistance(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float sumRayDistance = tex2D(_HitPointTexture, tsP).b; float sumWeight = 1.0; float roughness = 1.0 - tex2D(_CameraGBufferTexture1, tsP).a; float step = roughness * 3.0; /* for (float x = -step; x < step; x += 2 * step) { for (float y = -step; y < step; y += 2 * step) { float weight = 0.5; sumRayDistance += weight * tex2D(_HitPointTexture, (float2(x,y) * _InvScreenSize) + tsP).b; sumWeight += weight; } } */ return float4(sumRayDistance / sumWeight, 0.0, 0.0, 0.0); } static const int NUM_POISSON_TAPS = 12; // Same as used in CameraMotionBlur.shader static const float2 poissonSamples[NUM_POISSON_TAPS] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; float4 fragFilterSharpReflections(v2f i) : SV_Target { // Could improve perf by not computing blur when we won't be sampling the highest level anyways float2 tsP = i.uv2.xy; float4 sum = 0.0; float sampleRadius = _MainTex_TexelSize.xy * 2.0; for (int i = 0; i < NUM_POISSON_TAPS; i++) { float2 p = tsP + poissonSamples[i] * sampleRadius; float4 tap = tex2Dlod(_MainTex, float4(p, 0, 0)); if (_HighlightSuppression) { tap.rgb = highlightCompression(tap.rgb); } sum += tap; } float4 result = sum / float(NUM_POISSON_TAPS); if (_HighlightSuppression) { result.rgb = highlightDecompression(result.rgb); } return result; } #line 993 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif /* UNITY: Original start of shader */ #pragma exclude_renderers gles xbox360 ps3 #pragma vertex vert #pragma fragment fragRaytrace2 #pragma target 3.0 float4 fragRaytrace2(v2f i) : SV_Target { return fragRaytrace(i, 2); } SAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/ShadersÓ˙˙˙ăküń'¨ô™!]{Ňô SHADOWS_SOFTDIRLIGHTMAP_COMBINEDDIRLIGHTMAP_SEPARATEDYNAMICLIGHTMAP_ON INSTANCING_ON„‡#line 1004 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #line 12 #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "ScreenSpaceRaytrace.cginc" float4 _ProjInfo; float4x4 _WorldToCameraMatrix; float4x4 _CameraToWorldMatrix; float4x4 _ProjectToPixelMatrix; float2 _ScreenSize; float2 _ReflectionBufferSize; float2 _InvScreenSize; float3 _CameraClipInfo; sampler2D _CameraGBufferTexture0; sampler2D _CameraGBufferTexture1; sampler2D _CameraGBufferTexture2; sampler2D _CameraGBufferTexture3; sampler2D _CameraReflectionsTexture; float _CurrentMipLevel; float _RayStepSize; float _MaxRayTraceDistance; float _LayerThickness; float _FresnelFade; float _FresnelFadePower; sampler2D _MainTex; int _VisualizeWhereBilateral; int _EnableSSR; int _DebugMode; int _HalfResolution; int _TreatBackfaceHitAsMiss; int _AllowBackwardsRays; int _TraceEverywhere; // RG: SS Hitpoint of ray // B: distance ray travelled, used for mip-selection in the final resolve // A: confidence value sampler2D _HitPointTexture; sampler2D _FinalReflectionTexture; // RGB: camera-space normal (encoded in [0-1]) // A: Roughness sampler2D _NormalAndRoughnessTexture; float4 _MainTex_TexelSize; float4 _SourceToTempUV; int _EnableRefine; int _AdditiveReflection; int _ImproveCorners; float _ScreenEdgeFading; float _MipBias; int _UseOcclusion; int _MaxSteps; int _FullResolutionFiltering; int _BilateralUpsampling; float _MaxRoughness; float _RoughnessFalloffRange; float _SSRMultiplier; float _FadeDistance; float _DistanceBlur; int _TraceBehindObjects; int _FallbackToSky; int _UseEdgeDetector; int _HighlightSuppression; /** The height in pixels of a 1m object if viewed from 1m away. */ float _PixelsPerMeterAtOneMeter; int _UseAverageRayDistance; sampler2D _AverageRayDistanceBuffer; // For temporal filtering: float4x4 _CurrentCameraToPreviousCamera; sampler2D _PreviousReflectionTexture; sampler2D _PreviousCSZBuffer; float _TemporalAlpha; int _UseTemporalConfidence; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy; o.uv2 = v.texcoord.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv2.y = 1-o.uv2.y; #endif return o; } float2 mipToSize(int mip) { return floor(_ReflectionBufferSize * exp2(-mip)); } float3 ReconstructCSPosition(float2 S, float z) { float linEyeZ = -LinearEyeDepth(z); return float3(( (( S.xy * _MainTex_TexelSize.zw) ) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); } /** Read the camera-space position of the point at screen-space pixel ssP */ float3 GetPosition(float2 ssP) { float3 P; P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy); // Offset to pixel center P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); return P; } float square(float x) { return x*x; } float applyEdgeFade(float2 tsP, float fadeStrength) { float maxFade = 0.1; float2 itsP = float2(1.0, 1.0) - tsP; float dist = min(min(itsP.x, itsP.y), min(tsP.x, tsP.x)); float fade = dist / (maxFade*fadeStrength + 0.001); fade = max(min(fade, 1.0), 0.0); fade = pow(fade, 0.2); return fade; } float3 csMirrorVector(float3 csPosition, float3 csN) { float3 csE = -normalize(csPosition.xyz); float cos_o = dot(csN, csE); float3 c_mi = normalize((csN * (2.0 * cos_o)) - csE); return c_mi; } float4 fragRaytrace(v2f i, int stepRate) { float2 ssP = i.uv2.xy * _SourceToTempUV.zw; float3 csPosition = GetPosition(ssP); float smoothness = tex2D(_CameraGBufferTexture1, ssP).a; if (csPosition.z < -100.0 || smoothness == 0.0) { return float4(0.0,0.0,0.0,0.0); } if ((!_TraceEverywhere) && (smoothness < (1.0 - _MaxRoughness)) ) { return float4(0.0, 0.0, 0.0, 0.0); } float3 wsNormal = tex2D(_CameraGBufferTexture2, ssP).rgb * 2.0 - 1.0; int2 ssC = int2(ssP * _ScreenSize); float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 csRayDirection = csMirrorVector(csPosition, csN); if (_AllowBackwardsRays == 0 && csRayDirection.z > 0.0) { return float4(0.0, 0.0, 0.0, 0.0); } float maxRayTraceDistance = _MaxRayTraceDistance; float jitterFraction = 0.0f; float layerThickness = _LayerThickness; int maxSteps = _MaxSteps; // Bump the ray more in world space as it gets farther away (and so each pixel covers more WS distance) float rayBump = max(-0.01*csPosition.z, 0.001); float2 hitPixel; float3 csHitPoint; float stepCount; bool wasHit = castDenseScreenSpaceRay (csPosition + (csN) * rayBump, csRayDirection, _ProjectToPixelMatrix, _ScreenSize, _CameraClipInfo, jitterFraction, maxSteps, layerThickness, maxRayTraceDistance, hitPixel, stepRate, _EnableRefine == 1, _TraceBehindObjects == 1, csHitPoint, stepCount); float2 tsPResult = hitPixel / _ScreenSize; float rayDist = dot(csHitPoint - csPosition, csRayDirection); float confidence = 0.0; if (wasHit) { confidence = square(1.0 - max(2.0*float(stepCount) / float(maxSteps) - 1.0, 0.0)); confidence *= clamp(((_MaxRayTraceDistance - rayDist) / _FadeDistance), 0.0, 1.0); // Fake fresnel fade float3 csE = -normalize(csPosition.xyz); confidence *= max(0.0, lerp(pow(abs(dot(csRayDirection, -csE)), _FresnelFadePower), 1, 1.0 - _FresnelFade)); if (_TreatBackfaceHitAsMiss > 0) { float3 wsHitNormal = tex2Dlod(_CameraGBufferTexture2, float4(tsPResult, 0, 0)).rgb * 2.0 - 1.0; float3 wsRayDirection = mul(_CameraToWorldMatrix, float4(csRayDirection, 0)).xyz; if (dot(wsHitNormal, wsRayDirection) > 0) { confidence = 0.0; } } } else if (_FallbackToSky > 0) { float sceneZ = tex2Dlod(_CameraDepthTexture, float4(tsPResult, 0, 0)).r; sceneZ = -LinearEyeDepth(sceneZ); if (sceneZ < -1000) { // 1km ~= infinity confidence = 1.0; } } // Fade out reflections that hit near edge of screen, to prevent abrupt appearance/disappearance when object go off screen // Fade out reflections that hit near edge of screen, // to prevent abrupt appearance/disappearance when object go off screen float vignette = applyEdgeFade(tsPResult, _ScreenEdgeFading); confidence *= vignette; confidence *= vignette; return float4(tsPResult, rayDist, confidence); } float4 fragComposite(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; // View space point being shaded float3 C = GetPosition(tsP); // Final image before this pass float4 gbuffer3 = tex2D(_MainTex, i.uv); float4 specEmission = float4(0.0,0.0,0.0,0.0); float3 specColor = tex2D(_CameraGBufferTexture1, tsP).rgb; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP).a; float4 reflectionTexel = tex2D(_FinalReflectionTexture, tsP * _SourceToTempUV.xy); float4 gbuffer0 = tex2D(_CameraGBufferTexture0, tsP); // Let core Unity functions do the dirty work of applying the BRDF float3 baseColor = gbuffer0.rgb; float occlusion = gbuffer0.a; float oneMinusReflectivity; baseColor = EnergyConservationBetweenDiffuseAndSpecular(baseColor, specColor, oneMinusReflectivity); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csEyeVec = normalize(C); float3 eyeVec = mul(_CameraToWorldMatrix, float4(csEyeVec, 0)).xyz; float3 worldPos = mul(_CameraToWorldMatrix, float4(C, 1)).xyz; float cos_o = dot(wsNormal, eyeVec); float3 w_mi = -normalize((wsNormal * (2.0 * cos_o)) - eyeVec); float3 incomingRadiance = reflectionTexel.rgb; UnityLight light; light.color = 0; light.dir = 0; light.ndotl = 0; UnityIndirect ind; ind.diffuse = 0; ind.specular = incomingRadiance; float3 ssrResult = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, 1-roughness, wsNormal, -eyeVec, light, ind).rgb * _SSRMultiplier; float confidence = reflectionTexel.a; if(_EnableSSR == 0) { confidence = 0.0; } specEmission.rgb = tex2D(_CameraReflectionsTexture, tsP).rgb; float3 finalGlossyTerm; // Subtract out Unity's glossy result: (we're just applying the delta) if (_AdditiveReflection == 0) { gbuffer3 -= specEmission; // We may have blown out our dynamic range by adding then subtracting the reflection probes. // As a half-measure to fix this, simply clamp to zero gbuffer3 = max(gbuffer3, 0); finalGlossyTerm = lerp(specEmission.rgb, ssrResult, saturate(confidence)); } else { finalGlossyTerm = ssrResult*saturate(confidence); } if (_UseOcclusion) { finalGlossyTerm *= occlusion; } if (_DebugMode == 1) return float4(incomingRadiance,0); if (_DebugMode == 2) return float4(ssrResult,0); if (_DebugMode == 3) return float4(finalGlossyTerm,0); if (_DebugMode == 4) return confidence; if (_DebugMode == 5) return roughness; if (_DebugMode == 6) return float4(baseColor,0); if (_DebugMode == 7) return float4(specColor,0); if (_DebugMode == 8) return 1.0-oneMinusReflectivity; if (_DebugMode == 9) return float4(specEmission.rgb,0); if (_DebugMode == 10) return float4(specEmission.rgb-ssrResult,0); if (_DebugMode == 11) return float4(ssrResult-specEmission.rgb,0); if (_DebugMode == 12) return gbuffer3; if (_DebugMode == 13) return -float4(gbuffer3.rgb, 0.0); if (_DebugMode == 14) // Mip level, stored directly in ssrResult return float4(incomingRadiance, 0); // Additively blend the glossy GI result with the output buffer return gbuffer3 + float4(finalGlossyTerm, 0); } float roughnessWeight(float midpointRoughness, float tapRoughness) { return (1.0 - sqrt(sqrt(abs(midpointRoughness-tapRoughness)))); } float normalWeight(float3 midpointNormal, float3 tapNormal) { return clamp(dot(midpointNormal, tapNormal), 0, 1); } float highlightDecompression(float x) { return x / (1.0 - x); } float3 highlightDecompression(float3 x) { return float3( highlightDecompression(x.x), highlightDecompression(x.y), highlightDecompression(x.z)); } float highlightCompression(float x) { return x / (1.0 + x); } float3 highlightCompression(float3 x) { return float3( highlightCompression(x.x), highlightCompression(x.y), highlightCompression(x.z)); } float4 _Axis; float4 fragGBlur(v2f i) : SV_Target { int radius = 4; // Pixel being shaded float2 tsP = i.uv2.xy; float weightSum = 0.0; float gaussWeights[5] = {0.225, 0.150, 0.110, 0.075, 0.0525};//{0.225, 0.150, 0.110, 0.075, 0.0525}; float4 resultSum = float4(0.0, 0.0, 0.0, 0.0); float4 unweightedResultSum = float4(0.0, 0.0, 0.0, 0.0); float4 nAndRough = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = nAndRough.a; float3 midpointNormal = nAndRough.rgb * 2 - 1; if (_FullResolutionFiltering) { for (int i = -2*radius; i <= 2*radius; ++i) { float4 temp; float tapRoughness = midpointRoughness; float3 tapNormal = midpointNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)); temp = tex2D(_MainTex, tsTap); int gaussWeightIndex; #if defined(UNITY_COMPILER_HLSL) && !defined(SHADER_API_D3D9) gaussWeightIndex = abs(i) >> 1; // We purposefully do integer division, but that does not compile on hlsl2glsl #else gaussWeightIndex = frac(abs(i) / 2); #endif float weight = temp.a * gaussWeights[gaussWeightIndex]*0.5; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; resultSum += temp*weight; unweightedResultSum += temp; } } else { for (int i = -radius; i <= radius; ++i) { float4 temp; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)*2.0); temp = tex2D(_MainTex, tsTap); float weight = temp.a * gaussWeights[abs(i)]; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; if (_HighlightSuppression) { temp.rgb = highlightCompression(temp.rgb); } unweightedResultSum += temp; resultSum += temp*weight; } } if (weightSum > 0.01) { float invWeightSum = (1.0/weightSum); // Adding the sqrt seems to decrease temporal flickering at the expense // of having larger "halos" of fallback on rough surfaces // Subject to change with testing. Sqrt around only half the expression is *intentional*. float confidence = min(resultSum.a * sqrt(max(invWeightSum, 2.0)), 1.0); float3 finalColor = resultSum.rgb * invWeightSum; if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, confidence); } else { float3 finalColor = unweightedResultSum.rgb / (2 * radius + 1); if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, 0.0); } } sampler2D _ReflectionTexture0; sampler2D _ReflectionTexture1; sampler2D _ReflectionTexture2; sampler2D _ReflectionTexture3; sampler2D _ReflectionTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getReflectionValue(float2 tsP, int mip) { float4 coord = float4(tsP,0,0); if (mip == 0) { return tex2Dlod(_ReflectionTexture0, coord); } else if (mip == 1) { return tex2Dlod(_ReflectionTexture1, coord); } else if (mip == 2) { return tex2Dlod(_ReflectionTexture2, coord); } else if (mip == 3) { return tex2Dlod(_ReflectionTexture3, coord); } else { return tex2Dlod(_ReflectionTexture4, coord); } } sampler2D _EdgeTexture0; sampler2D _EdgeTexture1; sampler2D _EdgeTexture2; sampler2D _EdgeTexture3; sampler2D _EdgeTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getEdgeValue(float2 tsP, int mip) { float4 coord = float4(tsP + float2(1.0/(2 * mipToSize(mip))),0,0); if (mip == 0) { return tex2Dlod(_EdgeTexture0, coord); } else if (mip == 1) { return tex2Dlod(_EdgeTexture1, coord); } else if (mip == 2) { return tex2Dlod(_EdgeTexture2, coord); } else if (mip == 3) { return tex2Dlod(_EdgeTexture3, coord); } else { return tex2Dlod(_EdgeTexture4, coord); } } float2 centerPixel(float2 inputP) { return floor(inputP - float2(0.5,0.5)) + float2(0.5,0.5); } float2 snapToTexelCenter(float2 inputP, float2 texSize, float2 texSizeInv) { return centerPixel(inputP * texSize) * texSizeInv; } float4 bilateralUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; float2 fullTexSize = _ReflectionBufferSize; float2 fullTexSizeInv = 1.0 / fullTexSize; float4 hiP0 = float4(snapToTexelCenter(p0, fullTexSize, fullTexSizeInv), 0,0); float4 hiP3 = float4(snapToTexelCenter(p3, fullTexSize, fullTexSizeInv), 0,0); float4 hiP1 = float4(snapToTexelCenter(p1, fullTexSize, fullTexSizeInv), 0,0); float4 hiP2 = float4(snapToTexelCenter(p2, fullTexSize, fullTexSizeInv), 0,0); float4 tempCenter = tex2Dlod(_NormalAndRoughnessTexture, float4(tsP, 0, 0)); float3 n = tempCenter.xyz * 2 - 1; float4 temp0 = tex2Dlod(_NormalAndRoughnessTexture, hiP0); float4 temp1 = tex2Dlod(_NormalAndRoughnessTexture, hiP1); float4 temp2 = tex2Dlod(_NormalAndRoughnessTexture, hiP2); float4 temp3 = tex2Dlod(_NormalAndRoughnessTexture, hiP3); float3 n0 = temp0.xyz * 2 - 1; float3 n1 = temp1.xyz * 2 - 1; float3 n2 = temp2.xyz * 2 - 1; float3 n3 = temp3.xyz * 2 - 1; a0 *= normalWeight(n, n0); a1 *= normalWeight(n, n1); a2 *= normalWeight(n, n2); a3 *= normalWeight(n, n3); float r = tempCenter.a; float r0 = temp0.a; float r1 = temp1.a; float r2 = temp2.a; float r3 = temp3.a; a0 *= roughnessWeight(r, r0); a1 *= roughnessWeight(r, r1); a2 *= roughnessWeight(r, r2); a3 *= roughnessWeight(r, r3); // Slightly offset from zero a0 = max(a0, 0.001); a1 = max(a1, 0.001); a2 = max(a2, 0.001); a3 = max(a3, 0.001); // Nearest neighbor // a0 = a1 = a2 = a3 = 1.0; // Normalize the blending weights (weights were chosen so that // the denominator can never be zero) float norm = 1.0 / (a0 + a1 + a2 + a3); // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3) * norm; //return V0; return value; } /** Explicit bilinear fetches; must be used if the reflection buffer is bound using point sampling */ float4 bilinearUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3); return value; } // Unity's roughness is GGX roughness squared float roughnessToBlinnPhongExponent(float roughness) { float r2 = roughness*roughness; return 2.0f / r2*r2 - 2.0f; } float glossyLobeSlope(float roughness) { return pow(roughness, 4.0/3.0); } // Empirically based on our filter: // Mip | Pixels // -------------- // 0 | 1 no filter, so single pixel // 1 | 17 2r + 1 filter applied once, grabbing from pixels r away in either direction (r=8, four samples times stride of 2) // 2 | 50 2r + 1 filter applied on double size pixels, and each of those pixels had reached another r out to the side 2(2r + 1) + m_1 // 3 | 118 4(2r + 1) + m_2 // 4 | 254 8(2r + 1) + m_3 // // Approximated by pixels = 16*2^mip-15 // rearranging we get mip = log_2((pixels + 15) / 16) // float filterFootprintInPixelsToMip(float footprint) { return log2((footprint + 15) / 16); } float3 ansiGradient(float t) { //return float3(t, t, t); return fmod(floor(t * float3(8.0, 4.0, 2.0)), 2.0); } float4 fragCompositeSSR(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP * _SourceToTempUV.zw).a; float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).z; } // Get the camera space position of the reflection hit float3 csPosition = GetPosition(tsP); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 c_mi = csMirrorVector(csPosition, csN); float3 csHitpoint = c_mi * rayDistance + csPosition; float gatherFootprintInMeters = glossyLobeSlope(roughness) * rayDistance; // We could add a term that incorporates the normal // This approximation assumes reflections happen at a glancing angle float filterFootprintInPixels = gatherFootprintInMeters * _PixelsPerMeterAtOneMeter / csHitpoint.z; if (_HalfResolution == 1) { filterFootprintInPixels *= 0.5; } float mip = filterFootprintInPixelsToMip(filterFootprintInPixels); float nonPhysicalMip = pow(roughness, 3.0 / 4.0) * UNITY_SPECCUBE_LOD_STEPS; if (_HalfResolution == 1) { nonPhysicalMip = nonPhysicalMip * 0.7; } mip = lerp(nonPhysicalMip, mip, _DistanceBlur); mip = max(0, min(4, mip + _MipBias)); if (_DebugMode == 14) { return float4(ansiGradient(mip*0.25), 1.0); } float4 result; { int mipMin = int(mip); int mipMax = min(mipMin + 1, 4); float mipLerp = mip-mipMin; if (_BilateralUpsampling == 1) { if (_UseEdgeDetector == 1) { float nonEdginess = lerp(getEdgeValue(tsP, mipMin), getEdgeValue(tsP, mipMax), mipLerp); if (nonEdginess < 0.9) { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); if (_VisualizeWhereBilateral == 1) { result = float4(1, 0, 0, 0); } } else { result = lerp(bilinearUpsampleReflection(tsP, mipMin), bilinearUpsampleReflection(tsP, mipMax), mipLerp); } } else { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); } } else { float4 minResult = getReflectionValue(tsP, mipMin); float4 maxResult = getReflectionValue(tsP, mipMax); result = lerp(minResult, maxResult, mipLerp); result.a = min(minResult.a, maxResult.a); } } result.a = min(result.a, 1.0); float vignette = applyEdgeFade(tsP, _ScreenEdgeFading); result.a *= vignette; float alphaModifier = 1.0 - clamp((roughness - (_MaxRoughness - _RoughnessFalloffRange)) / _RoughnessFalloffRange, 0.0, 1.0); result.a *= alphaModifier; return result; } float4 fragBlit(v2f i) : SV_Target { // Pixel being shaded float2 ssP = i.uv2.xy; return tex2D(_MainTex, ssP); } float edgeDetector(float2 tsP, float2 axis, float2 texelSize) { float4 temp = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = temp.a; float3 midpointNormal = temp.rgb * 2 - 1; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (axis * _MainTex_TexelSize.xy); temp = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = temp.a; tapNormal = temp.rgb * 2 - 1; float weight = 1.0; weight *= roughnessWeight(midpointRoughness, tapRoughness); weight *= normalWeight(midpointNormal, tapNormal); return weight; } float4 fragEdge(v2f i) : SV_Target { float2 tsP = i.uv2.xy; return edgeDetector(tsP, float2(1,0), _MainTex_TexelSize.xy) * edgeDetector(tsP, float2(0,1), _MainTex_TexelSize.xy); } int _LastMip; float4 fragMin(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float2 lastTexSize = mipToSize(_LastMip); float2 lastTexSizeInv = 1.0 / lastTexSize;//_SourceToTempUV.xy * exp2(mip); float2 p00 = snapToTexelCenter(tsP, lastTexSize, lastTexSizeInv); float2 p11 = p00 + lastTexSizeInv; return min( min(tex2D(_MainTex, p00), tex2D(_MainTex, p11)), min(tex2D(_MainTex, float2(p00.x, p11.y)), tex2D(_MainTex, float2(p11.x, p00.y))) ); } float4 fragResolveHitPoints(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float4 temp = tex2D(_HitPointTexture, tsP); float2 hitPoint = temp.xy; float confidence = temp.w; float3 colorResult = confidence > 0.0 ? tex2D(_MainTex, hitPoint).rgb : tex2D(_CameraReflectionsTexture, tsP).rgb; #ifdef UNITY_COMPILER_HLSL if (any(isnan(colorResult))) colorResult = float3(0.0, 0.0, 0.0); // As of 11/29/2015, on Unity 5.3 on a Windows 8.1 computer with a NVIDIA GeForce 980, // with driver 347.62, the above check does not actually work to get rid of NaNs! // So we add this "redundant" check. if (!all(isfinite(colorResult))) colorResult = float3(0.0, 0.0, 0.0); #endif return float4(colorResult, confidence); } float4 fragBilatKeyPack(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float3 csN = tex2D(_CameraGBufferTexture2, tsP).xyz; float roughness = tex2D(_CameraGBufferTexture1, tsP).a; return float4(csN, roughness); } float4 fragDepthToCSZ(v2f i) : SV_Target { float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv2.xy); return float4(-LinearEyeDepth(depth), 0.0, 0.0, 0.0); } float3 csPositionToOldNormalizedScreenCoordinate(float3 csPosition) { // We could multiply these matrices together on the CPU float4 oldCSPosition = mul(_CurrentCameraToPreviousCamera, float4(csPosition, 1.0)); float4 oldSSCoordinate = mul(_ProjectToPixelMatrix, oldCSPosition); return float3((oldSSCoordinate.xy / oldSSCoordinate.w) * _InvScreenSize, oldCSPosition.z); } float4 fragTemporalFilter(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, tsP); float3 csPosition = GetPosition(tsP); float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).b; } // Virtual is the physics sense, not the other 20+ interpretations in CG float3 virtualReflectionCSPosition = normalize(csPosition) * rayDistance + csPosition; float3 oldTSP = csPositionToOldNormalizedScreenCoordinate(csPosition); float3 oldVirtualReflectionTSP = csPositionToOldNormalizedScreenCoordinate(virtualReflectionCSPosition); float oldCSZ = tex2D(_PreviousCSZBuffer, oldTSP.xy); float4 currentResult = tex2D(_FinalReflectionTexture, tsP); float4 oldResult = tex2D(_PreviousReflectionTexture, oldVirtualReflectionTSP.xy); float zError = abs(oldCSZ - oldTSP.z); // Start knocking down contribution at 5cm error, go to 0 at 5 more cm later float fallbackStart = 0.05; float fallbackEnd = 0.05; float zAlphaModifier = 1.0 - clamp((zError - fallbackStart) * (1.0 / fallbackEnd), 0.0, 1.0); float alpha = _TemporalAlpha; alpha *= zAlphaModifier; if (_UseTemporalConfidence) { alpha *= oldResult.w; } // Always compress currentResult.rgb = highlightCompression(currentResult.rgb); oldResult.rgb = highlightCompression(oldResult.rgb); float4 resultValue = lerp(currentResult, oldResult, alpha); resultValue.rgb = highlightDecompression(resultValue.rgb); return resultValue; } float4 fragAverageRayDistance(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float sumRayDistance = tex2D(_HitPointTexture, tsP).b; float sumWeight = 1.0; float roughness = 1.0 - tex2D(_CameraGBufferTexture1, tsP).a; float step = roughness * 3.0; /* for (float x = -step; x < step; x += 2 * step) { for (float y = -step; y < step; y += 2 * step) { float weight = 0.5; sumRayDistance += weight * tex2D(_HitPointTexture, (float2(x,y) * _InvScreenSize) + tsP).b; sumWeight += weight; } } */ return float4(sumRayDistance / sumWeight, 0.0, 0.0, 0.0); } static const int NUM_POISSON_TAPS = 12; // Same as used in CameraMotionBlur.shader static const float2 poissonSamples[NUM_POISSON_TAPS] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; float4 fragFilterSharpReflections(v2f i) : SV_Target { // Could improve perf by not computing blur when we won't be sampling the highest level anyways float2 tsP = i.uv2.xy; float4 sum = 0.0; float sampleRadius = _MainTex_TexelSize.xy * 2.0; for (int i = 0; i < NUM_POISSON_TAPS; i++) { float2 p = tsP + poissonSamples[i] * sampleRadius; float4 tap = tex2Dlod(_MainTex, float4(p, 0, 0)); if (_HighlightSuppression) { tap.rgb = highlightCompression(tap.rgb); } sum += tap; } float4 result = sum / float(NUM_POISSON_TAPS); if (_HighlightSuppression) { result.rgb = highlightDecompression(result.rgb); } return result; } #line 1004 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif /* UNITY: Original start of shader */ #pragma exclude_renderers gles xbox360 ps3 #pragma vertex vert #pragma fragment fragRaytrace4 #pragma target 3.0 float4 fragRaytrace4(v2f i) : SV_Target { return fragRaytrace(i, 4); } SAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/ShadersÓ˙˙˙îküń'¨ô™!]{Ňô SHADOWS_SOFTDIRLIGHTMAP_COMBINEDDIRLIGHTMAP_SEPARATEDYNAMICLIGHTMAP_ON INSTANCING_ON‘ɇ#line 1015 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #line 12 #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "ScreenSpaceRaytrace.cginc" float4 _ProjInfo; float4x4 _WorldToCameraMatrix; float4x4 _CameraToWorldMatrix; float4x4 _ProjectToPixelMatrix; float2 _ScreenSize; float2 _ReflectionBufferSize; float2 _InvScreenSize; float3 _CameraClipInfo; sampler2D _CameraGBufferTexture0; sampler2D _CameraGBufferTexture1; sampler2D _CameraGBufferTexture2; sampler2D _CameraGBufferTexture3; sampler2D _CameraReflectionsTexture; float _CurrentMipLevel; float _RayStepSize; float _MaxRayTraceDistance; float _LayerThickness; float _FresnelFade; float _FresnelFadePower; sampler2D _MainTex; int _VisualizeWhereBilateral; int _EnableSSR; int _DebugMode; int _HalfResolution; int _TreatBackfaceHitAsMiss; int _AllowBackwardsRays; int _TraceEverywhere; // RG: SS Hitpoint of ray // B: distance ray travelled, used for mip-selection in the final resolve // A: confidence value sampler2D _HitPointTexture; sampler2D _FinalReflectionTexture; // RGB: camera-space normal (encoded in [0-1]) // A: Roughness sampler2D _NormalAndRoughnessTexture; float4 _MainTex_TexelSize; float4 _SourceToTempUV; int _EnableRefine; int _AdditiveReflection; int _ImproveCorners; float _ScreenEdgeFading; float _MipBias; int _UseOcclusion; int _MaxSteps; int _FullResolutionFiltering; int _BilateralUpsampling; float _MaxRoughness; float _RoughnessFalloffRange; float _SSRMultiplier; float _FadeDistance; float _DistanceBlur; int _TraceBehindObjects; int _FallbackToSky; int _UseEdgeDetector; int _HighlightSuppression; /** The height in pixels of a 1m object if viewed from 1m away. */ float _PixelsPerMeterAtOneMeter; int _UseAverageRayDistance; sampler2D _AverageRayDistanceBuffer; // For temporal filtering: float4x4 _CurrentCameraToPreviousCamera; sampler2D _PreviousReflectionTexture; sampler2D _PreviousCSZBuffer; float _TemporalAlpha; int _UseTemporalConfidence; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy; o.uv2 = v.texcoord.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv2.y = 1-o.uv2.y; #endif return o; } float2 mipToSize(int mip) { return floor(_ReflectionBufferSize * exp2(-mip)); } float3 ReconstructCSPosition(float2 S, float z) { float linEyeZ = -LinearEyeDepth(z); return float3(( (( S.xy * _MainTex_TexelSize.zw) ) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); } /** Read the camera-space position of the point at screen-space pixel ssP */ float3 GetPosition(float2 ssP) { float3 P; P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy); // Offset to pixel center P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); return P; } float square(float x) { return x*x; } float applyEdgeFade(float2 tsP, float fadeStrength) { float maxFade = 0.1; float2 itsP = float2(1.0, 1.0) - tsP; float dist = min(min(itsP.x, itsP.y), min(tsP.x, tsP.x)); float fade = dist / (maxFade*fadeStrength + 0.001); fade = max(min(fade, 1.0), 0.0); fade = pow(fade, 0.2); return fade; } float3 csMirrorVector(float3 csPosition, float3 csN) { float3 csE = -normalize(csPosition.xyz); float cos_o = dot(csN, csE); float3 c_mi = normalize((csN * (2.0 * cos_o)) - csE); return c_mi; } float4 fragRaytrace(v2f i, int stepRate) { float2 ssP = i.uv2.xy * _SourceToTempUV.zw; float3 csPosition = GetPosition(ssP); float smoothness = tex2D(_CameraGBufferTexture1, ssP).a; if (csPosition.z < -100.0 || smoothness == 0.0) { return float4(0.0,0.0,0.0,0.0); } if ((!_TraceEverywhere) && (smoothness < (1.0 - _MaxRoughness)) ) { return float4(0.0, 0.0, 0.0, 0.0); } float3 wsNormal = tex2D(_CameraGBufferTexture2, ssP).rgb * 2.0 - 1.0; int2 ssC = int2(ssP * _ScreenSize); float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 csRayDirection = csMirrorVector(csPosition, csN); if (_AllowBackwardsRays == 0 && csRayDirection.z > 0.0) { return float4(0.0, 0.0, 0.0, 0.0); } float maxRayTraceDistance = _MaxRayTraceDistance; float jitterFraction = 0.0f; float layerThickness = _LayerThickness; int maxSteps = _MaxSteps; // Bump the ray more in world space as it gets farther away (and so each pixel covers more WS distance) float rayBump = max(-0.01*csPosition.z, 0.001); float2 hitPixel; float3 csHitPoint; float stepCount; bool wasHit = castDenseScreenSpaceRay (csPosition + (csN) * rayBump, csRayDirection, _ProjectToPixelMatrix, _ScreenSize, _CameraClipInfo, jitterFraction, maxSteps, layerThickness, maxRayTraceDistance, hitPixel, stepRate, _EnableRefine == 1, _TraceBehindObjects == 1, csHitPoint, stepCount); float2 tsPResult = hitPixel / _ScreenSize; float rayDist = dot(csHitPoint - csPosition, csRayDirection); float confidence = 0.0; if (wasHit) { confidence = square(1.0 - max(2.0*float(stepCount) / float(maxSteps) - 1.0, 0.0)); confidence *= clamp(((_MaxRayTraceDistance - rayDist) / _FadeDistance), 0.0, 1.0); // Fake fresnel fade float3 csE = -normalize(csPosition.xyz); confidence *= max(0.0, lerp(pow(abs(dot(csRayDirection, -csE)), _FresnelFadePower), 1, 1.0 - _FresnelFade)); if (_TreatBackfaceHitAsMiss > 0) { float3 wsHitNormal = tex2Dlod(_CameraGBufferTexture2, float4(tsPResult, 0, 0)).rgb * 2.0 - 1.0; float3 wsRayDirection = mul(_CameraToWorldMatrix, float4(csRayDirection, 0)).xyz; if (dot(wsHitNormal, wsRayDirection) > 0) { confidence = 0.0; } } } else if (_FallbackToSky > 0) { float sceneZ = tex2Dlod(_CameraDepthTexture, float4(tsPResult, 0, 0)).r; sceneZ = -LinearEyeDepth(sceneZ); if (sceneZ < -1000) { // 1km ~= infinity confidence = 1.0; } } // Fade out reflections that hit near edge of screen, to prevent abrupt appearance/disappearance when object go off screen // Fade out reflections that hit near edge of screen, // to prevent abrupt appearance/disappearance when object go off screen float vignette = applyEdgeFade(tsPResult, _ScreenEdgeFading); confidence *= vignette; confidence *= vignette; return float4(tsPResult, rayDist, confidence); } float4 fragComposite(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; // View space point being shaded float3 C = GetPosition(tsP); // Final image before this pass float4 gbuffer3 = tex2D(_MainTex, i.uv); float4 specEmission = float4(0.0,0.0,0.0,0.0); float3 specColor = tex2D(_CameraGBufferTexture1, tsP).rgb; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP).a; float4 reflectionTexel = tex2D(_FinalReflectionTexture, tsP * _SourceToTempUV.xy); float4 gbuffer0 = tex2D(_CameraGBufferTexture0, tsP); // Let core Unity functions do the dirty work of applying the BRDF float3 baseColor = gbuffer0.rgb; float occlusion = gbuffer0.a; float oneMinusReflectivity; baseColor = EnergyConservationBetweenDiffuseAndSpecular(baseColor, specColor, oneMinusReflectivity); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csEyeVec = normalize(C); float3 eyeVec = mul(_CameraToWorldMatrix, float4(csEyeVec, 0)).xyz; float3 worldPos = mul(_CameraToWorldMatrix, float4(C, 1)).xyz; float cos_o = dot(wsNormal, eyeVec); float3 w_mi = -normalize((wsNormal * (2.0 * cos_o)) - eyeVec); float3 incomingRadiance = reflectionTexel.rgb; UnityLight light; light.color = 0; light.dir = 0; light.ndotl = 0; UnityIndirect ind; ind.diffuse = 0; ind.specular = incomingRadiance; float3 ssrResult = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, 1-roughness, wsNormal, -eyeVec, light, ind).rgb * _SSRMultiplier; float confidence = reflectionTexel.a; if(_EnableSSR == 0) { confidence = 0.0; } specEmission.rgb = tex2D(_CameraReflectionsTexture, tsP).rgb; float3 finalGlossyTerm; // Subtract out Unity's glossy result: (we're just applying the delta) if (_AdditiveReflection == 0) { gbuffer3 -= specEmission; // We may have blown out our dynamic range by adding then subtracting the reflection probes. // As a half-measure to fix this, simply clamp to zero gbuffer3 = max(gbuffer3, 0); finalGlossyTerm = lerp(specEmission.rgb, ssrResult, saturate(confidence)); } else { finalGlossyTerm = ssrResult*saturate(confidence); } if (_UseOcclusion) { finalGlossyTerm *= occlusion; } if (_DebugMode == 1) return float4(incomingRadiance,0); if (_DebugMode == 2) return float4(ssrResult,0); if (_DebugMode == 3) return float4(finalGlossyTerm,0); if (_DebugMode == 4) return confidence; if (_DebugMode == 5) return roughness; if (_DebugMode == 6) return float4(baseColor,0); if (_DebugMode == 7) return float4(specColor,0); if (_DebugMode == 8) return 1.0-oneMinusReflectivity; if (_DebugMode == 9) return float4(specEmission.rgb,0); if (_DebugMode == 10) return float4(specEmission.rgb-ssrResult,0); if (_DebugMode == 11) return float4(ssrResult-specEmission.rgb,0); if (_DebugMode == 12) return gbuffer3; if (_DebugMode == 13) return -float4(gbuffer3.rgb, 0.0); if (_DebugMode == 14) // Mip level, stored directly in ssrResult return float4(incomingRadiance, 0); // Additively blend the glossy GI result with the output buffer return gbuffer3 + float4(finalGlossyTerm, 0); } float roughnessWeight(float midpointRoughness, float tapRoughness) { return (1.0 - sqrt(sqrt(abs(midpointRoughness-tapRoughness)))); } float normalWeight(float3 midpointNormal, float3 tapNormal) { return clamp(dot(midpointNormal, tapNormal), 0, 1); } float highlightDecompression(float x) { return x / (1.0 - x); } float3 highlightDecompression(float3 x) { return float3( highlightDecompression(x.x), highlightDecompression(x.y), highlightDecompression(x.z)); } float highlightCompression(float x) { return x / (1.0 + x); } float3 highlightCompression(float3 x) { return float3( highlightCompression(x.x), highlightCompression(x.y), highlightCompression(x.z)); } float4 _Axis; float4 fragGBlur(v2f i) : SV_Target { int radius = 4; // Pixel being shaded float2 tsP = i.uv2.xy; float weightSum = 0.0; float gaussWeights[5] = {0.225, 0.150, 0.110, 0.075, 0.0525};//{0.225, 0.150, 0.110, 0.075, 0.0525}; float4 resultSum = float4(0.0, 0.0, 0.0, 0.0); float4 unweightedResultSum = float4(0.0, 0.0, 0.0, 0.0); float4 nAndRough = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = nAndRough.a; float3 midpointNormal = nAndRough.rgb * 2 - 1; if (_FullResolutionFiltering) { for (int i = -2*radius; i <= 2*radius; ++i) { float4 temp; float tapRoughness = midpointRoughness; float3 tapNormal = midpointNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)); temp = tex2D(_MainTex, tsTap); int gaussWeightIndex; #if defined(UNITY_COMPILER_HLSL) && !defined(SHADER_API_D3D9) gaussWeightIndex = abs(i) >> 1; // We purposefully do integer division, but that does not compile on hlsl2glsl #else gaussWeightIndex = frac(abs(i) / 2); #endif float weight = temp.a * gaussWeights[gaussWeightIndex]*0.5; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; resultSum += temp*weight; unweightedResultSum += temp; } } else { for (int i = -radius; i <= radius; ++i) { float4 temp; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)*2.0); temp = tex2D(_MainTex, tsTap); float weight = temp.a * gaussWeights[abs(i)]; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; if (_HighlightSuppression) { temp.rgb = highlightCompression(temp.rgb); } unweightedResultSum += temp; resultSum += temp*weight; } } if (weightSum > 0.01) { float invWeightSum = (1.0/weightSum); // Adding the sqrt seems to decrease temporal flickering at the expense // of having larger "halos" of fallback on rough surfaces // Subject to change with testing. Sqrt around only half the expression is *intentional*. float confidence = min(resultSum.a * sqrt(max(invWeightSum, 2.0)), 1.0); float3 finalColor = resultSum.rgb * invWeightSum; if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, confidence); } else { float3 finalColor = unweightedResultSum.rgb / (2 * radius + 1); if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, 0.0); } } sampler2D _ReflectionTexture0; sampler2D _ReflectionTexture1; sampler2D _ReflectionTexture2; sampler2D _ReflectionTexture3; sampler2D _ReflectionTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getReflectionValue(float2 tsP, int mip) { float4 coord = float4(tsP,0,0); if (mip == 0) { return tex2Dlod(_ReflectionTexture0, coord); } else if (mip == 1) { return tex2Dlod(_ReflectionTexture1, coord); } else if (mip == 2) { return tex2Dlod(_ReflectionTexture2, coord); } else if (mip == 3) { return tex2Dlod(_ReflectionTexture3, coord); } else { return tex2Dlod(_ReflectionTexture4, coord); } } sampler2D _EdgeTexture0; sampler2D _EdgeTexture1; sampler2D _EdgeTexture2; sampler2D _EdgeTexture3; sampler2D _EdgeTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getEdgeValue(float2 tsP, int mip) { float4 coord = float4(tsP + float2(1.0/(2 * mipToSize(mip))),0,0); if (mip == 0) { return tex2Dlod(_EdgeTexture0, coord); } else if (mip == 1) { return tex2Dlod(_EdgeTexture1, coord); } else if (mip == 2) { return tex2Dlod(_EdgeTexture2, coord); } else if (mip == 3) { return tex2Dlod(_EdgeTexture3, coord); } else { return tex2Dlod(_EdgeTexture4, coord); } } float2 centerPixel(float2 inputP) { return floor(inputP - float2(0.5,0.5)) + float2(0.5,0.5); } float2 snapToTexelCenter(float2 inputP, float2 texSize, float2 texSizeInv) { return centerPixel(inputP * texSize) * texSizeInv; } float4 bilateralUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; float2 fullTexSize = _ReflectionBufferSize; float2 fullTexSizeInv = 1.0 / fullTexSize; float4 hiP0 = float4(snapToTexelCenter(p0, fullTexSize, fullTexSizeInv), 0,0); float4 hiP3 = float4(snapToTexelCenter(p3, fullTexSize, fullTexSizeInv), 0,0); float4 hiP1 = float4(snapToTexelCenter(p1, fullTexSize, fullTexSizeInv), 0,0); float4 hiP2 = float4(snapToTexelCenter(p2, fullTexSize, fullTexSizeInv), 0,0); float4 tempCenter = tex2Dlod(_NormalAndRoughnessTexture, float4(tsP, 0, 0)); float3 n = tempCenter.xyz * 2 - 1; float4 temp0 = tex2Dlod(_NormalAndRoughnessTexture, hiP0); float4 temp1 = tex2Dlod(_NormalAndRoughnessTexture, hiP1); float4 temp2 = tex2Dlod(_NormalAndRoughnessTexture, hiP2); float4 temp3 = tex2Dlod(_NormalAndRoughnessTexture, hiP3); float3 n0 = temp0.xyz * 2 - 1; float3 n1 = temp1.xyz * 2 - 1; float3 n2 = temp2.xyz * 2 - 1; float3 n3 = temp3.xyz * 2 - 1; a0 *= normalWeight(n, n0); a1 *= normalWeight(n, n1); a2 *= normalWeight(n, n2); a3 *= normalWeight(n, n3); float r = tempCenter.a; float r0 = temp0.a; float r1 = temp1.a; float r2 = temp2.a; float r3 = temp3.a; a0 *= roughnessWeight(r, r0); a1 *= roughnessWeight(r, r1); a2 *= roughnessWeight(r, r2); a3 *= roughnessWeight(r, r3); // Slightly offset from zero a0 = max(a0, 0.001); a1 = max(a1, 0.001); a2 = max(a2, 0.001); a3 = max(a3, 0.001); // Nearest neighbor // a0 = a1 = a2 = a3 = 1.0; // Normalize the blending weights (weights were chosen so that // the denominator can never be zero) float norm = 1.0 / (a0 + a1 + a2 + a3); // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3) * norm; //return V0; return value; } /** Explicit bilinear fetches; must be used if the reflection buffer is bound using point sampling */ float4 bilinearUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3); return value; } // Unity's roughness is GGX roughness squared float roughnessToBlinnPhongExponent(float roughness) { float r2 = roughness*roughness; return 2.0f / r2*r2 - 2.0f; } float glossyLobeSlope(float roughness) { return pow(roughness, 4.0/3.0); } // Empirically based on our filter: // Mip | Pixels // -------------- // 0 | 1 no filter, so single pixel // 1 | 17 2r + 1 filter applied once, grabbing from pixels r away in either direction (r=8, four samples times stride of 2) // 2 | 50 2r + 1 filter applied on double size pixels, and each of those pixels had reached another r out to the side 2(2r + 1) + m_1 // 3 | 118 4(2r + 1) + m_2 // 4 | 254 8(2r + 1) + m_3 // // Approximated by pixels = 16*2^mip-15 // rearranging we get mip = log_2((pixels + 15) / 16) // float filterFootprintInPixelsToMip(float footprint) { return log2((footprint + 15) / 16); } float3 ansiGradient(float t) { //return float3(t, t, t); return fmod(floor(t * float3(8.0, 4.0, 2.0)), 2.0); } float4 fragCompositeSSR(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP * _SourceToTempUV.zw).a; float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).z; } // Get the camera space position of the reflection hit float3 csPosition = GetPosition(tsP); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 c_mi = csMirrorVector(csPosition, csN); float3 csHitpoint = c_mi * rayDistance + csPosition; float gatherFootprintInMeters = glossyLobeSlope(roughness) * rayDistance; // We could add a term that incorporates the normal // This approximation assumes reflections happen at a glancing angle float filterFootprintInPixels = gatherFootprintInMeters * _PixelsPerMeterAtOneMeter / csHitpoint.z; if (_HalfResolution == 1) { filterFootprintInPixels *= 0.5; } float mip = filterFootprintInPixelsToMip(filterFootprintInPixels); float nonPhysicalMip = pow(roughness, 3.0 / 4.0) * UNITY_SPECCUBE_LOD_STEPS; if (_HalfResolution == 1) { nonPhysicalMip = nonPhysicalMip * 0.7; } mip = lerp(nonPhysicalMip, mip, _DistanceBlur); mip = max(0, min(4, mip + _MipBias)); if (_DebugMode == 14) { return float4(ansiGradient(mip*0.25), 1.0); } float4 result; { int mipMin = int(mip); int mipMax = min(mipMin + 1, 4); float mipLerp = mip-mipMin; if (_BilateralUpsampling == 1) { if (_UseEdgeDetector == 1) { float nonEdginess = lerp(getEdgeValue(tsP, mipMin), getEdgeValue(tsP, mipMax), mipLerp); if (nonEdginess < 0.9) { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); if (_VisualizeWhereBilateral == 1) { result = float4(1, 0, 0, 0); } } else { result = lerp(bilinearUpsampleReflection(tsP, mipMin), bilinearUpsampleReflection(tsP, mipMax), mipLerp); } } else { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); } } else { float4 minResult = getReflectionValue(tsP, mipMin); float4 maxResult = getReflectionValue(tsP, mipMax); result = lerp(minResult, maxResult, mipLerp); result.a = min(minResult.a, maxResult.a); } } result.a = min(result.a, 1.0); float vignette = applyEdgeFade(tsP, _ScreenEdgeFading); result.a *= vignette; float alphaModifier = 1.0 - clamp((roughness - (_MaxRoughness - _RoughnessFalloffRange)) / _RoughnessFalloffRange, 0.0, 1.0); result.a *= alphaModifier; return result; } float4 fragBlit(v2f i) : SV_Target { // Pixel being shaded float2 ssP = i.uv2.xy; return tex2D(_MainTex, ssP); } float edgeDetector(float2 tsP, float2 axis, float2 texelSize) { float4 temp = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = temp.a; float3 midpointNormal = temp.rgb * 2 - 1; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (axis * _MainTex_TexelSize.xy); temp = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = temp.a; tapNormal = temp.rgb * 2 - 1; float weight = 1.0; weight *= roughnessWeight(midpointRoughness, tapRoughness); weight *= normalWeight(midpointNormal, tapNormal); return weight; } float4 fragEdge(v2f i) : SV_Target { float2 tsP = i.uv2.xy; return edgeDetector(tsP, float2(1,0), _MainTex_TexelSize.xy) * edgeDetector(tsP, float2(0,1), _MainTex_TexelSize.xy); } int _LastMip; float4 fragMin(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float2 lastTexSize = mipToSize(_LastMip); float2 lastTexSizeInv = 1.0 / lastTexSize;//_SourceToTempUV.xy * exp2(mip); float2 p00 = snapToTexelCenter(tsP, lastTexSize, lastTexSizeInv); float2 p11 = p00 + lastTexSizeInv; return min( min(tex2D(_MainTex, p00), tex2D(_MainTex, p11)), min(tex2D(_MainTex, float2(p00.x, p11.y)), tex2D(_MainTex, float2(p11.x, p00.y))) ); } float4 fragResolveHitPoints(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float4 temp = tex2D(_HitPointTexture, tsP); float2 hitPoint = temp.xy; float confidence = temp.w; float3 colorResult = confidence > 0.0 ? tex2D(_MainTex, hitPoint).rgb : tex2D(_CameraReflectionsTexture, tsP).rgb; #ifdef UNITY_COMPILER_HLSL if (any(isnan(colorResult))) colorResult = float3(0.0, 0.0, 0.0); // As of 11/29/2015, on Unity 5.3 on a Windows 8.1 computer with a NVIDIA GeForce 980, // with driver 347.62, the above check does not actually work to get rid of NaNs! // So we add this "redundant" check. if (!all(isfinite(colorResult))) colorResult = float3(0.0, 0.0, 0.0); #endif return float4(colorResult, confidence); } float4 fragBilatKeyPack(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float3 csN = tex2D(_CameraGBufferTexture2, tsP).xyz; float roughness = tex2D(_CameraGBufferTexture1, tsP).a; return float4(csN, roughness); } float4 fragDepthToCSZ(v2f i) : SV_Target { float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv2.xy); return float4(-LinearEyeDepth(depth), 0.0, 0.0, 0.0); } float3 csPositionToOldNormalizedScreenCoordinate(float3 csPosition) { // We could multiply these matrices together on the CPU float4 oldCSPosition = mul(_CurrentCameraToPreviousCamera, float4(csPosition, 1.0)); float4 oldSSCoordinate = mul(_ProjectToPixelMatrix, oldCSPosition); return float3((oldSSCoordinate.xy / oldSSCoordinate.w) * _InvScreenSize, oldCSPosition.z); } float4 fragTemporalFilter(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, tsP); float3 csPosition = GetPosition(tsP); float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).b; } // Virtual is the physics sense, not the other 20+ interpretations in CG float3 virtualReflectionCSPosition = normalize(csPosition) * rayDistance + csPosition; float3 oldTSP = csPositionToOldNormalizedScreenCoordinate(csPosition); float3 oldVirtualReflectionTSP = csPositionToOldNormalizedScreenCoordinate(virtualReflectionCSPosition); float oldCSZ = tex2D(_PreviousCSZBuffer, oldTSP.xy); float4 currentResult = tex2D(_FinalReflectionTexture, tsP); float4 oldResult = tex2D(_PreviousReflectionTexture, oldVirtualReflectionTSP.xy); float zError = abs(oldCSZ - oldTSP.z); // Start knocking down contribution at 5cm error, go to 0 at 5 more cm later float fallbackStart = 0.05; float fallbackEnd = 0.05; float zAlphaModifier = 1.0 - clamp((zError - fallbackStart) * (1.0 / fallbackEnd), 0.0, 1.0); float alpha = _TemporalAlpha; alpha *= zAlphaModifier; if (_UseTemporalConfidence) { alpha *= oldResult.w; } // Always compress currentResult.rgb = highlightCompression(currentResult.rgb); oldResult.rgb = highlightCompression(oldResult.rgb); float4 resultValue = lerp(currentResult, oldResult, alpha); resultValue.rgb = highlightDecompression(resultValue.rgb); return resultValue; } float4 fragAverageRayDistance(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float sumRayDistance = tex2D(_HitPointTexture, tsP).b; float sumWeight = 1.0; float roughness = 1.0 - tex2D(_CameraGBufferTexture1, tsP).a; float step = roughness * 3.0; /* for (float x = -step; x < step; x += 2 * step) { for (float y = -step; y < step; y += 2 * step) { float weight = 0.5; sumRayDistance += weight * tex2D(_HitPointTexture, (float2(x,y) * _InvScreenSize) + tsP).b; sumWeight += weight; } } */ return float4(sumRayDistance / sumWeight, 0.0, 0.0, 0.0); } static const int NUM_POISSON_TAPS = 12; // Same as used in CameraMotionBlur.shader static const float2 poissonSamples[NUM_POISSON_TAPS] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; float4 fragFilterSharpReflections(v2f i) : SV_Target { // Could improve perf by not computing blur when we won't be sampling the highest level anyways float2 tsP = i.uv2.xy; float4 sum = 0.0; float sampleRadius = _MainTex_TexelSize.xy * 2.0; for (int i = 0; i < NUM_POISSON_TAPS; i++) { float2 p = tsP + poissonSamples[i] * sampleRadius; float4 tap = tex2Dlod(_MainTex, float4(p, 0, 0)); if (_HighlightSuppression) { tap.rgb = highlightCompression(tap.rgb); } sum += tap; } float4 result = sum / float(NUM_POISSON_TAPS); if (_HighlightSuppression) { result.rgb = highlightDecompression(result.rgb); } return result; } #line 1015 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif /* UNITY: Original start of shader */ #pragma exclude_renderers gles xbox360 ps3 #pragma vertex vert #pragma fragment fragRaytrace8 #pragma target 3.0 float4 fragRaytrace8(v2f i) : SV_Target { return fragRaytrace(i, 8); } SAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/ShadersÓ˙˙˙ůküń'¨ô™!]{Ňô SHADOWS_SOFTDIRLIGHTMAP_COMBINEDDIRLIGHTMAP_SEPARATEDYNAMICLIGHTMAP_ON INSTANCING_ON05 ‡#line 1026 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #line 12 #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "ScreenSpaceRaytrace.cginc" float4 _ProjInfo; float4x4 _WorldToCameraMatrix; float4x4 _CameraToWorldMatrix; float4x4 _ProjectToPixelMatrix; float2 _ScreenSize; float2 _ReflectionBufferSize; float2 _InvScreenSize; float3 _CameraClipInfo; sampler2D _CameraGBufferTexture0; sampler2D _CameraGBufferTexture1; sampler2D _CameraGBufferTexture2; sampler2D _CameraGBufferTexture3; sampler2D _CameraReflectionsTexture; float _CurrentMipLevel; float _RayStepSize; float _MaxRayTraceDistance; float _LayerThickness; float _FresnelFade; float _FresnelFadePower; sampler2D _MainTex; int _VisualizeWhereBilateral; int _EnableSSR; int _DebugMode; int _HalfResolution; int _TreatBackfaceHitAsMiss; int _AllowBackwardsRays; int _TraceEverywhere; // RG: SS Hitpoint of ray // B: distance ray travelled, used for mip-selection in the final resolve // A: confidence value sampler2D _HitPointTexture; sampler2D _FinalReflectionTexture; // RGB: camera-space normal (encoded in [0-1]) // A: Roughness sampler2D _NormalAndRoughnessTexture; float4 _MainTex_TexelSize; float4 _SourceToTempUV; int _EnableRefine; int _AdditiveReflection; int _ImproveCorners; float _ScreenEdgeFading; float _MipBias; int _UseOcclusion; int _MaxSteps; int _FullResolutionFiltering; int _BilateralUpsampling; float _MaxRoughness; float _RoughnessFalloffRange; float _SSRMultiplier; float _FadeDistance; float _DistanceBlur; int _TraceBehindObjects; int _FallbackToSky; int _UseEdgeDetector; int _HighlightSuppression; /** The height in pixels of a 1m object if viewed from 1m away. */ float _PixelsPerMeterAtOneMeter; int _UseAverageRayDistance; sampler2D _AverageRayDistanceBuffer; // For temporal filtering: float4x4 _CurrentCameraToPreviousCamera; sampler2D _PreviousReflectionTexture; sampler2D _PreviousCSZBuffer; float _TemporalAlpha; int _UseTemporalConfidence; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy; o.uv2 = v.texcoord.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv2.y = 1-o.uv2.y; #endif return o; } float2 mipToSize(int mip) { return floor(_ReflectionBufferSize * exp2(-mip)); } float3 ReconstructCSPosition(float2 S, float z) { float linEyeZ = -LinearEyeDepth(z); return float3(( (( S.xy * _MainTex_TexelSize.zw) ) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); } /** Read the camera-space position of the point at screen-space pixel ssP */ float3 GetPosition(float2 ssP) { float3 P; P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy); // Offset to pixel center P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); return P; } float square(float x) { return x*x; } float applyEdgeFade(float2 tsP, float fadeStrength) { float maxFade = 0.1; float2 itsP = float2(1.0, 1.0) - tsP; float dist = min(min(itsP.x, itsP.y), min(tsP.x, tsP.x)); float fade = dist / (maxFade*fadeStrength + 0.001); fade = max(min(fade, 1.0), 0.0); fade = pow(fade, 0.2); return fade; } float3 csMirrorVector(float3 csPosition, float3 csN) { float3 csE = -normalize(csPosition.xyz); float cos_o = dot(csN, csE); float3 c_mi = normalize((csN * (2.0 * cos_o)) - csE); return c_mi; } float4 fragRaytrace(v2f i, int stepRate) { float2 ssP = i.uv2.xy * _SourceToTempUV.zw; float3 csPosition = GetPosition(ssP); float smoothness = tex2D(_CameraGBufferTexture1, ssP).a; if (csPosition.z < -100.0 || smoothness == 0.0) { return float4(0.0,0.0,0.0,0.0); } if ((!_TraceEverywhere) && (smoothness < (1.0 - _MaxRoughness)) ) { return float4(0.0, 0.0, 0.0, 0.0); } float3 wsNormal = tex2D(_CameraGBufferTexture2, ssP).rgb * 2.0 - 1.0; int2 ssC = int2(ssP * _ScreenSize); float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 csRayDirection = csMirrorVector(csPosition, csN); if (_AllowBackwardsRays == 0 && csRayDirection.z > 0.0) { return float4(0.0, 0.0, 0.0, 0.0); } float maxRayTraceDistance = _MaxRayTraceDistance; float jitterFraction = 0.0f; float layerThickness = _LayerThickness; int maxSteps = _MaxSteps; // Bump the ray more in world space as it gets farther away (and so each pixel covers more WS distance) float rayBump = max(-0.01*csPosition.z, 0.001); float2 hitPixel; float3 csHitPoint; float stepCount; bool wasHit = castDenseScreenSpaceRay (csPosition + (csN) * rayBump, csRayDirection, _ProjectToPixelMatrix, _ScreenSize, _CameraClipInfo, jitterFraction, maxSteps, layerThickness, maxRayTraceDistance, hitPixel, stepRate, _EnableRefine == 1, _TraceBehindObjects == 1, csHitPoint, stepCount); float2 tsPResult = hitPixel / _ScreenSize; float rayDist = dot(csHitPoint - csPosition, csRayDirection); float confidence = 0.0; if (wasHit) { confidence = square(1.0 - max(2.0*float(stepCount) / float(maxSteps) - 1.0, 0.0)); confidence *= clamp(((_MaxRayTraceDistance - rayDist) / _FadeDistance), 0.0, 1.0); // Fake fresnel fade float3 csE = -normalize(csPosition.xyz); confidence *= max(0.0, lerp(pow(abs(dot(csRayDirection, -csE)), _FresnelFadePower), 1, 1.0 - _FresnelFade)); if (_TreatBackfaceHitAsMiss > 0) { float3 wsHitNormal = tex2Dlod(_CameraGBufferTexture2, float4(tsPResult, 0, 0)).rgb * 2.0 - 1.0; float3 wsRayDirection = mul(_CameraToWorldMatrix, float4(csRayDirection, 0)).xyz; if (dot(wsHitNormal, wsRayDirection) > 0) { confidence = 0.0; } } } else if (_FallbackToSky > 0) { float sceneZ = tex2Dlod(_CameraDepthTexture, float4(tsPResult, 0, 0)).r; sceneZ = -LinearEyeDepth(sceneZ); if (sceneZ < -1000) { // 1km ~= infinity confidence = 1.0; } } // Fade out reflections that hit near edge of screen, to prevent abrupt appearance/disappearance when object go off screen // Fade out reflections that hit near edge of screen, // to prevent abrupt appearance/disappearance when object go off screen float vignette = applyEdgeFade(tsPResult, _ScreenEdgeFading); confidence *= vignette; confidence *= vignette; return float4(tsPResult, rayDist, confidence); } float4 fragComposite(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; // View space point being shaded float3 C = GetPosition(tsP); // Final image before this pass float4 gbuffer3 = tex2D(_MainTex, i.uv); float4 specEmission = float4(0.0,0.0,0.0,0.0); float3 specColor = tex2D(_CameraGBufferTexture1, tsP).rgb; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP).a; float4 reflectionTexel = tex2D(_FinalReflectionTexture, tsP * _SourceToTempUV.xy); float4 gbuffer0 = tex2D(_CameraGBufferTexture0, tsP); // Let core Unity functions do the dirty work of applying the BRDF float3 baseColor = gbuffer0.rgb; float occlusion = gbuffer0.a; float oneMinusReflectivity; baseColor = EnergyConservationBetweenDiffuseAndSpecular(baseColor, specColor, oneMinusReflectivity); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csEyeVec = normalize(C); float3 eyeVec = mul(_CameraToWorldMatrix, float4(csEyeVec, 0)).xyz; float3 worldPos = mul(_CameraToWorldMatrix, float4(C, 1)).xyz; float cos_o = dot(wsNormal, eyeVec); float3 w_mi = -normalize((wsNormal * (2.0 * cos_o)) - eyeVec); float3 incomingRadiance = reflectionTexel.rgb; UnityLight light; light.color = 0; light.dir = 0; light.ndotl = 0; UnityIndirect ind; ind.diffuse = 0; ind.specular = incomingRadiance; float3 ssrResult = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, 1-roughness, wsNormal, -eyeVec, light, ind).rgb * _SSRMultiplier; float confidence = reflectionTexel.a; if(_EnableSSR == 0) { confidence = 0.0; } specEmission.rgb = tex2D(_CameraReflectionsTexture, tsP).rgb; float3 finalGlossyTerm; // Subtract out Unity's glossy result: (we're just applying the delta) if (_AdditiveReflection == 0) { gbuffer3 -= specEmission; // We may have blown out our dynamic range by adding then subtracting the reflection probes. // As a half-measure to fix this, simply clamp to zero gbuffer3 = max(gbuffer3, 0); finalGlossyTerm = lerp(specEmission.rgb, ssrResult, saturate(confidence)); } else { finalGlossyTerm = ssrResult*saturate(confidence); } if (_UseOcclusion) { finalGlossyTerm *= occlusion; } if (_DebugMode == 1) return float4(incomingRadiance,0); if (_DebugMode == 2) return float4(ssrResult,0); if (_DebugMode == 3) return float4(finalGlossyTerm,0); if (_DebugMode == 4) return confidence; if (_DebugMode == 5) return roughness; if (_DebugMode == 6) return float4(baseColor,0); if (_DebugMode == 7) return float4(specColor,0); if (_DebugMode == 8) return 1.0-oneMinusReflectivity; if (_DebugMode == 9) return float4(specEmission.rgb,0); if (_DebugMode == 10) return float4(specEmission.rgb-ssrResult,0); if (_DebugMode == 11) return float4(ssrResult-specEmission.rgb,0); if (_DebugMode == 12) return gbuffer3; if (_DebugMode == 13) return -float4(gbuffer3.rgb, 0.0); if (_DebugMode == 14) // Mip level, stored directly in ssrResult return float4(incomingRadiance, 0); // Additively blend the glossy GI result with the output buffer return gbuffer3 + float4(finalGlossyTerm, 0); } float roughnessWeight(float midpointRoughness, float tapRoughness) { return (1.0 - sqrt(sqrt(abs(midpointRoughness-tapRoughness)))); } float normalWeight(float3 midpointNormal, float3 tapNormal) { return clamp(dot(midpointNormal, tapNormal), 0, 1); } float highlightDecompression(float x) { return x / (1.0 - x); } float3 highlightDecompression(float3 x) { return float3( highlightDecompression(x.x), highlightDecompression(x.y), highlightDecompression(x.z)); } float highlightCompression(float x) { return x / (1.0 + x); } float3 highlightCompression(float3 x) { return float3( highlightCompression(x.x), highlightCompression(x.y), highlightCompression(x.z)); } float4 _Axis; float4 fragGBlur(v2f i) : SV_Target { int radius = 4; // Pixel being shaded float2 tsP = i.uv2.xy; float weightSum = 0.0; float gaussWeights[5] = {0.225, 0.150, 0.110, 0.075, 0.0525};//{0.225, 0.150, 0.110, 0.075, 0.0525}; float4 resultSum = float4(0.0, 0.0, 0.0, 0.0); float4 unweightedResultSum = float4(0.0, 0.0, 0.0, 0.0); float4 nAndRough = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = nAndRough.a; float3 midpointNormal = nAndRough.rgb * 2 - 1; if (_FullResolutionFiltering) { for (int i = -2*radius; i <= 2*radius; ++i) { float4 temp; float tapRoughness = midpointRoughness; float3 tapNormal = midpointNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)); temp = tex2D(_MainTex, tsTap); int gaussWeightIndex; #if defined(UNITY_COMPILER_HLSL) && !defined(SHADER_API_D3D9) gaussWeightIndex = abs(i) >> 1; // We purposefully do integer division, but that does not compile on hlsl2glsl #else gaussWeightIndex = frac(abs(i) / 2); #endif float weight = temp.a * gaussWeights[gaussWeightIndex]*0.5; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; resultSum += temp*weight; unweightedResultSum += temp; } } else { for (int i = -radius; i <= radius; ++i) { float4 temp; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)*2.0); temp = tex2D(_MainTex, tsTap); float weight = temp.a * gaussWeights[abs(i)]; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; if (_HighlightSuppression) { temp.rgb = highlightCompression(temp.rgb); } unweightedResultSum += temp; resultSum += temp*weight; } } if (weightSum > 0.01) { float invWeightSum = (1.0/weightSum); // Adding the sqrt seems to decrease temporal flickering at the expense // of having larger "halos" of fallback on rough surfaces // Subject to change with testing. Sqrt around only half the expression is *intentional*. float confidence = min(resultSum.a * sqrt(max(invWeightSum, 2.0)), 1.0); float3 finalColor = resultSum.rgb * invWeightSum; if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, confidence); } else { float3 finalColor = unweightedResultSum.rgb / (2 * radius + 1); if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, 0.0); } } sampler2D _ReflectionTexture0; sampler2D _ReflectionTexture1; sampler2D _ReflectionTexture2; sampler2D _ReflectionTexture3; sampler2D _ReflectionTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getReflectionValue(float2 tsP, int mip) { float4 coord = float4(tsP,0,0); if (mip == 0) { return tex2Dlod(_ReflectionTexture0, coord); } else if (mip == 1) { return tex2Dlod(_ReflectionTexture1, coord); } else if (mip == 2) { return tex2Dlod(_ReflectionTexture2, coord); } else if (mip == 3) { return tex2Dlod(_ReflectionTexture3, coord); } else { return tex2Dlod(_ReflectionTexture4, coord); } } sampler2D _EdgeTexture0; sampler2D _EdgeTexture1; sampler2D _EdgeTexture2; sampler2D _EdgeTexture3; sampler2D _EdgeTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getEdgeValue(float2 tsP, int mip) { float4 coord = float4(tsP + float2(1.0/(2 * mipToSize(mip))),0,0); if (mip == 0) { return tex2Dlod(_EdgeTexture0, coord); } else if (mip == 1) { return tex2Dlod(_EdgeTexture1, coord); } else if (mip == 2) { return tex2Dlod(_EdgeTexture2, coord); } else if (mip == 3) { return tex2Dlod(_EdgeTexture3, coord); } else { return tex2Dlod(_EdgeTexture4, coord); } } float2 centerPixel(float2 inputP) { return floor(inputP - float2(0.5,0.5)) + float2(0.5,0.5); } float2 snapToTexelCenter(float2 inputP, float2 texSize, float2 texSizeInv) { return centerPixel(inputP * texSize) * texSizeInv; } float4 bilateralUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; float2 fullTexSize = _ReflectionBufferSize; float2 fullTexSizeInv = 1.0 / fullTexSize; float4 hiP0 = float4(snapToTexelCenter(p0, fullTexSize, fullTexSizeInv), 0,0); float4 hiP3 = float4(snapToTexelCenter(p3, fullTexSize, fullTexSizeInv), 0,0); float4 hiP1 = float4(snapToTexelCenter(p1, fullTexSize, fullTexSizeInv), 0,0); float4 hiP2 = float4(snapToTexelCenter(p2, fullTexSize, fullTexSizeInv), 0,0); float4 tempCenter = tex2Dlod(_NormalAndRoughnessTexture, float4(tsP, 0, 0)); float3 n = tempCenter.xyz * 2 - 1; float4 temp0 = tex2Dlod(_NormalAndRoughnessTexture, hiP0); float4 temp1 = tex2Dlod(_NormalAndRoughnessTexture, hiP1); float4 temp2 = tex2Dlod(_NormalAndRoughnessTexture, hiP2); float4 temp3 = tex2Dlod(_NormalAndRoughnessTexture, hiP3); float3 n0 = temp0.xyz * 2 - 1; float3 n1 = temp1.xyz * 2 - 1; float3 n2 = temp2.xyz * 2 - 1; float3 n3 = temp3.xyz * 2 - 1; a0 *= normalWeight(n, n0); a1 *= normalWeight(n, n1); a2 *= normalWeight(n, n2); a3 *= normalWeight(n, n3); float r = tempCenter.a; float r0 = temp0.a; float r1 = temp1.a; float r2 = temp2.a; float r3 = temp3.a; a0 *= roughnessWeight(r, r0); a1 *= roughnessWeight(r, r1); a2 *= roughnessWeight(r, r2); a3 *= roughnessWeight(r, r3); // Slightly offset from zero a0 = max(a0, 0.001); a1 = max(a1, 0.001); a2 = max(a2, 0.001); a3 = max(a3, 0.001); // Nearest neighbor // a0 = a1 = a2 = a3 = 1.0; // Normalize the blending weights (weights were chosen so that // the denominator can never be zero) float norm = 1.0 / (a0 + a1 + a2 + a3); // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3) * norm; //return V0; return value; } /** Explicit bilinear fetches; must be used if the reflection buffer is bound using point sampling */ float4 bilinearUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3); return value; } // Unity's roughness is GGX roughness squared float roughnessToBlinnPhongExponent(float roughness) { float r2 = roughness*roughness; return 2.0f / r2*r2 - 2.0f; } float glossyLobeSlope(float roughness) { return pow(roughness, 4.0/3.0); } // Empirically based on our filter: // Mip | Pixels // -------------- // 0 | 1 no filter, so single pixel // 1 | 17 2r + 1 filter applied once, grabbing from pixels r away in either direction (r=8, four samples times stride of 2) // 2 | 50 2r + 1 filter applied on double size pixels, and each of those pixels had reached another r out to the side 2(2r + 1) + m_1 // 3 | 118 4(2r + 1) + m_2 // 4 | 254 8(2r + 1) + m_3 // // Approximated by pixels = 16*2^mip-15 // rearranging we get mip = log_2((pixels + 15) / 16) // float filterFootprintInPixelsToMip(float footprint) { return log2((footprint + 15) / 16); } float3 ansiGradient(float t) { //return float3(t, t, t); return fmod(floor(t * float3(8.0, 4.0, 2.0)), 2.0); } float4 fragCompositeSSR(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP * _SourceToTempUV.zw).a; float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).z; } // Get the camera space position of the reflection hit float3 csPosition = GetPosition(tsP); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 c_mi = csMirrorVector(csPosition, csN); float3 csHitpoint = c_mi * rayDistance + csPosition; float gatherFootprintInMeters = glossyLobeSlope(roughness) * rayDistance; // We could add a term that incorporates the normal // This approximation assumes reflections happen at a glancing angle float filterFootprintInPixels = gatherFootprintInMeters * _PixelsPerMeterAtOneMeter / csHitpoint.z; if (_HalfResolution == 1) { filterFootprintInPixels *= 0.5; } float mip = filterFootprintInPixelsToMip(filterFootprintInPixels); float nonPhysicalMip = pow(roughness, 3.0 / 4.0) * UNITY_SPECCUBE_LOD_STEPS; if (_HalfResolution == 1) { nonPhysicalMip = nonPhysicalMip * 0.7; } mip = lerp(nonPhysicalMip, mip, _DistanceBlur); mip = max(0, min(4, mip + _MipBias)); if (_DebugMode == 14) { return float4(ansiGradient(mip*0.25), 1.0); } float4 result; { int mipMin = int(mip); int mipMax = min(mipMin + 1, 4); float mipLerp = mip-mipMin; if (_BilateralUpsampling == 1) { if (_UseEdgeDetector == 1) { float nonEdginess = lerp(getEdgeValue(tsP, mipMin), getEdgeValue(tsP, mipMax), mipLerp); if (nonEdginess < 0.9) { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); if (_VisualizeWhereBilateral == 1) { result = float4(1, 0, 0, 0); } } else { result = lerp(bilinearUpsampleReflection(tsP, mipMin), bilinearUpsampleReflection(tsP, mipMax), mipLerp); } } else { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); } } else { float4 minResult = getReflectionValue(tsP, mipMin); float4 maxResult = getReflectionValue(tsP, mipMax); result = lerp(minResult, maxResult, mipLerp); result.a = min(minResult.a, maxResult.a); } } result.a = min(result.a, 1.0); float vignette = applyEdgeFade(tsP, _ScreenEdgeFading); result.a *= vignette; float alphaModifier = 1.0 - clamp((roughness - (_MaxRoughness - _RoughnessFalloffRange)) / _RoughnessFalloffRange, 0.0, 1.0); result.a *= alphaModifier; return result; } float4 fragBlit(v2f i) : SV_Target { // Pixel being shaded float2 ssP = i.uv2.xy; return tex2D(_MainTex, ssP); } float edgeDetector(float2 tsP, float2 axis, float2 texelSize) { float4 temp = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = temp.a; float3 midpointNormal = temp.rgb * 2 - 1; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (axis * _MainTex_TexelSize.xy); temp = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = temp.a; tapNormal = temp.rgb * 2 - 1; float weight = 1.0; weight *= roughnessWeight(midpointRoughness, tapRoughness); weight *= normalWeight(midpointNormal, tapNormal); return weight; } float4 fragEdge(v2f i) : SV_Target { float2 tsP = i.uv2.xy; return edgeDetector(tsP, float2(1,0), _MainTex_TexelSize.xy) * edgeDetector(tsP, float2(0,1), _MainTex_TexelSize.xy); } int _LastMip; float4 fragMin(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float2 lastTexSize = mipToSize(_LastMip); float2 lastTexSizeInv = 1.0 / lastTexSize;//_SourceToTempUV.xy * exp2(mip); float2 p00 = snapToTexelCenter(tsP, lastTexSize, lastTexSizeInv); float2 p11 = p00 + lastTexSizeInv; return min( min(tex2D(_MainTex, p00), tex2D(_MainTex, p11)), min(tex2D(_MainTex, float2(p00.x, p11.y)), tex2D(_MainTex, float2(p11.x, p00.y))) ); } float4 fragResolveHitPoints(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float4 temp = tex2D(_HitPointTexture, tsP); float2 hitPoint = temp.xy; float confidence = temp.w; float3 colorResult = confidence > 0.0 ? tex2D(_MainTex, hitPoint).rgb : tex2D(_CameraReflectionsTexture, tsP).rgb; #ifdef UNITY_COMPILER_HLSL if (any(isnan(colorResult))) colorResult = float3(0.0, 0.0, 0.0); // As of 11/29/2015, on Unity 5.3 on a Windows 8.1 computer with a NVIDIA GeForce 980, // with driver 347.62, the above check does not actually work to get rid of NaNs! // So we add this "redundant" check. if (!all(isfinite(colorResult))) colorResult = float3(0.0, 0.0, 0.0); #endif return float4(colorResult, confidence); } float4 fragBilatKeyPack(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float3 csN = tex2D(_CameraGBufferTexture2, tsP).xyz; float roughness = tex2D(_CameraGBufferTexture1, tsP).a; return float4(csN, roughness); } float4 fragDepthToCSZ(v2f i) : SV_Target { float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv2.xy); return float4(-LinearEyeDepth(depth), 0.0, 0.0, 0.0); } float3 csPositionToOldNormalizedScreenCoordinate(float3 csPosition) { // We could multiply these matrices together on the CPU float4 oldCSPosition = mul(_CurrentCameraToPreviousCamera, float4(csPosition, 1.0)); float4 oldSSCoordinate = mul(_ProjectToPixelMatrix, oldCSPosition); return float3((oldSSCoordinate.xy / oldSSCoordinate.w) * _InvScreenSize, oldCSPosition.z); } float4 fragTemporalFilter(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, tsP); float3 csPosition = GetPosition(tsP); float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).b; } // Virtual is the physics sense, not the other 20+ interpretations in CG float3 virtualReflectionCSPosition = normalize(csPosition) * rayDistance + csPosition; float3 oldTSP = csPositionToOldNormalizedScreenCoordinate(csPosition); float3 oldVirtualReflectionTSP = csPositionToOldNormalizedScreenCoordinate(virtualReflectionCSPosition); float oldCSZ = tex2D(_PreviousCSZBuffer, oldTSP.xy); float4 currentResult = tex2D(_FinalReflectionTexture, tsP); float4 oldResult = tex2D(_PreviousReflectionTexture, oldVirtualReflectionTSP.xy); float zError = abs(oldCSZ - oldTSP.z); // Start knocking down contribution at 5cm error, go to 0 at 5 more cm later float fallbackStart = 0.05; float fallbackEnd = 0.05; float zAlphaModifier = 1.0 - clamp((zError - fallbackStart) * (1.0 / fallbackEnd), 0.0, 1.0); float alpha = _TemporalAlpha; alpha *= zAlphaModifier; if (_UseTemporalConfidence) { alpha *= oldResult.w; } // Always compress currentResult.rgb = highlightCompression(currentResult.rgb); oldResult.rgb = highlightCompression(oldResult.rgb); float4 resultValue = lerp(currentResult, oldResult, alpha); resultValue.rgb = highlightDecompression(resultValue.rgb); return resultValue; } float4 fragAverageRayDistance(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float sumRayDistance = tex2D(_HitPointTexture, tsP).b; float sumWeight = 1.0; float roughness = 1.0 - tex2D(_CameraGBufferTexture1, tsP).a; float step = roughness * 3.0; /* for (float x = -step; x < step; x += 2 * step) { for (float y = -step; y < step; y += 2 * step) { float weight = 0.5; sumRayDistance += weight * tex2D(_HitPointTexture, (float2(x,y) * _InvScreenSize) + tsP).b; sumWeight += weight; } } */ return float4(sumRayDistance / sumWeight, 0.0, 0.0, 0.0); } static const int NUM_POISSON_TAPS = 12; // Same as used in CameraMotionBlur.shader static const float2 poissonSamples[NUM_POISSON_TAPS] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; float4 fragFilterSharpReflections(v2f i) : SV_Target { // Could improve perf by not computing blur when we won't be sampling the highest level anyways float2 tsP = i.uv2.xy; float4 sum = 0.0; float sampleRadius = _MainTex_TexelSize.xy * 2.0; for (int i = 0; i < NUM_POISSON_TAPS; i++) { float2 p = tsP + poissonSamples[i] * sampleRadius; float4 tap = tex2Dlod(_MainTex, float4(p, 0, 0)); if (_HighlightSuppression) { tap.rgb = highlightCompression(tap.rgb); } sum += tap; } float4 result = sum / float(NUM_POISSON_TAPS); if (_HighlightSuppression) { result.rgb = highlightDecompression(result.rgb); } return result; } #line 1026 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif /* UNITY: Original start of shader */ #pragma exclude_renderers gles xbox360 ps3 #pragma vertex vert #pragma fragment fragRaytrace16 #pragma target 3.0 float4 fragRaytrace16(v2f i) : SV_Target { return fragRaytrace(i, 16); } SAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/ShadersÓ˙˙˙küń'¨ô™!]{Ňô SHADOWS_SOFTDIRLIGHTMAP_COMBINEDDIRLIGHTMAP_SEPARATEDYNAMICLIGHTMAP_ON INSTANCING_ONz¸†#line 1037 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #line 12 #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "ScreenSpaceRaytrace.cginc" float4 _ProjInfo; float4x4 _WorldToCameraMatrix; float4x4 _CameraToWorldMatrix; float4x4 _ProjectToPixelMatrix; float2 _ScreenSize; float2 _ReflectionBufferSize; float2 _InvScreenSize; float3 _CameraClipInfo; sampler2D _CameraGBufferTexture0; sampler2D _CameraGBufferTexture1; sampler2D _CameraGBufferTexture2; sampler2D _CameraGBufferTexture3; sampler2D _CameraReflectionsTexture; float _CurrentMipLevel; float _RayStepSize; float _MaxRayTraceDistance; float _LayerThickness; float _FresnelFade; float _FresnelFadePower; sampler2D _MainTex; int _VisualizeWhereBilateral; int _EnableSSR; int _DebugMode; int _HalfResolution; int _TreatBackfaceHitAsMiss; int _AllowBackwardsRays; int _TraceEverywhere; // RG: SS Hitpoint of ray // B: distance ray travelled, used for mip-selection in the final resolve // A: confidence value sampler2D _HitPointTexture; sampler2D _FinalReflectionTexture; // RGB: camera-space normal (encoded in [0-1]) // A: Roughness sampler2D _NormalAndRoughnessTexture; float4 _MainTex_TexelSize; float4 _SourceToTempUV; int _EnableRefine; int _AdditiveReflection; int _ImproveCorners; float _ScreenEdgeFading; float _MipBias; int _UseOcclusion; int _MaxSteps; int _FullResolutionFiltering; int _BilateralUpsampling; float _MaxRoughness; float _RoughnessFalloffRange; float _SSRMultiplier; float _FadeDistance; float _DistanceBlur; int _TraceBehindObjects; int _FallbackToSky; int _UseEdgeDetector; int _HighlightSuppression; /** The height in pixels of a 1m object if viewed from 1m away. */ float _PixelsPerMeterAtOneMeter; int _UseAverageRayDistance; sampler2D _AverageRayDistanceBuffer; // For temporal filtering: float4x4 _CurrentCameraToPreviousCamera; sampler2D _PreviousReflectionTexture; sampler2D _PreviousCSZBuffer; float _TemporalAlpha; int _UseTemporalConfidence; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy; o.uv2 = v.texcoord.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv2.y = 1-o.uv2.y; #endif return o; } float2 mipToSize(int mip) { return floor(_ReflectionBufferSize * exp2(-mip)); } float3 ReconstructCSPosition(float2 S, float z) { float linEyeZ = -LinearEyeDepth(z); return float3(( (( S.xy * _MainTex_TexelSize.zw) ) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); } /** Read the camera-space position of the point at screen-space pixel ssP */ float3 GetPosition(float2 ssP) { float3 P; P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy); // Offset to pixel center P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); return P; } float square(float x) { return x*x; } float applyEdgeFade(float2 tsP, float fadeStrength) { float maxFade = 0.1; float2 itsP = float2(1.0, 1.0) - tsP; float dist = min(min(itsP.x, itsP.y), min(tsP.x, tsP.x)); float fade = dist / (maxFade*fadeStrength + 0.001); fade = max(min(fade, 1.0), 0.0); fade = pow(fade, 0.2); return fade; } float3 csMirrorVector(float3 csPosition, float3 csN) { float3 csE = -normalize(csPosition.xyz); float cos_o = dot(csN, csE); float3 c_mi = normalize((csN * (2.0 * cos_o)) - csE); return c_mi; } float4 fragRaytrace(v2f i, int stepRate) { float2 ssP = i.uv2.xy * _SourceToTempUV.zw; float3 csPosition = GetPosition(ssP); float smoothness = tex2D(_CameraGBufferTexture1, ssP).a; if (csPosition.z < -100.0 || smoothness == 0.0) { return float4(0.0,0.0,0.0,0.0); } if ((!_TraceEverywhere) && (smoothness < (1.0 - _MaxRoughness)) ) { return float4(0.0, 0.0, 0.0, 0.0); } float3 wsNormal = tex2D(_CameraGBufferTexture2, ssP).rgb * 2.0 - 1.0; int2 ssC = int2(ssP * _ScreenSize); float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 csRayDirection = csMirrorVector(csPosition, csN); if (_AllowBackwardsRays == 0 && csRayDirection.z > 0.0) { return float4(0.0, 0.0, 0.0, 0.0); } float maxRayTraceDistance = _MaxRayTraceDistance; float jitterFraction = 0.0f; float layerThickness = _LayerThickness; int maxSteps = _MaxSteps; // Bump the ray more in world space as it gets farther away (and so each pixel covers more WS distance) float rayBump = max(-0.01*csPosition.z, 0.001); float2 hitPixel; float3 csHitPoint; float stepCount; bool wasHit = castDenseScreenSpaceRay (csPosition + (csN) * rayBump, csRayDirection, _ProjectToPixelMatrix, _ScreenSize, _CameraClipInfo, jitterFraction, maxSteps, layerThickness, maxRayTraceDistance, hitPixel, stepRate, _EnableRefine == 1, _TraceBehindObjects == 1, csHitPoint, stepCount); float2 tsPResult = hitPixel / _ScreenSize; float rayDist = dot(csHitPoint - csPosition, csRayDirection); float confidence = 0.0; if (wasHit) { confidence = square(1.0 - max(2.0*float(stepCount) / float(maxSteps) - 1.0, 0.0)); confidence *= clamp(((_MaxRayTraceDistance - rayDist) / _FadeDistance), 0.0, 1.0); // Fake fresnel fade float3 csE = -normalize(csPosition.xyz); confidence *= max(0.0, lerp(pow(abs(dot(csRayDirection, -csE)), _FresnelFadePower), 1, 1.0 - _FresnelFade)); if (_TreatBackfaceHitAsMiss > 0) { float3 wsHitNormal = tex2Dlod(_CameraGBufferTexture2, float4(tsPResult, 0, 0)).rgb * 2.0 - 1.0; float3 wsRayDirection = mul(_CameraToWorldMatrix, float4(csRayDirection, 0)).xyz; if (dot(wsHitNormal, wsRayDirection) > 0) { confidence = 0.0; } } } else if (_FallbackToSky > 0) { float sceneZ = tex2Dlod(_CameraDepthTexture, float4(tsPResult, 0, 0)).r; sceneZ = -LinearEyeDepth(sceneZ); if (sceneZ < -1000) { // 1km ~= infinity confidence = 1.0; } } // Fade out reflections that hit near edge of screen, to prevent abrupt appearance/disappearance when object go off screen // Fade out reflections that hit near edge of screen, // to prevent abrupt appearance/disappearance when object go off screen float vignette = applyEdgeFade(tsPResult, _ScreenEdgeFading); confidence *= vignette; confidence *= vignette; return float4(tsPResult, rayDist, confidence); } float4 fragComposite(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; // View space point being shaded float3 C = GetPosition(tsP); // Final image before this pass float4 gbuffer3 = tex2D(_MainTex, i.uv); float4 specEmission = float4(0.0,0.0,0.0,0.0); float3 specColor = tex2D(_CameraGBufferTexture1, tsP).rgb; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP).a; float4 reflectionTexel = tex2D(_FinalReflectionTexture, tsP * _SourceToTempUV.xy); float4 gbuffer0 = tex2D(_CameraGBufferTexture0, tsP); // Let core Unity functions do the dirty work of applying the BRDF float3 baseColor = gbuffer0.rgb; float occlusion = gbuffer0.a; float oneMinusReflectivity; baseColor = EnergyConservationBetweenDiffuseAndSpecular(baseColor, specColor, oneMinusReflectivity); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csEyeVec = normalize(C); float3 eyeVec = mul(_CameraToWorldMatrix, float4(csEyeVec, 0)).xyz; float3 worldPos = mul(_CameraToWorldMatrix, float4(C, 1)).xyz; float cos_o = dot(wsNormal, eyeVec); float3 w_mi = -normalize((wsNormal * (2.0 * cos_o)) - eyeVec); float3 incomingRadiance = reflectionTexel.rgb; UnityLight light; light.color = 0; light.dir = 0; light.ndotl = 0; UnityIndirect ind; ind.diffuse = 0; ind.specular = incomingRadiance; float3 ssrResult = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, 1-roughness, wsNormal, -eyeVec, light, ind).rgb * _SSRMultiplier; float confidence = reflectionTexel.a; if(_EnableSSR == 0) { confidence = 0.0; } specEmission.rgb = tex2D(_CameraReflectionsTexture, tsP).rgb; float3 finalGlossyTerm; // Subtract out Unity's glossy result: (we're just applying the delta) if (_AdditiveReflection == 0) { gbuffer3 -= specEmission; // We may have blown out our dynamic range by adding then subtracting the reflection probes. // As a half-measure to fix this, simply clamp to zero gbuffer3 = max(gbuffer3, 0); finalGlossyTerm = lerp(specEmission.rgb, ssrResult, saturate(confidence)); } else { finalGlossyTerm = ssrResult*saturate(confidence); } if (_UseOcclusion) { finalGlossyTerm *= occlusion; } if (_DebugMode == 1) return float4(incomingRadiance,0); if (_DebugMode == 2) return float4(ssrResult,0); if (_DebugMode == 3) return float4(finalGlossyTerm,0); if (_DebugMode == 4) return confidence; if (_DebugMode == 5) return roughness; if (_DebugMode == 6) return float4(baseColor,0); if (_DebugMode == 7) return float4(specColor,0); if (_DebugMode == 8) return 1.0-oneMinusReflectivity; if (_DebugMode == 9) return float4(specEmission.rgb,0); if (_DebugMode == 10) return float4(specEmission.rgb-ssrResult,0); if (_DebugMode == 11) return float4(ssrResult-specEmission.rgb,0); if (_DebugMode == 12) return gbuffer3; if (_DebugMode == 13) return -float4(gbuffer3.rgb, 0.0); if (_DebugMode == 14) // Mip level, stored directly in ssrResult return float4(incomingRadiance, 0); // Additively blend the glossy GI result with the output buffer return gbuffer3 + float4(finalGlossyTerm, 0); } float roughnessWeight(float midpointRoughness, float tapRoughness) { return (1.0 - sqrt(sqrt(abs(midpointRoughness-tapRoughness)))); } float normalWeight(float3 midpointNormal, float3 tapNormal) { return clamp(dot(midpointNormal, tapNormal), 0, 1); } float highlightDecompression(float x) { return x / (1.0 - x); } float3 highlightDecompression(float3 x) { return float3( highlightDecompression(x.x), highlightDecompression(x.y), highlightDecompression(x.z)); } float highlightCompression(float x) { return x / (1.0 + x); } float3 highlightCompression(float3 x) { return float3( highlightCompression(x.x), highlightCompression(x.y), highlightCompression(x.z)); } float4 _Axis; float4 fragGBlur(v2f i) : SV_Target { int radius = 4; // Pixel being shaded float2 tsP = i.uv2.xy; float weightSum = 0.0; float gaussWeights[5] = {0.225, 0.150, 0.110, 0.075, 0.0525};//{0.225, 0.150, 0.110, 0.075, 0.0525}; float4 resultSum = float4(0.0, 0.0, 0.0, 0.0); float4 unweightedResultSum = float4(0.0, 0.0, 0.0, 0.0); float4 nAndRough = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = nAndRough.a; float3 midpointNormal = nAndRough.rgb * 2 - 1; if (_FullResolutionFiltering) { for (int i = -2*radius; i <= 2*radius; ++i) { float4 temp; float tapRoughness = midpointRoughness; float3 tapNormal = midpointNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)); temp = tex2D(_MainTex, tsTap); int gaussWeightIndex; #if defined(UNITY_COMPILER_HLSL) && !defined(SHADER_API_D3D9) gaussWeightIndex = abs(i) >> 1; // We purposefully do integer division, but that does not compile on hlsl2glsl #else gaussWeightIndex = frac(abs(i) / 2); #endif float weight = temp.a * gaussWeights[gaussWeightIndex]*0.5; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; resultSum += temp*weight; unweightedResultSum += temp; } } else { for (int i = -radius; i <= radius; ++i) { float4 temp; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)*2.0); temp = tex2D(_MainTex, tsTap); float weight = temp.a * gaussWeights[abs(i)]; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; if (_HighlightSuppression) { temp.rgb = highlightCompression(temp.rgb); } unweightedResultSum += temp; resultSum += temp*weight; } } if (weightSum > 0.01) { float invWeightSum = (1.0/weightSum); // Adding the sqrt seems to decrease temporal flickering at the expense // of having larger "halos" of fallback on rough surfaces // Subject to change with testing. Sqrt around only half the expression is *intentional*. float confidence = min(resultSum.a * sqrt(max(invWeightSum, 2.0)), 1.0); float3 finalColor = resultSum.rgb * invWeightSum; if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, confidence); } else { float3 finalColor = unweightedResultSum.rgb / (2 * radius + 1); if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, 0.0); } } sampler2D _ReflectionTexture0; sampler2D _ReflectionTexture1; sampler2D _ReflectionTexture2; sampler2D _ReflectionTexture3; sampler2D _ReflectionTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getReflectionValue(float2 tsP, int mip) { float4 coord = float4(tsP,0,0); if (mip == 0) { return tex2Dlod(_ReflectionTexture0, coord); } else if (mip == 1) { return tex2Dlod(_ReflectionTexture1, coord); } else if (mip == 2) { return tex2Dlod(_ReflectionTexture2, coord); } else if (mip == 3) { return tex2Dlod(_ReflectionTexture3, coord); } else { return tex2Dlod(_ReflectionTexture4, coord); } } sampler2D _EdgeTexture0; sampler2D _EdgeTexture1; sampler2D _EdgeTexture2; sampler2D _EdgeTexture3; sampler2D _EdgeTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getEdgeValue(float2 tsP, int mip) { float4 coord = float4(tsP + float2(1.0/(2 * mipToSize(mip))),0,0); if (mip == 0) { return tex2Dlod(_EdgeTexture0, coord); } else if (mip == 1) { return tex2Dlod(_EdgeTexture1, coord); } else if (mip == 2) { return tex2Dlod(_EdgeTexture2, coord); } else if (mip == 3) { return tex2Dlod(_EdgeTexture3, coord); } else { return tex2Dlod(_EdgeTexture4, coord); } } float2 centerPixel(float2 inputP) { return floor(inputP - float2(0.5,0.5)) + float2(0.5,0.5); } float2 snapToTexelCenter(float2 inputP, float2 texSize, float2 texSizeInv) { return centerPixel(inputP * texSize) * texSizeInv; } float4 bilateralUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; float2 fullTexSize = _ReflectionBufferSize; float2 fullTexSizeInv = 1.0 / fullTexSize; float4 hiP0 = float4(snapToTexelCenter(p0, fullTexSize, fullTexSizeInv), 0,0); float4 hiP3 = float4(snapToTexelCenter(p3, fullTexSize, fullTexSizeInv), 0,0); float4 hiP1 = float4(snapToTexelCenter(p1, fullTexSize, fullTexSizeInv), 0,0); float4 hiP2 = float4(snapToTexelCenter(p2, fullTexSize, fullTexSizeInv), 0,0); float4 tempCenter = tex2Dlod(_NormalAndRoughnessTexture, float4(tsP, 0, 0)); float3 n = tempCenter.xyz * 2 - 1; float4 temp0 = tex2Dlod(_NormalAndRoughnessTexture, hiP0); float4 temp1 = tex2Dlod(_NormalAndRoughnessTexture, hiP1); float4 temp2 = tex2Dlod(_NormalAndRoughnessTexture, hiP2); float4 temp3 = tex2Dlod(_NormalAndRoughnessTexture, hiP3); float3 n0 = temp0.xyz * 2 - 1; float3 n1 = temp1.xyz * 2 - 1; float3 n2 = temp2.xyz * 2 - 1; float3 n3 = temp3.xyz * 2 - 1; a0 *= normalWeight(n, n0); a1 *= normalWeight(n, n1); a2 *= normalWeight(n, n2); a3 *= normalWeight(n, n3); float r = tempCenter.a; float r0 = temp0.a; float r1 = temp1.a; float r2 = temp2.a; float r3 = temp3.a; a0 *= roughnessWeight(r, r0); a1 *= roughnessWeight(r, r1); a2 *= roughnessWeight(r, r2); a3 *= roughnessWeight(r, r3); // Slightly offset from zero a0 = max(a0, 0.001); a1 = max(a1, 0.001); a2 = max(a2, 0.001); a3 = max(a3, 0.001); // Nearest neighbor // a0 = a1 = a2 = a3 = 1.0; // Normalize the blending weights (weights were chosen so that // the denominator can never be zero) float norm = 1.0 / (a0 + a1 + a2 + a3); // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3) * norm; //return V0; return value; } /** Explicit bilinear fetches; must be used if the reflection buffer is bound using point sampling */ float4 bilinearUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3); return value; } // Unity's roughness is GGX roughness squared float roughnessToBlinnPhongExponent(float roughness) { float r2 = roughness*roughness; return 2.0f / r2*r2 - 2.0f; } float glossyLobeSlope(float roughness) { return pow(roughness, 4.0/3.0); } // Empirically based on our filter: // Mip | Pixels // -------------- // 0 | 1 no filter, so single pixel // 1 | 17 2r + 1 filter applied once, grabbing from pixels r away in either direction (r=8, four samples times stride of 2) // 2 | 50 2r + 1 filter applied on double size pixels, and each of those pixels had reached another r out to the side 2(2r + 1) + m_1 // 3 | 118 4(2r + 1) + m_2 // 4 | 254 8(2r + 1) + m_3 // // Approximated by pixels = 16*2^mip-15 // rearranging we get mip = log_2((pixels + 15) / 16) // float filterFootprintInPixelsToMip(float footprint) { return log2((footprint + 15) / 16); } float3 ansiGradient(float t) { //return float3(t, t, t); return fmod(floor(t * float3(8.0, 4.0, 2.0)), 2.0); } float4 fragCompositeSSR(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP * _SourceToTempUV.zw).a; float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).z; } // Get the camera space position of the reflection hit float3 csPosition = GetPosition(tsP); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 c_mi = csMirrorVector(csPosition, csN); float3 csHitpoint = c_mi * rayDistance + csPosition; float gatherFootprintInMeters = glossyLobeSlope(roughness) * rayDistance; // We could add a term that incorporates the normal // This approximation assumes reflections happen at a glancing angle float filterFootprintInPixels = gatherFootprintInMeters * _PixelsPerMeterAtOneMeter / csHitpoint.z; if (_HalfResolution == 1) { filterFootprintInPixels *= 0.5; } float mip = filterFootprintInPixelsToMip(filterFootprintInPixels); float nonPhysicalMip = pow(roughness, 3.0 / 4.0) * UNITY_SPECCUBE_LOD_STEPS; if (_HalfResolution == 1) { nonPhysicalMip = nonPhysicalMip * 0.7; } mip = lerp(nonPhysicalMip, mip, _DistanceBlur); mip = max(0, min(4, mip + _MipBias)); if (_DebugMode == 14) { return float4(ansiGradient(mip*0.25), 1.0); } float4 result; { int mipMin = int(mip); int mipMax = min(mipMin + 1, 4); float mipLerp = mip-mipMin; if (_BilateralUpsampling == 1) { if (_UseEdgeDetector == 1) { float nonEdginess = lerp(getEdgeValue(tsP, mipMin), getEdgeValue(tsP, mipMax), mipLerp); if (nonEdginess < 0.9) { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); if (_VisualizeWhereBilateral == 1) { result = float4(1, 0, 0, 0); } } else { result = lerp(bilinearUpsampleReflection(tsP, mipMin), bilinearUpsampleReflection(tsP, mipMax), mipLerp); } } else { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); } } else { float4 minResult = getReflectionValue(tsP, mipMin); float4 maxResult = getReflectionValue(tsP, mipMax); result = lerp(minResult, maxResult, mipLerp); result.a = min(minResult.a, maxResult.a); } } result.a = min(result.a, 1.0); float vignette = applyEdgeFade(tsP, _ScreenEdgeFading); result.a *= vignette; float alphaModifier = 1.0 - clamp((roughness - (_MaxRoughness - _RoughnessFalloffRange)) / _RoughnessFalloffRange, 0.0, 1.0); result.a *= alphaModifier; return result; } float4 fragBlit(v2f i) : SV_Target { // Pixel being shaded float2 ssP = i.uv2.xy; return tex2D(_MainTex, ssP); } float edgeDetector(float2 tsP, float2 axis, float2 texelSize) { float4 temp = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = temp.a; float3 midpointNormal = temp.rgb * 2 - 1; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (axis * _MainTex_TexelSize.xy); temp = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = temp.a; tapNormal = temp.rgb * 2 - 1; float weight = 1.0; weight *= roughnessWeight(midpointRoughness, tapRoughness); weight *= normalWeight(midpointNormal, tapNormal); return weight; } float4 fragEdge(v2f i) : SV_Target { float2 tsP = i.uv2.xy; return edgeDetector(tsP, float2(1,0), _MainTex_TexelSize.xy) * edgeDetector(tsP, float2(0,1), _MainTex_TexelSize.xy); } int _LastMip; float4 fragMin(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float2 lastTexSize = mipToSize(_LastMip); float2 lastTexSizeInv = 1.0 / lastTexSize;//_SourceToTempUV.xy * exp2(mip); float2 p00 = snapToTexelCenter(tsP, lastTexSize, lastTexSizeInv); float2 p11 = p00 + lastTexSizeInv; return min( min(tex2D(_MainTex, p00), tex2D(_MainTex, p11)), min(tex2D(_MainTex, float2(p00.x, p11.y)), tex2D(_MainTex, float2(p11.x, p00.y))) ); } float4 fragResolveHitPoints(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float4 temp = tex2D(_HitPointTexture, tsP); float2 hitPoint = temp.xy; float confidence = temp.w; float3 colorResult = confidence > 0.0 ? tex2D(_MainTex, hitPoint).rgb : tex2D(_CameraReflectionsTexture, tsP).rgb; #ifdef UNITY_COMPILER_HLSL if (any(isnan(colorResult))) colorResult = float3(0.0, 0.0, 0.0); // As of 11/29/2015, on Unity 5.3 on a Windows 8.1 computer with a NVIDIA GeForce 980, // with driver 347.62, the above check does not actually work to get rid of NaNs! // So we add this "redundant" check. if (!all(isfinite(colorResult))) colorResult = float3(0.0, 0.0, 0.0); #endif return float4(colorResult, confidence); } float4 fragBilatKeyPack(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float3 csN = tex2D(_CameraGBufferTexture2, tsP).xyz; float roughness = tex2D(_CameraGBufferTexture1, tsP).a; return float4(csN, roughness); } float4 fragDepthToCSZ(v2f i) : SV_Target { float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv2.xy); return float4(-LinearEyeDepth(depth), 0.0, 0.0, 0.0); } float3 csPositionToOldNormalizedScreenCoordinate(float3 csPosition) { // We could multiply these matrices together on the CPU float4 oldCSPosition = mul(_CurrentCameraToPreviousCamera, float4(csPosition, 1.0)); float4 oldSSCoordinate = mul(_ProjectToPixelMatrix, oldCSPosition); return float3((oldSSCoordinate.xy / oldSSCoordinate.w) * _InvScreenSize, oldCSPosition.z); } float4 fragTemporalFilter(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, tsP); float3 csPosition = GetPosition(tsP); float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).b; } // Virtual is the physics sense, not the other 20+ interpretations in CG float3 virtualReflectionCSPosition = normalize(csPosition) * rayDistance + csPosition; float3 oldTSP = csPositionToOldNormalizedScreenCoordinate(csPosition); float3 oldVirtualReflectionTSP = csPositionToOldNormalizedScreenCoordinate(virtualReflectionCSPosition); float oldCSZ = tex2D(_PreviousCSZBuffer, oldTSP.xy); float4 currentResult = tex2D(_FinalReflectionTexture, tsP); float4 oldResult = tex2D(_PreviousReflectionTexture, oldVirtualReflectionTSP.xy); float zError = abs(oldCSZ - oldTSP.z); // Start knocking down contribution at 5cm error, go to 0 at 5 more cm later float fallbackStart = 0.05; float fallbackEnd = 0.05; float zAlphaModifier = 1.0 - clamp((zError - fallbackStart) * (1.0 / fallbackEnd), 0.0, 1.0); float alpha = _TemporalAlpha; alpha *= zAlphaModifier; if (_UseTemporalConfidence) { alpha *= oldResult.w; } // Always compress currentResult.rgb = highlightCompression(currentResult.rgb); oldResult.rgb = highlightCompression(oldResult.rgb); float4 resultValue = lerp(currentResult, oldResult, alpha); resultValue.rgb = highlightDecompression(resultValue.rgb); return resultValue; } float4 fragAverageRayDistance(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float sumRayDistance = tex2D(_HitPointTexture, tsP).b; float sumWeight = 1.0; float roughness = 1.0 - tex2D(_CameraGBufferTexture1, tsP).a; float step = roughness * 3.0; /* for (float x = -step; x < step; x += 2 * step) { for (float y = -step; y < step; y += 2 * step) { float weight = 0.5; sumRayDistance += weight * tex2D(_HitPointTexture, (float2(x,y) * _InvScreenSize) + tsP).b; sumWeight += weight; } } */ return float4(sumRayDistance / sumWeight, 0.0, 0.0, 0.0); } static const int NUM_POISSON_TAPS = 12; // Same as used in CameraMotionBlur.shader static const float2 poissonSamples[NUM_POISSON_TAPS] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; float4 fragFilterSharpReflections(v2f i) : SV_Target { // Could improve perf by not computing blur when we won't be sampling the highest level anyways float2 tsP = i.uv2.xy; float4 sum = 0.0; float sampleRadius = _MainTex_TexelSize.xy * 2.0; for (int i = 0; i < NUM_POISSON_TAPS; i++) { float2 p = tsP + poissonSamples[i] * sampleRadius; float4 tap = tex2Dlod(_MainTex, float4(p, 0, 0)); if (_HighlightSuppression) { tap.rgb = highlightCompression(tap.rgb); } sum += tap; } float4 result = sum / float(NUM_POISSON_TAPS); if (_HighlightSuppression) { result.rgb = highlightDecompression(result.rgb); } return result; } #line 1037 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif /* UNITY: Original start of shader */ #pragma exclude_renderers gles xbox360 ps3 #pragma vertex vert #pragma fragment fragComposite #pragma target 3.0 SAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/ShadersÓ˙˙˙küń'¨ô™!]{Ňô SHADOWS_SOFTDIRLIGHTMAP_COMBINEDDIRLIGHTMAP_SEPARATEDYNAMICLIGHTMAP_ON INSTANCING_ON+‘´†#line 1047 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #line 12 #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "ScreenSpaceRaytrace.cginc" float4 _ProjInfo; float4x4 _WorldToCameraMatrix; float4x4 _CameraToWorldMatrix; float4x4 _ProjectToPixelMatrix; float2 _ScreenSize; float2 _ReflectionBufferSize; float2 _InvScreenSize; float3 _CameraClipInfo; sampler2D _CameraGBufferTexture0; sampler2D _CameraGBufferTexture1; sampler2D _CameraGBufferTexture2; sampler2D _CameraGBufferTexture3; sampler2D _CameraReflectionsTexture; float _CurrentMipLevel; float _RayStepSize; float _MaxRayTraceDistance; float _LayerThickness; float _FresnelFade; float _FresnelFadePower; sampler2D _MainTex; int _VisualizeWhereBilateral; int _EnableSSR; int _DebugMode; int _HalfResolution; int _TreatBackfaceHitAsMiss; int _AllowBackwardsRays; int _TraceEverywhere; // RG: SS Hitpoint of ray // B: distance ray travelled, used for mip-selection in the final resolve // A: confidence value sampler2D _HitPointTexture; sampler2D _FinalReflectionTexture; // RGB: camera-space normal (encoded in [0-1]) // A: Roughness sampler2D _NormalAndRoughnessTexture; float4 _MainTex_TexelSize; float4 _SourceToTempUV; int _EnableRefine; int _AdditiveReflection; int _ImproveCorners; float _ScreenEdgeFading; float _MipBias; int _UseOcclusion; int _MaxSteps; int _FullResolutionFiltering; int _BilateralUpsampling; float _MaxRoughness; float _RoughnessFalloffRange; float _SSRMultiplier; float _FadeDistance; float _DistanceBlur; int _TraceBehindObjects; int _FallbackToSky; int _UseEdgeDetector; int _HighlightSuppression; /** The height in pixels of a 1m object if viewed from 1m away. */ float _PixelsPerMeterAtOneMeter; int _UseAverageRayDistance; sampler2D _AverageRayDistanceBuffer; // For temporal filtering: float4x4 _CurrentCameraToPreviousCamera; sampler2D _PreviousReflectionTexture; sampler2D _PreviousCSZBuffer; float _TemporalAlpha; int _UseTemporalConfidence; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy; o.uv2 = v.texcoord.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv2.y = 1-o.uv2.y; #endif return o; } float2 mipToSize(int mip) { return floor(_ReflectionBufferSize * exp2(-mip)); } float3 ReconstructCSPosition(float2 S, float z) { float linEyeZ = -LinearEyeDepth(z); return float3(( (( S.xy * _MainTex_TexelSize.zw) ) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); } /** Read the camera-space position of the point at screen-space pixel ssP */ float3 GetPosition(float2 ssP) { float3 P; P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy); // Offset to pixel center P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); return P; } float square(float x) { return x*x; } float applyEdgeFade(float2 tsP, float fadeStrength) { float maxFade = 0.1; float2 itsP = float2(1.0, 1.0) - tsP; float dist = min(min(itsP.x, itsP.y), min(tsP.x, tsP.x)); float fade = dist / (maxFade*fadeStrength + 0.001); fade = max(min(fade, 1.0), 0.0); fade = pow(fade, 0.2); return fade; } float3 csMirrorVector(float3 csPosition, float3 csN) { float3 csE = -normalize(csPosition.xyz); float cos_o = dot(csN, csE); float3 c_mi = normalize((csN * (2.0 * cos_o)) - csE); return c_mi; } float4 fragRaytrace(v2f i, int stepRate) { float2 ssP = i.uv2.xy * _SourceToTempUV.zw; float3 csPosition = GetPosition(ssP); float smoothness = tex2D(_CameraGBufferTexture1, ssP).a; if (csPosition.z < -100.0 || smoothness == 0.0) { return float4(0.0,0.0,0.0,0.0); } if ((!_TraceEverywhere) && (smoothness < (1.0 - _MaxRoughness)) ) { return float4(0.0, 0.0, 0.0, 0.0); } float3 wsNormal = tex2D(_CameraGBufferTexture2, ssP).rgb * 2.0 - 1.0; int2 ssC = int2(ssP * _ScreenSize); float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 csRayDirection = csMirrorVector(csPosition, csN); if (_AllowBackwardsRays == 0 && csRayDirection.z > 0.0) { return float4(0.0, 0.0, 0.0, 0.0); } float maxRayTraceDistance = _MaxRayTraceDistance; float jitterFraction = 0.0f; float layerThickness = _LayerThickness; int maxSteps = _MaxSteps; // Bump the ray more in world space as it gets farther away (and so each pixel covers more WS distance) float rayBump = max(-0.01*csPosition.z, 0.001); float2 hitPixel; float3 csHitPoint; float stepCount; bool wasHit = castDenseScreenSpaceRay (csPosition + (csN) * rayBump, csRayDirection, _ProjectToPixelMatrix, _ScreenSize, _CameraClipInfo, jitterFraction, maxSteps, layerThickness, maxRayTraceDistance, hitPixel, stepRate, _EnableRefine == 1, _TraceBehindObjects == 1, csHitPoint, stepCount); float2 tsPResult = hitPixel / _ScreenSize; float rayDist = dot(csHitPoint - csPosition, csRayDirection); float confidence = 0.0; if (wasHit) { confidence = square(1.0 - max(2.0*float(stepCount) / float(maxSteps) - 1.0, 0.0)); confidence *= clamp(((_MaxRayTraceDistance - rayDist) / _FadeDistance), 0.0, 1.0); // Fake fresnel fade float3 csE = -normalize(csPosition.xyz); confidence *= max(0.0, lerp(pow(abs(dot(csRayDirection, -csE)), _FresnelFadePower), 1, 1.0 - _FresnelFade)); if (_TreatBackfaceHitAsMiss > 0) { float3 wsHitNormal = tex2Dlod(_CameraGBufferTexture2, float4(tsPResult, 0, 0)).rgb * 2.0 - 1.0; float3 wsRayDirection = mul(_CameraToWorldMatrix, float4(csRayDirection, 0)).xyz; if (dot(wsHitNormal, wsRayDirection) > 0) { confidence = 0.0; } } } else if (_FallbackToSky > 0) { float sceneZ = tex2Dlod(_CameraDepthTexture, float4(tsPResult, 0, 0)).r; sceneZ = -LinearEyeDepth(sceneZ); if (sceneZ < -1000) { // 1km ~= infinity confidence = 1.0; } } // Fade out reflections that hit near edge of screen, to prevent abrupt appearance/disappearance when object go off screen // Fade out reflections that hit near edge of screen, // to prevent abrupt appearance/disappearance when object go off screen float vignette = applyEdgeFade(tsPResult, _ScreenEdgeFading); confidence *= vignette; confidence *= vignette; return float4(tsPResult, rayDist, confidence); } float4 fragComposite(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; // View space point being shaded float3 C = GetPosition(tsP); // Final image before this pass float4 gbuffer3 = tex2D(_MainTex, i.uv); float4 specEmission = float4(0.0,0.0,0.0,0.0); float3 specColor = tex2D(_CameraGBufferTexture1, tsP).rgb; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP).a; float4 reflectionTexel = tex2D(_FinalReflectionTexture, tsP * _SourceToTempUV.xy); float4 gbuffer0 = tex2D(_CameraGBufferTexture0, tsP); // Let core Unity functions do the dirty work of applying the BRDF float3 baseColor = gbuffer0.rgb; float occlusion = gbuffer0.a; float oneMinusReflectivity; baseColor = EnergyConservationBetweenDiffuseAndSpecular(baseColor, specColor, oneMinusReflectivity); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csEyeVec = normalize(C); float3 eyeVec = mul(_CameraToWorldMatrix, float4(csEyeVec, 0)).xyz; float3 worldPos = mul(_CameraToWorldMatrix, float4(C, 1)).xyz; float cos_o = dot(wsNormal, eyeVec); float3 w_mi = -normalize((wsNormal * (2.0 * cos_o)) - eyeVec); float3 incomingRadiance = reflectionTexel.rgb; UnityLight light; light.color = 0; light.dir = 0; light.ndotl = 0; UnityIndirect ind; ind.diffuse = 0; ind.specular = incomingRadiance; float3 ssrResult = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, 1-roughness, wsNormal, -eyeVec, light, ind).rgb * _SSRMultiplier; float confidence = reflectionTexel.a; if(_EnableSSR == 0) { confidence = 0.0; } specEmission.rgb = tex2D(_CameraReflectionsTexture, tsP).rgb; float3 finalGlossyTerm; // Subtract out Unity's glossy result: (we're just applying the delta) if (_AdditiveReflection == 0) { gbuffer3 -= specEmission; // We may have blown out our dynamic range by adding then subtracting the reflection probes. // As a half-measure to fix this, simply clamp to zero gbuffer3 = max(gbuffer3, 0); finalGlossyTerm = lerp(specEmission.rgb, ssrResult, saturate(confidence)); } else { finalGlossyTerm = ssrResult*saturate(confidence); } if (_UseOcclusion) { finalGlossyTerm *= occlusion; } if (_DebugMode == 1) return float4(incomingRadiance,0); if (_DebugMode == 2) return float4(ssrResult,0); if (_DebugMode == 3) return float4(finalGlossyTerm,0); if (_DebugMode == 4) return confidence; if (_DebugMode == 5) return roughness; if (_DebugMode == 6) return float4(baseColor,0); if (_DebugMode == 7) return float4(specColor,0); if (_DebugMode == 8) return 1.0-oneMinusReflectivity; if (_DebugMode == 9) return float4(specEmission.rgb,0); if (_DebugMode == 10) return float4(specEmission.rgb-ssrResult,0); if (_DebugMode == 11) return float4(ssrResult-specEmission.rgb,0); if (_DebugMode == 12) return gbuffer3; if (_DebugMode == 13) return -float4(gbuffer3.rgb, 0.0); if (_DebugMode == 14) // Mip level, stored directly in ssrResult return float4(incomingRadiance, 0); // Additively blend the glossy GI result with the output buffer return gbuffer3 + float4(finalGlossyTerm, 0); } float roughnessWeight(float midpointRoughness, float tapRoughness) { return (1.0 - sqrt(sqrt(abs(midpointRoughness-tapRoughness)))); } float normalWeight(float3 midpointNormal, float3 tapNormal) { return clamp(dot(midpointNormal, tapNormal), 0, 1); } float highlightDecompression(float x) { return x / (1.0 - x); } float3 highlightDecompression(float3 x) { return float3( highlightDecompression(x.x), highlightDecompression(x.y), highlightDecompression(x.z)); } float highlightCompression(float x) { return x / (1.0 + x); } float3 highlightCompression(float3 x) { return float3( highlightCompression(x.x), highlightCompression(x.y), highlightCompression(x.z)); } float4 _Axis; float4 fragGBlur(v2f i) : SV_Target { int radius = 4; // Pixel being shaded float2 tsP = i.uv2.xy; float weightSum = 0.0; float gaussWeights[5] = {0.225, 0.150, 0.110, 0.075, 0.0525};//{0.225, 0.150, 0.110, 0.075, 0.0525}; float4 resultSum = float4(0.0, 0.0, 0.0, 0.0); float4 unweightedResultSum = float4(0.0, 0.0, 0.0, 0.0); float4 nAndRough = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = nAndRough.a; float3 midpointNormal = nAndRough.rgb * 2 - 1; if (_FullResolutionFiltering) { for (int i = -2*radius; i <= 2*radius; ++i) { float4 temp; float tapRoughness = midpointRoughness; float3 tapNormal = midpointNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)); temp = tex2D(_MainTex, tsTap); int gaussWeightIndex; #if defined(UNITY_COMPILER_HLSL) && !defined(SHADER_API_D3D9) gaussWeightIndex = abs(i) >> 1; // We purposefully do integer division, but that does not compile on hlsl2glsl #else gaussWeightIndex = frac(abs(i) / 2); #endif float weight = temp.a * gaussWeights[gaussWeightIndex]*0.5; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; resultSum += temp*weight; unweightedResultSum += temp; } } else { for (int i = -radius; i <= radius; ++i) { float4 temp; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)*2.0); temp = tex2D(_MainTex, tsTap); float weight = temp.a * gaussWeights[abs(i)]; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; if (_HighlightSuppression) { temp.rgb = highlightCompression(temp.rgb); } unweightedResultSum += temp; resultSum += temp*weight; } } if (weightSum > 0.01) { float invWeightSum = (1.0/weightSum); // Adding the sqrt seems to decrease temporal flickering at the expense // of having larger "halos" of fallback on rough surfaces // Subject to change with testing. Sqrt around only half the expression is *intentional*. float confidence = min(resultSum.a * sqrt(max(invWeightSum, 2.0)), 1.0); float3 finalColor = resultSum.rgb * invWeightSum; if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, confidence); } else { float3 finalColor = unweightedResultSum.rgb / (2 * radius + 1); if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, 0.0); } } sampler2D _ReflectionTexture0; sampler2D _ReflectionTexture1; sampler2D _ReflectionTexture2; sampler2D _ReflectionTexture3; sampler2D _ReflectionTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getReflectionValue(float2 tsP, int mip) { float4 coord = float4(tsP,0,0); if (mip == 0) { return tex2Dlod(_ReflectionTexture0, coord); } else if (mip == 1) { return tex2Dlod(_ReflectionTexture1, coord); } else if (mip == 2) { return tex2Dlod(_ReflectionTexture2, coord); } else if (mip == 3) { return tex2Dlod(_ReflectionTexture3, coord); } else { return tex2Dlod(_ReflectionTexture4, coord); } } sampler2D _EdgeTexture0; sampler2D _EdgeTexture1; sampler2D _EdgeTexture2; sampler2D _EdgeTexture3; sampler2D _EdgeTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getEdgeValue(float2 tsP, int mip) { float4 coord = float4(tsP + float2(1.0/(2 * mipToSize(mip))),0,0); if (mip == 0) { return tex2Dlod(_EdgeTexture0, coord); } else if (mip == 1) { return tex2Dlod(_EdgeTexture1, coord); } else if (mip == 2) { return tex2Dlod(_EdgeTexture2, coord); } else if (mip == 3) { return tex2Dlod(_EdgeTexture3, coord); } else { return tex2Dlod(_EdgeTexture4, coord); } } float2 centerPixel(float2 inputP) { return floor(inputP - float2(0.5,0.5)) + float2(0.5,0.5); } float2 snapToTexelCenter(float2 inputP, float2 texSize, float2 texSizeInv) { return centerPixel(inputP * texSize) * texSizeInv; } float4 bilateralUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; float2 fullTexSize = _ReflectionBufferSize; float2 fullTexSizeInv = 1.0 / fullTexSize; float4 hiP0 = float4(snapToTexelCenter(p0, fullTexSize, fullTexSizeInv), 0,0); float4 hiP3 = float4(snapToTexelCenter(p3, fullTexSize, fullTexSizeInv), 0,0); float4 hiP1 = float4(snapToTexelCenter(p1, fullTexSize, fullTexSizeInv), 0,0); float4 hiP2 = float4(snapToTexelCenter(p2, fullTexSize, fullTexSizeInv), 0,0); float4 tempCenter = tex2Dlod(_NormalAndRoughnessTexture, float4(tsP, 0, 0)); float3 n = tempCenter.xyz * 2 - 1; float4 temp0 = tex2Dlod(_NormalAndRoughnessTexture, hiP0); float4 temp1 = tex2Dlod(_NormalAndRoughnessTexture, hiP1); float4 temp2 = tex2Dlod(_NormalAndRoughnessTexture, hiP2); float4 temp3 = tex2Dlod(_NormalAndRoughnessTexture, hiP3); float3 n0 = temp0.xyz * 2 - 1; float3 n1 = temp1.xyz * 2 - 1; float3 n2 = temp2.xyz * 2 - 1; float3 n3 = temp3.xyz * 2 - 1; a0 *= normalWeight(n, n0); a1 *= normalWeight(n, n1); a2 *= normalWeight(n, n2); a3 *= normalWeight(n, n3); float r = tempCenter.a; float r0 = temp0.a; float r1 = temp1.a; float r2 = temp2.a; float r3 = temp3.a; a0 *= roughnessWeight(r, r0); a1 *= roughnessWeight(r, r1); a2 *= roughnessWeight(r, r2); a3 *= roughnessWeight(r, r3); // Slightly offset from zero a0 = max(a0, 0.001); a1 = max(a1, 0.001); a2 = max(a2, 0.001); a3 = max(a3, 0.001); // Nearest neighbor // a0 = a1 = a2 = a3 = 1.0; // Normalize the blending weights (weights were chosen so that // the denominator can never be zero) float norm = 1.0 / (a0 + a1 + a2 + a3); // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3) * norm; //return V0; return value; } /** Explicit bilinear fetches; must be used if the reflection buffer is bound using point sampling */ float4 bilinearUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3); return value; } // Unity's roughness is GGX roughness squared float roughnessToBlinnPhongExponent(float roughness) { float r2 = roughness*roughness; return 2.0f / r2*r2 - 2.0f; } float glossyLobeSlope(float roughness) { return pow(roughness, 4.0/3.0); } // Empirically based on our filter: // Mip | Pixels // -------------- // 0 | 1 no filter, so single pixel // 1 | 17 2r + 1 filter applied once, grabbing from pixels r away in either direction (r=8, four samples times stride of 2) // 2 | 50 2r + 1 filter applied on double size pixels, and each of those pixels had reached another r out to the side 2(2r + 1) + m_1 // 3 | 118 4(2r + 1) + m_2 // 4 | 254 8(2r + 1) + m_3 // // Approximated by pixels = 16*2^mip-15 // rearranging we get mip = log_2((pixels + 15) / 16) // float filterFootprintInPixelsToMip(float footprint) { return log2((footprint + 15) / 16); } float3 ansiGradient(float t) { //return float3(t, t, t); return fmod(floor(t * float3(8.0, 4.0, 2.0)), 2.0); } float4 fragCompositeSSR(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP * _SourceToTempUV.zw).a; float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).z; } // Get the camera space position of the reflection hit float3 csPosition = GetPosition(tsP); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 c_mi = csMirrorVector(csPosition, csN); float3 csHitpoint = c_mi * rayDistance + csPosition; float gatherFootprintInMeters = glossyLobeSlope(roughness) * rayDistance; // We could add a term that incorporates the normal // This approximation assumes reflections happen at a glancing angle float filterFootprintInPixels = gatherFootprintInMeters * _PixelsPerMeterAtOneMeter / csHitpoint.z; if (_HalfResolution == 1) { filterFootprintInPixels *= 0.5; } float mip = filterFootprintInPixelsToMip(filterFootprintInPixels); float nonPhysicalMip = pow(roughness, 3.0 / 4.0) * UNITY_SPECCUBE_LOD_STEPS; if (_HalfResolution == 1) { nonPhysicalMip = nonPhysicalMip * 0.7; } mip = lerp(nonPhysicalMip, mip, _DistanceBlur); mip = max(0, min(4, mip + _MipBias)); if (_DebugMode == 14) { return float4(ansiGradient(mip*0.25), 1.0); } float4 result; { int mipMin = int(mip); int mipMax = min(mipMin + 1, 4); float mipLerp = mip-mipMin; if (_BilateralUpsampling == 1) { if (_UseEdgeDetector == 1) { float nonEdginess = lerp(getEdgeValue(tsP, mipMin), getEdgeValue(tsP, mipMax), mipLerp); if (nonEdginess < 0.9) { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); if (_VisualizeWhereBilateral == 1) { result = float4(1, 0, 0, 0); } } else { result = lerp(bilinearUpsampleReflection(tsP, mipMin), bilinearUpsampleReflection(tsP, mipMax), mipLerp); } } else { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); } } else { float4 minResult = getReflectionValue(tsP, mipMin); float4 maxResult = getReflectionValue(tsP, mipMax); result = lerp(minResult, maxResult, mipLerp); result.a = min(minResult.a, maxResult.a); } } result.a = min(result.a, 1.0); float vignette = applyEdgeFade(tsP, _ScreenEdgeFading); result.a *= vignette; float alphaModifier = 1.0 - clamp((roughness - (_MaxRoughness - _RoughnessFalloffRange)) / _RoughnessFalloffRange, 0.0, 1.0); result.a *= alphaModifier; return result; } float4 fragBlit(v2f i) : SV_Target { // Pixel being shaded float2 ssP = i.uv2.xy; return tex2D(_MainTex, ssP); } float edgeDetector(float2 tsP, float2 axis, float2 texelSize) { float4 temp = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = temp.a; float3 midpointNormal = temp.rgb * 2 - 1; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (axis * _MainTex_TexelSize.xy); temp = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = temp.a; tapNormal = temp.rgb * 2 - 1; float weight = 1.0; weight *= roughnessWeight(midpointRoughness, tapRoughness); weight *= normalWeight(midpointNormal, tapNormal); return weight; } float4 fragEdge(v2f i) : SV_Target { float2 tsP = i.uv2.xy; return edgeDetector(tsP, float2(1,0), _MainTex_TexelSize.xy) * edgeDetector(tsP, float2(0,1), _MainTex_TexelSize.xy); } int _LastMip; float4 fragMin(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float2 lastTexSize = mipToSize(_LastMip); float2 lastTexSizeInv = 1.0 / lastTexSize;//_SourceToTempUV.xy * exp2(mip); float2 p00 = snapToTexelCenter(tsP, lastTexSize, lastTexSizeInv); float2 p11 = p00 + lastTexSizeInv; return min( min(tex2D(_MainTex, p00), tex2D(_MainTex, p11)), min(tex2D(_MainTex, float2(p00.x, p11.y)), tex2D(_MainTex, float2(p11.x, p00.y))) ); } float4 fragResolveHitPoints(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float4 temp = tex2D(_HitPointTexture, tsP); float2 hitPoint = temp.xy; float confidence = temp.w; float3 colorResult = confidence > 0.0 ? tex2D(_MainTex, hitPoint).rgb : tex2D(_CameraReflectionsTexture, tsP).rgb; #ifdef UNITY_COMPILER_HLSL if (any(isnan(colorResult))) colorResult = float3(0.0, 0.0, 0.0); // As of 11/29/2015, on Unity 5.3 on a Windows 8.1 computer with a NVIDIA GeForce 980, // with driver 347.62, the above check does not actually work to get rid of NaNs! // So we add this "redundant" check. if (!all(isfinite(colorResult))) colorResult = float3(0.0, 0.0, 0.0); #endif return float4(colorResult, confidence); } float4 fragBilatKeyPack(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float3 csN = tex2D(_CameraGBufferTexture2, tsP).xyz; float roughness = tex2D(_CameraGBufferTexture1, tsP).a; return float4(csN, roughness); } float4 fragDepthToCSZ(v2f i) : SV_Target { float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv2.xy); return float4(-LinearEyeDepth(depth), 0.0, 0.0, 0.0); } float3 csPositionToOldNormalizedScreenCoordinate(float3 csPosition) { // We could multiply these matrices together on the CPU float4 oldCSPosition = mul(_CurrentCameraToPreviousCamera, float4(csPosition, 1.0)); float4 oldSSCoordinate = mul(_ProjectToPixelMatrix, oldCSPosition); return float3((oldSSCoordinate.xy / oldSSCoordinate.w) * _InvScreenSize, oldCSPosition.z); } float4 fragTemporalFilter(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, tsP); float3 csPosition = GetPosition(tsP); float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).b; } // Virtual is the physics sense, not the other 20+ interpretations in CG float3 virtualReflectionCSPosition = normalize(csPosition) * rayDistance + csPosition; float3 oldTSP = csPositionToOldNormalizedScreenCoordinate(csPosition); float3 oldVirtualReflectionTSP = csPositionToOldNormalizedScreenCoordinate(virtualReflectionCSPosition); float oldCSZ = tex2D(_PreviousCSZBuffer, oldTSP.xy); float4 currentResult = tex2D(_FinalReflectionTexture, tsP); float4 oldResult = tex2D(_PreviousReflectionTexture, oldVirtualReflectionTSP.xy); float zError = abs(oldCSZ - oldTSP.z); // Start knocking down contribution at 5cm error, go to 0 at 5 more cm later float fallbackStart = 0.05; float fallbackEnd = 0.05; float zAlphaModifier = 1.0 - clamp((zError - fallbackStart) * (1.0 / fallbackEnd), 0.0, 1.0); float alpha = _TemporalAlpha; alpha *= zAlphaModifier; if (_UseTemporalConfidence) { alpha *= oldResult.w; } // Always compress currentResult.rgb = highlightCompression(currentResult.rgb); oldResult.rgb = highlightCompression(oldResult.rgb); float4 resultValue = lerp(currentResult, oldResult, alpha); resultValue.rgb = highlightDecompression(resultValue.rgb); return resultValue; } float4 fragAverageRayDistance(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float sumRayDistance = tex2D(_HitPointTexture, tsP).b; float sumWeight = 1.0; float roughness = 1.0 - tex2D(_CameraGBufferTexture1, tsP).a; float step = roughness * 3.0; /* for (float x = -step; x < step; x += 2 * step) { for (float y = -step; y < step; y += 2 * step) { float weight = 0.5; sumRayDistance += weight * tex2D(_HitPointTexture, (float2(x,y) * _InvScreenSize) + tsP).b; sumWeight += weight; } } */ return float4(sumRayDistance / sumWeight, 0.0, 0.0, 0.0); } static const int NUM_POISSON_TAPS = 12; // Same as used in CameraMotionBlur.shader static const float2 poissonSamples[NUM_POISSON_TAPS] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; float4 fragFilterSharpReflections(v2f i) : SV_Target { // Could improve perf by not computing blur when we won't be sampling the highest level anyways float2 tsP = i.uv2.xy; float4 sum = 0.0; float sampleRadius = _MainTex_TexelSize.xy * 2.0; for (int i = 0; i < NUM_POISSON_TAPS; i++) { float2 p = tsP + poissonSamples[i] * sampleRadius; float4 tap = tex2Dlod(_MainTex, float4(p, 0, 0)); if (_HighlightSuppression) { tap.rgb = highlightCompression(tap.rgb); } sum += tap; } float4 result = sum / float(NUM_POISSON_TAPS); if (_HighlightSuppression) { result.rgb = highlightDecompression(result.rgb); } return result; } #line 1047 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif /* UNITY: Original start of shader */ #pragma exclude_renderers gles xbox360 ps3 #pragma vertex vert #pragma fragment fragGBlur #pragma target 3.0 SAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/ShadersÓ˙˙˙küń'¨ô™!]{Ňô SHADOWS_SOFTDIRLIGHTMAP_COMBINEDDIRLIGHTMAP_SEPARATEDYNAMICLIGHTMAP_ON INSTANCING_ONڑť†#line 1057 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #line 12 #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "ScreenSpaceRaytrace.cginc" float4 _ProjInfo; float4x4 _WorldToCameraMatrix; float4x4 _CameraToWorldMatrix; float4x4 _ProjectToPixelMatrix; float2 _ScreenSize; float2 _ReflectionBufferSize; float2 _InvScreenSize; float3 _CameraClipInfo; sampler2D _CameraGBufferTexture0; sampler2D _CameraGBufferTexture1; sampler2D _CameraGBufferTexture2; sampler2D _CameraGBufferTexture3; sampler2D _CameraReflectionsTexture; float _CurrentMipLevel; float _RayStepSize; float _MaxRayTraceDistance; float _LayerThickness; float _FresnelFade; float _FresnelFadePower; sampler2D _MainTex; int _VisualizeWhereBilateral; int _EnableSSR; int _DebugMode; int _HalfResolution; int _TreatBackfaceHitAsMiss; int _AllowBackwardsRays; int _TraceEverywhere; // RG: SS Hitpoint of ray // B: distance ray travelled, used for mip-selection in the final resolve // A: confidence value sampler2D _HitPointTexture; sampler2D _FinalReflectionTexture; // RGB: camera-space normal (encoded in [0-1]) // A: Roughness sampler2D _NormalAndRoughnessTexture; float4 _MainTex_TexelSize; float4 _SourceToTempUV; int _EnableRefine; int _AdditiveReflection; int _ImproveCorners; float _ScreenEdgeFading; float _MipBias; int _UseOcclusion; int _MaxSteps; int _FullResolutionFiltering; int _BilateralUpsampling; float _MaxRoughness; float _RoughnessFalloffRange; float _SSRMultiplier; float _FadeDistance; float _DistanceBlur; int _TraceBehindObjects; int _FallbackToSky; int _UseEdgeDetector; int _HighlightSuppression; /** The height in pixels of a 1m object if viewed from 1m away. */ float _PixelsPerMeterAtOneMeter; int _UseAverageRayDistance; sampler2D _AverageRayDistanceBuffer; // For temporal filtering: float4x4 _CurrentCameraToPreviousCamera; sampler2D _PreviousReflectionTexture; sampler2D _PreviousCSZBuffer; float _TemporalAlpha; int _UseTemporalConfidence; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy; o.uv2 = v.texcoord.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv2.y = 1-o.uv2.y; #endif return o; } float2 mipToSize(int mip) { return floor(_ReflectionBufferSize * exp2(-mip)); } float3 ReconstructCSPosition(float2 S, float z) { float linEyeZ = -LinearEyeDepth(z); return float3(( (( S.xy * _MainTex_TexelSize.zw) ) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); } /** Read the camera-space position of the point at screen-space pixel ssP */ float3 GetPosition(float2 ssP) { float3 P; P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy); // Offset to pixel center P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); return P; } float square(float x) { return x*x; } float applyEdgeFade(float2 tsP, float fadeStrength) { float maxFade = 0.1; float2 itsP = float2(1.0, 1.0) - tsP; float dist = min(min(itsP.x, itsP.y), min(tsP.x, tsP.x)); float fade = dist / (maxFade*fadeStrength + 0.001); fade = max(min(fade, 1.0), 0.0); fade = pow(fade, 0.2); return fade; } float3 csMirrorVector(float3 csPosition, float3 csN) { float3 csE = -normalize(csPosition.xyz); float cos_o = dot(csN, csE); float3 c_mi = normalize((csN * (2.0 * cos_o)) - csE); return c_mi; } float4 fragRaytrace(v2f i, int stepRate) { float2 ssP = i.uv2.xy * _SourceToTempUV.zw; float3 csPosition = GetPosition(ssP); float smoothness = tex2D(_CameraGBufferTexture1, ssP).a; if (csPosition.z < -100.0 || smoothness == 0.0) { return float4(0.0,0.0,0.0,0.0); } if ((!_TraceEverywhere) && (smoothness < (1.0 - _MaxRoughness)) ) { return float4(0.0, 0.0, 0.0, 0.0); } float3 wsNormal = tex2D(_CameraGBufferTexture2, ssP).rgb * 2.0 - 1.0; int2 ssC = int2(ssP * _ScreenSize); float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 csRayDirection = csMirrorVector(csPosition, csN); if (_AllowBackwardsRays == 0 && csRayDirection.z > 0.0) { return float4(0.0, 0.0, 0.0, 0.0); } float maxRayTraceDistance = _MaxRayTraceDistance; float jitterFraction = 0.0f; float layerThickness = _LayerThickness; int maxSteps = _MaxSteps; // Bump the ray more in world space as it gets farther away (and so each pixel covers more WS distance) float rayBump = max(-0.01*csPosition.z, 0.001); float2 hitPixel; float3 csHitPoint; float stepCount; bool wasHit = castDenseScreenSpaceRay (csPosition + (csN) * rayBump, csRayDirection, _ProjectToPixelMatrix, _ScreenSize, _CameraClipInfo, jitterFraction, maxSteps, layerThickness, maxRayTraceDistance, hitPixel, stepRate, _EnableRefine == 1, _TraceBehindObjects == 1, csHitPoint, stepCount); float2 tsPResult = hitPixel / _ScreenSize; float rayDist = dot(csHitPoint - csPosition, csRayDirection); float confidence = 0.0; if (wasHit) { confidence = square(1.0 - max(2.0*float(stepCount) / float(maxSteps) - 1.0, 0.0)); confidence *= clamp(((_MaxRayTraceDistance - rayDist) / _FadeDistance), 0.0, 1.0); // Fake fresnel fade float3 csE = -normalize(csPosition.xyz); confidence *= max(0.0, lerp(pow(abs(dot(csRayDirection, -csE)), _FresnelFadePower), 1, 1.0 - _FresnelFade)); if (_TreatBackfaceHitAsMiss > 0) { float3 wsHitNormal = tex2Dlod(_CameraGBufferTexture2, float4(tsPResult, 0, 0)).rgb * 2.0 - 1.0; float3 wsRayDirection = mul(_CameraToWorldMatrix, float4(csRayDirection, 0)).xyz; if (dot(wsHitNormal, wsRayDirection) > 0) { confidence = 0.0; } } } else if (_FallbackToSky > 0) { float sceneZ = tex2Dlod(_CameraDepthTexture, float4(tsPResult, 0, 0)).r; sceneZ = -LinearEyeDepth(sceneZ); if (sceneZ < -1000) { // 1km ~= infinity confidence = 1.0; } } // Fade out reflections that hit near edge of screen, to prevent abrupt appearance/disappearance when object go off screen // Fade out reflections that hit near edge of screen, // to prevent abrupt appearance/disappearance when object go off screen float vignette = applyEdgeFade(tsPResult, _ScreenEdgeFading); confidence *= vignette; confidence *= vignette; return float4(tsPResult, rayDist, confidence); } float4 fragComposite(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; // View space point being shaded float3 C = GetPosition(tsP); // Final image before this pass float4 gbuffer3 = tex2D(_MainTex, i.uv); float4 specEmission = float4(0.0,0.0,0.0,0.0); float3 specColor = tex2D(_CameraGBufferTexture1, tsP).rgb; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP).a; float4 reflectionTexel = tex2D(_FinalReflectionTexture, tsP * _SourceToTempUV.xy); float4 gbuffer0 = tex2D(_CameraGBufferTexture0, tsP); // Let core Unity functions do the dirty work of applying the BRDF float3 baseColor = gbuffer0.rgb; float occlusion = gbuffer0.a; float oneMinusReflectivity; baseColor = EnergyConservationBetweenDiffuseAndSpecular(baseColor, specColor, oneMinusReflectivity); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csEyeVec = normalize(C); float3 eyeVec = mul(_CameraToWorldMatrix, float4(csEyeVec, 0)).xyz; float3 worldPos = mul(_CameraToWorldMatrix, float4(C, 1)).xyz; float cos_o = dot(wsNormal, eyeVec); float3 w_mi = -normalize((wsNormal * (2.0 * cos_o)) - eyeVec); float3 incomingRadiance = reflectionTexel.rgb; UnityLight light; light.color = 0; light.dir = 0; light.ndotl = 0; UnityIndirect ind; ind.diffuse = 0; ind.specular = incomingRadiance; float3 ssrResult = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, 1-roughness, wsNormal, -eyeVec, light, ind).rgb * _SSRMultiplier; float confidence = reflectionTexel.a; if(_EnableSSR == 0) { confidence = 0.0; } specEmission.rgb = tex2D(_CameraReflectionsTexture, tsP).rgb; float3 finalGlossyTerm; // Subtract out Unity's glossy result: (we're just applying the delta) if (_AdditiveReflection == 0) { gbuffer3 -= specEmission; // We may have blown out our dynamic range by adding then subtracting the reflection probes. // As a half-measure to fix this, simply clamp to zero gbuffer3 = max(gbuffer3, 0); finalGlossyTerm = lerp(specEmission.rgb, ssrResult, saturate(confidence)); } else { finalGlossyTerm = ssrResult*saturate(confidence); } if (_UseOcclusion) { finalGlossyTerm *= occlusion; } if (_DebugMode == 1) return float4(incomingRadiance,0); if (_DebugMode == 2) return float4(ssrResult,0); if (_DebugMode == 3) return float4(finalGlossyTerm,0); if (_DebugMode == 4) return confidence; if (_DebugMode == 5) return roughness; if (_DebugMode == 6) return float4(baseColor,0); if (_DebugMode == 7) return float4(specColor,0); if (_DebugMode == 8) return 1.0-oneMinusReflectivity; if (_DebugMode == 9) return float4(specEmission.rgb,0); if (_DebugMode == 10) return float4(specEmission.rgb-ssrResult,0); if (_DebugMode == 11) return float4(ssrResult-specEmission.rgb,0); if (_DebugMode == 12) return gbuffer3; if (_DebugMode == 13) return -float4(gbuffer3.rgb, 0.0); if (_DebugMode == 14) // Mip level, stored directly in ssrResult return float4(incomingRadiance, 0); // Additively blend the glossy GI result with the output buffer return gbuffer3 + float4(finalGlossyTerm, 0); } float roughnessWeight(float midpointRoughness, float tapRoughness) { return (1.0 - sqrt(sqrt(abs(midpointRoughness-tapRoughness)))); } float normalWeight(float3 midpointNormal, float3 tapNormal) { return clamp(dot(midpointNormal, tapNormal), 0, 1); } float highlightDecompression(float x) { return x / (1.0 - x); } float3 highlightDecompression(float3 x) { return float3( highlightDecompression(x.x), highlightDecompression(x.y), highlightDecompression(x.z)); } float highlightCompression(float x) { return x / (1.0 + x); } float3 highlightCompression(float3 x) { return float3( highlightCompression(x.x), highlightCompression(x.y), highlightCompression(x.z)); } float4 _Axis; float4 fragGBlur(v2f i) : SV_Target { int radius = 4; // Pixel being shaded float2 tsP = i.uv2.xy; float weightSum = 0.0; float gaussWeights[5] = {0.225, 0.150, 0.110, 0.075, 0.0525};//{0.225, 0.150, 0.110, 0.075, 0.0525}; float4 resultSum = float4(0.0, 0.0, 0.0, 0.0); float4 unweightedResultSum = float4(0.0, 0.0, 0.0, 0.0); float4 nAndRough = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = nAndRough.a; float3 midpointNormal = nAndRough.rgb * 2 - 1; if (_FullResolutionFiltering) { for (int i = -2*radius; i <= 2*radius; ++i) { float4 temp; float tapRoughness = midpointRoughness; float3 tapNormal = midpointNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)); temp = tex2D(_MainTex, tsTap); int gaussWeightIndex; #if defined(UNITY_COMPILER_HLSL) && !defined(SHADER_API_D3D9) gaussWeightIndex = abs(i) >> 1; // We purposefully do integer division, but that does not compile on hlsl2glsl #else gaussWeightIndex = frac(abs(i) / 2); #endif float weight = temp.a * gaussWeights[gaussWeightIndex]*0.5; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; resultSum += temp*weight; unweightedResultSum += temp; } } else { for (int i = -radius; i <= radius; ++i) { float4 temp; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)*2.0); temp = tex2D(_MainTex, tsTap); float weight = temp.a * gaussWeights[abs(i)]; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; if (_HighlightSuppression) { temp.rgb = highlightCompression(temp.rgb); } unweightedResultSum += temp; resultSum += temp*weight; } } if (weightSum > 0.01) { float invWeightSum = (1.0/weightSum); // Adding the sqrt seems to decrease temporal flickering at the expense // of having larger "halos" of fallback on rough surfaces // Subject to change with testing. Sqrt around only half the expression is *intentional*. float confidence = min(resultSum.a * sqrt(max(invWeightSum, 2.0)), 1.0); float3 finalColor = resultSum.rgb * invWeightSum; if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, confidence); } else { float3 finalColor = unweightedResultSum.rgb / (2 * radius + 1); if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, 0.0); } } sampler2D _ReflectionTexture0; sampler2D _ReflectionTexture1; sampler2D _ReflectionTexture2; sampler2D _ReflectionTexture3; sampler2D _ReflectionTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getReflectionValue(float2 tsP, int mip) { float4 coord = float4(tsP,0,0); if (mip == 0) { return tex2Dlod(_ReflectionTexture0, coord); } else if (mip == 1) { return tex2Dlod(_ReflectionTexture1, coord); } else if (mip == 2) { return tex2Dlod(_ReflectionTexture2, coord); } else if (mip == 3) { return tex2Dlod(_ReflectionTexture3, coord); } else { return tex2Dlod(_ReflectionTexture4, coord); } } sampler2D _EdgeTexture0; sampler2D _EdgeTexture1; sampler2D _EdgeTexture2; sampler2D _EdgeTexture3; sampler2D _EdgeTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getEdgeValue(float2 tsP, int mip) { float4 coord = float4(tsP + float2(1.0/(2 * mipToSize(mip))),0,0); if (mip == 0) { return tex2Dlod(_EdgeTexture0, coord); } else if (mip == 1) { return tex2Dlod(_EdgeTexture1, coord); } else if (mip == 2) { return tex2Dlod(_EdgeTexture2, coord); } else if (mip == 3) { return tex2Dlod(_EdgeTexture3, coord); } else { return tex2Dlod(_EdgeTexture4, coord); } } float2 centerPixel(float2 inputP) { return floor(inputP - float2(0.5,0.5)) + float2(0.5,0.5); } float2 snapToTexelCenter(float2 inputP, float2 texSize, float2 texSizeInv) { return centerPixel(inputP * texSize) * texSizeInv; } float4 bilateralUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; float2 fullTexSize = _ReflectionBufferSize; float2 fullTexSizeInv = 1.0 / fullTexSize; float4 hiP0 = float4(snapToTexelCenter(p0, fullTexSize, fullTexSizeInv), 0,0); float4 hiP3 = float4(snapToTexelCenter(p3, fullTexSize, fullTexSizeInv), 0,0); float4 hiP1 = float4(snapToTexelCenter(p1, fullTexSize, fullTexSizeInv), 0,0); float4 hiP2 = float4(snapToTexelCenter(p2, fullTexSize, fullTexSizeInv), 0,0); float4 tempCenter = tex2Dlod(_NormalAndRoughnessTexture, float4(tsP, 0, 0)); float3 n = tempCenter.xyz * 2 - 1; float4 temp0 = tex2Dlod(_NormalAndRoughnessTexture, hiP0); float4 temp1 = tex2Dlod(_NormalAndRoughnessTexture, hiP1); float4 temp2 = tex2Dlod(_NormalAndRoughnessTexture, hiP2); float4 temp3 = tex2Dlod(_NormalAndRoughnessTexture, hiP3); float3 n0 = temp0.xyz * 2 - 1; float3 n1 = temp1.xyz * 2 - 1; float3 n2 = temp2.xyz * 2 - 1; float3 n3 = temp3.xyz * 2 - 1; a0 *= normalWeight(n, n0); a1 *= normalWeight(n, n1); a2 *= normalWeight(n, n2); a3 *= normalWeight(n, n3); float r = tempCenter.a; float r0 = temp0.a; float r1 = temp1.a; float r2 = temp2.a; float r3 = temp3.a; a0 *= roughnessWeight(r, r0); a1 *= roughnessWeight(r, r1); a2 *= roughnessWeight(r, r2); a3 *= roughnessWeight(r, r3); // Slightly offset from zero a0 = max(a0, 0.001); a1 = max(a1, 0.001); a2 = max(a2, 0.001); a3 = max(a3, 0.001); // Nearest neighbor // a0 = a1 = a2 = a3 = 1.0; // Normalize the blending weights (weights were chosen so that // the denominator can never be zero) float norm = 1.0 / (a0 + a1 + a2 + a3); // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3) * norm; //return V0; return value; } /** Explicit bilinear fetches; must be used if the reflection buffer is bound using point sampling */ float4 bilinearUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3); return value; } // Unity's roughness is GGX roughness squared float roughnessToBlinnPhongExponent(float roughness) { float r2 = roughness*roughness; return 2.0f / r2*r2 - 2.0f; } float glossyLobeSlope(float roughness) { return pow(roughness, 4.0/3.0); } // Empirically based on our filter: // Mip | Pixels // -------------- // 0 | 1 no filter, so single pixel // 1 | 17 2r + 1 filter applied once, grabbing from pixels r away in either direction (r=8, four samples times stride of 2) // 2 | 50 2r + 1 filter applied on double size pixels, and each of those pixels had reached another r out to the side 2(2r + 1) + m_1 // 3 | 118 4(2r + 1) + m_2 // 4 | 254 8(2r + 1) + m_3 // // Approximated by pixels = 16*2^mip-15 // rearranging we get mip = log_2((pixels + 15) / 16) // float filterFootprintInPixelsToMip(float footprint) { return log2((footprint + 15) / 16); } float3 ansiGradient(float t) { //return float3(t, t, t); return fmod(floor(t * float3(8.0, 4.0, 2.0)), 2.0); } float4 fragCompositeSSR(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP * _SourceToTempUV.zw).a; float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).z; } // Get the camera space position of the reflection hit float3 csPosition = GetPosition(tsP); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 c_mi = csMirrorVector(csPosition, csN); float3 csHitpoint = c_mi * rayDistance + csPosition; float gatherFootprintInMeters = glossyLobeSlope(roughness) * rayDistance; // We could add a term that incorporates the normal // This approximation assumes reflections happen at a glancing angle float filterFootprintInPixels = gatherFootprintInMeters * _PixelsPerMeterAtOneMeter / csHitpoint.z; if (_HalfResolution == 1) { filterFootprintInPixels *= 0.5; } float mip = filterFootprintInPixelsToMip(filterFootprintInPixels); float nonPhysicalMip = pow(roughness, 3.0 / 4.0) * UNITY_SPECCUBE_LOD_STEPS; if (_HalfResolution == 1) { nonPhysicalMip = nonPhysicalMip * 0.7; } mip = lerp(nonPhysicalMip, mip, _DistanceBlur); mip = max(0, min(4, mip + _MipBias)); if (_DebugMode == 14) { return float4(ansiGradient(mip*0.25), 1.0); } float4 result; { int mipMin = int(mip); int mipMax = min(mipMin + 1, 4); float mipLerp = mip-mipMin; if (_BilateralUpsampling == 1) { if (_UseEdgeDetector == 1) { float nonEdginess = lerp(getEdgeValue(tsP, mipMin), getEdgeValue(tsP, mipMax), mipLerp); if (nonEdginess < 0.9) { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); if (_VisualizeWhereBilateral == 1) { result = float4(1, 0, 0, 0); } } else { result = lerp(bilinearUpsampleReflection(tsP, mipMin), bilinearUpsampleReflection(tsP, mipMax), mipLerp); } } else { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); } } else { float4 minResult = getReflectionValue(tsP, mipMin); float4 maxResult = getReflectionValue(tsP, mipMax); result = lerp(minResult, maxResult, mipLerp); result.a = min(minResult.a, maxResult.a); } } result.a = min(result.a, 1.0); float vignette = applyEdgeFade(tsP, _ScreenEdgeFading); result.a *= vignette; float alphaModifier = 1.0 - clamp((roughness - (_MaxRoughness - _RoughnessFalloffRange)) / _RoughnessFalloffRange, 0.0, 1.0); result.a *= alphaModifier; return result; } float4 fragBlit(v2f i) : SV_Target { // Pixel being shaded float2 ssP = i.uv2.xy; return tex2D(_MainTex, ssP); } float edgeDetector(float2 tsP, float2 axis, float2 texelSize) { float4 temp = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = temp.a; float3 midpointNormal = temp.rgb * 2 - 1; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (axis * _MainTex_TexelSize.xy); temp = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = temp.a; tapNormal = temp.rgb * 2 - 1; float weight = 1.0; weight *= roughnessWeight(midpointRoughness, tapRoughness); weight *= normalWeight(midpointNormal, tapNormal); return weight; } float4 fragEdge(v2f i) : SV_Target { float2 tsP = i.uv2.xy; return edgeDetector(tsP, float2(1,0), _MainTex_TexelSize.xy) * edgeDetector(tsP, float2(0,1), _MainTex_TexelSize.xy); } int _LastMip; float4 fragMin(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float2 lastTexSize = mipToSize(_LastMip); float2 lastTexSizeInv = 1.0 / lastTexSize;//_SourceToTempUV.xy * exp2(mip); float2 p00 = snapToTexelCenter(tsP, lastTexSize, lastTexSizeInv); float2 p11 = p00 + lastTexSizeInv; return min( min(tex2D(_MainTex, p00), tex2D(_MainTex, p11)), min(tex2D(_MainTex, float2(p00.x, p11.y)), tex2D(_MainTex, float2(p11.x, p00.y))) ); } float4 fragResolveHitPoints(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float4 temp = tex2D(_HitPointTexture, tsP); float2 hitPoint = temp.xy; float confidence = temp.w; float3 colorResult = confidence > 0.0 ? tex2D(_MainTex, hitPoint).rgb : tex2D(_CameraReflectionsTexture, tsP).rgb; #ifdef UNITY_COMPILER_HLSL if (any(isnan(colorResult))) colorResult = float3(0.0, 0.0, 0.0); // As of 11/29/2015, on Unity 5.3 on a Windows 8.1 computer with a NVIDIA GeForce 980, // with driver 347.62, the above check does not actually work to get rid of NaNs! // So we add this "redundant" check. if (!all(isfinite(colorResult))) colorResult = float3(0.0, 0.0, 0.0); #endif return float4(colorResult, confidence); } float4 fragBilatKeyPack(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float3 csN = tex2D(_CameraGBufferTexture2, tsP).xyz; float roughness = tex2D(_CameraGBufferTexture1, tsP).a; return float4(csN, roughness); } float4 fragDepthToCSZ(v2f i) : SV_Target { float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv2.xy); return float4(-LinearEyeDepth(depth), 0.0, 0.0, 0.0); } float3 csPositionToOldNormalizedScreenCoordinate(float3 csPosition) { // We could multiply these matrices together on the CPU float4 oldCSPosition = mul(_CurrentCameraToPreviousCamera, float4(csPosition, 1.0)); float4 oldSSCoordinate = mul(_ProjectToPixelMatrix, oldCSPosition); return float3((oldSSCoordinate.xy / oldSSCoordinate.w) * _InvScreenSize, oldCSPosition.z); } float4 fragTemporalFilter(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, tsP); float3 csPosition = GetPosition(tsP); float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).b; } // Virtual is the physics sense, not the other 20+ interpretations in CG float3 virtualReflectionCSPosition = normalize(csPosition) * rayDistance + csPosition; float3 oldTSP = csPositionToOldNormalizedScreenCoordinate(csPosition); float3 oldVirtualReflectionTSP = csPositionToOldNormalizedScreenCoordinate(virtualReflectionCSPosition); float oldCSZ = tex2D(_PreviousCSZBuffer, oldTSP.xy); float4 currentResult = tex2D(_FinalReflectionTexture, tsP); float4 oldResult = tex2D(_PreviousReflectionTexture, oldVirtualReflectionTSP.xy); float zError = abs(oldCSZ - oldTSP.z); // Start knocking down contribution at 5cm error, go to 0 at 5 more cm later float fallbackStart = 0.05; float fallbackEnd = 0.05; float zAlphaModifier = 1.0 - clamp((zError - fallbackStart) * (1.0 / fallbackEnd), 0.0, 1.0); float alpha = _TemporalAlpha; alpha *= zAlphaModifier; if (_UseTemporalConfidence) { alpha *= oldResult.w; } // Always compress currentResult.rgb = highlightCompression(currentResult.rgb); oldResult.rgb = highlightCompression(oldResult.rgb); float4 resultValue = lerp(currentResult, oldResult, alpha); resultValue.rgb = highlightDecompression(resultValue.rgb); return resultValue; } float4 fragAverageRayDistance(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float sumRayDistance = tex2D(_HitPointTexture, tsP).b; float sumWeight = 1.0; float roughness = 1.0 - tex2D(_CameraGBufferTexture1, tsP).a; float step = roughness * 3.0; /* for (float x = -step; x < step; x += 2 * step) { for (float y = -step; y < step; y += 2 * step) { float weight = 0.5; sumRayDistance += weight * tex2D(_HitPointTexture, (float2(x,y) * _InvScreenSize) + tsP).b; sumWeight += weight; } } */ return float4(sumRayDistance / sumWeight, 0.0, 0.0, 0.0); } static const int NUM_POISSON_TAPS = 12; // Same as used in CameraMotionBlur.shader static const float2 poissonSamples[NUM_POISSON_TAPS] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; float4 fragFilterSharpReflections(v2f i) : SV_Target { // Could improve perf by not computing blur when we won't be sampling the highest level anyways float2 tsP = i.uv2.xy; float4 sum = 0.0; float sampleRadius = _MainTex_TexelSize.xy * 2.0; for (int i = 0; i < NUM_POISSON_TAPS; i++) { float2 p = tsP + poissonSamples[i] * sampleRadius; float4 tap = tex2Dlod(_MainTex, float4(p, 0, 0)); if (_HighlightSuppression) { tap.rgb = highlightCompression(tap.rgb); } sum += tap; } float4 result = sum / float(NUM_POISSON_TAPS); if (_HighlightSuppression) { result.rgb = highlightDecompression(result.rgb); } return result; } #line 1057 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif /* UNITY: Original start of shader */ #pragma exclude_renderers gles xbox360 ps3 #pragma vertex vert #pragma fragment fragCompositeSSR #pragma target 3.0 SAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/ShadersÓ˙˙˙#küń'¨ô™!]{Ňô SHADOWS_SOFTDIRLIGHTMAP_COMBINEDDIRLIGHTMAP_SEPARATEDYNAMICLIGHTMAP_ON INSTANCING_ONćĚł†#line 1067 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #line 12 #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "ScreenSpaceRaytrace.cginc" float4 _ProjInfo; float4x4 _WorldToCameraMatrix; float4x4 _CameraToWorldMatrix; float4x4 _ProjectToPixelMatrix; float2 _ScreenSize; float2 _ReflectionBufferSize; float2 _InvScreenSize; float3 _CameraClipInfo; sampler2D _CameraGBufferTexture0; sampler2D _CameraGBufferTexture1; sampler2D _CameraGBufferTexture2; sampler2D _CameraGBufferTexture3; sampler2D _CameraReflectionsTexture; float _CurrentMipLevel; float _RayStepSize; float _MaxRayTraceDistance; float _LayerThickness; float _FresnelFade; float _FresnelFadePower; sampler2D _MainTex; int _VisualizeWhereBilateral; int _EnableSSR; int _DebugMode; int _HalfResolution; int _TreatBackfaceHitAsMiss; int _AllowBackwardsRays; int _TraceEverywhere; // RG: SS Hitpoint of ray // B: distance ray travelled, used for mip-selection in the final resolve // A: confidence value sampler2D _HitPointTexture; sampler2D _FinalReflectionTexture; // RGB: camera-space normal (encoded in [0-1]) // A: Roughness sampler2D _NormalAndRoughnessTexture; float4 _MainTex_TexelSize; float4 _SourceToTempUV; int _EnableRefine; int _AdditiveReflection; int _ImproveCorners; float _ScreenEdgeFading; float _MipBias; int _UseOcclusion; int _MaxSteps; int _FullResolutionFiltering; int _BilateralUpsampling; float _MaxRoughness; float _RoughnessFalloffRange; float _SSRMultiplier; float _FadeDistance; float _DistanceBlur; int _TraceBehindObjects; int _FallbackToSky; int _UseEdgeDetector; int _HighlightSuppression; /** The height in pixels of a 1m object if viewed from 1m away. */ float _PixelsPerMeterAtOneMeter; int _UseAverageRayDistance; sampler2D _AverageRayDistanceBuffer; // For temporal filtering: float4x4 _CurrentCameraToPreviousCamera; sampler2D _PreviousReflectionTexture; sampler2D _PreviousCSZBuffer; float _TemporalAlpha; int _UseTemporalConfidence; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy; o.uv2 = v.texcoord.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv2.y = 1-o.uv2.y; #endif return o; } float2 mipToSize(int mip) { return floor(_ReflectionBufferSize * exp2(-mip)); } float3 ReconstructCSPosition(float2 S, float z) { float linEyeZ = -LinearEyeDepth(z); return float3(( (( S.xy * _MainTex_TexelSize.zw) ) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); } /** Read the camera-space position of the point at screen-space pixel ssP */ float3 GetPosition(float2 ssP) { float3 P; P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy); // Offset to pixel center P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); return P; } float square(float x) { return x*x; } float applyEdgeFade(float2 tsP, float fadeStrength) { float maxFade = 0.1; float2 itsP = float2(1.0, 1.0) - tsP; float dist = min(min(itsP.x, itsP.y), min(tsP.x, tsP.x)); float fade = dist / (maxFade*fadeStrength + 0.001); fade = max(min(fade, 1.0), 0.0); fade = pow(fade, 0.2); return fade; } float3 csMirrorVector(float3 csPosition, float3 csN) { float3 csE = -normalize(csPosition.xyz); float cos_o = dot(csN, csE); float3 c_mi = normalize((csN * (2.0 * cos_o)) - csE); return c_mi; } float4 fragRaytrace(v2f i, int stepRate) { float2 ssP = i.uv2.xy * _SourceToTempUV.zw; float3 csPosition = GetPosition(ssP); float smoothness = tex2D(_CameraGBufferTexture1, ssP).a; if (csPosition.z < -100.0 || smoothness == 0.0) { return float4(0.0,0.0,0.0,0.0); } if ((!_TraceEverywhere) && (smoothness < (1.0 - _MaxRoughness)) ) { return float4(0.0, 0.0, 0.0, 0.0); } float3 wsNormal = tex2D(_CameraGBufferTexture2, ssP).rgb * 2.0 - 1.0; int2 ssC = int2(ssP * _ScreenSize); float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 csRayDirection = csMirrorVector(csPosition, csN); if (_AllowBackwardsRays == 0 && csRayDirection.z > 0.0) { return float4(0.0, 0.0, 0.0, 0.0); } float maxRayTraceDistance = _MaxRayTraceDistance; float jitterFraction = 0.0f; float layerThickness = _LayerThickness; int maxSteps = _MaxSteps; // Bump the ray more in world space as it gets farther away (and so each pixel covers more WS distance) float rayBump = max(-0.01*csPosition.z, 0.001); float2 hitPixel; float3 csHitPoint; float stepCount; bool wasHit = castDenseScreenSpaceRay (csPosition + (csN) * rayBump, csRayDirection, _ProjectToPixelMatrix, _ScreenSize, _CameraClipInfo, jitterFraction, maxSteps, layerThickness, maxRayTraceDistance, hitPixel, stepRate, _EnableRefine == 1, _TraceBehindObjects == 1, csHitPoint, stepCount); float2 tsPResult = hitPixel / _ScreenSize; float rayDist = dot(csHitPoint - csPosition, csRayDirection); float confidence = 0.0; if (wasHit) { confidence = square(1.0 - max(2.0*float(stepCount) / float(maxSteps) - 1.0, 0.0)); confidence *= clamp(((_MaxRayTraceDistance - rayDist) / _FadeDistance), 0.0, 1.0); // Fake fresnel fade float3 csE = -normalize(csPosition.xyz); confidence *= max(0.0, lerp(pow(abs(dot(csRayDirection, -csE)), _FresnelFadePower), 1, 1.0 - _FresnelFade)); if (_TreatBackfaceHitAsMiss > 0) { float3 wsHitNormal = tex2Dlod(_CameraGBufferTexture2, float4(tsPResult, 0, 0)).rgb * 2.0 - 1.0; float3 wsRayDirection = mul(_CameraToWorldMatrix, float4(csRayDirection, 0)).xyz; if (dot(wsHitNormal, wsRayDirection) > 0) { confidence = 0.0; } } } else if (_FallbackToSky > 0) { float sceneZ = tex2Dlod(_CameraDepthTexture, float4(tsPResult, 0, 0)).r; sceneZ = -LinearEyeDepth(sceneZ); if (sceneZ < -1000) { // 1km ~= infinity confidence = 1.0; } } // Fade out reflections that hit near edge of screen, to prevent abrupt appearance/disappearance when object go off screen // Fade out reflections that hit near edge of screen, // to prevent abrupt appearance/disappearance when object go off screen float vignette = applyEdgeFade(tsPResult, _ScreenEdgeFading); confidence *= vignette; confidence *= vignette; return float4(tsPResult, rayDist, confidence); } float4 fragComposite(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; // View space point being shaded float3 C = GetPosition(tsP); // Final image before this pass float4 gbuffer3 = tex2D(_MainTex, i.uv); float4 specEmission = float4(0.0,0.0,0.0,0.0); float3 specColor = tex2D(_CameraGBufferTexture1, tsP).rgb; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP).a; float4 reflectionTexel = tex2D(_FinalReflectionTexture, tsP * _SourceToTempUV.xy); float4 gbuffer0 = tex2D(_CameraGBufferTexture0, tsP); // Let core Unity functions do the dirty work of applying the BRDF float3 baseColor = gbuffer0.rgb; float occlusion = gbuffer0.a; float oneMinusReflectivity; baseColor = EnergyConservationBetweenDiffuseAndSpecular(baseColor, specColor, oneMinusReflectivity); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csEyeVec = normalize(C); float3 eyeVec = mul(_CameraToWorldMatrix, float4(csEyeVec, 0)).xyz; float3 worldPos = mul(_CameraToWorldMatrix, float4(C, 1)).xyz; float cos_o = dot(wsNormal, eyeVec); float3 w_mi = -normalize((wsNormal * (2.0 * cos_o)) - eyeVec); float3 incomingRadiance = reflectionTexel.rgb; UnityLight light; light.color = 0; light.dir = 0; light.ndotl = 0; UnityIndirect ind; ind.diffuse = 0; ind.specular = incomingRadiance; float3 ssrResult = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, 1-roughness, wsNormal, -eyeVec, light, ind).rgb * _SSRMultiplier; float confidence = reflectionTexel.a; if(_EnableSSR == 0) { confidence = 0.0; } specEmission.rgb = tex2D(_CameraReflectionsTexture, tsP).rgb; float3 finalGlossyTerm; // Subtract out Unity's glossy result: (we're just applying the delta) if (_AdditiveReflection == 0) { gbuffer3 -= specEmission; // We may have blown out our dynamic range by adding then subtracting the reflection probes. // As a half-measure to fix this, simply clamp to zero gbuffer3 = max(gbuffer3, 0); finalGlossyTerm = lerp(specEmission.rgb, ssrResult, saturate(confidence)); } else { finalGlossyTerm = ssrResult*saturate(confidence); } if (_UseOcclusion) { finalGlossyTerm *= occlusion; } if (_DebugMode == 1) return float4(incomingRadiance,0); if (_DebugMode == 2) return float4(ssrResult,0); if (_DebugMode == 3) return float4(finalGlossyTerm,0); if (_DebugMode == 4) return confidence; if (_DebugMode == 5) return roughness; if (_DebugMode == 6) return float4(baseColor,0); if (_DebugMode == 7) return float4(specColor,0); if (_DebugMode == 8) return 1.0-oneMinusReflectivity; if (_DebugMode == 9) return float4(specEmission.rgb,0); if (_DebugMode == 10) return float4(specEmission.rgb-ssrResult,0); if (_DebugMode == 11) return float4(ssrResult-specEmission.rgb,0); if (_DebugMode == 12) return gbuffer3; if (_DebugMode == 13) return -float4(gbuffer3.rgb, 0.0); if (_DebugMode == 14) // Mip level, stored directly in ssrResult return float4(incomingRadiance, 0); // Additively blend the glossy GI result with the output buffer return gbuffer3 + float4(finalGlossyTerm, 0); } float roughnessWeight(float midpointRoughness, float tapRoughness) { return (1.0 - sqrt(sqrt(abs(midpointRoughness-tapRoughness)))); } float normalWeight(float3 midpointNormal, float3 tapNormal) { return clamp(dot(midpointNormal, tapNormal), 0, 1); } float highlightDecompression(float x) { return x / (1.0 - x); } float3 highlightDecompression(float3 x) { return float3( highlightDecompression(x.x), highlightDecompression(x.y), highlightDecompression(x.z)); } float highlightCompression(float x) { return x / (1.0 + x); } float3 highlightCompression(float3 x) { return float3( highlightCompression(x.x), highlightCompression(x.y), highlightCompression(x.z)); } float4 _Axis; float4 fragGBlur(v2f i) : SV_Target { int radius = 4; // Pixel being shaded float2 tsP = i.uv2.xy; float weightSum = 0.0; float gaussWeights[5] = {0.225, 0.150, 0.110, 0.075, 0.0525};//{0.225, 0.150, 0.110, 0.075, 0.0525}; float4 resultSum = float4(0.0, 0.0, 0.0, 0.0); float4 unweightedResultSum = float4(0.0, 0.0, 0.0, 0.0); float4 nAndRough = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = nAndRough.a; float3 midpointNormal = nAndRough.rgb * 2 - 1; if (_FullResolutionFiltering) { for (int i = -2*radius; i <= 2*radius; ++i) { float4 temp; float tapRoughness = midpointRoughness; float3 tapNormal = midpointNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)); temp = tex2D(_MainTex, tsTap); int gaussWeightIndex; #if defined(UNITY_COMPILER_HLSL) && !defined(SHADER_API_D3D9) gaussWeightIndex = abs(i) >> 1; // We purposefully do integer division, but that does not compile on hlsl2glsl #else gaussWeightIndex = frac(abs(i) / 2); #endif float weight = temp.a * gaussWeights[gaussWeightIndex]*0.5; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; resultSum += temp*weight; unweightedResultSum += temp; } } else { for (int i = -radius; i <= radius; ++i) { float4 temp; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)*2.0); temp = tex2D(_MainTex, tsTap); float weight = temp.a * gaussWeights[abs(i)]; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; if (_HighlightSuppression) { temp.rgb = highlightCompression(temp.rgb); } unweightedResultSum += temp; resultSum += temp*weight; } } if (weightSum > 0.01) { float invWeightSum = (1.0/weightSum); // Adding the sqrt seems to decrease temporal flickering at the expense // of having larger "halos" of fallback on rough surfaces // Subject to change with testing. Sqrt around only half the expression is *intentional*. float confidence = min(resultSum.a * sqrt(max(invWeightSum, 2.0)), 1.0); float3 finalColor = resultSum.rgb * invWeightSum; if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, confidence); } else { float3 finalColor = unweightedResultSum.rgb / (2 * radius + 1); if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, 0.0); } } sampler2D _ReflectionTexture0; sampler2D _ReflectionTexture1; sampler2D _ReflectionTexture2; sampler2D _ReflectionTexture3; sampler2D _ReflectionTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getReflectionValue(float2 tsP, int mip) { float4 coord = float4(tsP,0,0); if (mip == 0) { return tex2Dlod(_ReflectionTexture0, coord); } else if (mip == 1) { return tex2Dlod(_ReflectionTexture1, coord); } else if (mip == 2) { return tex2Dlod(_ReflectionTexture2, coord); } else if (mip == 3) { return tex2Dlod(_ReflectionTexture3, coord); } else { return tex2Dlod(_ReflectionTexture4, coord); } } sampler2D _EdgeTexture0; sampler2D _EdgeTexture1; sampler2D _EdgeTexture2; sampler2D _EdgeTexture3; sampler2D _EdgeTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getEdgeValue(float2 tsP, int mip) { float4 coord = float4(tsP + float2(1.0/(2 * mipToSize(mip))),0,0); if (mip == 0) { return tex2Dlod(_EdgeTexture0, coord); } else if (mip == 1) { return tex2Dlod(_EdgeTexture1, coord); } else if (mip == 2) { return tex2Dlod(_EdgeTexture2, coord); } else if (mip == 3) { return tex2Dlod(_EdgeTexture3, coord); } else { return tex2Dlod(_EdgeTexture4, coord); } } float2 centerPixel(float2 inputP) { return floor(inputP - float2(0.5,0.5)) + float2(0.5,0.5); } float2 snapToTexelCenter(float2 inputP, float2 texSize, float2 texSizeInv) { return centerPixel(inputP * texSize) * texSizeInv; } float4 bilateralUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; float2 fullTexSize = _ReflectionBufferSize; float2 fullTexSizeInv = 1.0 / fullTexSize; float4 hiP0 = float4(snapToTexelCenter(p0, fullTexSize, fullTexSizeInv), 0,0); float4 hiP3 = float4(snapToTexelCenter(p3, fullTexSize, fullTexSizeInv), 0,0); float4 hiP1 = float4(snapToTexelCenter(p1, fullTexSize, fullTexSizeInv), 0,0); float4 hiP2 = float4(snapToTexelCenter(p2, fullTexSize, fullTexSizeInv), 0,0); float4 tempCenter = tex2Dlod(_NormalAndRoughnessTexture, float4(tsP, 0, 0)); float3 n = tempCenter.xyz * 2 - 1; float4 temp0 = tex2Dlod(_NormalAndRoughnessTexture, hiP0); float4 temp1 = tex2Dlod(_NormalAndRoughnessTexture, hiP1); float4 temp2 = tex2Dlod(_NormalAndRoughnessTexture, hiP2); float4 temp3 = tex2Dlod(_NormalAndRoughnessTexture, hiP3); float3 n0 = temp0.xyz * 2 - 1; float3 n1 = temp1.xyz * 2 - 1; float3 n2 = temp2.xyz * 2 - 1; float3 n3 = temp3.xyz * 2 - 1; a0 *= normalWeight(n, n0); a1 *= normalWeight(n, n1); a2 *= normalWeight(n, n2); a3 *= normalWeight(n, n3); float r = tempCenter.a; float r0 = temp0.a; float r1 = temp1.a; float r2 = temp2.a; float r3 = temp3.a; a0 *= roughnessWeight(r, r0); a1 *= roughnessWeight(r, r1); a2 *= roughnessWeight(r, r2); a3 *= roughnessWeight(r, r3); // Slightly offset from zero a0 = max(a0, 0.001); a1 = max(a1, 0.001); a2 = max(a2, 0.001); a3 = max(a3, 0.001); // Nearest neighbor // a0 = a1 = a2 = a3 = 1.0; // Normalize the blending weights (weights were chosen so that // the denominator can never be zero) float norm = 1.0 / (a0 + a1 + a2 + a3); // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3) * norm; //return V0; return value; } /** Explicit bilinear fetches; must be used if the reflection buffer is bound using point sampling */ float4 bilinearUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3); return value; } // Unity's roughness is GGX roughness squared float roughnessToBlinnPhongExponent(float roughness) { float r2 = roughness*roughness; return 2.0f / r2*r2 - 2.0f; } float glossyLobeSlope(float roughness) { return pow(roughness, 4.0/3.0); } // Empirically based on our filter: // Mip | Pixels // -------------- // 0 | 1 no filter, so single pixel // 1 | 17 2r + 1 filter applied once, grabbing from pixels r away in either direction (r=8, four samples times stride of 2) // 2 | 50 2r + 1 filter applied on double size pixels, and each of those pixels had reached another r out to the side 2(2r + 1) + m_1 // 3 | 118 4(2r + 1) + m_2 // 4 | 254 8(2r + 1) + m_3 // // Approximated by pixels = 16*2^mip-15 // rearranging we get mip = log_2((pixels + 15) / 16) // float filterFootprintInPixelsToMip(float footprint) { return log2((footprint + 15) / 16); } float3 ansiGradient(float t) { //return float3(t, t, t); return fmod(floor(t * float3(8.0, 4.0, 2.0)), 2.0); } float4 fragCompositeSSR(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP * _SourceToTempUV.zw).a; float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).z; } // Get the camera space position of the reflection hit float3 csPosition = GetPosition(tsP); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 c_mi = csMirrorVector(csPosition, csN); float3 csHitpoint = c_mi * rayDistance + csPosition; float gatherFootprintInMeters = glossyLobeSlope(roughness) * rayDistance; // We could add a term that incorporates the normal // This approximation assumes reflections happen at a glancing angle float filterFootprintInPixels = gatherFootprintInMeters * _PixelsPerMeterAtOneMeter / csHitpoint.z; if (_HalfResolution == 1) { filterFootprintInPixels *= 0.5; } float mip = filterFootprintInPixelsToMip(filterFootprintInPixels); float nonPhysicalMip = pow(roughness, 3.0 / 4.0) * UNITY_SPECCUBE_LOD_STEPS; if (_HalfResolution == 1) { nonPhysicalMip = nonPhysicalMip * 0.7; } mip = lerp(nonPhysicalMip, mip, _DistanceBlur); mip = max(0, min(4, mip + _MipBias)); if (_DebugMode == 14) { return float4(ansiGradient(mip*0.25), 1.0); } float4 result; { int mipMin = int(mip); int mipMax = min(mipMin + 1, 4); float mipLerp = mip-mipMin; if (_BilateralUpsampling == 1) { if (_UseEdgeDetector == 1) { float nonEdginess = lerp(getEdgeValue(tsP, mipMin), getEdgeValue(tsP, mipMax), mipLerp); if (nonEdginess < 0.9) { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); if (_VisualizeWhereBilateral == 1) { result = float4(1, 0, 0, 0); } } else { result = lerp(bilinearUpsampleReflection(tsP, mipMin), bilinearUpsampleReflection(tsP, mipMax), mipLerp); } } else { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); } } else { float4 minResult = getReflectionValue(tsP, mipMin); float4 maxResult = getReflectionValue(tsP, mipMax); result = lerp(minResult, maxResult, mipLerp); result.a = min(minResult.a, maxResult.a); } } result.a = min(result.a, 1.0); float vignette = applyEdgeFade(tsP, _ScreenEdgeFading); result.a *= vignette; float alphaModifier = 1.0 - clamp((roughness - (_MaxRoughness - _RoughnessFalloffRange)) / _RoughnessFalloffRange, 0.0, 1.0); result.a *= alphaModifier; return result; } float4 fragBlit(v2f i) : SV_Target { // Pixel being shaded float2 ssP = i.uv2.xy; return tex2D(_MainTex, ssP); } float edgeDetector(float2 tsP, float2 axis, float2 texelSize) { float4 temp = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = temp.a; float3 midpointNormal = temp.rgb * 2 - 1; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (axis * _MainTex_TexelSize.xy); temp = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = temp.a; tapNormal = temp.rgb * 2 - 1; float weight = 1.0; weight *= roughnessWeight(midpointRoughness, tapRoughness); weight *= normalWeight(midpointNormal, tapNormal); return weight; } float4 fragEdge(v2f i) : SV_Target { float2 tsP = i.uv2.xy; return edgeDetector(tsP, float2(1,0), _MainTex_TexelSize.xy) * edgeDetector(tsP, float2(0,1), _MainTex_TexelSize.xy); } int _LastMip; float4 fragMin(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float2 lastTexSize = mipToSize(_LastMip); float2 lastTexSizeInv = 1.0 / lastTexSize;//_SourceToTempUV.xy * exp2(mip); float2 p00 = snapToTexelCenter(tsP, lastTexSize, lastTexSizeInv); float2 p11 = p00 + lastTexSizeInv; return min( min(tex2D(_MainTex, p00), tex2D(_MainTex, p11)), min(tex2D(_MainTex, float2(p00.x, p11.y)), tex2D(_MainTex, float2(p11.x, p00.y))) ); } float4 fragResolveHitPoints(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float4 temp = tex2D(_HitPointTexture, tsP); float2 hitPoint = temp.xy; float confidence = temp.w; float3 colorResult = confidence > 0.0 ? tex2D(_MainTex, hitPoint).rgb : tex2D(_CameraReflectionsTexture, tsP).rgb; #ifdef UNITY_COMPILER_HLSL if (any(isnan(colorResult))) colorResult = float3(0.0, 0.0, 0.0); // As of 11/29/2015, on Unity 5.3 on a Windows 8.1 computer with a NVIDIA GeForce 980, // with driver 347.62, the above check does not actually work to get rid of NaNs! // So we add this "redundant" check. if (!all(isfinite(colorResult))) colorResult = float3(0.0, 0.0, 0.0); #endif return float4(colorResult, confidence); } float4 fragBilatKeyPack(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float3 csN = tex2D(_CameraGBufferTexture2, tsP).xyz; float roughness = tex2D(_CameraGBufferTexture1, tsP).a; return float4(csN, roughness); } float4 fragDepthToCSZ(v2f i) : SV_Target { float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv2.xy); return float4(-LinearEyeDepth(depth), 0.0, 0.0, 0.0); } float3 csPositionToOldNormalizedScreenCoordinate(float3 csPosition) { // We could multiply these matrices together on the CPU float4 oldCSPosition = mul(_CurrentCameraToPreviousCamera, float4(csPosition, 1.0)); float4 oldSSCoordinate = mul(_ProjectToPixelMatrix, oldCSPosition); return float3((oldSSCoordinate.xy / oldSSCoordinate.w) * _InvScreenSize, oldCSPosition.z); } float4 fragTemporalFilter(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, tsP); float3 csPosition = GetPosition(tsP); float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).b; } // Virtual is the physics sense, not the other 20+ interpretations in CG float3 virtualReflectionCSPosition = normalize(csPosition) * rayDistance + csPosition; float3 oldTSP = csPositionToOldNormalizedScreenCoordinate(csPosition); float3 oldVirtualReflectionTSP = csPositionToOldNormalizedScreenCoordinate(virtualReflectionCSPosition); float oldCSZ = tex2D(_PreviousCSZBuffer, oldTSP.xy); float4 currentResult = tex2D(_FinalReflectionTexture, tsP); float4 oldResult = tex2D(_PreviousReflectionTexture, oldVirtualReflectionTSP.xy); float zError = abs(oldCSZ - oldTSP.z); // Start knocking down contribution at 5cm error, go to 0 at 5 more cm later float fallbackStart = 0.05; float fallbackEnd = 0.05; float zAlphaModifier = 1.0 - clamp((zError - fallbackStart) * (1.0 / fallbackEnd), 0.0, 1.0); float alpha = _TemporalAlpha; alpha *= zAlphaModifier; if (_UseTemporalConfidence) { alpha *= oldResult.w; } // Always compress currentResult.rgb = highlightCompression(currentResult.rgb); oldResult.rgb = highlightCompression(oldResult.rgb); float4 resultValue = lerp(currentResult, oldResult, alpha); resultValue.rgb = highlightDecompression(resultValue.rgb); return resultValue; } float4 fragAverageRayDistance(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float sumRayDistance = tex2D(_HitPointTexture, tsP).b; float sumWeight = 1.0; float roughness = 1.0 - tex2D(_CameraGBufferTexture1, tsP).a; float step = roughness * 3.0; /* for (float x = -step; x < step; x += 2 * step) { for (float y = -step; y < step; y += 2 * step) { float weight = 0.5; sumRayDistance += weight * tex2D(_HitPointTexture, (float2(x,y) * _InvScreenSize) + tsP).b; sumWeight += weight; } } */ return float4(sumRayDistance / sumWeight, 0.0, 0.0, 0.0); } static const int NUM_POISSON_TAPS = 12; // Same as used in CameraMotionBlur.shader static const float2 poissonSamples[NUM_POISSON_TAPS] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; float4 fragFilterSharpReflections(v2f i) : SV_Target { // Could improve perf by not computing blur when we won't be sampling the highest level anyways float2 tsP = i.uv2.xy; float4 sum = 0.0; float sampleRadius = _MainTex_TexelSize.xy * 2.0; for (int i = 0; i < NUM_POISSON_TAPS; i++) { float2 p = tsP + poissonSamples[i] * sampleRadius; float4 tap = tex2Dlod(_MainTex, float4(p, 0, 0)); if (_HighlightSuppression) { tap.rgb = highlightCompression(tap.rgb); } sum += tap; } float4 result = sum / float(NUM_POISSON_TAPS); if (_HighlightSuppression) { result.rgb = highlightDecompression(result.rgb); } return result; } #line 1067 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif /* UNITY: Original start of shader */ #pragma exclude_renderers gles xbox360 ps3 #pragma vertex vert #pragma fragment fragBlit #pragma target 3.0 SAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/ShadersÓ˙˙˙-küń'¨ô™!]{Ňô SHADOWS_SOFTDIRLIGHTMAP_COMBINEDDIRLIGHTMAP_SEPARATEDYNAMICLIGHTMAP_ON INSTANCING_ON_ ł†#line 1077 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #line 12 #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "ScreenSpaceRaytrace.cginc" float4 _ProjInfo; float4x4 _WorldToCameraMatrix; float4x4 _CameraToWorldMatrix; float4x4 _ProjectToPixelMatrix; float2 _ScreenSize; float2 _ReflectionBufferSize; float2 _InvScreenSize; float3 _CameraClipInfo; sampler2D _CameraGBufferTexture0; sampler2D _CameraGBufferTexture1; sampler2D _CameraGBufferTexture2; sampler2D _CameraGBufferTexture3; sampler2D _CameraReflectionsTexture; float _CurrentMipLevel; float _RayStepSize; float _MaxRayTraceDistance; float _LayerThickness; float _FresnelFade; float _FresnelFadePower; sampler2D _MainTex; int _VisualizeWhereBilateral; int _EnableSSR; int _DebugMode; int _HalfResolution; int _TreatBackfaceHitAsMiss; int _AllowBackwardsRays; int _TraceEverywhere; // RG: SS Hitpoint of ray // B: distance ray travelled, used for mip-selection in the final resolve // A: confidence value sampler2D _HitPointTexture; sampler2D _FinalReflectionTexture; // RGB: camera-space normal (encoded in [0-1]) // A: Roughness sampler2D _NormalAndRoughnessTexture; float4 _MainTex_TexelSize; float4 _SourceToTempUV; int _EnableRefine; int _AdditiveReflection; int _ImproveCorners; float _ScreenEdgeFading; float _MipBias; int _UseOcclusion; int _MaxSteps; int _FullResolutionFiltering; int _BilateralUpsampling; float _MaxRoughness; float _RoughnessFalloffRange; float _SSRMultiplier; float _FadeDistance; float _DistanceBlur; int _TraceBehindObjects; int _FallbackToSky; int _UseEdgeDetector; int _HighlightSuppression; /** The height in pixels of a 1m object if viewed from 1m away. */ float _PixelsPerMeterAtOneMeter; int _UseAverageRayDistance; sampler2D _AverageRayDistanceBuffer; // For temporal filtering: float4x4 _CurrentCameraToPreviousCamera; sampler2D _PreviousReflectionTexture; sampler2D _PreviousCSZBuffer; float _TemporalAlpha; int _UseTemporalConfidence; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy; o.uv2 = v.texcoord.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv2.y = 1-o.uv2.y; #endif return o; } float2 mipToSize(int mip) { return floor(_ReflectionBufferSize * exp2(-mip)); } float3 ReconstructCSPosition(float2 S, float z) { float linEyeZ = -LinearEyeDepth(z); return float3(( (( S.xy * _MainTex_TexelSize.zw) ) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); } /** Read the camera-space position of the point at screen-space pixel ssP */ float3 GetPosition(float2 ssP) { float3 P; P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy); // Offset to pixel center P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); return P; } float square(float x) { return x*x; } float applyEdgeFade(float2 tsP, float fadeStrength) { float maxFade = 0.1; float2 itsP = float2(1.0, 1.0) - tsP; float dist = min(min(itsP.x, itsP.y), min(tsP.x, tsP.x)); float fade = dist / (maxFade*fadeStrength + 0.001); fade = max(min(fade, 1.0), 0.0); fade = pow(fade, 0.2); return fade; } float3 csMirrorVector(float3 csPosition, float3 csN) { float3 csE = -normalize(csPosition.xyz); float cos_o = dot(csN, csE); float3 c_mi = normalize((csN * (2.0 * cos_o)) - csE); return c_mi; } float4 fragRaytrace(v2f i, int stepRate) { float2 ssP = i.uv2.xy * _SourceToTempUV.zw; float3 csPosition = GetPosition(ssP); float smoothness = tex2D(_CameraGBufferTexture1, ssP).a; if (csPosition.z < -100.0 || smoothness == 0.0) { return float4(0.0,0.0,0.0,0.0); } if ((!_TraceEverywhere) && (smoothness < (1.0 - _MaxRoughness)) ) { return float4(0.0, 0.0, 0.0, 0.0); } float3 wsNormal = tex2D(_CameraGBufferTexture2, ssP).rgb * 2.0 - 1.0; int2 ssC = int2(ssP * _ScreenSize); float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 csRayDirection = csMirrorVector(csPosition, csN); if (_AllowBackwardsRays == 0 && csRayDirection.z > 0.0) { return float4(0.0, 0.0, 0.0, 0.0); } float maxRayTraceDistance = _MaxRayTraceDistance; float jitterFraction = 0.0f; float layerThickness = _LayerThickness; int maxSteps = _MaxSteps; // Bump the ray more in world space as it gets farther away (and so each pixel covers more WS distance) float rayBump = max(-0.01*csPosition.z, 0.001); float2 hitPixel; float3 csHitPoint; float stepCount; bool wasHit = castDenseScreenSpaceRay (csPosition + (csN) * rayBump, csRayDirection, _ProjectToPixelMatrix, _ScreenSize, _CameraClipInfo, jitterFraction, maxSteps, layerThickness, maxRayTraceDistance, hitPixel, stepRate, _EnableRefine == 1, _TraceBehindObjects == 1, csHitPoint, stepCount); float2 tsPResult = hitPixel / _ScreenSize; float rayDist = dot(csHitPoint - csPosition, csRayDirection); float confidence = 0.0; if (wasHit) { confidence = square(1.0 - max(2.0*float(stepCount) / float(maxSteps) - 1.0, 0.0)); confidence *= clamp(((_MaxRayTraceDistance - rayDist) / _FadeDistance), 0.0, 1.0); // Fake fresnel fade float3 csE = -normalize(csPosition.xyz); confidence *= max(0.0, lerp(pow(abs(dot(csRayDirection, -csE)), _FresnelFadePower), 1, 1.0 - _FresnelFade)); if (_TreatBackfaceHitAsMiss > 0) { float3 wsHitNormal = tex2Dlod(_CameraGBufferTexture2, float4(tsPResult, 0, 0)).rgb * 2.0 - 1.0; float3 wsRayDirection = mul(_CameraToWorldMatrix, float4(csRayDirection, 0)).xyz; if (dot(wsHitNormal, wsRayDirection) > 0) { confidence = 0.0; } } } else if (_FallbackToSky > 0) { float sceneZ = tex2Dlod(_CameraDepthTexture, float4(tsPResult, 0, 0)).r; sceneZ = -LinearEyeDepth(sceneZ); if (sceneZ < -1000) { // 1km ~= infinity confidence = 1.0; } } // Fade out reflections that hit near edge of screen, to prevent abrupt appearance/disappearance when object go off screen // Fade out reflections that hit near edge of screen, // to prevent abrupt appearance/disappearance when object go off screen float vignette = applyEdgeFade(tsPResult, _ScreenEdgeFading); confidence *= vignette; confidence *= vignette; return float4(tsPResult, rayDist, confidence); } float4 fragComposite(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; // View space point being shaded float3 C = GetPosition(tsP); // Final image before this pass float4 gbuffer3 = tex2D(_MainTex, i.uv); float4 specEmission = float4(0.0,0.0,0.0,0.0); float3 specColor = tex2D(_CameraGBufferTexture1, tsP).rgb; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP).a; float4 reflectionTexel = tex2D(_FinalReflectionTexture, tsP * _SourceToTempUV.xy); float4 gbuffer0 = tex2D(_CameraGBufferTexture0, tsP); // Let core Unity functions do the dirty work of applying the BRDF float3 baseColor = gbuffer0.rgb; float occlusion = gbuffer0.a; float oneMinusReflectivity; baseColor = EnergyConservationBetweenDiffuseAndSpecular(baseColor, specColor, oneMinusReflectivity); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csEyeVec = normalize(C); float3 eyeVec = mul(_CameraToWorldMatrix, float4(csEyeVec, 0)).xyz; float3 worldPos = mul(_CameraToWorldMatrix, float4(C, 1)).xyz; float cos_o = dot(wsNormal, eyeVec); float3 w_mi = -normalize((wsNormal * (2.0 * cos_o)) - eyeVec); float3 incomingRadiance = reflectionTexel.rgb; UnityLight light; light.color = 0; light.dir = 0; light.ndotl = 0; UnityIndirect ind; ind.diffuse = 0; ind.specular = incomingRadiance; float3 ssrResult = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, 1-roughness, wsNormal, -eyeVec, light, ind).rgb * _SSRMultiplier; float confidence = reflectionTexel.a; if(_EnableSSR == 0) { confidence = 0.0; } specEmission.rgb = tex2D(_CameraReflectionsTexture, tsP).rgb; float3 finalGlossyTerm; // Subtract out Unity's glossy result: (we're just applying the delta) if (_AdditiveReflection == 0) { gbuffer3 -= specEmission; // We may have blown out our dynamic range by adding then subtracting the reflection probes. // As a half-measure to fix this, simply clamp to zero gbuffer3 = max(gbuffer3, 0); finalGlossyTerm = lerp(specEmission.rgb, ssrResult, saturate(confidence)); } else { finalGlossyTerm = ssrResult*saturate(confidence); } if (_UseOcclusion) { finalGlossyTerm *= occlusion; } if (_DebugMode == 1) return float4(incomingRadiance,0); if (_DebugMode == 2) return float4(ssrResult,0); if (_DebugMode == 3) return float4(finalGlossyTerm,0); if (_DebugMode == 4) return confidence; if (_DebugMode == 5) return roughness; if (_DebugMode == 6) return float4(baseColor,0); if (_DebugMode == 7) return float4(specColor,0); if (_DebugMode == 8) return 1.0-oneMinusReflectivity; if (_DebugMode == 9) return float4(specEmission.rgb,0); if (_DebugMode == 10) return float4(specEmission.rgb-ssrResult,0); if (_DebugMode == 11) return float4(ssrResult-specEmission.rgb,0); if (_DebugMode == 12) return gbuffer3; if (_DebugMode == 13) return -float4(gbuffer3.rgb, 0.0); if (_DebugMode == 14) // Mip level, stored directly in ssrResult return float4(incomingRadiance, 0); // Additively blend the glossy GI result with the output buffer return gbuffer3 + float4(finalGlossyTerm, 0); } float roughnessWeight(float midpointRoughness, float tapRoughness) { return (1.0 - sqrt(sqrt(abs(midpointRoughness-tapRoughness)))); } float normalWeight(float3 midpointNormal, float3 tapNormal) { return clamp(dot(midpointNormal, tapNormal), 0, 1); } float highlightDecompression(float x) { return x / (1.0 - x); } float3 highlightDecompression(float3 x) { return float3( highlightDecompression(x.x), highlightDecompression(x.y), highlightDecompression(x.z)); } float highlightCompression(float x) { return x / (1.0 + x); } float3 highlightCompression(float3 x) { return float3( highlightCompression(x.x), highlightCompression(x.y), highlightCompression(x.z)); } float4 _Axis; float4 fragGBlur(v2f i) : SV_Target { int radius = 4; // Pixel being shaded float2 tsP = i.uv2.xy; float weightSum = 0.0; float gaussWeights[5] = {0.225, 0.150, 0.110, 0.075, 0.0525};//{0.225, 0.150, 0.110, 0.075, 0.0525}; float4 resultSum = float4(0.0, 0.0, 0.0, 0.0); float4 unweightedResultSum = float4(0.0, 0.0, 0.0, 0.0); float4 nAndRough = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = nAndRough.a; float3 midpointNormal = nAndRough.rgb * 2 - 1; if (_FullResolutionFiltering) { for (int i = -2*radius; i <= 2*radius; ++i) { float4 temp; float tapRoughness = midpointRoughness; float3 tapNormal = midpointNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)); temp = tex2D(_MainTex, tsTap); int gaussWeightIndex; #if defined(UNITY_COMPILER_HLSL) && !defined(SHADER_API_D3D9) gaussWeightIndex = abs(i) >> 1; // We purposefully do integer division, but that does not compile on hlsl2glsl #else gaussWeightIndex = frac(abs(i) / 2); #endif float weight = temp.a * gaussWeights[gaussWeightIndex]*0.5; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; resultSum += temp*weight; unweightedResultSum += temp; } } else { for (int i = -radius; i <= radius; ++i) { float4 temp; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)*2.0); temp = tex2D(_MainTex, tsTap); float weight = temp.a * gaussWeights[abs(i)]; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; if (_HighlightSuppression) { temp.rgb = highlightCompression(temp.rgb); } unweightedResultSum += temp; resultSum += temp*weight; } } if (weightSum > 0.01) { float invWeightSum = (1.0/weightSum); // Adding the sqrt seems to decrease temporal flickering at the expense // of having larger "halos" of fallback on rough surfaces // Subject to change with testing. Sqrt around only half the expression is *intentional*. float confidence = min(resultSum.a * sqrt(max(invWeightSum, 2.0)), 1.0); float3 finalColor = resultSum.rgb * invWeightSum; if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, confidence); } else { float3 finalColor = unweightedResultSum.rgb / (2 * radius + 1); if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, 0.0); } } sampler2D _ReflectionTexture0; sampler2D _ReflectionTexture1; sampler2D _ReflectionTexture2; sampler2D _ReflectionTexture3; sampler2D _ReflectionTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getReflectionValue(float2 tsP, int mip) { float4 coord = float4(tsP,0,0); if (mip == 0) { return tex2Dlod(_ReflectionTexture0, coord); } else if (mip == 1) { return tex2Dlod(_ReflectionTexture1, coord); } else if (mip == 2) { return tex2Dlod(_ReflectionTexture2, coord); } else if (mip == 3) { return tex2Dlod(_ReflectionTexture3, coord); } else { return tex2Dlod(_ReflectionTexture4, coord); } } sampler2D _EdgeTexture0; sampler2D _EdgeTexture1; sampler2D _EdgeTexture2; sampler2D _EdgeTexture3; sampler2D _EdgeTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getEdgeValue(float2 tsP, int mip) { float4 coord = float4(tsP + float2(1.0/(2 * mipToSize(mip))),0,0); if (mip == 0) { return tex2Dlod(_EdgeTexture0, coord); } else if (mip == 1) { return tex2Dlod(_EdgeTexture1, coord); } else if (mip == 2) { return tex2Dlod(_EdgeTexture2, coord); } else if (mip == 3) { return tex2Dlod(_EdgeTexture3, coord); } else { return tex2Dlod(_EdgeTexture4, coord); } } float2 centerPixel(float2 inputP) { return floor(inputP - float2(0.5,0.5)) + float2(0.5,0.5); } float2 snapToTexelCenter(float2 inputP, float2 texSize, float2 texSizeInv) { return centerPixel(inputP * texSize) * texSizeInv; } float4 bilateralUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; float2 fullTexSize = _ReflectionBufferSize; float2 fullTexSizeInv = 1.0 / fullTexSize; float4 hiP0 = float4(snapToTexelCenter(p0, fullTexSize, fullTexSizeInv), 0,0); float4 hiP3 = float4(snapToTexelCenter(p3, fullTexSize, fullTexSizeInv), 0,0); float4 hiP1 = float4(snapToTexelCenter(p1, fullTexSize, fullTexSizeInv), 0,0); float4 hiP2 = float4(snapToTexelCenter(p2, fullTexSize, fullTexSizeInv), 0,0); float4 tempCenter = tex2Dlod(_NormalAndRoughnessTexture, float4(tsP, 0, 0)); float3 n = tempCenter.xyz * 2 - 1; float4 temp0 = tex2Dlod(_NormalAndRoughnessTexture, hiP0); float4 temp1 = tex2Dlod(_NormalAndRoughnessTexture, hiP1); float4 temp2 = tex2Dlod(_NormalAndRoughnessTexture, hiP2); float4 temp3 = tex2Dlod(_NormalAndRoughnessTexture, hiP3); float3 n0 = temp0.xyz * 2 - 1; float3 n1 = temp1.xyz * 2 - 1; float3 n2 = temp2.xyz * 2 - 1; float3 n3 = temp3.xyz * 2 - 1; a0 *= normalWeight(n, n0); a1 *= normalWeight(n, n1); a2 *= normalWeight(n, n2); a3 *= normalWeight(n, n3); float r = tempCenter.a; float r0 = temp0.a; float r1 = temp1.a; float r2 = temp2.a; float r3 = temp3.a; a0 *= roughnessWeight(r, r0); a1 *= roughnessWeight(r, r1); a2 *= roughnessWeight(r, r2); a3 *= roughnessWeight(r, r3); // Slightly offset from zero a0 = max(a0, 0.001); a1 = max(a1, 0.001); a2 = max(a2, 0.001); a3 = max(a3, 0.001); // Nearest neighbor // a0 = a1 = a2 = a3 = 1.0; // Normalize the blending weights (weights were chosen so that // the denominator can never be zero) float norm = 1.0 / (a0 + a1 + a2 + a3); // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3) * norm; //return V0; return value; } /** Explicit bilinear fetches; must be used if the reflection buffer is bound using point sampling */ float4 bilinearUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3); return value; } // Unity's roughness is GGX roughness squared float roughnessToBlinnPhongExponent(float roughness) { float r2 = roughness*roughness; return 2.0f / r2*r2 - 2.0f; } float glossyLobeSlope(float roughness) { return pow(roughness, 4.0/3.0); } // Empirically based on our filter: // Mip | Pixels // -------------- // 0 | 1 no filter, so single pixel // 1 | 17 2r + 1 filter applied once, grabbing from pixels r away in either direction (r=8, four samples times stride of 2) // 2 | 50 2r + 1 filter applied on double size pixels, and each of those pixels had reached another r out to the side 2(2r + 1) + m_1 // 3 | 118 4(2r + 1) + m_2 // 4 | 254 8(2r + 1) + m_3 // // Approximated by pixels = 16*2^mip-15 // rearranging we get mip = log_2((pixels + 15) / 16) // float filterFootprintInPixelsToMip(float footprint) { return log2((footprint + 15) / 16); } float3 ansiGradient(float t) { //return float3(t, t, t); return fmod(floor(t * float3(8.0, 4.0, 2.0)), 2.0); } float4 fragCompositeSSR(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP * _SourceToTempUV.zw).a; float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).z; } // Get the camera space position of the reflection hit float3 csPosition = GetPosition(tsP); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 c_mi = csMirrorVector(csPosition, csN); float3 csHitpoint = c_mi * rayDistance + csPosition; float gatherFootprintInMeters = glossyLobeSlope(roughness) * rayDistance; // We could add a term that incorporates the normal // This approximation assumes reflections happen at a glancing angle float filterFootprintInPixels = gatherFootprintInMeters * _PixelsPerMeterAtOneMeter / csHitpoint.z; if (_HalfResolution == 1) { filterFootprintInPixels *= 0.5; } float mip = filterFootprintInPixelsToMip(filterFootprintInPixels); float nonPhysicalMip = pow(roughness, 3.0 / 4.0) * UNITY_SPECCUBE_LOD_STEPS; if (_HalfResolution == 1) { nonPhysicalMip = nonPhysicalMip * 0.7; } mip = lerp(nonPhysicalMip, mip, _DistanceBlur); mip = max(0, min(4, mip + _MipBias)); if (_DebugMode == 14) { return float4(ansiGradient(mip*0.25), 1.0); } float4 result; { int mipMin = int(mip); int mipMax = min(mipMin + 1, 4); float mipLerp = mip-mipMin; if (_BilateralUpsampling == 1) { if (_UseEdgeDetector == 1) { float nonEdginess = lerp(getEdgeValue(tsP, mipMin), getEdgeValue(tsP, mipMax), mipLerp); if (nonEdginess < 0.9) { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); if (_VisualizeWhereBilateral == 1) { result = float4(1, 0, 0, 0); } } else { result = lerp(bilinearUpsampleReflection(tsP, mipMin), bilinearUpsampleReflection(tsP, mipMax), mipLerp); } } else { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); } } else { float4 minResult = getReflectionValue(tsP, mipMin); float4 maxResult = getReflectionValue(tsP, mipMax); result = lerp(minResult, maxResult, mipLerp); result.a = min(minResult.a, maxResult.a); } } result.a = min(result.a, 1.0); float vignette = applyEdgeFade(tsP, _ScreenEdgeFading); result.a *= vignette; float alphaModifier = 1.0 - clamp((roughness - (_MaxRoughness - _RoughnessFalloffRange)) / _RoughnessFalloffRange, 0.0, 1.0); result.a *= alphaModifier; return result; } float4 fragBlit(v2f i) : SV_Target { // Pixel being shaded float2 ssP = i.uv2.xy; return tex2D(_MainTex, ssP); } float edgeDetector(float2 tsP, float2 axis, float2 texelSize) { float4 temp = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = temp.a; float3 midpointNormal = temp.rgb * 2 - 1; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (axis * _MainTex_TexelSize.xy); temp = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = temp.a; tapNormal = temp.rgb * 2 - 1; float weight = 1.0; weight *= roughnessWeight(midpointRoughness, tapRoughness); weight *= normalWeight(midpointNormal, tapNormal); return weight; } float4 fragEdge(v2f i) : SV_Target { float2 tsP = i.uv2.xy; return edgeDetector(tsP, float2(1,0), _MainTex_TexelSize.xy) * edgeDetector(tsP, float2(0,1), _MainTex_TexelSize.xy); } int _LastMip; float4 fragMin(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float2 lastTexSize = mipToSize(_LastMip); float2 lastTexSizeInv = 1.0 / lastTexSize;//_SourceToTempUV.xy * exp2(mip); float2 p00 = snapToTexelCenter(tsP, lastTexSize, lastTexSizeInv); float2 p11 = p00 + lastTexSizeInv; return min( min(tex2D(_MainTex, p00), tex2D(_MainTex, p11)), min(tex2D(_MainTex, float2(p00.x, p11.y)), tex2D(_MainTex, float2(p11.x, p00.y))) ); } float4 fragResolveHitPoints(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float4 temp = tex2D(_HitPointTexture, tsP); float2 hitPoint = temp.xy; float confidence = temp.w; float3 colorResult = confidence > 0.0 ? tex2D(_MainTex, hitPoint).rgb : tex2D(_CameraReflectionsTexture, tsP).rgb; #ifdef UNITY_COMPILER_HLSL if (any(isnan(colorResult))) colorResult = float3(0.0, 0.0, 0.0); // As of 11/29/2015, on Unity 5.3 on a Windows 8.1 computer with a NVIDIA GeForce 980, // with driver 347.62, the above check does not actually work to get rid of NaNs! // So we add this "redundant" check. if (!all(isfinite(colorResult))) colorResult = float3(0.0, 0.0, 0.0); #endif return float4(colorResult, confidence); } float4 fragBilatKeyPack(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float3 csN = tex2D(_CameraGBufferTexture2, tsP).xyz; float roughness = tex2D(_CameraGBufferTexture1, tsP).a; return float4(csN, roughness); } float4 fragDepthToCSZ(v2f i) : SV_Target { float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv2.xy); return float4(-LinearEyeDepth(depth), 0.0, 0.0, 0.0); } float3 csPositionToOldNormalizedScreenCoordinate(float3 csPosition) { // We could multiply these matrices together on the CPU float4 oldCSPosition = mul(_CurrentCameraToPreviousCamera, float4(csPosition, 1.0)); float4 oldSSCoordinate = mul(_ProjectToPixelMatrix, oldCSPosition); return float3((oldSSCoordinate.xy / oldSSCoordinate.w) * _InvScreenSize, oldCSPosition.z); } float4 fragTemporalFilter(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, tsP); float3 csPosition = GetPosition(tsP); float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).b; } // Virtual is the physics sense, not the other 20+ interpretations in CG float3 virtualReflectionCSPosition = normalize(csPosition) * rayDistance + csPosition; float3 oldTSP = csPositionToOldNormalizedScreenCoordinate(csPosition); float3 oldVirtualReflectionTSP = csPositionToOldNormalizedScreenCoordinate(virtualReflectionCSPosition); float oldCSZ = tex2D(_PreviousCSZBuffer, oldTSP.xy); float4 currentResult = tex2D(_FinalReflectionTexture, tsP); float4 oldResult = tex2D(_PreviousReflectionTexture, oldVirtualReflectionTSP.xy); float zError = abs(oldCSZ - oldTSP.z); // Start knocking down contribution at 5cm error, go to 0 at 5 more cm later float fallbackStart = 0.05; float fallbackEnd = 0.05; float zAlphaModifier = 1.0 - clamp((zError - fallbackStart) * (1.0 / fallbackEnd), 0.0, 1.0); float alpha = _TemporalAlpha; alpha *= zAlphaModifier; if (_UseTemporalConfidence) { alpha *= oldResult.w; } // Always compress currentResult.rgb = highlightCompression(currentResult.rgb); oldResult.rgb = highlightCompression(oldResult.rgb); float4 resultValue = lerp(currentResult, oldResult, alpha); resultValue.rgb = highlightDecompression(resultValue.rgb); return resultValue; } float4 fragAverageRayDistance(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float sumRayDistance = tex2D(_HitPointTexture, tsP).b; float sumWeight = 1.0; float roughness = 1.0 - tex2D(_CameraGBufferTexture1, tsP).a; float step = roughness * 3.0; /* for (float x = -step; x < step; x += 2 * step) { for (float y = -step; y < step; y += 2 * step) { float weight = 0.5; sumRayDistance += weight * tex2D(_HitPointTexture, (float2(x,y) * _InvScreenSize) + tsP).b; sumWeight += weight; } } */ return float4(sumRayDistance / sumWeight, 0.0, 0.0, 0.0); } static const int NUM_POISSON_TAPS = 12; // Same as used in CameraMotionBlur.shader static const float2 poissonSamples[NUM_POISSON_TAPS] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; float4 fragFilterSharpReflections(v2f i) : SV_Target { // Could improve perf by not computing blur when we won't be sampling the highest level anyways float2 tsP = i.uv2.xy; float4 sum = 0.0; float sampleRadius = _MainTex_TexelSize.xy * 2.0; for (int i = 0; i < NUM_POISSON_TAPS; i++) { float2 p = tsP + poissonSamples[i] * sampleRadius; float4 tap = tex2Dlod(_MainTex, float4(p, 0, 0)); if (_HighlightSuppression) { tap.rgb = highlightCompression(tap.rgb); } sum += tap; } float4 result = sum / float(NUM_POISSON_TAPS); if (_HighlightSuppression) { result.rgb = highlightDecompression(result.rgb); } return result; } #line 1077 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif /* UNITY: Original start of shader */ #pragma exclude_renderers gles xbox360 ps3 #pragma vertex vert #pragma fragment fragEdge #pragma target 3.0 SAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/ShadersÓ˙˙˙7küń'¨ô™!]{Ňô SHADOWS_SOFTDIRLIGHTMAP_COMBINEDDIRLIGHTMAP_SEPARATEDYNAMICLIGHTMAP_ON INSTANCING_ONg™ ˛†#line 1087 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #line 12 #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "ScreenSpaceRaytrace.cginc" float4 _ProjInfo; float4x4 _WorldToCameraMatrix; float4x4 _CameraToWorldMatrix; float4x4 _ProjectToPixelMatrix; float2 _ScreenSize; float2 _ReflectionBufferSize; float2 _InvScreenSize; float3 _CameraClipInfo; sampler2D _CameraGBufferTexture0; sampler2D _CameraGBufferTexture1; sampler2D _CameraGBufferTexture2; sampler2D _CameraGBufferTexture3; sampler2D _CameraReflectionsTexture; float _CurrentMipLevel; float _RayStepSize; float _MaxRayTraceDistance; float _LayerThickness; float _FresnelFade; float _FresnelFadePower; sampler2D _MainTex; int _VisualizeWhereBilateral; int _EnableSSR; int _DebugMode; int _HalfResolution; int _TreatBackfaceHitAsMiss; int _AllowBackwardsRays; int _TraceEverywhere; // RG: SS Hitpoint of ray // B: distance ray travelled, used for mip-selection in the final resolve // A: confidence value sampler2D _HitPointTexture; sampler2D _FinalReflectionTexture; // RGB: camera-space normal (encoded in [0-1]) // A: Roughness sampler2D _NormalAndRoughnessTexture; float4 _MainTex_TexelSize; float4 _SourceToTempUV; int _EnableRefine; int _AdditiveReflection; int _ImproveCorners; float _ScreenEdgeFading; float _MipBias; int _UseOcclusion; int _MaxSteps; int _FullResolutionFiltering; int _BilateralUpsampling; float _MaxRoughness; float _RoughnessFalloffRange; float _SSRMultiplier; float _FadeDistance; float _DistanceBlur; int _TraceBehindObjects; int _FallbackToSky; int _UseEdgeDetector; int _HighlightSuppression; /** The height in pixels of a 1m object if viewed from 1m away. */ float _PixelsPerMeterAtOneMeter; int _UseAverageRayDistance; sampler2D _AverageRayDistanceBuffer; // For temporal filtering: float4x4 _CurrentCameraToPreviousCamera; sampler2D _PreviousReflectionTexture; sampler2D _PreviousCSZBuffer; float _TemporalAlpha; int _UseTemporalConfidence; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy; o.uv2 = v.texcoord.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv2.y = 1-o.uv2.y; #endif return o; } float2 mipToSize(int mip) { return floor(_ReflectionBufferSize * exp2(-mip)); } float3 ReconstructCSPosition(float2 S, float z) { float linEyeZ = -LinearEyeDepth(z); return float3(( (( S.xy * _MainTex_TexelSize.zw) ) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); } /** Read the camera-space position of the point at screen-space pixel ssP */ float3 GetPosition(float2 ssP) { float3 P; P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy); // Offset to pixel center P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); return P; } float square(float x) { return x*x; } float applyEdgeFade(float2 tsP, float fadeStrength) { float maxFade = 0.1; float2 itsP = float2(1.0, 1.0) - tsP; float dist = min(min(itsP.x, itsP.y), min(tsP.x, tsP.x)); float fade = dist / (maxFade*fadeStrength + 0.001); fade = max(min(fade, 1.0), 0.0); fade = pow(fade, 0.2); return fade; } float3 csMirrorVector(float3 csPosition, float3 csN) { float3 csE = -normalize(csPosition.xyz); float cos_o = dot(csN, csE); float3 c_mi = normalize((csN * (2.0 * cos_o)) - csE); return c_mi; } float4 fragRaytrace(v2f i, int stepRate) { float2 ssP = i.uv2.xy * _SourceToTempUV.zw; float3 csPosition = GetPosition(ssP); float smoothness = tex2D(_CameraGBufferTexture1, ssP).a; if (csPosition.z < -100.0 || smoothness == 0.0) { return float4(0.0,0.0,0.0,0.0); } if ((!_TraceEverywhere) && (smoothness < (1.0 - _MaxRoughness)) ) { return float4(0.0, 0.0, 0.0, 0.0); } float3 wsNormal = tex2D(_CameraGBufferTexture2, ssP).rgb * 2.0 - 1.0; int2 ssC = int2(ssP * _ScreenSize); float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 csRayDirection = csMirrorVector(csPosition, csN); if (_AllowBackwardsRays == 0 && csRayDirection.z > 0.0) { return float4(0.0, 0.0, 0.0, 0.0); } float maxRayTraceDistance = _MaxRayTraceDistance; float jitterFraction = 0.0f; float layerThickness = _LayerThickness; int maxSteps = _MaxSteps; // Bump the ray more in world space as it gets farther away (and so each pixel covers more WS distance) float rayBump = max(-0.01*csPosition.z, 0.001); float2 hitPixel; float3 csHitPoint; float stepCount; bool wasHit = castDenseScreenSpaceRay (csPosition + (csN) * rayBump, csRayDirection, _ProjectToPixelMatrix, _ScreenSize, _CameraClipInfo, jitterFraction, maxSteps, layerThickness, maxRayTraceDistance, hitPixel, stepRate, _EnableRefine == 1, _TraceBehindObjects == 1, csHitPoint, stepCount); float2 tsPResult = hitPixel / _ScreenSize; float rayDist = dot(csHitPoint - csPosition, csRayDirection); float confidence = 0.0; if (wasHit) { confidence = square(1.0 - max(2.0*float(stepCount) / float(maxSteps) - 1.0, 0.0)); confidence *= clamp(((_MaxRayTraceDistance - rayDist) / _FadeDistance), 0.0, 1.0); // Fake fresnel fade float3 csE = -normalize(csPosition.xyz); confidence *= max(0.0, lerp(pow(abs(dot(csRayDirection, -csE)), _FresnelFadePower), 1, 1.0 - _FresnelFade)); if (_TreatBackfaceHitAsMiss > 0) { float3 wsHitNormal = tex2Dlod(_CameraGBufferTexture2, float4(tsPResult, 0, 0)).rgb * 2.0 - 1.0; float3 wsRayDirection = mul(_CameraToWorldMatrix, float4(csRayDirection, 0)).xyz; if (dot(wsHitNormal, wsRayDirection) > 0) { confidence = 0.0; } } } else if (_FallbackToSky > 0) { float sceneZ = tex2Dlod(_CameraDepthTexture, float4(tsPResult, 0, 0)).r; sceneZ = -LinearEyeDepth(sceneZ); if (sceneZ < -1000) { // 1km ~= infinity confidence = 1.0; } } // Fade out reflections that hit near edge of screen, to prevent abrupt appearance/disappearance when object go off screen // Fade out reflections that hit near edge of screen, // to prevent abrupt appearance/disappearance when object go off screen float vignette = applyEdgeFade(tsPResult, _ScreenEdgeFading); confidence *= vignette; confidence *= vignette; return float4(tsPResult, rayDist, confidence); } float4 fragComposite(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; // View space point being shaded float3 C = GetPosition(tsP); // Final image before this pass float4 gbuffer3 = tex2D(_MainTex, i.uv); float4 specEmission = float4(0.0,0.0,0.0,0.0); float3 specColor = tex2D(_CameraGBufferTexture1, tsP).rgb; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP).a; float4 reflectionTexel = tex2D(_FinalReflectionTexture, tsP * _SourceToTempUV.xy); float4 gbuffer0 = tex2D(_CameraGBufferTexture0, tsP); // Let core Unity functions do the dirty work of applying the BRDF float3 baseColor = gbuffer0.rgb; float occlusion = gbuffer0.a; float oneMinusReflectivity; baseColor = EnergyConservationBetweenDiffuseAndSpecular(baseColor, specColor, oneMinusReflectivity); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csEyeVec = normalize(C); float3 eyeVec = mul(_CameraToWorldMatrix, float4(csEyeVec, 0)).xyz; float3 worldPos = mul(_CameraToWorldMatrix, float4(C, 1)).xyz; float cos_o = dot(wsNormal, eyeVec); float3 w_mi = -normalize((wsNormal * (2.0 * cos_o)) - eyeVec); float3 incomingRadiance = reflectionTexel.rgb; UnityLight light; light.color = 0; light.dir = 0; light.ndotl = 0; UnityIndirect ind; ind.diffuse = 0; ind.specular = incomingRadiance; float3 ssrResult = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, 1-roughness, wsNormal, -eyeVec, light, ind).rgb * _SSRMultiplier; float confidence = reflectionTexel.a; if(_EnableSSR == 0) { confidence = 0.0; } specEmission.rgb = tex2D(_CameraReflectionsTexture, tsP).rgb; float3 finalGlossyTerm; // Subtract out Unity's glossy result: (we're just applying the delta) if (_AdditiveReflection == 0) { gbuffer3 -= specEmission; // We may have blown out our dynamic range by adding then subtracting the reflection probes. // As a half-measure to fix this, simply clamp to zero gbuffer3 = max(gbuffer3, 0); finalGlossyTerm = lerp(specEmission.rgb, ssrResult, saturate(confidence)); } else { finalGlossyTerm = ssrResult*saturate(confidence); } if (_UseOcclusion) { finalGlossyTerm *= occlusion; } if (_DebugMode == 1) return float4(incomingRadiance,0); if (_DebugMode == 2) return float4(ssrResult,0); if (_DebugMode == 3) return float4(finalGlossyTerm,0); if (_DebugMode == 4) return confidence; if (_DebugMode == 5) return roughness; if (_DebugMode == 6) return float4(baseColor,0); if (_DebugMode == 7) return float4(specColor,0); if (_DebugMode == 8) return 1.0-oneMinusReflectivity; if (_DebugMode == 9) return float4(specEmission.rgb,0); if (_DebugMode == 10) return float4(specEmission.rgb-ssrResult,0); if (_DebugMode == 11) return float4(ssrResult-specEmission.rgb,0); if (_DebugMode == 12) return gbuffer3; if (_DebugMode == 13) return -float4(gbuffer3.rgb, 0.0); if (_DebugMode == 14) // Mip level, stored directly in ssrResult return float4(incomingRadiance, 0); // Additively blend the glossy GI result with the output buffer return gbuffer3 + float4(finalGlossyTerm, 0); } float roughnessWeight(float midpointRoughness, float tapRoughness) { return (1.0 - sqrt(sqrt(abs(midpointRoughness-tapRoughness)))); } float normalWeight(float3 midpointNormal, float3 tapNormal) { return clamp(dot(midpointNormal, tapNormal), 0, 1); } float highlightDecompression(float x) { return x / (1.0 - x); } float3 highlightDecompression(float3 x) { return float3( highlightDecompression(x.x), highlightDecompression(x.y), highlightDecompression(x.z)); } float highlightCompression(float x) { return x / (1.0 + x); } float3 highlightCompression(float3 x) { return float3( highlightCompression(x.x), highlightCompression(x.y), highlightCompression(x.z)); } float4 _Axis; float4 fragGBlur(v2f i) : SV_Target { int radius = 4; // Pixel being shaded float2 tsP = i.uv2.xy; float weightSum = 0.0; float gaussWeights[5] = {0.225, 0.150, 0.110, 0.075, 0.0525};//{0.225, 0.150, 0.110, 0.075, 0.0525}; float4 resultSum = float4(0.0, 0.0, 0.0, 0.0); float4 unweightedResultSum = float4(0.0, 0.0, 0.0, 0.0); float4 nAndRough = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = nAndRough.a; float3 midpointNormal = nAndRough.rgb * 2 - 1; if (_FullResolutionFiltering) { for (int i = -2*radius; i <= 2*radius; ++i) { float4 temp; float tapRoughness = midpointRoughness; float3 tapNormal = midpointNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)); temp = tex2D(_MainTex, tsTap); int gaussWeightIndex; #if defined(UNITY_COMPILER_HLSL) && !defined(SHADER_API_D3D9) gaussWeightIndex = abs(i) >> 1; // We purposefully do integer division, but that does not compile on hlsl2glsl #else gaussWeightIndex = frac(abs(i) / 2); #endif float weight = temp.a * gaussWeights[gaussWeightIndex]*0.5; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; resultSum += temp*weight; unweightedResultSum += temp; } } else { for (int i = -radius; i <= radius; ++i) { float4 temp; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)*2.0); temp = tex2D(_MainTex, tsTap); float weight = temp.a * gaussWeights[abs(i)]; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; if (_HighlightSuppression) { temp.rgb = highlightCompression(temp.rgb); } unweightedResultSum += temp; resultSum += temp*weight; } } if (weightSum > 0.01) { float invWeightSum = (1.0/weightSum); // Adding the sqrt seems to decrease temporal flickering at the expense // of having larger "halos" of fallback on rough surfaces // Subject to change with testing. Sqrt around only half the expression is *intentional*. float confidence = min(resultSum.a * sqrt(max(invWeightSum, 2.0)), 1.0); float3 finalColor = resultSum.rgb * invWeightSum; if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, confidence); } else { float3 finalColor = unweightedResultSum.rgb / (2 * radius + 1); if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, 0.0); } } sampler2D _ReflectionTexture0; sampler2D _ReflectionTexture1; sampler2D _ReflectionTexture2; sampler2D _ReflectionTexture3; sampler2D _ReflectionTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getReflectionValue(float2 tsP, int mip) { float4 coord = float4(tsP,0,0); if (mip == 0) { return tex2Dlod(_ReflectionTexture0, coord); } else if (mip == 1) { return tex2Dlod(_ReflectionTexture1, coord); } else if (mip == 2) { return tex2Dlod(_ReflectionTexture2, coord); } else if (mip == 3) { return tex2Dlod(_ReflectionTexture3, coord); } else { return tex2Dlod(_ReflectionTexture4, coord); } } sampler2D _EdgeTexture0; sampler2D _EdgeTexture1; sampler2D _EdgeTexture2; sampler2D _EdgeTexture3; sampler2D _EdgeTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getEdgeValue(float2 tsP, int mip) { float4 coord = float4(tsP + float2(1.0/(2 * mipToSize(mip))),0,0); if (mip == 0) { return tex2Dlod(_EdgeTexture0, coord); } else if (mip == 1) { return tex2Dlod(_EdgeTexture1, coord); } else if (mip == 2) { return tex2Dlod(_EdgeTexture2, coord); } else if (mip == 3) { return tex2Dlod(_EdgeTexture3, coord); } else { return tex2Dlod(_EdgeTexture4, coord); } } float2 centerPixel(float2 inputP) { return floor(inputP - float2(0.5,0.5)) + float2(0.5,0.5); } float2 snapToTexelCenter(float2 inputP, float2 texSize, float2 texSizeInv) { return centerPixel(inputP * texSize) * texSizeInv; } float4 bilateralUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; float2 fullTexSize = _ReflectionBufferSize; float2 fullTexSizeInv = 1.0 / fullTexSize; float4 hiP0 = float4(snapToTexelCenter(p0, fullTexSize, fullTexSizeInv), 0,0); float4 hiP3 = float4(snapToTexelCenter(p3, fullTexSize, fullTexSizeInv), 0,0); float4 hiP1 = float4(snapToTexelCenter(p1, fullTexSize, fullTexSizeInv), 0,0); float4 hiP2 = float4(snapToTexelCenter(p2, fullTexSize, fullTexSizeInv), 0,0); float4 tempCenter = tex2Dlod(_NormalAndRoughnessTexture, float4(tsP, 0, 0)); float3 n = tempCenter.xyz * 2 - 1; float4 temp0 = tex2Dlod(_NormalAndRoughnessTexture, hiP0); float4 temp1 = tex2Dlod(_NormalAndRoughnessTexture, hiP1); float4 temp2 = tex2Dlod(_NormalAndRoughnessTexture, hiP2); float4 temp3 = tex2Dlod(_NormalAndRoughnessTexture, hiP3); float3 n0 = temp0.xyz * 2 - 1; float3 n1 = temp1.xyz * 2 - 1; float3 n2 = temp2.xyz * 2 - 1; float3 n3 = temp3.xyz * 2 - 1; a0 *= normalWeight(n, n0); a1 *= normalWeight(n, n1); a2 *= normalWeight(n, n2); a3 *= normalWeight(n, n3); float r = tempCenter.a; float r0 = temp0.a; float r1 = temp1.a; float r2 = temp2.a; float r3 = temp3.a; a0 *= roughnessWeight(r, r0); a1 *= roughnessWeight(r, r1); a2 *= roughnessWeight(r, r2); a3 *= roughnessWeight(r, r3); // Slightly offset from zero a0 = max(a0, 0.001); a1 = max(a1, 0.001); a2 = max(a2, 0.001); a3 = max(a3, 0.001); // Nearest neighbor // a0 = a1 = a2 = a3 = 1.0; // Normalize the blending weights (weights were chosen so that // the denominator can never be zero) float norm = 1.0 / (a0 + a1 + a2 + a3); // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3) * norm; //return V0; return value; } /** Explicit bilinear fetches; must be used if the reflection buffer is bound using point sampling */ float4 bilinearUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3); return value; } // Unity's roughness is GGX roughness squared float roughnessToBlinnPhongExponent(float roughness) { float r2 = roughness*roughness; return 2.0f / r2*r2 - 2.0f; } float glossyLobeSlope(float roughness) { return pow(roughness, 4.0/3.0); } // Empirically based on our filter: // Mip | Pixels // -------------- // 0 | 1 no filter, so single pixel // 1 | 17 2r + 1 filter applied once, grabbing from pixels r away in either direction (r=8, four samples times stride of 2) // 2 | 50 2r + 1 filter applied on double size pixels, and each of those pixels had reached another r out to the side 2(2r + 1) + m_1 // 3 | 118 4(2r + 1) + m_2 // 4 | 254 8(2r + 1) + m_3 // // Approximated by pixels = 16*2^mip-15 // rearranging we get mip = log_2((pixels + 15) / 16) // float filterFootprintInPixelsToMip(float footprint) { return log2((footprint + 15) / 16); } float3 ansiGradient(float t) { //return float3(t, t, t); return fmod(floor(t * float3(8.0, 4.0, 2.0)), 2.0); } float4 fragCompositeSSR(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP * _SourceToTempUV.zw).a; float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).z; } // Get the camera space position of the reflection hit float3 csPosition = GetPosition(tsP); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 c_mi = csMirrorVector(csPosition, csN); float3 csHitpoint = c_mi * rayDistance + csPosition; float gatherFootprintInMeters = glossyLobeSlope(roughness) * rayDistance; // We could add a term that incorporates the normal // This approximation assumes reflections happen at a glancing angle float filterFootprintInPixels = gatherFootprintInMeters * _PixelsPerMeterAtOneMeter / csHitpoint.z; if (_HalfResolution == 1) { filterFootprintInPixels *= 0.5; } float mip = filterFootprintInPixelsToMip(filterFootprintInPixels); float nonPhysicalMip = pow(roughness, 3.0 / 4.0) * UNITY_SPECCUBE_LOD_STEPS; if (_HalfResolution == 1) { nonPhysicalMip = nonPhysicalMip * 0.7; } mip = lerp(nonPhysicalMip, mip, _DistanceBlur); mip = max(0, min(4, mip + _MipBias)); if (_DebugMode == 14) { return float4(ansiGradient(mip*0.25), 1.0); } float4 result; { int mipMin = int(mip); int mipMax = min(mipMin + 1, 4); float mipLerp = mip-mipMin; if (_BilateralUpsampling == 1) { if (_UseEdgeDetector == 1) { float nonEdginess = lerp(getEdgeValue(tsP, mipMin), getEdgeValue(tsP, mipMax), mipLerp); if (nonEdginess < 0.9) { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); if (_VisualizeWhereBilateral == 1) { result = float4(1, 0, 0, 0); } } else { result = lerp(bilinearUpsampleReflection(tsP, mipMin), bilinearUpsampleReflection(tsP, mipMax), mipLerp); } } else { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); } } else { float4 minResult = getReflectionValue(tsP, mipMin); float4 maxResult = getReflectionValue(tsP, mipMax); result = lerp(minResult, maxResult, mipLerp); result.a = min(minResult.a, maxResult.a); } } result.a = min(result.a, 1.0); float vignette = applyEdgeFade(tsP, _ScreenEdgeFading); result.a *= vignette; float alphaModifier = 1.0 - clamp((roughness - (_MaxRoughness - _RoughnessFalloffRange)) / _RoughnessFalloffRange, 0.0, 1.0); result.a *= alphaModifier; return result; } float4 fragBlit(v2f i) : SV_Target { // Pixel being shaded float2 ssP = i.uv2.xy; return tex2D(_MainTex, ssP); } float edgeDetector(float2 tsP, float2 axis, float2 texelSize) { float4 temp = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = temp.a; float3 midpointNormal = temp.rgb * 2 - 1; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (axis * _MainTex_TexelSize.xy); temp = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = temp.a; tapNormal = temp.rgb * 2 - 1; float weight = 1.0; weight *= roughnessWeight(midpointRoughness, tapRoughness); weight *= normalWeight(midpointNormal, tapNormal); return weight; } float4 fragEdge(v2f i) : SV_Target { float2 tsP = i.uv2.xy; return edgeDetector(tsP, float2(1,0), _MainTex_TexelSize.xy) * edgeDetector(tsP, float2(0,1), _MainTex_TexelSize.xy); } int _LastMip; float4 fragMin(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float2 lastTexSize = mipToSize(_LastMip); float2 lastTexSizeInv = 1.0 / lastTexSize;//_SourceToTempUV.xy * exp2(mip); float2 p00 = snapToTexelCenter(tsP, lastTexSize, lastTexSizeInv); float2 p11 = p00 + lastTexSizeInv; return min( min(tex2D(_MainTex, p00), tex2D(_MainTex, p11)), min(tex2D(_MainTex, float2(p00.x, p11.y)), tex2D(_MainTex, float2(p11.x, p00.y))) ); } float4 fragResolveHitPoints(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float4 temp = tex2D(_HitPointTexture, tsP); float2 hitPoint = temp.xy; float confidence = temp.w; float3 colorResult = confidence > 0.0 ? tex2D(_MainTex, hitPoint).rgb : tex2D(_CameraReflectionsTexture, tsP).rgb; #ifdef UNITY_COMPILER_HLSL if (any(isnan(colorResult))) colorResult = float3(0.0, 0.0, 0.0); // As of 11/29/2015, on Unity 5.3 on a Windows 8.1 computer with a NVIDIA GeForce 980, // with driver 347.62, the above check does not actually work to get rid of NaNs! // So we add this "redundant" check. if (!all(isfinite(colorResult))) colorResult = float3(0.0, 0.0, 0.0); #endif return float4(colorResult, confidence); } float4 fragBilatKeyPack(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float3 csN = tex2D(_CameraGBufferTexture2, tsP).xyz; float roughness = tex2D(_CameraGBufferTexture1, tsP).a; return float4(csN, roughness); } float4 fragDepthToCSZ(v2f i) : SV_Target { float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv2.xy); return float4(-LinearEyeDepth(depth), 0.0, 0.0, 0.0); } float3 csPositionToOldNormalizedScreenCoordinate(float3 csPosition) { // We could multiply these matrices together on the CPU float4 oldCSPosition = mul(_CurrentCameraToPreviousCamera, float4(csPosition, 1.0)); float4 oldSSCoordinate = mul(_ProjectToPixelMatrix, oldCSPosition); return float3((oldSSCoordinate.xy / oldSSCoordinate.w) * _InvScreenSize, oldCSPosition.z); } float4 fragTemporalFilter(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, tsP); float3 csPosition = GetPosition(tsP); float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).b; } // Virtual is the physics sense, not the other 20+ interpretations in CG float3 virtualReflectionCSPosition = normalize(csPosition) * rayDistance + csPosition; float3 oldTSP = csPositionToOldNormalizedScreenCoordinate(csPosition); float3 oldVirtualReflectionTSP = csPositionToOldNormalizedScreenCoordinate(virtualReflectionCSPosition); float oldCSZ = tex2D(_PreviousCSZBuffer, oldTSP.xy); float4 currentResult = tex2D(_FinalReflectionTexture, tsP); float4 oldResult = tex2D(_PreviousReflectionTexture, oldVirtualReflectionTSP.xy); float zError = abs(oldCSZ - oldTSP.z); // Start knocking down contribution at 5cm error, go to 0 at 5 more cm later float fallbackStart = 0.05; float fallbackEnd = 0.05; float zAlphaModifier = 1.0 - clamp((zError - fallbackStart) * (1.0 / fallbackEnd), 0.0, 1.0); float alpha = _TemporalAlpha; alpha *= zAlphaModifier; if (_UseTemporalConfidence) { alpha *= oldResult.w; } // Always compress currentResult.rgb = highlightCompression(currentResult.rgb); oldResult.rgb = highlightCompression(oldResult.rgb); float4 resultValue = lerp(currentResult, oldResult, alpha); resultValue.rgb = highlightDecompression(resultValue.rgb); return resultValue; } float4 fragAverageRayDistance(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float sumRayDistance = tex2D(_HitPointTexture, tsP).b; float sumWeight = 1.0; float roughness = 1.0 - tex2D(_CameraGBufferTexture1, tsP).a; float step = roughness * 3.0; /* for (float x = -step; x < step; x += 2 * step) { for (float y = -step; y < step; y += 2 * step) { float weight = 0.5; sumRayDistance += weight * tex2D(_HitPointTexture, (float2(x,y) * _InvScreenSize) + tsP).b; sumWeight += weight; } } */ return float4(sumRayDistance / sumWeight, 0.0, 0.0, 0.0); } static const int NUM_POISSON_TAPS = 12; // Same as used in CameraMotionBlur.shader static const float2 poissonSamples[NUM_POISSON_TAPS] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; float4 fragFilterSharpReflections(v2f i) : SV_Target { // Could improve perf by not computing blur when we won't be sampling the highest level anyways float2 tsP = i.uv2.xy; float4 sum = 0.0; float sampleRadius = _MainTex_TexelSize.xy * 2.0; for (int i = 0; i < NUM_POISSON_TAPS; i++) { float2 p = tsP + poissonSamples[i] * sampleRadius; float4 tap = tex2Dlod(_MainTex, float4(p, 0, 0)); if (_HighlightSuppression) { tap.rgb = highlightCompression(tap.rgb); } sum += tap; } float4 result = sum / float(NUM_POISSON_TAPS); if (_HighlightSuppression) { result.rgb = highlightDecompression(result.rgb); } return result; } #line 1087 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif /* UNITY: Original start of shader */ #pragma exclude_renderers gles xbox360 ps3 #pragma vertex vert #pragma fragment fragMin #pragma target 3.0 SAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/ShadersÓ˙˙˙Aküń'¨ô™!]{Ňô SHADOWS_SOFTDIRLIGHTMAP_COMBINEDDIRLIGHTMAP_SEPARATEDYNAMICLIGHTMAP_ON INSTANCING_ONkĄ ż†#line 1097 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #line 12 #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "ScreenSpaceRaytrace.cginc" float4 _ProjInfo; float4x4 _WorldToCameraMatrix; float4x4 _CameraToWorldMatrix; float4x4 _ProjectToPixelMatrix; float2 _ScreenSize; float2 _ReflectionBufferSize; float2 _InvScreenSize; float3 _CameraClipInfo; sampler2D _CameraGBufferTexture0; sampler2D _CameraGBufferTexture1; sampler2D _CameraGBufferTexture2; sampler2D _CameraGBufferTexture3; sampler2D _CameraReflectionsTexture; float _CurrentMipLevel; float _RayStepSize; float _MaxRayTraceDistance; float _LayerThickness; float _FresnelFade; float _FresnelFadePower; sampler2D _MainTex; int _VisualizeWhereBilateral; int _EnableSSR; int _DebugMode; int _HalfResolution; int _TreatBackfaceHitAsMiss; int _AllowBackwardsRays; int _TraceEverywhere; // RG: SS Hitpoint of ray // B: distance ray travelled, used for mip-selection in the final resolve // A: confidence value sampler2D _HitPointTexture; sampler2D _FinalReflectionTexture; // RGB: camera-space normal (encoded in [0-1]) // A: Roughness sampler2D _NormalAndRoughnessTexture; float4 _MainTex_TexelSize; float4 _SourceToTempUV; int _EnableRefine; int _AdditiveReflection; int _ImproveCorners; float _ScreenEdgeFading; float _MipBias; int _UseOcclusion; int _MaxSteps; int _FullResolutionFiltering; int _BilateralUpsampling; float _MaxRoughness; float _RoughnessFalloffRange; float _SSRMultiplier; float _FadeDistance; float _DistanceBlur; int _TraceBehindObjects; int _FallbackToSky; int _UseEdgeDetector; int _HighlightSuppression; /** The height in pixels of a 1m object if viewed from 1m away. */ float _PixelsPerMeterAtOneMeter; int _UseAverageRayDistance; sampler2D _AverageRayDistanceBuffer; // For temporal filtering: float4x4 _CurrentCameraToPreviousCamera; sampler2D _PreviousReflectionTexture; sampler2D _PreviousCSZBuffer; float _TemporalAlpha; int _UseTemporalConfidence; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy; o.uv2 = v.texcoord.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv2.y = 1-o.uv2.y; #endif return o; } float2 mipToSize(int mip) { return floor(_ReflectionBufferSize * exp2(-mip)); } float3 ReconstructCSPosition(float2 S, float z) { float linEyeZ = -LinearEyeDepth(z); return float3(( (( S.xy * _MainTex_TexelSize.zw) ) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); } /** Read the camera-space position of the point at screen-space pixel ssP */ float3 GetPosition(float2 ssP) { float3 P; P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy); // Offset to pixel center P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); return P; } float square(float x) { return x*x; } float applyEdgeFade(float2 tsP, float fadeStrength) { float maxFade = 0.1; float2 itsP = float2(1.0, 1.0) - tsP; float dist = min(min(itsP.x, itsP.y), min(tsP.x, tsP.x)); float fade = dist / (maxFade*fadeStrength + 0.001); fade = max(min(fade, 1.0), 0.0); fade = pow(fade, 0.2); return fade; } float3 csMirrorVector(float3 csPosition, float3 csN) { float3 csE = -normalize(csPosition.xyz); float cos_o = dot(csN, csE); float3 c_mi = normalize((csN * (2.0 * cos_o)) - csE); return c_mi; } float4 fragRaytrace(v2f i, int stepRate) { float2 ssP = i.uv2.xy * _SourceToTempUV.zw; float3 csPosition = GetPosition(ssP); float smoothness = tex2D(_CameraGBufferTexture1, ssP).a; if (csPosition.z < -100.0 || smoothness == 0.0) { return float4(0.0,0.0,0.0,0.0); } if ((!_TraceEverywhere) && (smoothness < (1.0 - _MaxRoughness)) ) { return float4(0.0, 0.0, 0.0, 0.0); } float3 wsNormal = tex2D(_CameraGBufferTexture2, ssP).rgb * 2.0 - 1.0; int2 ssC = int2(ssP * _ScreenSize); float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 csRayDirection = csMirrorVector(csPosition, csN); if (_AllowBackwardsRays == 0 && csRayDirection.z > 0.0) { return float4(0.0, 0.0, 0.0, 0.0); } float maxRayTraceDistance = _MaxRayTraceDistance; float jitterFraction = 0.0f; float layerThickness = _LayerThickness; int maxSteps = _MaxSteps; // Bump the ray more in world space as it gets farther away (and so each pixel covers more WS distance) float rayBump = max(-0.01*csPosition.z, 0.001); float2 hitPixel; float3 csHitPoint; float stepCount; bool wasHit = castDenseScreenSpaceRay (csPosition + (csN) * rayBump, csRayDirection, _ProjectToPixelMatrix, _ScreenSize, _CameraClipInfo, jitterFraction, maxSteps, layerThickness, maxRayTraceDistance, hitPixel, stepRate, _EnableRefine == 1, _TraceBehindObjects == 1, csHitPoint, stepCount); float2 tsPResult = hitPixel / _ScreenSize; float rayDist = dot(csHitPoint - csPosition, csRayDirection); float confidence = 0.0; if (wasHit) { confidence = square(1.0 - max(2.0*float(stepCount) / float(maxSteps) - 1.0, 0.0)); confidence *= clamp(((_MaxRayTraceDistance - rayDist) / _FadeDistance), 0.0, 1.0); // Fake fresnel fade float3 csE = -normalize(csPosition.xyz); confidence *= max(0.0, lerp(pow(abs(dot(csRayDirection, -csE)), _FresnelFadePower), 1, 1.0 - _FresnelFade)); if (_TreatBackfaceHitAsMiss > 0) { float3 wsHitNormal = tex2Dlod(_CameraGBufferTexture2, float4(tsPResult, 0, 0)).rgb * 2.0 - 1.0; float3 wsRayDirection = mul(_CameraToWorldMatrix, float4(csRayDirection, 0)).xyz; if (dot(wsHitNormal, wsRayDirection) > 0) { confidence = 0.0; } } } else if (_FallbackToSky > 0) { float sceneZ = tex2Dlod(_CameraDepthTexture, float4(tsPResult, 0, 0)).r; sceneZ = -LinearEyeDepth(sceneZ); if (sceneZ < -1000) { // 1km ~= infinity confidence = 1.0; } } // Fade out reflections that hit near edge of screen, to prevent abrupt appearance/disappearance when object go off screen // Fade out reflections that hit near edge of screen, // to prevent abrupt appearance/disappearance when object go off screen float vignette = applyEdgeFade(tsPResult, _ScreenEdgeFading); confidence *= vignette; confidence *= vignette; return float4(tsPResult, rayDist, confidence); } float4 fragComposite(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; // View space point being shaded float3 C = GetPosition(tsP); // Final image before this pass float4 gbuffer3 = tex2D(_MainTex, i.uv); float4 specEmission = float4(0.0,0.0,0.0,0.0); float3 specColor = tex2D(_CameraGBufferTexture1, tsP).rgb; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP).a; float4 reflectionTexel = tex2D(_FinalReflectionTexture, tsP * _SourceToTempUV.xy); float4 gbuffer0 = tex2D(_CameraGBufferTexture0, tsP); // Let core Unity functions do the dirty work of applying the BRDF float3 baseColor = gbuffer0.rgb; float occlusion = gbuffer0.a; float oneMinusReflectivity; baseColor = EnergyConservationBetweenDiffuseAndSpecular(baseColor, specColor, oneMinusReflectivity); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csEyeVec = normalize(C); float3 eyeVec = mul(_CameraToWorldMatrix, float4(csEyeVec, 0)).xyz; float3 worldPos = mul(_CameraToWorldMatrix, float4(C, 1)).xyz; float cos_o = dot(wsNormal, eyeVec); float3 w_mi = -normalize((wsNormal * (2.0 * cos_o)) - eyeVec); float3 incomingRadiance = reflectionTexel.rgb; UnityLight light; light.color = 0; light.dir = 0; light.ndotl = 0; UnityIndirect ind; ind.diffuse = 0; ind.specular = incomingRadiance; float3 ssrResult = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, 1-roughness, wsNormal, -eyeVec, light, ind).rgb * _SSRMultiplier; float confidence = reflectionTexel.a; if(_EnableSSR == 0) { confidence = 0.0; } specEmission.rgb = tex2D(_CameraReflectionsTexture, tsP).rgb; float3 finalGlossyTerm; // Subtract out Unity's glossy result: (we're just applying the delta) if (_AdditiveReflection == 0) { gbuffer3 -= specEmission; // We may have blown out our dynamic range by adding then subtracting the reflection probes. // As a half-measure to fix this, simply clamp to zero gbuffer3 = max(gbuffer3, 0); finalGlossyTerm = lerp(specEmission.rgb, ssrResult, saturate(confidence)); } else { finalGlossyTerm = ssrResult*saturate(confidence); } if (_UseOcclusion) { finalGlossyTerm *= occlusion; } if (_DebugMode == 1) return float4(incomingRadiance,0); if (_DebugMode == 2) return float4(ssrResult,0); if (_DebugMode == 3) return float4(finalGlossyTerm,0); if (_DebugMode == 4) return confidence; if (_DebugMode == 5) return roughness; if (_DebugMode == 6) return float4(baseColor,0); if (_DebugMode == 7) return float4(specColor,0); if (_DebugMode == 8) return 1.0-oneMinusReflectivity; if (_DebugMode == 9) return float4(specEmission.rgb,0); if (_DebugMode == 10) return float4(specEmission.rgb-ssrResult,0); if (_DebugMode == 11) return float4(ssrResult-specEmission.rgb,0); if (_DebugMode == 12) return gbuffer3; if (_DebugMode == 13) return -float4(gbuffer3.rgb, 0.0); if (_DebugMode == 14) // Mip level, stored directly in ssrResult return float4(incomingRadiance, 0); // Additively blend the glossy GI result with the output buffer return gbuffer3 + float4(finalGlossyTerm, 0); } float roughnessWeight(float midpointRoughness, float tapRoughness) { return (1.0 - sqrt(sqrt(abs(midpointRoughness-tapRoughness)))); } float normalWeight(float3 midpointNormal, float3 tapNormal) { return clamp(dot(midpointNormal, tapNormal), 0, 1); } float highlightDecompression(float x) { return x / (1.0 - x); } float3 highlightDecompression(float3 x) { return float3( highlightDecompression(x.x), highlightDecompression(x.y), highlightDecompression(x.z)); } float highlightCompression(float x) { return x / (1.0 + x); } float3 highlightCompression(float3 x) { return float3( highlightCompression(x.x), highlightCompression(x.y), highlightCompression(x.z)); } float4 _Axis; float4 fragGBlur(v2f i) : SV_Target { int radius = 4; // Pixel being shaded float2 tsP = i.uv2.xy; float weightSum = 0.0; float gaussWeights[5] = {0.225, 0.150, 0.110, 0.075, 0.0525};//{0.225, 0.150, 0.110, 0.075, 0.0525}; float4 resultSum = float4(0.0, 0.0, 0.0, 0.0); float4 unweightedResultSum = float4(0.0, 0.0, 0.0, 0.0); float4 nAndRough = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = nAndRough.a; float3 midpointNormal = nAndRough.rgb * 2 - 1; if (_FullResolutionFiltering) { for (int i = -2*radius; i <= 2*radius; ++i) { float4 temp; float tapRoughness = midpointRoughness; float3 tapNormal = midpointNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)); temp = tex2D(_MainTex, tsTap); int gaussWeightIndex; #if defined(UNITY_COMPILER_HLSL) && !defined(SHADER_API_D3D9) gaussWeightIndex = abs(i) >> 1; // We purposefully do integer division, but that does not compile on hlsl2glsl #else gaussWeightIndex = frac(abs(i) / 2); #endif float weight = temp.a * gaussWeights[gaussWeightIndex]*0.5; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; resultSum += temp*weight; unweightedResultSum += temp; } } else { for (int i = -radius; i <= radius; ++i) { float4 temp; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)*2.0); temp = tex2D(_MainTex, tsTap); float weight = temp.a * gaussWeights[abs(i)]; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; if (_HighlightSuppression) { temp.rgb = highlightCompression(temp.rgb); } unweightedResultSum += temp; resultSum += temp*weight; } } if (weightSum > 0.01) { float invWeightSum = (1.0/weightSum); // Adding the sqrt seems to decrease temporal flickering at the expense // of having larger "halos" of fallback on rough surfaces // Subject to change with testing. Sqrt around only half the expression is *intentional*. float confidence = min(resultSum.a * sqrt(max(invWeightSum, 2.0)), 1.0); float3 finalColor = resultSum.rgb * invWeightSum; if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, confidence); } else { float3 finalColor = unweightedResultSum.rgb / (2 * radius + 1); if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, 0.0); } } sampler2D _ReflectionTexture0; sampler2D _ReflectionTexture1; sampler2D _ReflectionTexture2; sampler2D _ReflectionTexture3; sampler2D _ReflectionTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getReflectionValue(float2 tsP, int mip) { float4 coord = float4(tsP,0,0); if (mip == 0) { return tex2Dlod(_ReflectionTexture0, coord); } else if (mip == 1) { return tex2Dlod(_ReflectionTexture1, coord); } else if (mip == 2) { return tex2Dlod(_ReflectionTexture2, coord); } else if (mip == 3) { return tex2Dlod(_ReflectionTexture3, coord); } else { return tex2Dlod(_ReflectionTexture4, coord); } } sampler2D _EdgeTexture0; sampler2D _EdgeTexture1; sampler2D _EdgeTexture2; sampler2D _EdgeTexture3; sampler2D _EdgeTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getEdgeValue(float2 tsP, int mip) { float4 coord = float4(tsP + float2(1.0/(2 * mipToSize(mip))),0,0); if (mip == 0) { return tex2Dlod(_EdgeTexture0, coord); } else if (mip == 1) { return tex2Dlod(_EdgeTexture1, coord); } else if (mip == 2) { return tex2Dlod(_EdgeTexture2, coord); } else if (mip == 3) { return tex2Dlod(_EdgeTexture3, coord); } else { return tex2Dlod(_EdgeTexture4, coord); } } float2 centerPixel(float2 inputP) { return floor(inputP - float2(0.5,0.5)) + float2(0.5,0.5); } float2 snapToTexelCenter(float2 inputP, float2 texSize, float2 texSizeInv) { return centerPixel(inputP * texSize) * texSizeInv; } float4 bilateralUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; float2 fullTexSize = _ReflectionBufferSize; float2 fullTexSizeInv = 1.0 / fullTexSize; float4 hiP0 = float4(snapToTexelCenter(p0, fullTexSize, fullTexSizeInv), 0,0); float4 hiP3 = float4(snapToTexelCenter(p3, fullTexSize, fullTexSizeInv), 0,0); float4 hiP1 = float4(snapToTexelCenter(p1, fullTexSize, fullTexSizeInv), 0,0); float4 hiP2 = float4(snapToTexelCenter(p2, fullTexSize, fullTexSizeInv), 0,0); float4 tempCenter = tex2Dlod(_NormalAndRoughnessTexture, float4(tsP, 0, 0)); float3 n = tempCenter.xyz * 2 - 1; float4 temp0 = tex2Dlod(_NormalAndRoughnessTexture, hiP0); float4 temp1 = tex2Dlod(_NormalAndRoughnessTexture, hiP1); float4 temp2 = tex2Dlod(_NormalAndRoughnessTexture, hiP2); float4 temp3 = tex2Dlod(_NormalAndRoughnessTexture, hiP3); float3 n0 = temp0.xyz * 2 - 1; float3 n1 = temp1.xyz * 2 - 1; float3 n2 = temp2.xyz * 2 - 1; float3 n3 = temp3.xyz * 2 - 1; a0 *= normalWeight(n, n0); a1 *= normalWeight(n, n1); a2 *= normalWeight(n, n2); a3 *= normalWeight(n, n3); float r = tempCenter.a; float r0 = temp0.a; float r1 = temp1.a; float r2 = temp2.a; float r3 = temp3.a; a0 *= roughnessWeight(r, r0); a1 *= roughnessWeight(r, r1); a2 *= roughnessWeight(r, r2); a3 *= roughnessWeight(r, r3); // Slightly offset from zero a0 = max(a0, 0.001); a1 = max(a1, 0.001); a2 = max(a2, 0.001); a3 = max(a3, 0.001); // Nearest neighbor // a0 = a1 = a2 = a3 = 1.0; // Normalize the blending weights (weights were chosen so that // the denominator can never be zero) float norm = 1.0 / (a0 + a1 + a2 + a3); // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3) * norm; //return V0; return value; } /** Explicit bilinear fetches; must be used if the reflection buffer is bound using point sampling */ float4 bilinearUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3); return value; } // Unity's roughness is GGX roughness squared float roughnessToBlinnPhongExponent(float roughness) { float r2 = roughness*roughness; return 2.0f / r2*r2 - 2.0f; } float glossyLobeSlope(float roughness) { return pow(roughness, 4.0/3.0); } // Empirically based on our filter: // Mip | Pixels // -------------- // 0 | 1 no filter, so single pixel // 1 | 17 2r + 1 filter applied once, grabbing from pixels r away in either direction (r=8, four samples times stride of 2) // 2 | 50 2r + 1 filter applied on double size pixels, and each of those pixels had reached another r out to the side 2(2r + 1) + m_1 // 3 | 118 4(2r + 1) + m_2 // 4 | 254 8(2r + 1) + m_3 // // Approximated by pixels = 16*2^mip-15 // rearranging we get mip = log_2((pixels + 15) / 16) // float filterFootprintInPixelsToMip(float footprint) { return log2((footprint + 15) / 16); } float3 ansiGradient(float t) { //return float3(t, t, t); return fmod(floor(t * float3(8.0, 4.0, 2.0)), 2.0); } float4 fragCompositeSSR(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP * _SourceToTempUV.zw).a; float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).z; } // Get the camera space position of the reflection hit float3 csPosition = GetPosition(tsP); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 c_mi = csMirrorVector(csPosition, csN); float3 csHitpoint = c_mi * rayDistance + csPosition; float gatherFootprintInMeters = glossyLobeSlope(roughness) * rayDistance; // We could add a term that incorporates the normal // This approximation assumes reflections happen at a glancing angle float filterFootprintInPixels = gatherFootprintInMeters * _PixelsPerMeterAtOneMeter / csHitpoint.z; if (_HalfResolution == 1) { filterFootprintInPixels *= 0.5; } float mip = filterFootprintInPixelsToMip(filterFootprintInPixels); float nonPhysicalMip = pow(roughness, 3.0 / 4.0) * UNITY_SPECCUBE_LOD_STEPS; if (_HalfResolution == 1) { nonPhysicalMip = nonPhysicalMip * 0.7; } mip = lerp(nonPhysicalMip, mip, _DistanceBlur); mip = max(0, min(4, mip + _MipBias)); if (_DebugMode == 14) { return float4(ansiGradient(mip*0.25), 1.0); } float4 result; { int mipMin = int(mip); int mipMax = min(mipMin + 1, 4); float mipLerp = mip-mipMin; if (_BilateralUpsampling == 1) { if (_UseEdgeDetector == 1) { float nonEdginess = lerp(getEdgeValue(tsP, mipMin), getEdgeValue(tsP, mipMax), mipLerp); if (nonEdginess < 0.9) { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); if (_VisualizeWhereBilateral == 1) { result = float4(1, 0, 0, 0); } } else { result = lerp(bilinearUpsampleReflection(tsP, mipMin), bilinearUpsampleReflection(tsP, mipMax), mipLerp); } } else { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); } } else { float4 minResult = getReflectionValue(tsP, mipMin); float4 maxResult = getReflectionValue(tsP, mipMax); result = lerp(minResult, maxResult, mipLerp); result.a = min(minResult.a, maxResult.a); } } result.a = min(result.a, 1.0); float vignette = applyEdgeFade(tsP, _ScreenEdgeFading); result.a *= vignette; float alphaModifier = 1.0 - clamp((roughness - (_MaxRoughness - _RoughnessFalloffRange)) / _RoughnessFalloffRange, 0.0, 1.0); result.a *= alphaModifier; return result; } float4 fragBlit(v2f i) : SV_Target { // Pixel being shaded float2 ssP = i.uv2.xy; return tex2D(_MainTex, ssP); } float edgeDetector(float2 tsP, float2 axis, float2 texelSize) { float4 temp = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = temp.a; float3 midpointNormal = temp.rgb * 2 - 1; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (axis * _MainTex_TexelSize.xy); temp = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = temp.a; tapNormal = temp.rgb * 2 - 1; float weight = 1.0; weight *= roughnessWeight(midpointRoughness, tapRoughness); weight *= normalWeight(midpointNormal, tapNormal); return weight; } float4 fragEdge(v2f i) : SV_Target { float2 tsP = i.uv2.xy; return edgeDetector(tsP, float2(1,0), _MainTex_TexelSize.xy) * edgeDetector(tsP, float2(0,1), _MainTex_TexelSize.xy); } int _LastMip; float4 fragMin(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float2 lastTexSize = mipToSize(_LastMip); float2 lastTexSizeInv = 1.0 / lastTexSize;//_SourceToTempUV.xy * exp2(mip); float2 p00 = snapToTexelCenter(tsP, lastTexSize, lastTexSizeInv); float2 p11 = p00 + lastTexSizeInv; return min( min(tex2D(_MainTex, p00), tex2D(_MainTex, p11)), min(tex2D(_MainTex, float2(p00.x, p11.y)), tex2D(_MainTex, float2(p11.x, p00.y))) ); } float4 fragResolveHitPoints(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float4 temp = tex2D(_HitPointTexture, tsP); float2 hitPoint = temp.xy; float confidence = temp.w; float3 colorResult = confidence > 0.0 ? tex2D(_MainTex, hitPoint).rgb : tex2D(_CameraReflectionsTexture, tsP).rgb; #ifdef UNITY_COMPILER_HLSL if (any(isnan(colorResult))) colorResult = float3(0.0, 0.0, 0.0); // As of 11/29/2015, on Unity 5.3 on a Windows 8.1 computer with a NVIDIA GeForce 980, // with driver 347.62, the above check does not actually work to get rid of NaNs! // So we add this "redundant" check. if (!all(isfinite(colorResult))) colorResult = float3(0.0, 0.0, 0.0); #endif return float4(colorResult, confidence); } float4 fragBilatKeyPack(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float3 csN = tex2D(_CameraGBufferTexture2, tsP).xyz; float roughness = tex2D(_CameraGBufferTexture1, tsP).a; return float4(csN, roughness); } float4 fragDepthToCSZ(v2f i) : SV_Target { float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv2.xy); return float4(-LinearEyeDepth(depth), 0.0, 0.0, 0.0); } float3 csPositionToOldNormalizedScreenCoordinate(float3 csPosition) { // We could multiply these matrices together on the CPU float4 oldCSPosition = mul(_CurrentCameraToPreviousCamera, float4(csPosition, 1.0)); float4 oldSSCoordinate = mul(_ProjectToPixelMatrix, oldCSPosition); return float3((oldSSCoordinate.xy / oldSSCoordinate.w) * _InvScreenSize, oldCSPosition.z); } float4 fragTemporalFilter(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, tsP); float3 csPosition = GetPosition(tsP); float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).b; } // Virtual is the physics sense, not the other 20+ interpretations in CG float3 virtualReflectionCSPosition = normalize(csPosition) * rayDistance + csPosition; float3 oldTSP = csPositionToOldNormalizedScreenCoordinate(csPosition); float3 oldVirtualReflectionTSP = csPositionToOldNormalizedScreenCoordinate(virtualReflectionCSPosition); float oldCSZ = tex2D(_PreviousCSZBuffer, oldTSP.xy); float4 currentResult = tex2D(_FinalReflectionTexture, tsP); float4 oldResult = tex2D(_PreviousReflectionTexture, oldVirtualReflectionTSP.xy); float zError = abs(oldCSZ - oldTSP.z); // Start knocking down contribution at 5cm error, go to 0 at 5 more cm later float fallbackStart = 0.05; float fallbackEnd = 0.05; float zAlphaModifier = 1.0 - clamp((zError - fallbackStart) * (1.0 / fallbackEnd), 0.0, 1.0); float alpha = _TemporalAlpha; alpha *= zAlphaModifier; if (_UseTemporalConfidence) { alpha *= oldResult.w; } // Always compress currentResult.rgb = highlightCompression(currentResult.rgb); oldResult.rgb = highlightCompression(oldResult.rgb); float4 resultValue = lerp(currentResult, oldResult, alpha); resultValue.rgb = highlightDecompression(resultValue.rgb); return resultValue; } float4 fragAverageRayDistance(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float sumRayDistance = tex2D(_HitPointTexture, tsP).b; float sumWeight = 1.0; float roughness = 1.0 - tex2D(_CameraGBufferTexture1, tsP).a; float step = roughness * 3.0; /* for (float x = -step; x < step; x += 2 * step) { for (float y = -step; y < step; y += 2 * step) { float weight = 0.5; sumRayDistance += weight * tex2D(_HitPointTexture, (float2(x,y) * _InvScreenSize) + tsP).b; sumWeight += weight; } } */ return float4(sumRayDistance / sumWeight, 0.0, 0.0, 0.0); } static const int NUM_POISSON_TAPS = 12; // Same as used in CameraMotionBlur.shader static const float2 poissonSamples[NUM_POISSON_TAPS] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; float4 fragFilterSharpReflections(v2f i) : SV_Target { // Could improve perf by not computing blur when we won't be sampling the highest level anyways float2 tsP = i.uv2.xy; float4 sum = 0.0; float sampleRadius = _MainTex_TexelSize.xy * 2.0; for (int i = 0; i < NUM_POISSON_TAPS; i++) { float2 p = tsP + poissonSamples[i] * sampleRadius; float4 tap = tex2Dlod(_MainTex, float4(p, 0, 0)); if (_HighlightSuppression) { tap.rgb = highlightCompression(tap.rgb); } sum += tap; } float4 result = sum / float(NUM_POISSON_TAPS); if (_HighlightSuppression) { result.rgb = highlightDecompression(result.rgb); } return result; } #line 1097 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif /* UNITY: Original start of shader */ #pragma exclude_renderers gles xbox360 ps3 #pragma vertex vert #pragma fragment fragResolveHitPoints #pragma target 3.0 SAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/ShadersÓ˙˙˙Kküń'¨ô™!]{Ňô SHADOWS_SOFTDIRLIGHTMAP_COMBINEDDIRLIGHTMAP_SEPARATEDYNAMICLIGHTMAP_ON INSTANCING_ON7 ť†#line 1107 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #line 12 #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "ScreenSpaceRaytrace.cginc" float4 _ProjInfo; float4x4 _WorldToCameraMatrix; float4x4 _CameraToWorldMatrix; float4x4 _ProjectToPixelMatrix; float2 _ScreenSize; float2 _ReflectionBufferSize; float2 _InvScreenSize; float3 _CameraClipInfo; sampler2D _CameraGBufferTexture0; sampler2D _CameraGBufferTexture1; sampler2D _CameraGBufferTexture2; sampler2D _CameraGBufferTexture3; sampler2D _CameraReflectionsTexture; float _CurrentMipLevel; float _RayStepSize; float _MaxRayTraceDistance; float _LayerThickness; float _FresnelFade; float _FresnelFadePower; sampler2D _MainTex; int _VisualizeWhereBilateral; int _EnableSSR; int _DebugMode; int _HalfResolution; int _TreatBackfaceHitAsMiss; int _AllowBackwardsRays; int _TraceEverywhere; // RG: SS Hitpoint of ray // B: distance ray travelled, used for mip-selection in the final resolve // A: confidence value sampler2D _HitPointTexture; sampler2D _FinalReflectionTexture; // RGB: camera-space normal (encoded in [0-1]) // A: Roughness sampler2D _NormalAndRoughnessTexture; float4 _MainTex_TexelSize; float4 _SourceToTempUV; int _EnableRefine; int _AdditiveReflection; int _ImproveCorners; float _ScreenEdgeFading; float _MipBias; int _UseOcclusion; int _MaxSteps; int _FullResolutionFiltering; int _BilateralUpsampling; float _MaxRoughness; float _RoughnessFalloffRange; float _SSRMultiplier; float _FadeDistance; float _DistanceBlur; int _TraceBehindObjects; int _FallbackToSky; int _UseEdgeDetector; int _HighlightSuppression; /** The height in pixels of a 1m object if viewed from 1m away. */ float _PixelsPerMeterAtOneMeter; int _UseAverageRayDistance; sampler2D _AverageRayDistanceBuffer; // For temporal filtering: float4x4 _CurrentCameraToPreviousCamera; sampler2D _PreviousReflectionTexture; sampler2D _PreviousCSZBuffer; float _TemporalAlpha; int _UseTemporalConfidence; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy; o.uv2 = v.texcoord.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv2.y = 1-o.uv2.y; #endif return o; } float2 mipToSize(int mip) { return floor(_ReflectionBufferSize * exp2(-mip)); } float3 ReconstructCSPosition(float2 S, float z) { float linEyeZ = -LinearEyeDepth(z); return float3(( (( S.xy * _MainTex_TexelSize.zw) ) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); } /** Read the camera-space position of the point at screen-space pixel ssP */ float3 GetPosition(float2 ssP) { float3 P; P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy); // Offset to pixel center P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); return P; } float square(float x) { return x*x; } float applyEdgeFade(float2 tsP, float fadeStrength) { float maxFade = 0.1; float2 itsP = float2(1.0, 1.0) - tsP; float dist = min(min(itsP.x, itsP.y), min(tsP.x, tsP.x)); float fade = dist / (maxFade*fadeStrength + 0.001); fade = max(min(fade, 1.0), 0.0); fade = pow(fade, 0.2); return fade; } float3 csMirrorVector(float3 csPosition, float3 csN) { float3 csE = -normalize(csPosition.xyz); float cos_o = dot(csN, csE); float3 c_mi = normalize((csN * (2.0 * cos_o)) - csE); return c_mi; } float4 fragRaytrace(v2f i, int stepRate) { float2 ssP = i.uv2.xy * _SourceToTempUV.zw; float3 csPosition = GetPosition(ssP); float smoothness = tex2D(_CameraGBufferTexture1, ssP).a; if (csPosition.z < -100.0 || smoothness == 0.0) { return float4(0.0,0.0,0.0,0.0); } if ((!_TraceEverywhere) && (smoothness < (1.0 - _MaxRoughness)) ) { return float4(0.0, 0.0, 0.0, 0.0); } float3 wsNormal = tex2D(_CameraGBufferTexture2, ssP).rgb * 2.0 - 1.0; int2 ssC = int2(ssP * _ScreenSize); float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 csRayDirection = csMirrorVector(csPosition, csN); if (_AllowBackwardsRays == 0 && csRayDirection.z > 0.0) { return float4(0.0, 0.0, 0.0, 0.0); } float maxRayTraceDistance = _MaxRayTraceDistance; float jitterFraction = 0.0f; float layerThickness = _LayerThickness; int maxSteps = _MaxSteps; // Bump the ray more in world space as it gets farther away (and so each pixel covers more WS distance) float rayBump = max(-0.01*csPosition.z, 0.001); float2 hitPixel; float3 csHitPoint; float stepCount; bool wasHit = castDenseScreenSpaceRay (csPosition + (csN) * rayBump, csRayDirection, _ProjectToPixelMatrix, _ScreenSize, _CameraClipInfo, jitterFraction, maxSteps, layerThickness, maxRayTraceDistance, hitPixel, stepRate, _EnableRefine == 1, _TraceBehindObjects == 1, csHitPoint, stepCount); float2 tsPResult = hitPixel / _ScreenSize; float rayDist = dot(csHitPoint - csPosition, csRayDirection); float confidence = 0.0; if (wasHit) { confidence = square(1.0 - max(2.0*float(stepCount) / float(maxSteps) - 1.0, 0.0)); confidence *= clamp(((_MaxRayTraceDistance - rayDist) / _FadeDistance), 0.0, 1.0); // Fake fresnel fade float3 csE = -normalize(csPosition.xyz); confidence *= max(0.0, lerp(pow(abs(dot(csRayDirection, -csE)), _FresnelFadePower), 1, 1.0 - _FresnelFade)); if (_TreatBackfaceHitAsMiss > 0) { float3 wsHitNormal = tex2Dlod(_CameraGBufferTexture2, float4(tsPResult, 0, 0)).rgb * 2.0 - 1.0; float3 wsRayDirection = mul(_CameraToWorldMatrix, float4(csRayDirection, 0)).xyz; if (dot(wsHitNormal, wsRayDirection) > 0) { confidence = 0.0; } } } else if (_FallbackToSky > 0) { float sceneZ = tex2Dlod(_CameraDepthTexture, float4(tsPResult, 0, 0)).r; sceneZ = -LinearEyeDepth(sceneZ); if (sceneZ < -1000) { // 1km ~= infinity confidence = 1.0; } } // Fade out reflections that hit near edge of screen, to prevent abrupt appearance/disappearance when object go off screen // Fade out reflections that hit near edge of screen, // to prevent abrupt appearance/disappearance when object go off screen float vignette = applyEdgeFade(tsPResult, _ScreenEdgeFading); confidence *= vignette; confidence *= vignette; return float4(tsPResult, rayDist, confidence); } float4 fragComposite(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; // View space point being shaded float3 C = GetPosition(tsP); // Final image before this pass float4 gbuffer3 = tex2D(_MainTex, i.uv); float4 specEmission = float4(0.0,0.0,0.0,0.0); float3 specColor = tex2D(_CameraGBufferTexture1, tsP).rgb; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP).a; float4 reflectionTexel = tex2D(_FinalReflectionTexture, tsP * _SourceToTempUV.xy); float4 gbuffer0 = tex2D(_CameraGBufferTexture0, tsP); // Let core Unity functions do the dirty work of applying the BRDF float3 baseColor = gbuffer0.rgb; float occlusion = gbuffer0.a; float oneMinusReflectivity; baseColor = EnergyConservationBetweenDiffuseAndSpecular(baseColor, specColor, oneMinusReflectivity); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csEyeVec = normalize(C); float3 eyeVec = mul(_CameraToWorldMatrix, float4(csEyeVec, 0)).xyz; float3 worldPos = mul(_CameraToWorldMatrix, float4(C, 1)).xyz; float cos_o = dot(wsNormal, eyeVec); float3 w_mi = -normalize((wsNormal * (2.0 * cos_o)) - eyeVec); float3 incomingRadiance = reflectionTexel.rgb; UnityLight light; light.color = 0; light.dir = 0; light.ndotl = 0; UnityIndirect ind; ind.diffuse = 0; ind.specular = incomingRadiance; float3 ssrResult = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, 1-roughness, wsNormal, -eyeVec, light, ind).rgb * _SSRMultiplier; float confidence = reflectionTexel.a; if(_EnableSSR == 0) { confidence = 0.0; } specEmission.rgb = tex2D(_CameraReflectionsTexture, tsP).rgb; float3 finalGlossyTerm; // Subtract out Unity's glossy result: (we're just applying the delta) if (_AdditiveReflection == 0) { gbuffer3 -= specEmission; // We may have blown out our dynamic range by adding then subtracting the reflection probes. // As a half-measure to fix this, simply clamp to zero gbuffer3 = max(gbuffer3, 0); finalGlossyTerm = lerp(specEmission.rgb, ssrResult, saturate(confidence)); } else { finalGlossyTerm = ssrResult*saturate(confidence); } if (_UseOcclusion) { finalGlossyTerm *= occlusion; } if (_DebugMode == 1) return float4(incomingRadiance,0); if (_DebugMode == 2) return float4(ssrResult,0); if (_DebugMode == 3) return float4(finalGlossyTerm,0); if (_DebugMode == 4) return confidence; if (_DebugMode == 5) return roughness; if (_DebugMode == 6) return float4(baseColor,0); if (_DebugMode == 7) return float4(specColor,0); if (_DebugMode == 8) return 1.0-oneMinusReflectivity; if (_DebugMode == 9) return float4(specEmission.rgb,0); if (_DebugMode == 10) return float4(specEmission.rgb-ssrResult,0); if (_DebugMode == 11) return float4(ssrResult-specEmission.rgb,0); if (_DebugMode == 12) return gbuffer3; if (_DebugMode == 13) return -float4(gbuffer3.rgb, 0.0); if (_DebugMode == 14) // Mip level, stored directly in ssrResult return float4(incomingRadiance, 0); // Additively blend the glossy GI result with the output buffer return gbuffer3 + float4(finalGlossyTerm, 0); } float roughnessWeight(float midpointRoughness, float tapRoughness) { return (1.0 - sqrt(sqrt(abs(midpointRoughness-tapRoughness)))); } float normalWeight(float3 midpointNormal, float3 tapNormal) { return clamp(dot(midpointNormal, tapNormal), 0, 1); } float highlightDecompression(float x) { return x / (1.0 - x); } float3 highlightDecompression(float3 x) { return float3( highlightDecompression(x.x), highlightDecompression(x.y), highlightDecompression(x.z)); } float highlightCompression(float x) { return x / (1.0 + x); } float3 highlightCompression(float3 x) { return float3( highlightCompression(x.x), highlightCompression(x.y), highlightCompression(x.z)); } float4 _Axis; float4 fragGBlur(v2f i) : SV_Target { int radius = 4; // Pixel being shaded float2 tsP = i.uv2.xy; float weightSum = 0.0; float gaussWeights[5] = {0.225, 0.150, 0.110, 0.075, 0.0525};//{0.225, 0.150, 0.110, 0.075, 0.0525}; float4 resultSum = float4(0.0, 0.0, 0.0, 0.0); float4 unweightedResultSum = float4(0.0, 0.0, 0.0, 0.0); float4 nAndRough = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = nAndRough.a; float3 midpointNormal = nAndRough.rgb * 2 - 1; if (_FullResolutionFiltering) { for (int i = -2*radius; i <= 2*radius; ++i) { float4 temp; float tapRoughness = midpointRoughness; float3 tapNormal = midpointNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)); temp = tex2D(_MainTex, tsTap); int gaussWeightIndex; #if defined(UNITY_COMPILER_HLSL) && !defined(SHADER_API_D3D9) gaussWeightIndex = abs(i) >> 1; // We purposefully do integer division, but that does not compile on hlsl2glsl #else gaussWeightIndex = frac(abs(i) / 2); #endif float weight = temp.a * gaussWeights[gaussWeightIndex]*0.5; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; resultSum += temp*weight; unweightedResultSum += temp; } } else { for (int i = -radius; i <= radius; ++i) { float4 temp; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)*2.0); temp = tex2D(_MainTex, tsTap); float weight = temp.a * gaussWeights[abs(i)]; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; if (_HighlightSuppression) { temp.rgb = highlightCompression(temp.rgb); } unweightedResultSum += temp; resultSum += temp*weight; } } if (weightSum > 0.01) { float invWeightSum = (1.0/weightSum); // Adding the sqrt seems to decrease temporal flickering at the expense // of having larger "halos" of fallback on rough surfaces // Subject to change with testing. Sqrt around only half the expression is *intentional*. float confidence = min(resultSum.a * sqrt(max(invWeightSum, 2.0)), 1.0); float3 finalColor = resultSum.rgb * invWeightSum; if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, confidence); } else { float3 finalColor = unweightedResultSum.rgb / (2 * radius + 1); if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, 0.0); } } sampler2D _ReflectionTexture0; sampler2D _ReflectionTexture1; sampler2D _ReflectionTexture2; sampler2D _ReflectionTexture3; sampler2D _ReflectionTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getReflectionValue(float2 tsP, int mip) { float4 coord = float4(tsP,0,0); if (mip == 0) { return tex2Dlod(_ReflectionTexture0, coord); } else if (mip == 1) { return tex2Dlod(_ReflectionTexture1, coord); } else if (mip == 2) { return tex2Dlod(_ReflectionTexture2, coord); } else if (mip == 3) { return tex2Dlod(_ReflectionTexture3, coord); } else { return tex2Dlod(_ReflectionTexture4, coord); } } sampler2D _EdgeTexture0; sampler2D _EdgeTexture1; sampler2D _EdgeTexture2; sampler2D _EdgeTexture3; sampler2D _EdgeTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getEdgeValue(float2 tsP, int mip) { float4 coord = float4(tsP + float2(1.0/(2 * mipToSize(mip))),0,0); if (mip == 0) { return tex2Dlod(_EdgeTexture0, coord); } else if (mip == 1) { return tex2Dlod(_EdgeTexture1, coord); } else if (mip == 2) { return tex2Dlod(_EdgeTexture2, coord); } else if (mip == 3) { return tex2Dlod(_EdgeTexture3, coord); } else { return tex2Dlod(_EdgeTexture4, coord); } } float2 centerPixel(float2 inputP) { return floor(inputP - float2(0.5,0.5)) + float2(0.5,0.5); } float2 snapToTexelCenter(float2 inputP, float2 texSize, float2 texSizeInv) { return centerPixel(inputP * texSize) * texSizeInv; } float4 bilateralUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; float2 fullTexSize = _ReflectionBufferSize; float2 fullTexSizeInv = 1.0 / fullTexSize; float4 hiP0 = float4(snapToTexelCenter(p0, fullTexSize, fullTexSizeInv), 0,0); float4 hiP3 = float4(snapToTexelCenter(p3, fullTexSize, fullTexSizeInv), 0,0); float4 hiP1 = float4(snapToTexelCenter(p1, fullTexSize, fullTexSizeInv), 0,0); float4 hiP2 = float4(snapToTexelCenter(p2, fullTexSize, fullTexSizeInv), 0,0); float4 tempCenter = tex2Dlod(_NormalAndRoughnessTexture, float4(tsP, 0, 0)); float3 n = tempCenter.xyz * 2 - 1; float4 temp0 = tex2Dlod(_NormalAndRoughnessTexture, hiP0); float4 temp1 = tex2Dlod(_NormalAndRoughnessTexture, hiP1); float4 temp2 = tex2Dlod(_NormalAndRoughnessTexture, hiP2); float4 temp3 = tex2Dlod(_NormalAndRoughnessTexture, hiP3); float3 n0 = temp0.xyz * 2 - 1; float3 n1 = temp1.xyz * 2 - 1; float3 n2 = temp2.xyz * 2 - 1; float3 n3 = temp3.xyz * 2 - 1; a0 *= normalWeight(n, n0); a1 *= normalWeight(n, n1); a2 *= normalWeight(n, n2); a3 *= normalWeight(n, n3); float r = tempCenter.a; float r0 = temp0.a; float r1 = temp1.a; float r2 = temp2.a; float r3 = temp3.a; a0 *= roughnessWeight(r, r0); a1 *= roughnessWeight(r, r1); a2 *= roughnessWeight(r, r2); a3 *= roughnessWeight(r, r3); // Slightly offset from zero a0 = max(a0, 0.001); a1 = max(a1, 0.001); a2 = max(a2, 0.001); a3 = max(a3, 0.001); // Nearest neighbor // a0 = a1 = a2 = a3 = 1.0; // Normalize the blending weights (weights were chosen so that // the denominator can never be zero) float norm = 1.0 / (a0 + a1 + a2 + a3); // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3) * norm; //return V0; return value; } /** Explicit bilinear fetches; must be used if the reflection buffer is bound using point sampling */ float4 bilinearUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3); return value; } // Unity's roughness is GGX roughness squared float roughnessToBlinnPhongExponent(float roughness) { float r2 = roughness*roughness; return 2.0f / r2*r2 - 2.0f; } float glossyLobeSlope(float roughness) { return pow(roughness, 4.0/3.0); } // Empirically based on our filter: // Mip | Pixels // -------------- // 0 | 1 no filter, so single pixel // 1 | 17 2r + 1 filter applied once, grabbing from pixels r away in either direction (r=8, four samples times stride of 2) // 2 | 50 2r + 1 filter applied on double size pixels, and each of those pixels had reached another r out to the side 2(2r + 1) + m_1 // 3 | 118 4(2r + 1) + m_2 // 4 | 254 8(2r + 1) + m_3 // // Approximated by pixels = 16*2^mip-15 // rearranging we get mip = log_2((pixels + 15) / 16) // float filterFootprintInPixelsToMip(float footprint) { return log2((footprint + 15) / 16); } float3 ansiGradient(float t) { //return float3(t, t, t); return fmod(floor(t * float3(8.0, 4.0, 2.0)), 2.0); } float4 fragCompositeSSR(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP * _SourceToTempUV.zw).a; float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).z; } // Get the camera space position of the reflection hit float3 csPosition = GetPosition(tsP); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 c_mi = csMirrorVector(csPosition, csN); float3 csHitpoint = c_mi * rayDistance + csPosition; float gatherFootprintInMeters = glossyLobeSlope(roughness) * rayDistance; // We could add a term that incorporates the normal // This approximation assumes reflections happen at a glancing angle float filterFootprintInPixels = gatherFootprintInMeters * _PixelsPerMeterAtOneMeter / csHitpoint.z; if (_HalfResolution == 1) { filterFootprintInPixels *= 0.5; } float mip = filterFootprintInPixelsToMip(filterFootprintInPixels); float nonPhysicalMip = pow(roughness, 3.0 / 4.0) * UNITY_SPECCUBE_LOD_STEPS; if (_HalfResolution == 1) { nonPhysicalMip = nonPhysicalMip * 0.7; } mip = lerp(nonPhysicalMip, mip, _DistanceBlur); mip = max(0, min(4, mip + _MipBias)); if (_DebugMode == 14) { return float4(ansiGradient(mip*0.25), 1.0); } float4 result; { int mipMin = int(mip); int mipMax = min(mipMin + 1, 4); float mipLerp = mip-mipMin; if (_BilateralUpsampling == 1) { if (_UseEdgeDetector == 1) { float nonEdginess = lerp(getEdgeValue(tsP, mipMin), getEdgeValue(tsP, mipMax), mipLerp); if (nonEdginess < 0.9) { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); if (_VisualizeWhereBilateral == 1) { result = float4(1, 0, 0, 0); } } else { result = lerp(bilinearUpsampleReflection(tsP, mipMin), bilinearUpsampleReflection(tsP, mipMax), mipLerp); } } else { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); } } else { float4 minResult = getReflectionValue(tsP, mipMin); float4 maxResult = getReflectionValue(tsP, mipMax); result = lerp(minResult, maxResult, mipLerp); result.a = min(minResult.a, maxResult.a); } } result.a = min(result.a, 1.0); float vignette = applyEdgeFade(tsP, _ScreenEdgeFading); result.a *= vignette; float alphaModifier = 1.0 - clamp((roughness - (_MaxRoughness - _RoughnessFalloffRange)) / _RoughnessFalloffRange, 0.0, 1.0); result.a *= alphaModifier; return result; } float4 fragBlit(v2f i) : SV_Target { // Pixel being shaded float2 ssP = i.uv2.xy; return tex2D(_MainTex, ssP); } float edgeDetector(float2 tsP, float2 axis, float2 texelSize) { float4 temp = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = temp.a; float3 midpointNormal = temp.rgb * 2 - 1; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (axis * _MainTex_TexelSize.xy); temp = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = temp.a; tapNormal = temp.rgb * 2 - 1; float weight = 1.0; weight *= roughnessWeight(midpointRoughness, tapRoughness); weight *= normalWeight(midpointNormal, tapNormal); return weight; } float4 fragEdge(v2f i) : SV_Target { float2 tsP = i.uv2.xy; return edgeDetector(tsP, float2(1,0), _MainTex_TexelSize.xy) * edgeDetector(tsP, float2(0,1), _MainTex_TexelSize.xy); } int _LastMip; float4 fragMin(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float2 lastTexSize = mipToSize(_LastMip); float2 lastTexSizeInv = 1.0 / lastTexSize;//_SourceToTempUV.xy * exp2(mip); float2 p00 = snapToTexelCenter(tsP, lastTexSize, lastTexSizeInv); float2 p11 = p00 + lastTexSizeInv; return min( min(tex2D(_MainTex, p00), tex2D(_MainTex, p11)), min(tex2D(_MainTex, float2(p00.x, p11.y)), tex2D(_MainTex, float2(p11.x, p00.y))) ); } float4 fragResolveHitPoints(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float4 temp = tex2D(_HitPointTexture, tsP); float2 hitPoint = temp.xy; float confidence = temp.w; float3 colorResult = confidence > 0.0 ? tex2D(_MainTex, hitPoint).rgb : tex2D(_CameraReflectionsTexture, tsP).rgb; #ifdef UNITY_COMPILER_HLSL if (any(isnan(colorResult))) colorResult = float3(0.0, 0.0, 0.0); // As of 11/29/2015, on Unity 5.3 on a Windows 8.1 computer with a NVIDIA GeForce 980, // with driver 347.62, the above check does not actually work to get rid of NaNs! // So we add this "redundant" check. if (!all(isfinite(colorResult))) colorResult = float3(0.0, 0.0, 0.0); #endif return float4(colorResult, confidence); } float4 fragBilatKeyPack(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float3 csN = tex2D(_CameraGBufferTexture2, tsP).xyz; float roughness = tex2D(_CameraGBufferTexture1, tsP).a; return float4(csN, roughness); } float4 fragDepthToCSZ(v2f i) : SV_Target { float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv2.xy); return float4(-LinearEyeDepth(depth), 0.0, 0.0, 0.0); } float3 csPositionToOldNormalizedScreenCoordinate(float3 csPosition) { // We could multiply these matrices together on the CPU float4 oldCSPosition = mul(_CurrentCameraToPreviousCamera, float4(csPosition, 1.0)); float4 oldSSCoordinate = mul(_ProjectToPixelMatrix, oldCSPosition); return float3((oldSSCoordinate.xy / oldSSCoordinate.w) * _InvScreenSize, oldCSPosition.z); } float4 fragTemporalFilter(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, tsP); float3 csPosition = GetPosition(tsP); float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).b; } // Virtual is the physics sense, not the other 20+ interpretations in CG float3 virtualReflectionCSPosition = normalize(csPosition) * rayDistance + csPosition; float3 oldTSP = csPositionToOldNormalizedScreenCoordinate(csPosition); float3 oldVirtualReflectionTSP = csPositionToOldNormalizedScreenCoordinate(virtualReflectionCSPosition); float oldCSZ = tex2D(_PreviousCSZBuffer, oldTSP.xy); float4 currentResult = tex2D(_FinalReflectionTexture, tsP); float4 oldResult = tex2D(_PreviousReflectionTexture, oldVirtualReflectionTSP.xy); float zError = abs(oldCSZ - oldTSP.z); // Start knocking down contribution at 5cm error, go to 0 at 5 more cm later float fallbackStart = 0.05; float fallbackEnd = 0.05; float zAlphaModifier = 1.0 - clamp((zError - fallbackStart) * (1.0 / fallbackEnd), 0.0, 1.0); float alpha = _TemporalAlpha; alpha *= zAlphaModifier; if (_UseTemporalConfidence) { alpha *= oldResult.w; } // Always compress currentResult.rgb = highlightCompression(currentResult.rgb); oldResult.rgb = highlightCompression(oldResult.rgb); float4 resultValue = lerp(currentResult, oldResult, alpha); resultValue.rgb = highlightDecompression(resultValue.rgb); return resultValue; } float4 fragAverageRayDistance(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float sumRayDistance = tex2D(_HitPointTexture, tsP).b; float sumWeight = 1.0; float roughness = 1.0 - tex2D(_CameraGBufferTexture1, tsP).a; float step = roughness * 3.0; /* for (float x = -step; x < step; x += 2 * step) { for (float y = -step; y < step; y += 2 * step) { float weight = 0.5; sumRayDistance += weight * tex2D(_HitPointTexture, (float2(x,y) * _InvScreenSize) + tsP).b; sumWeight += weight; } } */ return float4(sumRayDistance / sumWeight, 0.0, 0.0, 0.0); } static const int NUM_POISSON_TAPS = 12; // Same as used in CameraMotionBlur.shader static const float2 poissonSamples[NUM_POISSON_TAPS] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; float4 fragFilterSharpReflections(v2f i) : SV_Target { // Could improve perf by not computing blur when we won't be sampling the highest level anyways float2 tsP = i.uv2.xy; float4 sum = 0.0; float sampleRadius = _MainTex_TexelSize.xy * 2.0; for (int i = 0; i < NUM_POISSON_TAPS; i++) { float2 p = tsP + poissonSamples[i] * sampleRadius; float4 tap = tex2Dlod(_MainTex, float4(p, 0, 0)); if (_HighlightSuppression) { tap.rgb = highlightCompression(tap.rgb); } sum += tap; } float4 result = sum / float(NUM_POISSON_TAPS); if (_HighlightSuppression) { result.rgb = highlightDecompression(result.rgb); } return result; } #line 1107 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif /* UNITY: Original start of shader */ #pragma exclude_renderers gles xbox360 ps3 #pragma vertex vert #pragma fragment fragBilatKeyPack #pragma target 3.0 SAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/ShadersÓ˙˙˙Uküń'¨ô™!]{Ňô SHADOWS_SOFTDIRLIGHTMAP_COMBINEDDIRLIGHTMAP_SEPARATEDYNAMICLIGHTMAP_ON INSTANCING_ON˝B š†#line 1117 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #line 12 #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "ScreenSpaceRaytrace.cginc" float4 _ProjInfo; float4x4 _WorldToCameraMatrix; float4x4 _CameraToWorldMatrix; float4x4 _ProjectToPixelMatrix; float2 _ScreenSize; float2 _ReflectionBufferSize; float2 _InvScreenSize; float3 _CameraClipInfo; sampler2D _CameraGBufferTexture0; sampler2D _CameraGBufferTexture1; sampler2D _CameraGBufferTexture2; sampler2D _CameraGBufferTexture3; sampler2D _CameraReflectionsTexture; float _CurrentMipLevel; float _RayStepSize; float _MaxRayTraceDistance; float _LayerThickness; float _FresnelFade; float _FresnelFadePower; sampler2D _MainTex; int _VisualizeWhereBilateral; int _EnableSSR; int _DebugMode; int _HalfResolution; int _TreatBackfaceHitAsMiss; int _AllowBackwardsRays; int _TraceEverywhere; // RG: SS Hitpoint of ray // B: distance ray travelled, used for mip-selection in the final resolve // A: confidence value sampler2D _HitPointTexture; sampler2D _FinalReflectionTexture; // RGB: camera-space normal (encoded in [0-1]) // A: Roughness sampler2D _NormalAndRoughnessTexture; float4 _MainTex_TexelSize; float4 _SourceToTempUV; int _EnableRefine; int _AdditiveReflection; int _ImproveCorners; float _ScreenEdgeFading; float _MipBias; int _UseOcclusion; int _MaxSteps; int _FullResolutionFiltering; int _BilateralUpsampling; float _MaxRoughness; float _RoughnessFalloffRange; float _SSRMultiplier; float _FadeDistance; float _DistanceBlur; int _TraceBehindObjects; int _FallbackToSky; int _UseEdgeDetector; int _HighlightSuppression; /** The height in pixels of a 1m object if viewed from 1m away. */ float _PixelsPerMeterAtOneMeter; int _UseAverageRayDistance; sampler2D _AverageRayDistanceBuffer; // For temporal filtering: float4x4 _CurrentCameraToPreviousCamera; sampler2D _PreviousReflectionTexture; sampler2D _PreviousCSZBuffer; float _TemporalAlpha; int _UseTemporalConfidence; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy; o.uv2 = v.texcoord.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv2.y = 1-o.uv2.y; #endif return o; } float2 mipToSize(int mip) { return floor(_ReflectionBufferSize * exp2(-mip)); } float3 ReconstructCSPosition(float2 S, float z) { float linEyeZ = -LinearEyeDepth(z); return float3(( (( S.xy * _MainTex_TexelSize.zw) ) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); } /** Read the camera-space position of the point at screen-space pixel ssP */ float3 GetPosition(float2 ssP) { float3 P; P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy); // Offset to pixel center P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); return P; } float square(float x) { return x*x; } float applyEdgeFade(float2 tsP, float fadeStrength) { float maxFade = 0.1; float2 itsP = float2(1.0, 1.0) - tsP; float dist = min(min(itsP.x, itsP.y), min(tsP.x, tsP.x)); float fade = dist / (maxFade*fadeStrength + 0.001); fade = max(min(fade, 1.0), 0.0); fade = pow(fade, 0.2); return fade; } float3 csMirrorVector(float3 csPosition, float3 csN) { float3 csE = -normalize(csPosition.xyz); float cos_o = dot(csN, csE); float3 c_mi = normalize((csN * (2.0 * cos_o)) - csE); return c_mi; } float4 fragRaytrace(v2f i, int stepRate) { float2 ssP = i.uv2.xy * _SourceToTempUV.zw; float3 csPosition = GetPosition(ssP); float smoothness = tex2D(_CameraGBufferTexture1, ssP).a; if (csPosition.z < -100.0 || smoothness == 0.0) { return float4(0.0,0.0,0.0,0.0); } if ((!_TraceEverywhere) && (smoothness < (1.0 - _MaxRoughness)) ) { return float4(0.0, 0.0, 0.0, 0.0); } float3 wsNormal = tex2D(_CameraGBufferTexture2, ssP).rgb * 2.0 - 1.0; int2 ssC = int2(ssP * _ScreenSize); float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 csRayDirection = csMirrorVector(csPosition, csN); if (_AllowBackwardsRays == 0 && csRayDirection.z > 0.0) { return float4(0.0, 0.0, 0.0, 0.0); } float maxRayTraceDistance = _MaxRayTraceDistance; float jitterFraction = 0.0f; float layerThickness = _LayerThickness; int maxSteps = _MaxSteps; // Bump the ray more in world space as it gets farther away (and so each pixel covers more WS distance) float rayBump = max(-0.01*csPosition.z, 0.001); float2 hitPixel; float3 csHitPoint; float stepCount; bool wasHit = castDenseScreenSpaceRay (csPosition + (csN) * rayBump, csRayDirection, _ProjectToPixelMatrix, _ScreenSize, _CameraClipInfo, jitterFraction, maxSteps, layerThickness, maxRayTraceDistance, hitPixel, stepRate, _EnableRefine == 1, _TraceBehindObjects == 1, csHitPoint, stepCount); float2 tsPResult = hitPixel / _ScreenSize; float rayDist = dot(csHitPoint - csPosition, csRayDirection); float confidence = 0.0; if (wasHit) { confidence = square(1.0 - max(2.0*float(stepCount) / float(maxSteps) - 1.0, 0.0)); confidence *= clamp(((_MaxRayTraceDistance - rayDist) / _FadeDistance), 0.0, 1.0); // Fake fresnel fade float3 csE = -normalize(csPosition.xyz); confidence *= max(0.0, lerp(pow(abs(dot(csRayDirection, -csE)), _FresnelFadePower), 1, 1.0 - _FresnelFade)); if (_TreatBackfaceHitAsMiss > 0) { float3 wsHitNormal = tex2Dlod(_CameraGBufferTexture2, float4(tsPResult, 0, 0)).rgb * 2.0 - 1.0; float3 wsRayDirection = mul(_CameraToWorldMatrix, float4(csRayDirection, 0)).xyz; if (dot(wsHitNormal, wsRayDirection) > 0) { confidence = 0.0; } } } else if (_FallbackToSky > 0) { float sceneZ = tex2Dlod(_CameraDepthTexture, float4(tsPResult, 0, 0)).r; sceneZ = -LinearEyeDepth(sceneZ); if (sceneZ < -1000) { // 1km ~= infinity confidence = 1.0; } } // Fade out reflections that hit near edge of screen, to prevent abrupt appearance/disappearance when object go off screen // Fade out reflections that hit near edge of screen, // to prevent abrupt appearance/disappearance when object go off screen float vignette = applyEdgeFade(tsPResult, _ScreenEdgeFading); confidence *= vignette; confidence *= vignette; return float4(tsPResult, rayDist, confidence); } float4 fragComposite(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; // View space point being shaded float3 C = GetPosition(tsP); // Final image before this pass float4 gbuffer3 = tex2D(_MainTex, i.uv); float4 specEmission = float4(0.0,0.0,0.0,0.0); float3 specColor = tex2D(_CameraGBufferTexture1, tsP).rgb; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP).a; float4 reflectionTexel = tex2D(_FinalReflectionTexture, tsP * _SourceToTempUV.xy); float4 gbuffer0 = tex2D(_CameraGBufferTexture0, tsP); // Let core Unity functions do the dirty work of applying the BRDF float3 baseColor = gbuffer0.rgb; float occlusion = gbuffer0.a; float oneMinusReflectivity; baseColor = EnergyConservationBetweenDiffuseAndSpecular(baseColor, specColor, oneMinusReflectivity); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csEyeVec = normalize(C); float3 eyeVec = mul(_CameraToWorldMatrix, float4(csEyeVec, 0)).xyz; float3 worldPos = mul(_CameraToWorldMatrix, float4(C, 1)).xyz; float cos_o = dot(wsNormal, eyeVec); float3 w_mi = -normalize((wsNormal * (2.0 * cos_o)) - eyeVec); float3 incomingRadiance = reflectionTexel.rgb; UnityLight light; light.color = 0; light.dir = 0; light.ndotl = 0; UnityIndirect ind; ind.diffuse = 0; ind.specular = incomingRadiance; float3 ssrResult = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, 1-roughness, wsNormal, -eyeVec, light, ind).rgb * _SSRMultiplier; float confidence = reflectionTexel.a; if(_EnableSSR == 0) { confidence = 0.0; } specEmission.rgb = tex2D(_CameraReflectionsTexture, tsP).rgb; float3 finalGlossyTerm; // Subtract out Unity's glossy result: (we're just applying the delta) if (_AdditiveReflection == 0) { gbuffer3 -= specEmission; // We may have blown out our dynamic range by adding then subtracting the reflection probes. // As a half-measure to fix this, simply clamp to zero gbuffer3 = max(gbuffer3, 0); finalGlossyTerm = lerp(specEmission.rgb, ssrResult, saturate(confidence)); } else { finalGlossyTerm = ssrResult*saturate(confidence); } if (_UseOcclusion) { finalGlossyTerm *= occlusion; } if (_DebugMode == 1) return float4(incomingRadiance,0); if (_DebugMode == 2) return float4(ssrResult,0); if (_DebugMode == 3) return float4(finalGlossyTerm,0); if (_DebugMode == 4) return confidence; if (_DebugMode == 5) return roughness; if (_DebugMode == 6) return float4(baseColor,0); if (_DebugMode == 7) return float4(specColor,0); if (_DebugMode == 8) return 1.0-oneMinusReflectivity; if (_DebugMode == 9) return float4(specEmission.rgb,0); if (_DebugMode == 10) return float4(specEmission.rgb-ssrResult,0); if (_DebugMode == 11) return float4(ssrResult-specEmission.rgb,0); if (_DebugMode == 12) return gbuffer3; if (_DebugMode == 13) return -float4(gbuffer3.rgb, 0.0); if (_DebugMode == 14) // Mip level, stored directly in ssrResult return float4(incomingRadiance, 0); // Additively blend the glossy GI result with the output buffer return gbuffer3 + float4(finalGlossyTerm, 0); } float roughnessWeight(float midpointRoughness, float tapRoughness) { return (1.0 - sqrt(sqrt(abs(midpointRoughness-tapRoughness)))); } float normalWeight(float3 midpointNormal, float3 tapNormal) { return clamp(dot(midpointNormal, tapNormal), 0, 1); } float highlightDecompression(float x) { return x / (1.0 - x); } float3 highlightDecompression(float3 x) { return float3( highlightDecompression(x.x), highlightDecompression(x.y), highlightDecompression(x.z)); } float highlightCompression(float x) { return x / (1.0 + x); } float3 highlightCompression(float3 x) { return float3( highlightCompression(x.x), highlightCompression(x.y), highlightCompression(x.z)); } float4 _Axis; float4 fragGBlur(v2f i) : SV_Target { int radius = 4; // Pixel being shaded float2 tsP = i.uv2.xy; float weightSum = 0.0; float gaussWeights[5] = {0.225, 0.150, 0.110, 0.075, 0.0525};//{0.225, 0.150, 0.110, 0.075, 0.0525}; float4 resultSum = float4(0.0, 0.0, 0.0, 0.0); float4 unweightedResultSum = float4(0.0, 0.0, 0.0, 0.0); float4 nAndRough = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = nAndRough.a; float3 midpointNormal = nAndRough.rgb * 2 - 1; if (_FullResolutionFiltering) { for (int i = -2*radius; i <= 2*radius; ++i) { float4 temp; float tapRoughness = midpointRoughness; float3 tapNormal = midpointNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)); temp = tex2D(_MainTex, tsTap); int gaussWeightIndex; #if defined(UNITY_COMPILER_HLSL) && !defined(SHADER_API_D3D9) gaussWeightIndex = abs(i) >> 1; // We purposefully do integer division, but that does not compile on hlsl2glsl #else gaussWeightIndex = frac(abs(i) / 2); #endif float weight = temp.a * gaussWeights[gaussWeightIndex]*0.5; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; resultSum += temp*weight; unweightedResultSum += temp; } } else { for (int i = -radius; i <= radius; ++i) { float4 temp; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)*2.0); temp = tex2D(_MainTex, tsTap); float weight = temp.a * gaussWeights[abs(i)]; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; if (_HighlightSuppression) { temp.rgb = highlightCompression(temp.rgb); } unweightedResultSum += temp; resultSum += temp*weight; } } if (weightSum > 0.01) { float invWeightSum = (1.0/weightSum); // Adding the sqrt seems to decrease temporal flickering at the expense // of having larger "halos" of fallback on rough surfaces // Subject to change with testing. Sqrt around only half the expression is *intentional*. float confidence = min(resultSum.a * sqrt(max(invWeightSum, 2.0)), 1.0); float3 finalColor = resultSum.rgb * invWeightSum; if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, confidence); } else { float3 finalColor = unweightedResultSum.rgb / (2 * radius + 1); if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, 0.0); } } sampler2D _ReflectionTexture0; sampler2D _ReflectionTexture1; sampler2D _ReflectionTexture2; sampler2D _ReflectionTexture3; sampler2D _ReflectionTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getReflectionValue(float2 tsP, int mip) { float4 coord = float4(tsP,0,0); if (mip == 0) { return tex2Dlod(_ReflectionTexture0, coord); } else if (mip == 1) { return tex2Dlod(_ReflectionTexture1, coord); } else if (mip == 2) { return tex2Dlod(_ReflectionTexture2, coord); } else if (mip == 3) { return tex2Dlod(_ReflectionTexture3, coord); } else { return tex2Dlod(_ReflectionTexture4, coord); } } sampler2D _EdgeTexture0; sampler2D _EdgeTexture1; sampler2D _EdgeTexture2; sampler2D _EdgeTexture3; sampler2D _EdgeTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getEdgeValue(float2 tsP, int mip) { float4 coord = float4(tsP + float2(1.0/(2 * mipToSize(mip))),0,0); if (mip == 0) { return tex2Dlod(_EdgeTexture0, coord); } else if (mip == 1) { return tex2Dlod(_EdgeTexture1, coord); } else if (mip == 2) { return tex2Dlod(_EdgeTexture2, coord); } else if (mip == 3) { return tex2Dlod(_EdgeTexture3, coord); } else { return tex2Dlod(_EdgeTexture4, coord); } } float2 centerPixel(float2 inputP) { return floor(inputP - float2(0.5,0.5)) + float2(0.5,0.5); } float2 snapToTexelCenter(float2 inputP, float2 texSize, float2 texSizeInv) { return centerPixel(inputP * texSize) * texSizeInv; } float4 bilateralUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; float2 fullTexSize = _ReflectionBufferSize; float2 fullTexSizeInv = 1.0 / fullTexSize; float4 hiP0 = float4(snapToTexelCenter(p0, fullTexSize, fullTexSizeInv), 0,0); float4 hiP3 = float4(snapToTexelCenter(p3, fullTexSize, fullTexSizeInv), 0,0); float4 hiP1 = float4(snapToTexelCenter(p1, fullTexSize, fullTexSizeInv), 0,0); float4 hiP2 = float4(snapToTexelCenter(p2, fullTexSize, fullTexSizeInv), 0,0); float4 tempCenter = tex2Dlod(_NormalAndRoughnessTexture, float4(tsP, 0, 0)); float3 n = tempCenter.xyz * 2 - 1; float4 temp0 = tex2Dlod(_NormalAndRoughnessTexture, hiP0); float4 temp1 = tex2Dlod(_NormalAndRoughnessTexture, hiP1); float4 temp2 = tex2Dlod(_NormalAndRoughnessTexture, hiP2); float4 temp3 = tex2Dlod(_NormalAndRoughnessTexture, hiP3); float3 n0 = temp0.xyz * 2 - 1; float3 n1 = temp1.xyz * 2 - 1; float3 n2 = temp2.xyz * 2 - 1; float3 n3 = temp3.xyz * 2 - 1; a0 *= normalWeight(n, n0); a1 *= normalWeight(n, n1); a2 *= normalWeight(n, n2); a3 *= normalWeight(n, n3); float r = tempCenter.a; float r0 = temp0.a; float r1 = temp1.a; float r2 = temp2.a; float r3 = temp3.a; a0 *= roughnessWeight(r, r0); a1 *= roughnessWeight(r, r1); a2 *= roughnessWeight(r, r2); a3 *= roughnessWeight(r, r3); // Slightly offset from zero a0 = max(a0, 0.001); a1 = max(a1, 0.001); a2 = max(a2, 0.001); a3 = max(a3, 0.001); // Nearest neighbor // a0 = a1 = a2 = a3 = 1.0; // Normalize the blending weights (weights were chosen so that // the denominator can never be zero) float norm = 1.0 / (a0 + a1 + a2 + a3); // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3) * norm; //return V0; return value; } /** Explicit bilinear fetches; must be used if the reflection buffer is bound using point sampling */ float4 bilinearUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3); return value; } // Unity's roughness is GGX roughness squared float roughnessToBlinnPhongExponent(float roughness) { float r2 = roughness*roughness; return 2.0f / r2*r2 - 2.0f; } float glossyLobeSlope(float roughness) { return pow(roughness, 4.0/3.0); } // Empirically based on our filter: // Mip | Pixels // -------------- // 0 | 1 no filter, so single pixel // 1 | 17 2r + 1 filter applied once, grabbing from pixels r away in either direction (r=8, four samples times stride of 2) // 2 | 50 2r + 1 filter applied on double size pixels, and each of those pixels had reached another r out to the side 2(2r + 1) + m_1 // 3 | 118 4(2r + 1) + m_2 // 4 | 254 8(2r + 1) + m_3 // // Approximated by pixels = 16*2^mip-15 // rearranging we get mip = log_2((pixels + 15) / 16) // float filterFootprintInPixelsToMip(float footprint) { return log2((footprint + 15) / 16); } float3 ansiGradient(float t) { //return float3(t, t, t); return fmod(floor(t * float3(8.0, 4.0, 2.0)), 2.0); } float4 fragCompositeSSR(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP * _SourceToTempUV.zw).a; float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).z; } // Get the camera space position of the reflection hit float3 csPosition = GetPosition(tsP); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 c_mi = csMirrorVector(csPosition, csN); float3 csHitpoint = c_mi * rayDistance + csPosition; float gatherFootprintInMeters = glossyLobeSlope(roughness) * rayDistance; // We could add a term that incorporates the normal // This approximation assumes reflections happen at a glancing angle float filterFootprintInPixels = gatherFootprintInMeters * _PixelsPerMeterAtOneMeter / csHitpoint.z; if (_HalfResolution == 1) { filterFootprintInPixels *= 0.5; } float mip = filterFootprintInPixelsToMip(filterFootprintInPixels); float nonPhysicalMip = pow(roughness, 3.0 / 4.0) * UNITY_SPECCUBE_LOD_STEPS; if (_HalfResolution == 1) { nonPhysicalMip = nonPhysicalMip * 0.7; } mip = lerp(nonPhysicalMip, mip, _DistanceBlur); mip = max(0, min(4, mip + _MipBias)); if (_DebugMode == 14) { return float4(ansiGradient(mip*0.25), 1.0); } float4 result; { int mipMin = int(mip); int mipMax = min(mipMin + 1, 4); float mipLerp = mip-mipMin; if (_BilateralUpsampling == 1) { if (_UseEdgeDetector == 1) { float nonEdginess = lerp(getEdgeValue(tsP, mipMin), getEdgeValue(tsP, mipMax), mipLerp); if (nonEdginess < 0.9) { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); if (_VisualizeWhereBilateral == 1) { result = float4(1, 0, 0, 0); } } else { result = lerp(bilinearUpsampleReflection(tsP, mipMin), bilinearUpsampleReflection(tsP, mipMax), mipLerp); } } else { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); } } else { float4 minResult = getReflectionValue(tsP, mipMin); float4 maxResult = getReflectionValue(tsP, mipMax); result = lerp(minResult, maxResult, mipLerp); result.a = min(minResult.a, maxResult.a); } } result.a = min(result.a, 1.0); float vignette = applyEdgeFade(tsP, _ScreenEdgeFading); result.a *= vignette; float alphaModifier = 1.0 - clamp((roughness - (_MaxRoughness - _RoughnessFalloffRange)) / _RoughnessFalloffRange, 0.0, 1.0); result.a *= alphaModifier; return result; } float4 fragBlit(v2f i) : SV_Target { // Pixel being shaded float2 ssP = i.uv2.xy; return tex2D(_MainTex, ssP); } float edgeDetector(float2 tsP, float2 axis, float2 texelSize) { float4 temp = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = temp.a; float3 midpointNormal = temp.rgb * 2 - 1; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (axis * _MainTex_TexelSize.xy); temp = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = temp.a; tapNormal = temp.rgb * 2 - 1; float weight = 1.0; weight *= roughnessWeight(midpointRoughness, tapRoughness); weight *= normalWeight(midpointNormal, tapNormal); return weight; } float4 fragEdge(v2f i) : SV_Target { float2 tsP = i.uv2.xy; return edgeDetector(tsP, float2(1,0), _MainTex_TexelSize.xy) * edgeDetector(tsP, float2(0,1), _MainTex_TexelSize.xy); } int _LastMip; float4 fragMin(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float2 lastTexSize = mipToSize(_LastMip); float2 lastTexSizeInv = 1.0 / lastTexSize;//_SourceToTempUV.xy * exp2(mip); float2 p00 = snapToTexelCenter(tsP, lastTexSize, lastTexSizeInv); float2 p11 = p00 + lastTexSizeInv; return min( min(tex2D(_MainTex, p00), tex2D(_MainTex, p11)), min(tex2D(_MainTex, float2(p00.x, p11.y)), tex2D(_MainTex, float2(p11.x, p00.y))) ); } float4 fragResolveHitPoints(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float4 temp = tex2D(_HitPointTexture, tsP); float2 hitPoint = temp.xy; float confidence = temp.w; float3 colorResult = confidence > 0.0 ? tex2D(_MainTex, hitPoint).rgb : tex2D(_CameraReflectionsTexture, tsP).rgb; #ifdef UNITY_COMPILER_HLSL if (any(isnan(colorResult))) colorResult = float3(0.0, 0.0, 0.0); // As of 11/29/2015, on Unity 5.3 on a Windows 8.1 computer with a NVIDIA GeForce 980, // with driver 347.62, the above check does not actually work to get rid of NaNs! // So we add this "redundant" check. if (!all(isfinite(colorResult))) colorResult = float3(0.0, 0.0, 0.0); #endif return float4(colorResult, confidence); } float4 fragBilatKeyPack(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float3 csN = tex2D(_CameraGBufferTexture2, tsP).xyz; float roughness = tex2D(_CameraGBufferTexture1, tsP).a; return float4(csN, roughness); } float4 fragDepthToCSZ(v2f i) : SV_Target { float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv2.xy); return float4(-LinearEyeDepth(depth), 0.0, 0.0, 0.0); } float3 csPositionToOldNormalizedScreenCoordinate(float3 csPosition) { // We could multiply these matrices together on the CPU float4 oldCSPosition = mul(_CurrentCameraToPreviousCamera, float4(csPosition, 1.0)); float4 oldSSCoordinate = mul(_ProjectToPixelMatrix, oldCSPosition); return float3((oldSSCoordinate.xy / oldSSCoordinate.w) * _InvScreenSize, oldCSPosition.z); } float4 fragTemporalFilter(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, tsP); float3 csPosition = GetPosition(tsP); float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).b; } // Virtual is the physics sense, not the other 20+ interpretations in CG float3 virtualReflectionCSPosition = normalize(csPosition) * rayDistance + csPosition; float3 oldTSP = csPositionToOldNormalizedScreenCoordinate(csPosition); float3 oldVirtualReflectionTSP = csPositionToOldNormalizedScreenCoordinate(virtualReflectionCSPosition); float oldCSZ = tex2D(_PreviousCSZBuffer, oldTSP.xy); float4 currentResult = tex2D(_FinalReflectionTexture, tsP); float4 oldResult = tex2D(_PreviousReflectionTexture, oldVirtualReflectionTSP.xy); float zError = abs(oldCSZ - oldTSP.z); // Start knocking down contribution at 5cm error, go to 0 at 5 more cm later float fallbackStart = 0.05; float fallbackEnd = 0.05; float zAlphaModifier = 1.0 - clamp((zError - fallbackStart) * (1.0 / fallbackEnd), 0.0, 1.0); float alpha = _TemporalAlpha; alpha *= zAlphaModifier; if (_UseTemporalConfidence) { alpha *= oldResult.w; } // Always compress currentResult.rgb = highlightCompression(currentResult.rgb); oldResult.rgb = highlightCompression(oldResult.rgb); float4 resultValue = lerp(currentResult, oldResult, alpha); resultValue.rgb = highlightDecompression(resultValue.rgb); return resultValue; } float4 fragAverageRayDistance(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float sumRayDistance = tex2D(_HitPointTexture, tsP).b; float sumWeight = 1.0; float roughness = 1.0 - tex2D(_CameraGBufferTexture1, tsP).a; float step = roughness * 3.0; /* for (float x = -step; x < step; x += 2 * step) { for (float y = -step; y < step; y += 2 * step) { float weight = 0.5; sumRayDistance += weight * tex2D(_HitPointTexture, (float2(x,y) * _InvScreenSize) + tsP).b; sumWeight += weight; } } */ return float4(sumRayDistance / sumWeight, 0.0, 0.0, 0.0); } static const int NUM_POISSON_TAPS = 12; // Same as used in CameraMotionBlur.shader static const float2 poissonSamples[NUM_POISSON_TAPS] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; float4 fragFilterSharpReflections(v2f i) : SV_Target { // Could improve perf by not computing blur when we won't be sampling the highest level anyways float2 tsP = i.uv2.xy; float4 sum = 0.0; float sampleRadius = _MainTex_TexelSize.xy * 2.0; for (int i = 0; i < NUM_POISSON_TAPS; i++) { float2 p = tsP + poissonSamples[i] * sampleRadius; float4 tap = tex2Dlod(_MainTex, float4(p, 0, 0)); if (_HighlightSuppression) { tap.rgb = highlightCompression(tap.rgb); } sum += tap; } float4 result = sum / float(NUM_POISSON_TAPS); if (_HighlightSuppression) { result.rgb = highlightDecompression(result.rgb); } return result; } #line 1117 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif /* UNITY: Original start of shader */ #pragma exclude_renderers gles xbox360 ps3 #pragma vertex vert #pragma fragment fragDepthToCSZ #pragma target 3.0 SAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/ShadersÓ˙˙˙_küń'¨ô™!]{Ňô SHADOWS_SOFTDIRLIGHTMAP_COMBINEDDIRLIGHTMAP_SEPARATEDYNAMICLIGHTMAP_ON INSTANCING_ONÜh˝†#line 1127 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #line 12 #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "ScreenSpaceRaytrace.cginc" float4 _ProjInfo; float4x4 _WorldToCameraMatrix; float4x4 _CameraToWorldMatrix; float4x4 _ProjectToPixelMatrix; float2 _ScreenSize; float2 _ReflectionBufferSize; float2 _InvScreenSize; float3 _CameraClipInfo; sampler2D _CameraGBufferTexture0; sampler2D _CameraGBufferTexture1; sampler2D _CameraGBufferTexture2; sampler2D _CameraGBufferTexture3; sampler2D _CameraReflectionsTexture; float _CurrentMipLevel; float _RayStepSize; float _MaxRayTraceDistance; float _LayerThickness; float _FresnelFade; float _FresnelFadePower; sampler2D _MainTex; int _VisualizeWhereBilateral; int _EnableSSR; int _DebugMode; int _HalfResolution; int _TreatBackfaceHitAsMiss; int _AllowBackwardsRays; int _TraceEverywhere; // RG: SS Hitpoint of ray // B: distance ray travelled, used for mip-selection in the final resolve // A: confidence value sampler2D _HitPointTexture; sampler2D _FinalReflectionTexture; // RGB: camera-space normal (encoded in [0-1]) // A: Roughness sampler2D _NormalAndRoughnessTexture; float4 _MainTex_TexelSize; float4 _SourceToTempUV; int _EnableRefine; int _AdditiveReflection; int _ImproveCorners; float _ScreenEdgeFading; float _MipBias; int _UseOcclusion; int _MaxSteps; int _FullResolutionFiltering; int _BilateralUpsampling; float _MaxRoughness; float _RoughnessFalloffRange; float _SSRMultiplier; float _FadeDistance; float _DistanceBlur; int _TraceBehindObjects; int _FallbackToSky; int _UseEdgeDetector; int _HighlightSuppression; /** The height in pixels of a 1m object if viewed from 1m away. */ float _PixelsPerMeterAtOneMeter; int _UseAverageRayDistance; sampler2D _AverageRayDistanceBuffer; // For temporal filtering: float4x4 _CurrentCameraToPreviousCamera; sampler2D _PreviousReflectionTexture; sampler2D _PreviousCSZBuffer; float _TemporalAlpha; int _UseTemporalConfidence; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy; o.uv2 = v.texcoord.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv2.y = 1-o.uv2.y; #endif return o; } float2 mipToSize(int mip) { return floor(_ReflectionBufferSize * exp2(-mip)); } float3 ReconstructCSPosition(float2 S, float z) { float linEyeZ = -LinearEyeDepth(z); return float3(( (( S.xy * _MainTex_TexelSize.zw) ) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); } /** Read the camera-space position of the point at screen-space pixel ssP */ float3 GetPosition(float2 ssP) { float3 P; P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy); // Offset to pixel center P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); return P; } float square(float x) { return x*x; } float applyEdgeFade(float2 tsP, float fadeStrength) { float maxFade = 0.1; float2 itsP = float2(1.0, 1.0) - tsP; float dist = min(min(itsP.x, itsP.y), min(tsP.x, tsP.x)); float fade = dist / (maxFade*fadeStrength + 0.001); fade = max(min(fade, 1.0), 0.0); fade = pow(fade, 0.2); return fade; } float3 csMirrorVector(float3 csPosition, float3 csN) { float3 csE = -normalize(csPosition.xyz); float cos_o = dot(csN, csE); float3 c_mi = normalize((csN * (2.0 * cos_o)) - csE); return c_mi; } float4 fragRaytrace(v2f i, int stepRate) { float2 ssP = i.uv2.xy * _SourceToTempUV.zw; float3 csPosition = GetPosition(ssP); float smoothness = tex2D(_CameraGBufferTexture1, ssP).a; if (csPosition.z < -100.0 || smoothness == 0.0) { return float4(0.0,0.0,0.0,0.0); } if ((!_TraceEverywhere) && (smoothness < (1.0 - _MaxRoughness)) ) { return float4(0.0, 0.0, 0.0, 0.0); } float3 wsNormal = tex2D(_CameraGBufferTexture2, ssP).rgb * 2.0 - 1.0; int2 ssC = int2(ssP * _ScreenSize); float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 csRayDirection = csMirrorVector(csPosition, csN); if (_AllowBackwardsRays == 0 && csRayDirection.z > 0.0) { return float4(0.0, 0.0, 0.0, 0.0); } float maxRayTraceDistance = _MaxRayTraceDistance; float jitterFraction = 0.0f; float layerThickness = _LayerThickness; int maxSteps = _MaxSteps; // Bump the ray more in world space as it gets farther away (and so each pixel covers more WS distance) float rayBump = max(-0.01*csPosition.z, 0.001); float2 hitPixel; float3 csHitPoint; float stepCount; bool wasHit = castDenseScreenSpaceRay (csPosition + (csN) * rayBump, csRayDirection, _ProjectToPixelMatrix, _ScreenSize, _CameraClipInfo, jitterFraction, maxSteps, layerThickness, maxRayTraceDistance, hitPixel, stepRate, _EnableRefine == 1, _TraceBehindObjects == 1, csHitPoint, stepCount); float2 tsPResult = hitPixel / _ScreenSize; float rayDist = dot(csHitPoint - csPosition, csRayDirection); float confidence = 0.0; if (wasHit) { confidence = square(1.0 - max(2.0*float(stepCount) / float(maxSteps) - 1.0, 0.0)); confidence *= clamp(((_MaxRayTraceDistance - rayDist) / _FadeDistance), 0.0, 1.0); // Fake fresnel fade float3 csE = -normalize(csPosition.xyz); confidence *= max(0.0, lerp(pow(abs(dot(csRayDirection, -csE)), _FresnelFadePower), 1, 1.0 - _FresnelFade)); if (_TreatBackfaceHitAsMiss > 0) { float3 wsHitNormal = tex2Dlod(_CameraGBufferTexture2, float4(tsPResult, 0, 0)).rgb * 2.0 - 1.0; float3 wsRayDirection = mul(_CameraToWorldMatrix, float4(csRayDirection, 0)).xyz; if (dot(wsHitNormal, wsRayDirection) > 0) { confidence = 0.0; } } } else if (_FallbackToSky > 0) { float sceneZ = tex2Dlod(_CameraDepthTexture, float4(tsPResult, 0, 0)).r; sceneZ = -LinearEyeDepth(sceneZ); if (sceneZ < -1000) { // 1km ~= infinity confidence = 1.0; } } // Fade out reflections that hit near edge of screen, to prevent abrupt appearance/disappearance when object go off screen // Fade out reflections that hit near edge of screen, // to prevent abrupt appearance/disappearance when object go off screen float vignette = applyEdgeFade(tsPResult, _ScreenEdgeFading); confidence *= vignette; confidence *= vignette; return float4(tsPResult, rayDist, confidence); } float4 fragComposite(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; // View space point being shaded float3 C = GetPosition(tsP); // Final image before this pass float4 gbuffer3 = tex2D(_MainTex, i.uv); float4 specEmission = float4(0.0,0.0,0.0,0.0); float3 specColor = tex2D(_CameraGBufferTexture1, tsP).rgb; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP).a; float4 reflectionTexel = tex2D(_FinalReflectionTexture, tsP * _SourceToTempUV.xy); float4 gbuffer0 = tex2D(_CameraGBufferTexture0, tsP); // Let core Unity functions do the dirty work of applying the BRDF float3 baseColor = gbuffer0.rgb; float occlusion = gbuffer0.a; float oneMinusReflectivity; baseColor = EnergyConservationBetweenDiffuseAndSpecular(baseColor, specColor, oneMinusReflectivity); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csEyeVec = normalize(C); float3 eyeVec = mul(_CameraToWorldMatrix, float4(csEyeVec, 0)).xyz; float3 worldPos = mul(_CameraToWorldMatrix, float4(C, 1)).xyz; float cos_o = dot(wsNormal, eyeVec); float3 w_mi = -normalize((wsNormal * (2.0 * cos_o)) - eyeVec); float3 incomingRadiance = reflectionTexel.rgb; UnityLight light; light.color = 0; light.dir = 0; light.ndotl = 0; UnityIndirect ind; ind.diffuse = 0; ind.specular = incomingRadiance; float3 ssrResult = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, 1-roughness, wsNormal, -eyeVec, light, ind).rgb * _SSRMultiplier; float confidence = reflectionTexel.a; if(_EnableSSR == 0) { confidence = 0.0; } specEmission.rgb = tex2D(_CameraReflectionsTexture, tsP).rgb; float3 finalGlossyTerm; // Subtract out Unity's glossy result: (we're just applying the delta) if (_AdditiveReflection == 0) { gbuffer3 -= specEmission; // We may have blown out our dynamic range by adding then subtracting the reflection probes. // As a half-measure to fix this, simply clamp to zero gbuffer3 = max(gbuffer3, 0); finalGlossyTerm = lerp(specEmission.rgb, ssrResult, saturate(confidence)); } else { finalGlossyTerm = ssrResult*saturate(confidence); } if (_UseOcclusion) { finalGlossyTerm *= occlusion; } if (_DebugMode == 1) return float4(incomingRadiance,0); if (_DebugMode == 2) return float4(ssrResult,0); if (_DebugMode == 3) return float4(finalGlossyTerm,0); if (_DebugMode == 4) return confidence; if (_DebugMode == 5) return roughness; if (_DebugMode == 6) return float4(baseColor,0); if (_DebugMode == 7) return float4(specColor,0); if (_DebugMode == 8) return 1.0-oneMinusReflectivity; if (_DebugMode == 9) return float4(specEmission.rgb,0); if (_DebugMode == 10) return float4(specEmission.rgb-ssrResult,0); if (_DebugMode == 11) return float4(ssrResult-specEmission.rgb,0); if (_DebugMode == 12) return gbuffer3; if (_DebugMode == 13) return -float4(gbuffer3.rgb, 0.0); if (_DebugMode == 14) // Mip level, stored directly in ssrResult return float4(incomingRadiance, 0); // Additively blend the glossy GI result with the output buffer return gbuffer3 + float4(finalGlossyTerm, 0); } float roughnessWeight(float midpointRoughness, float tapRoughness) { return (1.0 - sqrt(sqrt(abs(midpointRoughness-tapRoughness)))); } float normalWeight(float3 midpointNormal, float3 tapNormal) { return clamp(dot(midpointNormal, tapNormal), 0, 1); } float highlightDecompression(float x) { return x / (1.0 - x); } float3 highlightDecompression(float3 x) { return float3( highlightDecompression(x.x), highlightDecompression(x.y), highlightDecompression(x.z)); } float highlightCompression(float x) { return x / (1.0 + x); } float3 highlightCompression(float3 x) { return float3( highlightCompression(x.x), highlightCompression(x.y), highlightCompression(x.z)); } float4 _Axis; float4 fragGBlur(v2f i) : SV_Target { int radius = 4; // Pixel being shaded float2 tsP = i.uv2.xy; float weightSum = 0.0; float gaussWeights[5] = {0.225, 0.150, 0.110, 0.075, 0.0525};//{0.225, 0.150, 0.110, 0.075, 0.0525}; float4 resultSum = float4(0.0, 0.0, 0.0, 0.0); float4 unweightedResultSum = float4(0.0, 0.0, 0.0, 0.0); float4 nAndRough = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = nAndRough.a; float3 midpointNormal = nAndRough.rgb * 2 - 1; if (_FullResolutionFiltering) { for (int i = -2*radius; i <= 2*radius; ++i) { float4 temp; float tapRoughness = midpointRoughness; float3 tapNormal = midpointNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)); temp = tex2D(_MainTex, tsTap); int gaussWeightIndex; #if defined(UNITY_COMPILER_HLSL) && !defined(SHADER_API_D3D9) gaussWeightIndex = abs(i) >> 1; // We purposefully do integer division, but that does not compile on hlsl2glsl #else gaussWeightIndex = frac(abs(i) / 2); #endif float weight = temp.a * gaussWeights[gaussWeightIndex]*0.5; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; resultSum += temp*weight; unweightedResultSum += temp; } } else { for (int i = -radius; i <= radius; ++i) { float4 temp; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)*2.0); temp = tex2D(_MainTex, tsTap); float weight = temp.a * gaussWeights[abs(i)]; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; if (_HighlightSuppression) { temp.rgb = highlightCompression(temp.rgb); } unweightedResultSum += temp; resultSum += temp*weight; } } if (weightSum > 0.01) { float invWeightSum = (1.0/weightSum); // Adding the sqrt seems to decrease temporal flickering at the expense // of having larger "halos" of fallback on rough surfaces // Subject to change with testing. Sqrt around only half the expression is *intentional*. float confidence = min(resultSum.a * sqrt(max(invWeightSum, 2.0)), 1.0); float3 finalColor = resultSum.rgb * invWeightSum; if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, confidence); } else { float3 finalColor = unweightedResultSum.rgb / (2 * radius + 1); if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, 0.0); } } sampler2D _ReflectionTexture0; sampler2D _ReflectionTexture1; sampler2D _ReflectionTexture2; sampler2D _ReflectionTexture3; sampler2D _ReflectionTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getReflectionValue(float2 tsP, int mip) { float4 coord = float4(tsP,0,0); if (mip == 0) { return tex2Dlod(_ReflectionTexture0, coord); } else if (mip == 1) { return tex2Dlod(_ReflectionTexture1, coord); } else if (mip == 2) { return tex2Dlod(_ReflectionTexture2, coord); } else if (mip == 3) { return tex2Dlod(_ReflectionTexture3, coord); } else { return tex2Dlod(_ReflectionTexture4, coord); } } sampler2D _EdgeTexture0; sampler2D _EdgeTexture1; sampler2D _EdgeTexture2; sampler2D _EdgeTexture3; sampler2D _EdgeTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getEdgeValue(float2 tsP, int mip) { float4 coord = float4(tsP + float2(1.0/(2 * mipToSize(mip))),0,0); if (mip == 0) { return tex2Dlod(_EdgeTexture0, coord); } else if (mip == 1) { return tex2Dlod(_EdgeTexture1, coord); } else if (mip == 2) { return tex2Dlod(_EdgeTexture2, coord); } else if (mip == 3) { return tex2Dlod(_EdgeTexture3, coord); } else { return tex2Dlod(_EdgeTexture4, coord); } } float2 centerPixel(float2 inputP) { return floor(inputP - float2(0.5,0.5)) + float2(0.5,0.5); } float2 snapToTexelCenter(float2 inputP, float2 texSize, float2 texSizeInv) { return centerPixel(inputP * texSize) * texSizeInv; } float4 bilateralUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; float2 fullTexSize = _ReflectionBufferSize; float2 fullTexSizeInv = 1.0 / fullTexSize; float4 hiP0 = float4(snapToTexelCenter(p0, fullTexSize, fullTexSizeInv), 0,0); float4 hiP3 = float4(snapToTexelCenter(p3, fullTexSize, fullTexSizeInv), 0,0); float4 hiP1 = float4(snapToTexelCenter(p1, fullTexSize, fullTexSizeInv), 0,0); float4 hiP2 = float4(snapToTexelCenter(p2, fullTexSize, fullTexSizeInv), 0,0); float4 tempCenter = tex2Dlod(_NormalAndRoughnessTexture, float4(tsP, 0, 0)); float3 n = tempCenter.xyz * 2 - 1; float4 temp0 = tex2Dlod(_NormalAndRoughnessTexture, hiP0); float4 temp1 = tex2Dlod(_NormalAndRoughnessTexture, hiP1); float4 temp2 = tex2Dlod(_NormalAndRoughnessTexture, hiP2); float4 temp3 = tex2Dlod(_NormalAndRoughnessTexture, hiP3); float3 n0 = temp0.xyz * 2 - 1; float3 n1 = temp1.xyz * 2 - 1; float3 n2 = temp2.xyz * 2 - 1; float3 n3 = temp3.xyz * 2 - 1; a0 *= normalWeight(n, n0); a1 *= normalWeight(n, n1); a2 *= normalWeight(n, n2); a3 *= normalWeight(n, n3); float r = tempCenter.a; float r0 = temp0.a; float r1 = temp1.a; float r2 = temp2.a; float r3 = temp3.a; a0 *= roughnessWeight(r, r0); a1 *= roughnessWeight(r, r1); a2 *= roughnessWeight(r, r2); a3 *= roughnessWeight(r, r3); // Slightly offset from zero a0 = max(a0, 0.001); a1 = max(a1, 0.001); a2 = max(a2, 0.001); a3 = max(a3, 0.001); // Nearest neighbor // a0 = a1 = a2 = a3 = 1.0; // Normalize the blending weights (weights were chosen so that // the denominator can never be zero) float norm = 1.0 / (a0 + a1 + a2 + a3); // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3) * norm; //return V0; return value; } /** Explicit bilinear fetches; must be used if the reflection buffer is bound using point sampling */ float4 bilinearUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3); return value; } // Unity's roughness is GGX roughness squared float roughnessToBlinnPhongExponent(float roughness) { float r2 = roughness*roughness; return 2.0f / r2*r2 - 2.0f; } float glossyLobeSlope(float roughness) { return pow(roughness, 4.0/3.0); } // Empirically based on our filter: // Mip | Pixels // -------------- // 0 | 1 no filter, so single pixel // 1 | 17 2r + 1 filter applied once, grabbing from pixels r away in either direction (r=8, four samples times stride of 2) // 2 | 50 2r + 1 filter applied on double size pixels, and each of those pixels had reached another r out to the side 2(2r + 1) + m_1 // 3 | 118 4(2r + 1) + m_2 // 4 | 254 8(2r + 1) + m_3 // // Approximated by pixels = 16*2^mip-15 // rearranging we get mip = log_2((pixels + 15) / 16) // float filterFootprintInPixelsToMip(float footprint) { return log2((footprint + 15) / 16); } float3 ansiGradient(float t) { //return float3(t, t, t); return fmod(floor(t * float3(8.0, 4.0, 2.0)), 2.0); } float4 fragCompositeSSR(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP * _SourceToTempUV.zw).a; float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).z; } // Get the camera space position of the reflection hit float3 csPosition = GetPosition(tsP); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 c_mi = csMirrorVector(csPosition, csN); float3 csHitpoint = c_mi * rayDistance + csPosition; float gatherFootprintInMeters = glossyLobeSlope(roughness) * rayDistance; // We could add a term that incorporates the normal // This approximation assumes reflections happen at a glancing angle float filterFootprintInPixels = gatherFootprintInMeters * _PixelsPerMeterAtOneMeter / csHitpoint.z; if (_HalfResolution == 1) { filterFootprintInPixels *= 0.5; } float mip = filterFootprintInPixelsToMip(filterFootprintInPixels); float nonPhysicalMip = pow(roughness, 3.0 / 4.0) * UNITY_SPECCUBE_LOD_STEPS; if (_HalfResolution == 1) { nonPhysicalMip = nonPhysicalMip * 0.7; } mip = lerp(nonPhysicalMip, mip, _DistanceBlur); mip = max(0, min(4, mip + _MipBias)); if (_DebugMode == 14) { return float4(ansiGradient(mip*0.25), 1.0); } float4 result; { int mipMin = int(mip); int mipMax = min(mipMin + 1, 4); float mipLerp = mip-mipMin; if (_BilateralUpsampling == 1) { if (_UseEdgeDetector == 1) { float nonEdginess = lerp(getEdgeValue(tsP, mipMin), getEdgeValue(tsP, mipMax), mipLerp); if (nonEdginess < 0.9) { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); if (_VisualizeWhereBilateral == 1) { result = float4(1, 0, 0, 0); } } else { result = lerp(bilinearUpsampleReflection(tsP, mipMin), bilinearUpsampleReflection(tsP, mipMax), mipLerp); } } else { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); } } else { float4 minResult = getReflectionValue(tsP, mipMin); float4 maxResult = getReflectionValue(tsP, mipMax); result = lerp(minResult, maxResult, mipLerp); result.a = min(minResult.a, maxResult.a); } } result.a = min(result.a, 1.0); float vignette = applyEdgeFade(tsP, _ScreenEdgeFading); result.a *= vignette; float alphaModifier = 1.0 - clamp((roughness - (_MaxRoughness - _RoughnessFalloffRange)) / _RoughnessFalloffRange, 0.0, 1.0); result.a *= alphaModifier; return result; } float4 fragBlit(v2f i) : SV_Target { // Pixel being shaded float2 ssP = i.uv2.xy; return tex2D(_MainTex, ssP); } float edgeDetector(float2 tsP, float2 axis, float2 texelSize) { float4 temp = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = temp.a; float3 midpointNormal = temp.rgb * 2 - 1; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (axis * _MainTex_TexelSize.xy); temp = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = temp.a; tapNormal = temp.rgb * 2 - 1; float weight = 1.0; weight *= roughnessWeight(midpointRoughness, tapRoughness); weight *= normalWeight(midpointNormal, tapNormal); return weight; } float4 fragEdge(v2f i) : SV_Target { float2 tsP = i.uv2.xy; return edgeDetector(tsP, float2(1,0), _MainTex_TexelSize.xy) * edgeDetector(tsP, float2(0,1), _MainTex_TexelSize.xy); } int _LastMip; float4 fragMin(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float2 lastTexSize = mipToSize(_LastMip); float2 lastTexSizeInv = 1.0 / lastTexSize;//_SourceToTempUV.xy * exp2(mip); float2 p00 = snapToTexelCenter(tsP, lastTexSize, lastTexSizeInv); float2 p11 = p00 + lastTexSizeInv; return min( min(tex2D(_MainTex, p00), tex2D(_MainTex, p11)), min(tex2D(_MainTex, float2(p00.x, p11.y)), tex2D(_MainTex, float2(p11.x, p00.y))) ); } float4 fragResolveHitPoints(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float4 temp = tex2D(_HitPointTexture, tsP); float2 hitPoint = temp.xy; float confidence = temp.w; float3 colorResult = confidence > 0.0 ? tex2D(_MainTex, hitPoint).rgb : tex2D(_CameraReflectionsTexture, tsP).rgb; #ifdef UNITY_COMPILER_HLSL if (any(isnan(colorResult))) colorResult = float3(0.0, 0.0, 0.0); // As of 11/29/2015, on Unity 5.3 on a Windows 8.1 computer with a NVIDIA GeForce 980, // with driver 347.62, the above check does not actually work to get rid of NaNs! // So we add this "redundant" check. if (!all(isfinite(colorResult))) colorResult = float3(0.0, 0.0, 0.0); #endif return float4(colorResult, confidence); } float4 fragBilatKeyPack(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float3 csN = tex2D(_CameraGBufferTexture2, tsP).xyz; float roughness = tex2D(_CameraGBufferTexture1, tsP).a; return float4(csN, roughness); } float4 fragDepthToCSZ(v2f i) : SV_Target { float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv2.xy); return float4(-LinearEyeDepth(depth), 0.0, 0.0, 0.0); } float3 csPositionToOldNormalizedScreenCoordinate(float3 csPosition) { // We could multiply these matrices together on the CPU float4 oldCSPosition = mul(_CurrentCameraToPreviousCamera, float4(csPosition, 1.0)); float4 oldSSCoordinate = mul(_ProjectToPixelMatrix, oldCSPosition); return float3((oldSSCoordinate.xy / oldSSCoordinate.w) * _InvScreenSize, oldCSPosition.z); } float4 fragTemporalFilter(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, tsP); float3 csPosition = GetPosition(tsP); float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).b; } // Virtual is the physics sense, not the other 20+ interpretations in CG float3 virtualReflectionCSPosition = normalize(csPosition) * rayDistance + csPosition; float3 oldTSP = csPositionToOldNormalizedScreenCoordinate(csPosition); float3 oldVirtualReflectionTSP = csPositionToOldNormalizedScreenCoordinate(virtualReflectionCSPosition); float oldCSZ = tex2D(_PreviousCSZBuffer, oldTSP.xy); float4 currentResult = tex2D(_FinalReflectionTexture, tsP); float4 oldResult = tex2D(_PreviousReflectionTexture, oldVirtualReflectionTSP.xy); float zError = abs(oldCSZ - oldTSP.z); // Start knocking down contribution at 5cm error, go to 0 at 5 more cm later float fallbackStart = 0.05; float fallbackEnd = 0.05; float zAlphaModifier = 1.0 - clamp((zError - fallbackStart) * (1.0 / fallbackEnd), 0.0, 1.0); float alpha = _TemporalAlpha; alpha *= zAlphaModifier; if (_UseTemporalConfidence) { alpha *= oldResult.w; } // Always compress currentResult.rgb = highlightCompression(currentResult.rgb); oldResult.rgb = highlightCompression(oldResult.rgb); float4 resultValue = lerp(currentResult, oldResult, alpha); resultValue.rgb = highlightDecompression(resultValue.rgb); return resultValue; } float4 fragAverageRayDistance(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float sumRayDistance = tex2D(_HitPointTexture, tsP).b; float sumWeight = 1.0; float roughness = 1.0 - tex2D(_CameraGBufferTexture1, tsP).a; float step = roughness * 3.0; /* for (float x = -step; x < step; x += 2 * step) { for (float y = -step; y < step; y += 2 * step) { float weight = 0.5; sumRayDistance += weight * tex2D(_HitPointTexture, (float2(x,y) * _InvScreenSize) + tsP).b; sumWeight += weight; } } */ return float4(sumRayDistance / sumWeight, 0.0, 0.0, 0.0); } static const int NUM_POISSON_TAPS = 12; // Same as used in CameraMotionBlur.shader static const float2 poissonSamples[NUM_POISSON_TAPS] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; float4 fragFilterSharpReflections(v2f i) : SV_Target { // Could improve perf by not computing blur when we won't be sampling the highest level anyways float2 tsP = i.uv2.xy; float4 sum = 0.0; float sampleRadius = _MainTex_TexelSize.xy * 2.0; for (int i = 0; i < NUM_POISSON_TAPS; i++) { float2 p = tsP + poissonSamples[i] * sampleRadius; float4 tap = tex2Dlod(_MainTex, float4(p, 0, 0)); if (_HighlightSuppression) { tap.rgb = highlightCompression(tap.rgb); } sum += tap; } float4 result = sum / float(NUM_POISSON_TAPS); if (_HighlightSuppression) { result.rgb = highlightDecompression(result.rgb); } return result; } #line 1127 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif /* UNITY: Original start of shader */ #pragma exclude_renderers gles xbox360 ps3 #pragma vertex vert #pragma fragment fragTemporalFilter #pragma target 3.0 SAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/ShadersÓ˙˙˙iküń'¨ô™!]{Ňô SHADOWS_SOFTDIRLIGHTMAP_COMBINEDDIRLIGHTMAP_SEPARATEDYNAMICLIGHTMAP_ON INSTANCING_ON›Á†#line 1137 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #line 12 #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "ScreenSpaceRaytrace.cginc" float4 _ProjInfo; float4x4 _WorldToCameraMatrix; float4x4 _CameraToWorldMatrix; float4x4 _ProjectToPixelMatrix; float2 _ScreenSize; float2 _ReflectionBufferSize; float2 _InvScreenSize; float3 _CameraClipInfo; sampler2D _CameraGBufferTexture0; sampler2D _CameraGBufferTexture1; sampler2D _CameraGBufferTexture2; sampler2D _CameraGBufferTexture3; sampler2D _CameraReflectionsTexture; float _CurrentMipLevel; float _RayStepSize; float _MaxRayTraceDistance; float _LayerThickness; float _FresnelFade; float _FresnelFadePower; sampler2D _MainTex; int _VisualizeWhereBilateral; int _EnableSSR; int _DebugMode; int _HalfResolution; int _TreatBackfaceHitAsMiss; int _AllowBackwardsRays; int _TraceEverywhere; // RG: SS Hitpoint of ray // B: distance ray travelled, used for mip-selection in the final resolve // A: confidence value sampler2D _HitPointTexture; sampler2D _FinalReflectionTexture; // RGB: camera-space normal (encoded in [0-1]) // A: Roughness sampler2D _NormalAndRoughnessTexture; float4 _MainTex_TexelSize; float4 _SourceToTempUV; int _EnableRefine; int _AdditiveReflection; int _ImproveCorners; float _ScreenEdgeFading; float _MipBias; int _UseOcclusion; int _MaxSteps; int _FullResolutionFiltering; int _BilateralUpsampling; float _MaxRoughness; float _RoughnessFalloffRange; float _SSRMultiplier; float _FadeDistance; float _DistanceBlur; int _TraceBehindObjects; int _FallbackToSky; int _UseEdgeDetector; int _HighlightSuppression; /** The height in pixels of a 1m object if viewed from 1m away. */ float _PixelsPerMeterAtOneMeter; int _UseAverageRayDistance; sampler2D _AverageRayDistanceBuffer; // For temporal filtering: float4x4 _CurrentCameraToPreviousCamera; sampler2D _PreviousReflectionTexture; sampler2D _PreviousCSZBuffer; float _TemporalAlpha; int _UseTemporalConfidence; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy; o.uv2 = v.texcoord.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv2.y = 1-o.uv2.y; #endif return o; } float2 mipToSize(int mip) { return floor(_ReflectionBufferSize * exp2(-mip)); } float3 ReconstructCSPosition(float2 S, float z) { float linEyeZ = -LinearEyeDepth(z); return float3(( (( S.xy * _MainTex_TexelSize.zw) ) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); } /** Read the camera-space position of the point at screen-space pixel ssP */ float3 GetPosition(float2 ssP) { float3 P; P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy); // Offset to pixel center P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); return P; } float square(float x) { return x*x; } float applyEdgeFade(float2 tsP, float fadeStrength) { float maxFade = 0.1; float2 itsP = float2(1.0, 1.0) - tsP; float dist = min(min(itsP.x, itsP.y), min(tsP.x, tsP.x)); float fade = dist / (maxFade*fadeStrength + 0.001); fade = max(min(fade, 1.0), 0.0); fade = pow(fade, 0.2); return fade; } float3 csMirrorVector(float3 csPosition, float3 csN) { float3 csE = -normalize(csPosition.xyz); float cos_o = dot(csN, csE); float3 c_mi = normalize((csN * (2.0 * cos_o)) - csE); return c_mi; } float4 fragRaytrace(v2f i, int stepRate) { float2 ssP = i.uv2.xy * _SourceToTempUV.zw; float3 csPosition = GetPosition(ssP); float smoothness = tex2D(_CameraGBufferTexture1, ssP).a; if (csPosition.z < -100.0 || smoothness == 0.0) { return float4(0.0,0.0,0.0,0.0); } if ((!_TraceEverywhere) && (smoothness < (1.0 - _MaxRoughness)) ) { return float4(0.0, 0.0, 0.0, 0.0); } float3 wsNormal = tex2D(_CameraGBufferTexture2, ssP).rgb * 2.0 - 1.0; int2 ssC = int2(ssP * _ScreenSize); float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 csRayDirection = csMirrorVector(csPosition, csN); if (_AllowBackwardsRays == 0 && csRayDirection.z > 0.0) { return float4(0.0, 0.0, 0.0, 0.0); } float maxRayTraceDistance = _MaxRayTraceDistance; float jitterFraction = 0.0f; float layerThickness = _LayerThickness; int maxSteps = _MaxSteps; // Bump the ray more in world space as it gets farther away (and so each pixel covers more WS distance) float rayBump = max(-0.01*csPosition.z, 0.001); float2 hitPixel; float3 csHitPoint; float stepCount; bool wasHit = castDenseScreenSpaceRay (csPosition + (csN) * rayBump, csRayDirection, _ProjectToPixelMatrix, _ScreenSize, _CameraClipInfo, jitterFraction, maxSteps, layerThickness, maxRayTraceDistance, hitPixel, stepRate, _EnableRefine == 1, _TraceBehindObjects == 1, csHitPoint, stepCount); float2 tsPResult = hitPixel / _ScreenSize; float rayDist = dot(csHitPoint - csPosition, csRayDirection); float confidence = 0.0; if (wasHit) { confidence = square(1.0 - max(2.0*float(stepCount) / float(maxSteps) - 1.0, 0.0)); confidence *= clamp(((_MaxRayTraceDistance - rayDist) / _FadeDistance), 0.0, 1.0); // Fake fresnel fade float3 csE = -normalize(csPosition.xyz); confidence *= max(0.0, lerp(pow(abs(dot(csRayDirection, -csE)), _FresnelFadePower), 1, 1.0 - _FresnelFade)); if (_TreatBackfaceHitAsMiss > 0) { float3 wsHitNormal = tex2Dlod(_CameraGBufferTexture2, float4(tsPResult, 0, 0)).rgb * 2.0 - 1.0; float3 wsRayDirection = mul(_CameraToWorldMatrix, float4(csRayDirection, 0)).xyz; if (dot(wsHitNormal, wsRayDirection) > 0) { confidence = 0.0; } } } else if (_FallbackToSky > 0) { float sceneZ = tex2Dlod(_CameraDepthTexture, float4(tsPResult, 0, 0)).r; sceneZ = -LinearEyeDepth(sceneZ); if (sceneZ < -1000) { // 1km ~= infinity confidence = 1.0; } } // Fade out reflections that hit near edge of screen, to prevent abrupt appearance/disappearance when object go off screen // Fade out reflections that hit near edge of screen, // to prevent abrupt appearance/disappearance when object go off screen float vignette = applyEdgeFade(tsPResult, _ScreenEdgeFading); confidence *= vignette; confidence *= vignette; return float4(tsPResult, rayDist, confidence); } float4 fragComposite(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; // View space point being shaded float3 C = GetPosition(tsP); // Final image before this pass float4 gbuffer3 = tex2D(_MainTex, i.uv); float4 specEmission = float4(0.0,0.0,0.0,0.0); float3 specColor = tex2D(_CameraGBufferTexture1, tsP).rgb; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP).a; float4 reflectionTexel = tex2D(_FinalReflectionTexture, tsP * _SourceToTempUV.xy); float4 gbuffer0 = tex2D(_CameraGBufferTexture0, tsP); // Let core Unity functions do the dirty work of applying the BRDF float3 baseColor = gbuffer0.rgb; float occlusion = gbuffer0.a; float oneMinusReflectivity; baseColor = EnergyConservationBetweenDiffuseAndSpecular(baseColor, specColor, oneMinusReflectivity); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csEyeVec = normalize(C); float3 eyeVec = mul(_CameraToWorldMatrix, float4(csEyeVec, 0)).xyz; float3 worldPos = mul(_CameraToWorldMatrix, float4(C, 1)).xyz; float cos_o = dot(wsNormal, eyeVec); float3 w_mi = -normalize((wsNormal * (2.0 * cos_o)) - eyeVec); float3 incomingRadiance = reflectionTexel.rgb; UnityLight light; light.color = 0; light.dir = 0; light.ndotl = 0; UnityIndirect ind; ind.diffuse = 0; ind.specular = incomingRadiance; float3 ssrResult = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, 1-roughness, wsNormal, -eyeVec, light, ind).rgb * _SSRMultiplier; float confidence = reflectionTexel.a; if(_EnableSSR == 0) { confidence = 0.0; } specEmission.rgb = tex2D(_CameraReflectionsTexture, tsP).rgb; float3 finalGlossyTerm; // Subtract out Unity's glossy result: (we're just applying the delta) if (_AdditiveReflection == 0) { gbuffer3 -= specEmission; // We may have blown out our dynamic range by adding then subtracting the reflection probes. // As a half-measure to fix this, simply clamp to zero gbuffer3 = max(gbuffer3, 0); finalGlossyTerm = lerp(specEmission.rgb, ssrResult, saturate(confidence)); } else { finalGlossyTerm = ssrResult*saturate(confidence); } if (_UseOcclusion) { finalGlossyTerm *= occlusion; } if (_DebugMode == 1) return float4(incomingRadiance,0); if (_DebugMode == 2) return float4(ssrResult,0); if (_DebugMode == 3) return float4(finalGlossyTerm,0); if (_DebugMode == 4) return confidence; if (_DebugMode == 5) return roughness; if (_DebugMode == 6) return float4(baseColor,0); if (_DebugMode == 7) return float4(specColor,0); if (_DebugMode == 8) return 1.0-oneMinusReflectivity; if (_DebugMode == 9) return float4(specEmission.rgb,0); if (_DebugMode == 10) return float4(specEmission.rgb-ssrResult,0); if (_DebugMode == 11) return float4(ssrResult-specEmission.rgb,0); if (_DebugMode == 12) return gbuffer3; if (_DebugMode == 13) return -float4(gbuffer3.rgb, 0.0); if (_DebugMode == 14) // Mip level, stored directly in ssrResult return float4(incomingRadiance, 0); // Additively blend the glossy GI result with the output buffer return gbuffer3 + float4(finalGlossyTerm, 0); } float roughnessWeight(float midpointRoughness, float tapRoughness) { return (1.0 - sqrt(sqrt(abs(midpointRoughness-tapRoughness)))); } float normalWeight(float3 midpointNormal, float3 tapNormal) { return clamp(dot(midpointNormal, tapNormal), 0, 1); } float highlightDecompression(float x) { return x / (1.0 - x); } float3 highlightDecompression(float3 x) { return float3( highlightDecompression(x.x), highlightDecompression(x.y), highlightDecompression(x.z)); } float highlightCompression(float x) { return x / (1.0 + x); } float3 highlightCompression(float3 x) { return float3( highlightCompression(x.x), highlightCompression(x.y), highlightCompression(x.z)); } float4 _Axis; float4 fragGBlur(v2f i) : SV_Target { int radius = 4; // Pixel being shaded float2 tsP = i.uv2.xy; float weightSum = 0.0; float gaussWeights[5] = {0.225, 0.150, 0.110, 0.075, 0.0525};//{0.225, 0.150, 0.110, 0.075, 0.0525}; float4 resultSum = float4(0.0, 0.0, 0.0, 0.0); float4 unweightedResultSum = float4(0.0, 0.0, 0.0, 0.0); float4 nAndRough = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = nAndRough.a; float3 midpointNormal = nAndRough.rgb * 2 - 1; if (_FullResolutionFiltering) { for (int i = -2*radius; i <= 2*radius; ++i) { float4 temp; float tapRoughness = midpointRoughness; float3 tapNormal = midpointNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)); temp = tex2D(_MainTex, tsTap); int gaussWeightIndex; #if defined(UNITY_COMPILER_HLSL) && !defined(SHADER_API_D3D9) gaussWeightIndex = abs(i) >> 1; // We purposefully do integer division, but that does not compile on hlsl2glsl #else gaussWeightIndex = frac(abs(i) / 2); #endif float weight = temp.a * gaussWeights[gaussWeightIndex]*0.5; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; resultSum += temp*weight; unweightedResultSum += temp; } } else { for (int i = -radius; i <= radius; ++i) { float4 temp; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)*2.0); temp = tex2D(_MainTex, tsTap); float weight = temp.a * gaussWeights[abs(i)]; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; if (_HighlightSuppression) { temp.rgb = highlightCompression(temp.rgb); } unweightedResultSum += temp; resultSum += temp*weight; } } if (weightSum > 0.01) { float invWeightSum = (1.0/weightSum); // Adding the sqrt seems to decrease temporal flickering at the expense // of having larger "halos" of fallback on rough surfaces // Subject to change with testing. Sqrt around only half the expression is *intentional*. float confidence = min(resultSum.a * sqrt(max(invWeightSum, 2.0)), 1.0); float3 finalColor = resultSum.rgb * invWeightSum; if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, confidence); } else { float3 finalColor = unweightedResultSum.rgb / (2 * radius + 1); if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, 0.0); } } sampler2D _ReflectionTexture0; sampler2D _ReflectionTexture1; sampler2D _ReflectionTexture2; sampler2D _ReflectionTexture3; sampler2D _ReflectionTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getReflectionValue(float2 tsP, int mip) { float4 coord = float4(tsP,0,0); if (mip == 0) { return tex2Dlod(_ReflectionTexture0, coord); } else if (mip == 1) { return tex2Dlod(_ReflectionTexture1, coord); } else if (mip == 2) { return tex2Dlod(_ReflectionTexture2, coord); } else if (mip == 3) { return tex2Dlod(_ReflectionTexture3, coord); } else { return tex2Dlod(_ReflectionTexture4, coord); } } sampler2D _EdgeTexture0; sampler2D _EdgeTexture1; sampler2D _EdgeTexture2; sampler2D _EdgeTexture3; sampler2D _EdgeTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getEdgeValue(float2 tsP, int mip) { float4 coord = float4(tsP + float2(1.0/(2 * mipToSize(mip))),0,0); if (mip == 0) { return tex2Dlod(_EdgeTexture0, coord); } else if (mip == 1) { return tex2Dlod(_EdgeTexture1, coord); } else if (mip == 2) { return tex2Dlod(_EdgeTexture2, coord); } else if (mip == 3) { return tex2Dlod(_EdgeTexture3, coord); } else { return tex2Dlod(_EdgeTexture4, coord); } } float2 centerPixel(float2 inputP) { return floor(inputP - float2(0.5,0.5)) + float2(0.5,0.5); } float2 snapToTexelCenter(float2 inputP, float2 texSize, float2 texSizeInv) { return centerPixel(inputP * texSize) * texSizeInv; } float4 bilateralUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; float2 fullTexSize = _ReflectionBufferSize; float2 fullTexSizeInv = 1.0 / fullTexSize; float4 hiP0 = float4(snapToTexelCenter(p0, fullTexSize, fullTexSizeInv), 0,0); float4 hiP3 = float4(snapToTexelCenter(p3, fullTexSize, fullTexSizeInv), 0,0); float4 hiP1 = float4(snapToTexelCenter(p1, fullTexSize, fullTexSizeInv), 0,0); float4 hiP2 = float4(snapToTexelCenter(p2, fullTexSize, fullTexSizeInv), 0,0); float4 tempCenter = tex2Dlod(_NormalAndRoughnessTexture, float4(tsP, 0, 0)); float3 n = tempCenter.xyz * 2 - 1; float4 temp0 = tex2Dlod(_NormalAndRoughnessTexture, hiP0); float4 temp1 = tex2Dlod(_NormalAndRoughnessTexture, hiP1); float4 temp2 = tex2Dlod(_NormalAndRoughnessTexture, hiP2); float4 temp3 = tex2Dlod(_NormalAndRoughnessTexture, hiP3); float3 n0 = temp0.xyz * 2 - 1; float3 n1 = temp1.xyz * 2 - 1; float3 n2 = temp2.xyz * 2 - 1; float3 n3 = temp3.xyz * 2 - 1; a0 *= normalWeight(n, n0); a1 *= normalWeight(n, n1); a2 *= normalWeight(n, n2); a3 *= normalWeight(n, n3); float r = tempCenter.a; float r0 = temp0.a; float r1 = temp1.a; float r2 = temp2.a; float r3 = temp3.a; a0 *= roughnessWeight(r, r0); a1 *= roughnessWeight(r, r1); a2 *= roughnessWeight(r, r2); a3 *= roughnessWeight(r, r3); // Slightly offset from zero a0 = max(a0, 0.001); a1 = max(a1, 0.001); a2 = max(a2, 0.001); a3 = max(a3, 0.001); // Nearest neighbor // a0 = a1 = a2 = a3 = 1.0; // Normalize the blending weights (weights were chosen so that // the denominator can never be zero) float norm = 1.0 / (a0 + a1 + a2 + a3); // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3) * norm; //return V0; return value; } /** Explicit bilinear fetches; must be used if the reflection buffer is bound using point sampling */ float4 bilinearUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3); return value; } // Unity's roughness is GGX roughness squared float roughnessToBlinnPhongExponent(float roughness) { float r2 = roughness*roughness; return 2.0f / r2*r2 - 2.0f; } float glossyLobeSlope(float roughness) { return pow(roughness, 4.0/3.0); } // Empirically based on our filter: // Mip | Pixels // -------------- // 0 | 1 no filter, so single pixel // 1 | 17 2r + 1 filter applied once, grabbing from pixels r away in either direction (r=8, four samples times stride of 2) // 2 | 50 2r + 1 filter applied on double size pixels, and each of those pixels had reached another r out to the side 2(2r + 1) + m_1 // 3 | 118 4(2r + 1) + m_2 // 4 | 254 8(2r + 1) + m_3 // // Approximated by pixels = 16*2^mip-15 // rearranging we get mip = log_2((pixels + 15) / 16) // float filterFootprintInPixelsToMip(float footprint) { return log2((footprint + 15) / 16); } float3 ansiGradient(float t) { //return float3(t, t, t); return fmod(floor(t * float3(8.0, 4.0, 2.0)), 2.0); } float4 fragCompositeSSR(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP * _SourceToTempUV.zw).a; float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).z; } // Get the camera space position of the reflection hit float3 csPosition = GetPosition(tsP); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 c_mi = csMirrorVector(csPosition, csN); float3 csHitpoint = c_mi * rayDistance + csPosition; float gatherFootprintInMeters = glossyLobeSlope(roughness) * rayDistance; // We could add a term that incorporates the normal // This approximation assumes reflections happen at a glancing angle float filterFootprintInPixels = gatherFootprintInMeters * _PixelsPerMeterAtOneMeter / csHitpoint.z; if (_HalfResolution == 1) { filterFootprintInPixels *= 0.5; } float mip = filterFootprintInPixelsToMip(filterFootprintInPixels); float nonPhysicalMip = pow(roughness, 3.0 / 4.0) * UNITY_SPECCUBE_LOD_STEPS; if (_HalfResolution == 1) { nonPhysicalMip = nonPhysicalMip * 0.7; } mip = lerp(nonPhysicalMip, mip, _DistanceBlur); mip = max(0, min(4, mip + _MipBias)); if (_DebugMode == 14) { return float4(ansiGradient(mip*0.25), 1.0); } float4 result; { int mipMin = int(mip); int mipMax = min(mipMin + 1, 4); float mipLerp = mip-mipMin; if (_BilateralUpsampling == 1) { if (_UseEdgeDetector == 1) { float nonEdginess = lerp(getEdgeValue(tsP, mipMin), getEdgeValue(tsP, mipMax), mipLerp); if (nonEdginess < 0.9) { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); if (_VisualizeWhereBilateral == 1) { result = float4(1, 0, 0, 0); } } else { result = lerp(bilinearUpsampleReflection(tsP, mipMin), bilinearUpsampleReflection(tsP, mipMax), mipLerp); } } else { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); } } else { float4 minResult = getReflectionValue(tsP, mipMin); float4 maxResult = getReflectionValue(tsP, mipMax); result = lerp(minResult, maxResult, mipLerp); result.a = min(minResult.a, maxResult.a); } } result.a = min(result.a, 1.0); float vignette = applyEdgeFade(tsP, _ScreenEdgeFading); result.a *= vignette; float alphaModifier = 1.0 - clamp((roughness - (_MaxRoughness - _RoughnessFalloffRange)) / _RoughnessFalloffRange, 0.0, 1.0); result.a *= alphaModifier; return result; } float4 fragBlit(v2f i) : SV_Target { // Pixel being shaded float2 ssP = i.uv2.xy; return tex2D(_MainTex, ssP); } float edgeDetector(float2 tsP, float2 axis, float2 texelSize) { float4 temp = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = temp.a; float3 midpointNormal = temp.rgb * 2 - 1; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (axis * _MainTex_TexelSize.xy); temp = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = temp.a; tapNormal = temp.rgb * 2 - 1; float weight = 1.0; weight *= roughnessWeight(midpointRoughness, tapRoughness); weight *= normalWeight(midpointNormal, tapNormal); return weight; } float4 fragEdge(v2f i) : SV_Target { float2 tsP = i.uv2.xy; return edgeDetector(tsP, float2(1,0), _MainTex_TexelSize.xy) * edgeDetector(tsP, float2(0,1), _MainTex_TexelSize.xy); } int _LastMip; float4 fragMin(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float2 lastTexSize = mipToSize(_LastMip); float2 lastTexSizeInv = 1.0 / lastTexSize;//_SourceToTempUV.xy * exp2(mip); float2 p00 = snapToTexelCenter(tsP, lastTexSize, lastTexSizeInv); float2 p11 = p00 + lastTexSizeInv; return min( min(tex2D(_MainTex, p00), tex2D(_MainTex, p11)), min(tex2D(_MainTex, float2(p00.x, p11.y)), tex2D(_MainTex, float2(p11.x, p00.y))) ); } float4 fragResolveHitPoints(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float4 temp = tex2D(_HitPointTexture, tsP); float2 hitPoint = temp.xy; float confidence = temp.w; float3 colorResult = confidence > 0.0 ? tex2D(_MainTex, hitPoint).rgb : tex2D(_CameraReflectionsTexture, tsP).rgb; #ifdef UNITY_COMPILER_HLSL if (any(isnan(colorResult))) colorResult = float3(0.0, 0.0, 0.0); // As of 11/29/2015, on Unity 5.3 on a Windows 8.1 computer with a NVIDIA GeForce 980, // with driver 347.62, the above check does not actually work to get rid of NaNs! // So we add this "redundant" check. if (!all(isfinite(colorResult))) colorResult = float3(0.0, 0.0, 0.0); #endif return float4(colorResult, confidence); } float4 fragBilatKeyPack(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float3 csN = tex2D(_CameraGBufferTexture2, tsP).xyz; float roughness = tex2D(_CameraGBufferTexture1, tsP).a; return float4(csN, roughness); } float4 fragDepthToCSZ(v2f i) : SV_Target { float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv2.xy); return float4(-LinearEyeDepth(depth), 0.0, 0.0, 0.0); } float3 csPositionToOldNormalizedScreenCoordinate(float3 csPosition) { // We could multiply these matrices together on the CPU float4 oldCSPosition = mul(_CurrentCameraToPreviousCamera, float4(csPosition, 1.0)); float4 oldSSCoordinate = mul(_ProjectToPixelMatrix, oldCSPosition); return float3((oldSSCoordinate.xy / oldSSCoordinate.w) * _InvScreenSize, oldCSPosition.z); } float4 fragTemporalFilter(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, tsP); float3 csPosition = GetPosition(tsP); float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).b; } // Virtual is the physics sense, not the other 20+ interpretations in CG float3 virtualReflectionCSPosition = normalize(csPosition) * rayDistance + csPosition; float3 oldTSP = csPositionToOldNormalizedScreenCoordinate(csPosition); float3 oldVirtualReflectionTSP = csPositionToOldNormalizedScreenCoordinate(virtualReflectionCSPosition); float oldCSZ = tex2D(_PreviousCSZBuffer, oldTSP.xy); float4 currentResult = tex2D(_FinalReflectionTexture, tsP); float4 oldResult = tex2D(_PreviousReflectionTexture, oldVirtualReflectionTSP.xy); float zError = abs(oldCSZ - oldTSP.z); // Start knocking down contribution at 5cm error, go to 0 at 5 more cm later float fallbackStart = 0.05; float fallbackEnd = 0.05; float zAlphaModifier = 1.0 - clamp((zError - fallbackStart) * (1.0 / fallbackEnd), 0.0, 1.0); float alpha = _TemporalAlpha; alpha *= zAlphaModifier; if (_UseTemporalConfidence) { alpha *= oldResult.w; } // Always compress currentResult.rgb = highlightCompression(currentResult.rgb); oldResult.rgb = highlightCompression(oldResult.rgb); float4 resultValue = lerp(currentResult, oldResult, alpha); resultValue.rgb = highlightDecompression(resultValue.rgb); return resultValue; } float4 fragAverageRayDistance(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float sumRayDistance = tex2D(_HitPointTexture, tsP).b; float sumWeight = 1.0; float roughness = 1.0 - tex2D(_CameraGBufferTexture1, tsP).a; float step = roughness * 3.0; /* for (float x = -step; x < step; x += 2 * step) { for (float y = -step; y < step; y += 2 * step) { float weight = 0.5; sumRayDistance += weight * tex2D(_HitPointTexture, (float2(x,y) * _InvScreenSize) + tsP).b; sumWeight += weight; } } */ return float4(sumRayDistance / sumWeight, 0.0, 0.0, 0.0); } static const int NUM_POISSON_TAPS = 12; // Same as used in CameraMotionBlur.shader static const float2 poissonSamples[NUM_POISSON_TAPS] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; float4 fragFilterSharpReflections(v2f i) : SV_Target { // Could improve perf by not computing blur when we won't be sampling the highest level anyways float2 tsP = i.uv2.xy; float4 sum = 0.0; float sampleRadius = _MainTex_TexelSize.xy * 2.0; for (int i = 0; i < NUM_POISSON_TAPS; i++) { float2 p = tsP + poissonSamples[i] * sampleRadius; float4 tap = tex2Dlod(_MainTex, float4(p, 0, 0)); if (_HighlightSuppression) { tap.rgb = highlightCompression(tap.rgb); } sum += tap; } float4 result = sum / float(NUM_POISSON_TAPS); if (_HighlightSuppression) { result.rgb = highlightDecompression(result.rgb); } return result; } #line 1137 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif /* UNITY: Original start of shader */ #pragma exclude_renderers gles xbox360 ps3 #pragma vertex vert #pragma fragment fragAverageRayDistance #pragma target 3.0 SAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/ShadersÓ˙˙˙sküń'¨ô™!]{Ňô SHADOWS_SOFTDIRLIGHTMAP_COMBINEDDIRLIGHTMAP_SEPARATEDYNAMICLIGHTMAP_ON INSTANCING_ON2eņ#line 1147 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #line 12 #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "ScreenSpaceRaytrace.cginc" float4 _ProjInfo; float4x4 _WorldToCameraMatrix; float4x4 _CameraToWorldMatrix; float4x4 _ProjectToPixelMatrix; float2 _ScreenSize; float2 _ReflectionBufferSize; float2 _InvScreenSize; float3 _CameraClipInfo; sampler2D _CameraGBufferTexture0; sampler2D _CameraGBufferTexture1; sampler2D _CameraGBufferTexture2; sampler2D _CameraGBufferTexture3; sampler2D _CameraReflectionsTexture; float _CurrentMipLevel; float _RayStepSize; float _MaxRayTraceDistance; float _LayerThickness; float _FresnelFade; float _FresnelFadePower; sampler2D _MainTex; int _VisualizeWhereBilateral; int _EnableSSR; int _DebugMode; int _HalfResolution; int _TreatBackfaceHitAsMiss; int _AllowBackwardsRays; int _TraceEverywhere; // RG: SS Hitpoint of ray // B: distance ray travelled, used for mip-selection in the final resolve // A: confidence value sampler2D _HitPointTexture; sampler2D _FinalReflectionTexture; // RGB: camera-space normal (encoded in [0-1]) // A: Roughness sampler2D _NormalAndRoughnessTexture; float4 _MainTex_TexelSize; float4 _SourceToTempUV; int _EnableRefine; int _AdditiveReflection; int _ImproveCorners; float _ScreenEdgeFading; float _MipBias; int _UseOcclusion; int _MaxSteps; int _FullResolutionFiltering; int _BilateralUpsampling; float _MaxRoughness; float _RoughnessFalloffRange; float _SSRMultiplier; float _FadeDistance; float _DistanceBlur; int _TraceBehindObjects; int _FallbackToSky; int _UseEdgeDetector; int _HighlightSuppression; /** The height in pixels of a 1m object if viewed from 1m away. */ float _PixelsPerMeterAtOneMeter; int _UseAverageRayDistance; sampler2D _AverageRayDistanceBuffer; // For temporal filtering: float4x4 _CurrentCameraToPreviousCamera; sampler2D _PreviousReflectionTexture; sampler2D _PreviousCSZBuffer; float _TemporalAlpha; int _UseTemporalConfidence; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy; o.uv2 = v.texcoord.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv2.y = 1-o.uv2.y; #endif return o; } float2 mipToSize(int mip) { return floor(_ReflectionBufferSize * exp2(-mip)); } float3 ReconstructCSPosition(float2 S, float z) { float linEyeZ = -LinearEyeDepth(z); return float3(( (( S.xy * _MainTex_TexelSize.zw) ) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); } /** Read the camera-space position of the point at screen-space pixel ssP */ float3 GetPosition(float2 ssP) { float3 P; P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy); // Offset to pixel center P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); return P; } float square(float x) { return x*x; } float applyEdgeFade(float2 tsP, float fadeStrength) { float maxFade = 0.1; float2 itsP = float2(1.0, 1.0) - tsP; float dist = min(min(itsP.x, itsP.y), min(tsP.x, tsP.x)); float fade = dist / (maxFade*fadeStrength + 0.001); fade = max(min(fade, 1.0), 0.0); fade = pow(fade, 0.2); return fade; } float3 csMirrorVector(float3 csPosition, float3 csN) { float3 csE = -normalize(csPosition.xyz); float cos_o = dot(csN, csE); float3 c_mi = normalize((csN * (2.0 * cos_o)) - csE); return c_mi; } float4 fragRaytrace(v2f i, int stepRate) { float2 ssP = i.uv2.xy * _SourceToTempUV.zw; float3 csPosition = GetPosition(ssP); float smoothness = tex2D(_CameraGBufferTexture1, ssP).a; if (csPosition.z < -100.0 || smoothness == 0.0) { return float4(0.0,0.0,0.0,0.0); } if ((!_TraceEverywhere) && (smoothness < (1.0 - _MaxRoughness)) ) { return float4(0.0, 0.0, 0.0, 0.0); } float3 wsNormal = tex2D(_CameraGBufferTexture2, ssP).rgb * 2.0 - 1.0; int2 ssC = int2(ssP * _ScreenSize); float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 csRayDirection = csMirrorVector(csPosition, csN); if (_AllowBackwardsRays == 0 && csRayDirection.z > 0.0) { return float4(0.0, 0.0, 0.0, 0.0); } float maxRayTraceDistance = _MaxRayTraceDistance; float jitterFraction = 0.0f; float layerThickness = _LayerThickness; int maxSteps = _MaxSteps; // Bump the ray more in world space as it gets farther away (and so each pixel covers more WS distance) float rayBump = max(-0.01*csPosition.z, 0.001); float2 hitPixel; float3 csHitPoint; float stepCount; bool wasHit = castDenseScreenSpaceRay (csPosition + (csN) * rayBump, csRayDirection, _ProjectToPixelMatrix, _ScreenSize, _CameraClipInfo, jitterFraction, maxSteps, layerThickness, maxRayTraceDistance, hitPixel, stepRate, _EnableRefine == 1, _TraceBehindObjects == 1, csHitPoint, stepCount); float2 tsPResult = hitPixel / _ScreenSize; float rayDist = dot(csHitPoint - csPosition, csRayDirection); float confidence = 0.0; if (wasHit) { confidence = square(1.0 - max(2.0*float(stepCount) / float(maxSteps) - 1.0, 0.0)); confidence *= clamp(((_MaxRayTraceDistance - rayDist) / _FadeDistance), 0.0, 1.0); // Fake fresnel fade float3 csE = -normalize(csPosition.xyz); confidence *= max(0.0, lerp(pow(abs(dot(csRayDirection, -csE)), _FresnelFadePower), 1, 1.0 - _FresnelFade)); if (_TreatBackfaceHitAsMiss > 0) { float3 wsHitNormal = tex2Dlod(_CameraGBufferTexture2, float4(tsPResult, 0, 0)).rgb * 2.0 - 1.0; float3 wsRayDirection = mul(_CameraToWorldMatrix, float4(csRayDirection, 0)).xyz; if (dot(wsHitNormal, wsRayDirection) > 0) { confidence = 0.0; } } } else if (_FallbackToSky > 0) { float sceneZ = tex2Dlod(_CameraDepthTexture, float4(tsPResult, 0, 0)).r; sceneZ = -LinearEyeDepth(sceneZ); if (sceneZ < -1000) { // 1km ~= infinity confidence = 1.0; } } // Fade out reflections that hit near edge of screen, to prevent abrupt appearance/disappearance when object go off screen // Fade out reflections that hit near edge of screen, // to prevent abrupt appearance/disappearance when object go off screen float vignette = applyEdgeFade(tsPResult, _ScreenEdgeFading); confidence *= vignette; confidence *= vignette; return float4(tsPResult, rayDist, confidence); } float4 fragComposite(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; // View space point being shaded float3 C = GetPosition(tsP); // Final image before this pass float4 gbuffer3 = tex2D(_MainTex, i.uv); float4 specEmission = float4(0.0,0.0,0.0,0.0); float3 specColor = tex2D(_CameraGBufferTexture1, tsP).rgb; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP).a; float4 reflectionTexel = tex2D(_FinalReflectionTexture, tsP * _SourceToTempUV.xy); float4 gbuffer0 = tex2D(_CameraGBufferTexture0, tsP); // Let core Unity functions do the dirty work of applying the BRDF float3 baseColor = gbuffer0.rgb; float occlusion = gbuffer0.a; float oneMinusReflectivity; baseColor = EnergyConservationBetweenDiffuseAndSpecular(baseColor, specColor, oneMinusReflectivity); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csEyeVec = normalize(C); float3 eyeVec = mul(_CameraToWorldMatrix, float4(csEyeVec, 0)).xyz; float3 worldPos = mul(_CameraToWorldMatrix, float4(C, 1)).xyz; float cos_o = dot(wsNormal, eyeVec); float3 w_mi = -normalize((wsNormal * (2.0 * cos_o)) - eyeVec); float3 incomingRadiance = reflectionTexel.rgb; UnityLight light; light.color = 0; light.dir = 0; light.ndotl = 0; UnityIndirect ind; ind.diffuse = 0; ind.specular = incomingRadiance; float3 ssrResult = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, 1-roughness, wsNormal, -eyeVec, light, ind).rgb * _SSRMultiplier; float confidence = reflectionTexel.a; if(_EnableSSR == 0) { confidence = 0.0; } specEmission.rgb = tex2D(_CameraReflectionsTexture, tsP).rgb; float3 finalGlossyTerm; // Subtract out Unity's glossy result: (we're just applying the delta) if (_AdditiveReflection == 0) { gbuffer3 -= specEmission; // We may have blown out our dynamic range by adding then subtracting the reflection probes. // As a half-measure to fix this, simply clamp to zero gbuffer3 = max(gbuffer3, 0); finalGlossyTerm = lerp(specEmission.rgb, ssrResult, saturate(confidence)); } else { finalGlossyTerm = ssrResult*saturate(confidence); } if (_UseOcclusion) { finalGlossyTerm *= occlusion; } if (_DebugMode == 1) return float4(incomingRadiance,0); if (_DebugMode == 2) return float4(ssrResult,0); if (_DebugMode == 3) return float4(finalGlossyTerm,0); if (_DebugMode == 4) return confidence; if (_DebugMode == 5) return roughness; if (_DebugMode == 6) return float4(baseColor,0); if (_DebugMode == 7) return float4(specColor,0); if (_DebugMode == 8) return 1.0-oneMinusReflectivity; if (_DebugMode == 9) return float4(specEmission.rgb,0); if (_DebugMode == 10) return float4(specEmission.rgb-ssrResult,0); if (_DebugMode == 11) return float4(ssrResult-specEmission.rgb,0); if (_DebugMode == 12) return gbuffer3; if (_DebugMode == 13) return -float4(gbuffer3.rgb, 0.0); if (_DebugMode == 14) // Mip level, stored directly in ssrResult return float4(incomingRadiance, 0); // Additively blend the glossy GI result with the output buffer return gbuffer3 + float4(finalGlossyTerm, 0); } float roughnessWeight(float midpointRoughness, float tapRoughness) { return (1.0 - sqrt(sqrt(abs(midpointRoughness-tapRoughness)))); } float normalWeight(float3 midpointNormal, float3 tapNormal) { return clamp(dot(midpointNormal, tapNormal), 0, 1); } float highlightDecompression(float x) { return x / (1.0 - x); } float3 highlightDecompression(float3 x) { return float3( highlightDecompression(x.x), highlightDecompression(x.y), highlightDecompression(x.z)); } float highlightCompression(float x) { return x / (1.0 + x); } float3 highlightCompression(float3 x) { return float3( highlightCompression(x.x), highlightCompression(x.y), highlightCompression(x.z)); } float4 _Axis; float4 fragGBlur(v2f i) : SV_Target { int radius = 4; // Pixel being shaded float2 tsP = i.uv2.xy; float weightSum = 0.0; float gaussWeights[5] = {0.225, 0.150, 0.110, 0.075, 0.0525};//{0.225, 0.150, 0.110, 0.075, 0.0525}; float4 resultSum = float4(0.0, 0.0, 0.0, 0.0); float4 unweightedResultSum = float4(0.0, 0.0, 0.0, 0.0); float4 nAndRough = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = nAndRough.a; float3 midpointNormal = nAndRough.rgb * 2 - 1; if (_FullResolutionFiltering) { for (int i = -2*radius; i <= 2*radius; ++i) { float4 temp; float tapRoughness = midpointRoughness; float3 tapNormal = midpointNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)); temp = tex2D(_MainTex, tsTap); int gaussWeightIndex; #if defined(UNITY_COMPILER_HLSL) && !defined(SHADER_API_D3D9) gaussWeightIndex = abs(i) >> 1; // We purposefully do integer division, but that does not compile on hlsl2glsl #else gaussWeightIndex = frac(abs(i) / 2); #endif float weight = temp.a * gaussWeights[gaussWeightIndex]*0.5; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; resultSum += temp*weight; unweightedResultSum += temp; } } else { for (int i = -radius; i <= radius; ++i) { float4 temp; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (_Axis.xy * _MainTex_TexelSize.xy * float2(i,i)*2.0); temp = tex2D(_MainTex, tsTap); float weight = temp.a * gaussWeights[abs(i)]; // Bilateral filtering if (_ImproveCorners) { nAndRough = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = nAndRough.a; tapNormal = nAndRough.rgb * 2 - 1; weight *= normalWeight(midpointNormal, tapNormal); } weightSum += weight; if (_HighlightSuppression) { temp.rgb = highlightCompression(temp.rgb); } unweightedResultSum += temp; resultSum += temp*weight; } } if (weightSum > 0.01) { float invWeightSum = (1.0/weightSum); // Adding the sqrt seems to decrease temporal flickering at the expense // of having larger "halos" of fallback on rough surfaces // Subject to change with testing. Sqrt around only half the expression is *intentional*. float confidence = min(resultSum.a * sqrt(max(invWeightSum, 2.0)), 1.0); float3 finalColor = resultSum.rgb * invWeightSum; if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, confidence); } else { float3 finalColor = unweightedResultSum.rgb / (2 * radius + 1); if (_HighlightSuppression) { finalColor = highlightDecompression(finalColor); } return float4(finalColor, 0.0); } } sampler2D _ReflectionTexture0; sampler2D _ReflectionTexture1; sampler2D _ReflectionTexture2; sampler2D _ReflectionTexture3; sampler2D _ReflectionTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getReflectionValue(float2 tsP, int mip) { float4 coord = float4(tsP,0,0); if (mip == 0) { return tex2Dlod(_ReflectionTexture0, coord); } else if (mip == 1) { return tex2Dlod(_ReflectionTexture1, coord); } else if (mip == 2) { return tex2Dlod(_ReflectionTexture2, coord); } else if (mip == 3) { return tex2Dlod(_ReflectionTexture3, coord); } else { return tex2Dlod(_ReflectionTexture4, coord); } } sampler2D _EdgeTexture0; sampler2D _EdgeTexture1; sampler2D _EdgeTexture2; sampler2D _EdgeTexture3; sampler2D _EdgeTexture4; // Simulate mip maps, since we don't have NPOT mip-chains float4 getEdgeValue(float2 tsP, int mip) { float4 coord = float4(tsP + float2(1.0/(2 * mipToSize(mip))),0,0); if (mip == 0) { return tex2Dlod(_EdgeTexture0, coord); } else if (mip == 1) { return tex2Dlod(_EdgeTexture1, coord); } else if (mip == 2) { return tex2Dlod(_EdgeTexture2, coord); } else if (mip == 3) { return tex2Dlod(_EdgeTexture3, coord); } else { return tex2Dlod(_EdgeTexture4, coord); } } float2 centerPixel(float2 inputP) { return floor(inputP - float2(0.5,0.5)) + float2(0.5,0.5); } float2 snapToTexelCenter(float2 inputP, float2 texSize, float2 texSizeInv) { return centerPixel(inputP * texSize) * texSizeInv; } float4 bilateralUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; float2 fullTexSize = _ReflectionBufferSize; float2 fullTexSizeInv = 1.0 / fullTexSize; float4 hiP0 = float4(snapToTexelCenter(p0, fullTexSize, fullTexSizeInv), 0,0); float4 hiP3 = float4(snapToTexelCenter(p3, fullTexSize, fullTexSizeInv), 0,0); float4 hiP1 = float4(snapToTexelCenter(p1, fullTexSize, fullTexSizeInv), 0,0); float4 hiP2 = float4(snapToTexelCenter(p2, fullTexSize, fullTexSizeInv), 0,0); float4 tempCenter = tex2Dlod(_NormalAndRoughnessTexture, float4(tsP, 0, 0)); float3 n = tempCenter.xyz * 2 - 1; float4 temp0 = tex2Dlod(_NormalAndRoughnessTexture, hiP0); float4 temp1 = tex2Dlod(_NormalAndRoughnessTexture, hiP1); float4 temp2 = tex2Dlod(_NormalAndRoughnessTexture, hiP2); float4 temp3 = tex2Dlod(_NormalAndRoughnessTexture, hiP3); float3 n0 = temp0.xyz * 2 - 1; float3 n1 = temp1.xyz * 2 - 1; float3 n2 = temp2.xyz * 2 - 1; float3 n3 = temp3.xyz * 2 - 1; a0 *= normalWeight(n, n0); a1 *= normalWeight(n, n1); a2 *= normalWeight(n, n2); a3 *= normalWeight(n, n3); float r = tempCenter.a; float r0 = temp0.a; float r1 = temp1.a; float r2 = temp2.a; float r3 = temp3.a; a0 *= roughnessWeight(r, r0); a1 *= roughnessWeight(r, r1); a2 *= roughnessWeight(r, r2); a3 *= roughnessWeight(r, r3); // Slightly offset from zero a0 = max(a0, 0.001); a1 = max(a1, 0.001); a2 = max(a2, 0.001); a3 = max(a3, 0.001); // Nearest neighbor // a0 = a1 = a2 = a3 = 1.0; // Normalize the blending weights (weights were chosen so that // the denominator can never be zero) float norm = 1.0 / (a0 + a1 + a2 + a3); // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3) * norm; //return V0; return value; } /** Explicit bilinear fetches; must be used if the reflection buffer is bound using point sampling */ float4 bilinearUpsampleReflection(float2 tsP, int mip) { float2 smallTexSize = mipToSize(mip); float2 smallPixelPos = tsP * smallTexSize; float2 smallPixelPosi = centerPixel(smallPixelPos); float2 smallTexSizeInv = 1.0 / smallTexSize; float2 p0 = smallPixelPosi * smallTexSizeInv; float2 p3 = (smallPixelPosi + float2(1.0, 1.0)) * smallTexSizeInv; float2 p1 = float2(p3.x, p0.y); float2 p2 = float2(p0.x, p3.y); float4 V0 = getReflectionValue(p0.xy, mip); float4 V1 = getReflectionValue(p1.xy, mip); float4 V2 = getReflectionValue(p2.xy, mip); float4 V3 = getReflectionValue(p3.xy, mip); float a0 = 1.0; float a1 = 1.0; float a2 = 1.0; float a3 = 1.0; // Bilateral weights: // Bilinear interpolation (filter distance) float2 smallPixelPosf = smallPixelPos - smallPixelPosi; a0 = (1.0 - smallPixelPosf.x) * (1.0 - smallPixelPosf.y); a1 = smallPixelPosf.x * (1.0 - smallPixelPosf.y); a2 = (1.0 - smallPixelPosf.x) * smallPixelPosf.y; a3 = smallPixelPosf.x * smallPixelPosf.y; // Blend float4 value = (V0 * a0 + V1 * a1 + V2 * a2 + V3 * a3); return value; } // Unity's roughness is GGX roughness squared float roughnessToBlinnPhongExponent(float roughness) { float r2 = roughness*roughness; return 2.0f / r2*r2 - 2.0f; } float glossyLobeSlope(float roughness) { return pow(roughness, 4.0/3.0); } // Empirically based on our filter: // Mip | Pixels // -------------- // 0 | 1 no filter, so single pixel // 1 | 17 2r + 1 filter applied once, grabbing from pixels r away in either direction (r=8, four samples times stride of 2) // 2 | 50 2r + 1 filter applied on double size pixels, and each of those pixels had reached another r out to the side 2(2r + 1) + m_1 // 3 | 118 4(2r + 1) + m_2 // 4 | 254 8(2r + 1) + m_3 // // Approximated by pixels = 16*2^mip-15 // rearranging we get mip = log_2((pixels + 15) / 16) // float filterFootprintInPixelsToMip(float footprint) { return log2((footprint + 15) / 16); } float3 ansiGradient(float t) { //return float3(t, t, t); return fmod(floor(t * float3(8.0, 4.0, 2.0)), 2.0); } float4 fragCompositeSSR(v2f i) : SV_Target { // Pixel being shaded float2 tsP = i.uv2.xy; float roughness = 1.0-tex2D(_CameraGBufferTexture1, tsP * _SourceToTempUV.zw).a; float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).z; } // Get the camera space position of the reflection hit float3 csPosition = GetPosition(tsP); float3 wsNormal = tex2D(_CameraGBufferTexture2, tsP).rgb * 2.0 - 1.0; float3 csN = mul((float3x3)(_WorldToCameraMatrix), wsNormal); float3 c_mi = csMirrorVector(csPosition, csN); float3 csHitpoint = c_mi * rayDistance + csPosition; float gatherFootprintInMeters = glossyLobeSlope(roughness) * rayDistance; // We could add a term that incorporates the normal // This approximation assumes reflections happen at a glancing angle float filterFootprintInPixels = gatherFootprintInMeters * _PixelsPerMeterAtOneMeter / csHitpoint.z; if (_HalfResolution == 1) { filterFootprintInPixels *= 0.5; } float mip = filterFootprintInPixelsToMip(filterFootprintInPixels); float nonPhysicalMip = pow(roughness, 3.0 / 4.0) * UNITY_SPECCUBE_LOD_STEPS; if (_HalfResolution == 1) { nonPhysicalMip = nonPhysicalMip * 0.7; } mip = lerp(nonPhysicalMip, mip, _DistanceBlur); mip = max(0, min(4, mip + _MipBias)); if (_DebugMode == 14) { return float4(ansiGradient(mip*0.25), 1.0); } float4 result; { int mipMin = int(mip); int mipMax = min(mipMin + 1, 4); float mipLerp = mip-mipMin; if (_BilateralUpsampling == 1) { if (_UseEdgeDetector == 1) { float nonEdginess = lerp(getEdgeValue(tsP, mipMin), getEdgeValue(tsP, mipMax), mipLerp); if (nonEdginess < 0.9) { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); if (_VisualizeWhereBilateral == 1) { result = float4(1, 0, 0, 0); } } else { result = lerp(bilinearUpsampleReflection(tsP, mipMin), bilinearUpsampleReflection(tsP, mipMax), mipLerp); } } else { result = lerp(bilateralUpsampleReflection(tsP, mipMin), bilateralUpsampleReflection(tsP, mipMax), mipLerp); } } else { float4 minResult = getReflectionValue(tsP, mipMin); float4 maxResult = getReflectionValue(tsP, mipMax); result = lerp(minResult, maxResult, mipLerp); result.a = min(minResult.a, maxResult.a); } } result.a = min(result.a, 1.0); float vignette = applyEdgeFade(tsP, _ScreenEdgeFading); result.a *= vignette; float alphaModifier = 1.0 - clamp((roughness - (_MaxRoughness - _RoughnessFalloffRange)) / _RoughnessFalloffRange, 0.0, 1.0); result.a *= alphaModifier; return result; } float4 fragBlit(v2f i) : SV_Target { // Pixel being shaded float2 ssP = i.uv2.xy; return tex2D(_MainTex, ssP); } float edgeDetector(float2 tsP, float2 axis, float2 texelSize) { float4 temp = tex2D(_NormalAndRoughnessTexture, tsP); float midpointRoughness = temp.a; float3 midpointNormal = temp.rgb * 2 - 1; float tapRoughness; float3 tapNormal; float2 tsTap = tsP + (axis * _MainTex_TexelSize.xy); temp = tex2D(_NormalAndRoughnessTexture, tsTap); tapRoughness = temp.a; tapNormal = temp.rgb * 2 - 1; float weight = 1.0; weight *= roughnessWeight(midpointRoughness, tapRoughness); weight *= normalWeight(midpointNormal, tapNormal); return weight; } float4 fragEdge(v2f i) : SV_Target { float2 tsP = i.uv2.xy; return edgeDetector(tsP, float2(1,0), _MainTex_TexelSize.xy) * edgeDetector(tsP, float2(0,1), _MainTex_TexelSize.xy); } int _LastMip; float4 fragMin(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float2 lastTexSize = mipToSize(_LastMip); float2 lastTexSizeInv = 1.0 / lastTexSize;//_SourceToTempUV.xy * exp2(mip); float2 p00 = snapToTexelCenter(tsP, lastTexSize, lastTexSizeInv); float2 p11 = p00 + lastTexSizeInv; return min( min(tex2D(_MainTex, p00), tex2D(_MainTex, p11)), min(tex2D(_MainTex, float2(p00.x, p11.y)), tex2D(_MainTex, float2(p11.x, p00.y))) ); } float4 fragResolveHitPoints(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float4 temp = tex2D(_HitPointTexture, tsP); float2 hitPoint = temp.xy; float confidence = temp.w; float3 colorResult = confidence > 0.0 ? tex2D(_MainTex, hitPoint).rgb : tex2D(_CameraReflectionsTexture, tsP).rgb; #ifdef UNITY_COMPILER_HLSL if (any(isnan(colorResult))) colorResult = float3(0.0, 0.0, 0.0); // As of 11/29/2015, on Unity 5.3 on a Windows 8.1 computer with a NVIDIA GeForce 980, // with driver 347.62, the above check does not actually work to get rid of NaNs! // So we add this "redundant" check. if (!all(isfinite(colorResult))) colorResult = float3(0.0, 0.0, 0.0); #endif return float4(colorResult, confidence); } float4 fragBilatKeyPack(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float3 csN = tex2D(_CameraGBufferTexture2, tsP).xyz; float roughness = tex2D(_CameraGBufferTexture1, tsP).a; return float4(csN, roughness); } float4 fragDepthToCSZ(v2f i) : SV_Target { float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv2.xy); return float4(-LinearEyeDepth(depth), 0.0, 0.0, 0.0); } float3 csPositionToOldNormalizedScreenCoordinate(float3 csPosition) { // We could multiply these matrices together on the CPU float4 oldCSPosition = mul(_CurrentCameraToPreviousCamera, float4(csPosition, 1.0)); float4 oldSSCoordinate = mul(_ProjectToPixelMatrix, oldCSPosition); return float3((oldSSCoordinate.xy / oldSSCoordinate.w) * _InvScreenSize, oldCSPosition.z); } float4 fragTemporalFilter(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, tsP); float3 csPosition = GetPosition(tsP); float rayDistance = 0.0; if (_UseAverageRayDistance) { rayDistance = tex2D(_AverageRayDistanceBuffer, tsP).r; } else { rayDistance = tex2D(_HitPointTexture, tsP).b; } // Virtual is the physics sense, not the other 20+ interpretations in CG float3 virtualReflectionCSPosition = normalize(csPosition) * rayDistance + csPosition; float3 oldTSP = csPositionToOldNormalizedScreenCoordinate(csPosition); float3 oldVirtualReflectionTSP = csPositionToOldNormalizedScreenCoordinate(virtualReflectionCSPosition); float oldCSZ = tex2D(_PreviousCSZBuffer, oldTSP.xy); float4 currentResult = tex2D(_FinalReflectionTexture, tsP); float4 oldResult = tex2D(_PreviousReflectionTexture, oldVirtualReflectionTSP.xy); float zError = abs(oldCSZ - oldTSP.z); // Start knocking down contribution at 5cm error, go to 0 at 5 more cm later float fallbackStart = 0.05; float fallbackEnd = 0.05; float zAlphaModifier = 1.0 - clamp((zError - fallbackStart) * (1.0 / fallbackEnd), 0.0, 1.0); float alpha = _TemporalAlpha; alpha *= zAlphaModifier; if (_UseTemporalConfidence) { alpha *= oldResult.w; } // Always compress currentResult.rgb = highlightCompression(currentResult.rgb); oldResult.rgb = highlightCompression(oldResult.rgb); float4 resultValue = lerp(currentResult, oldResult, alpha); resultValue.rgb = highlightDecompression(resultValue.rgb); return resultValue; } float4 fragAverageRayDistance(v2f i) : SV_Target { float2 tsP = i.uv2.xy; float sumRayDistance = tex2D(_HitPointTexture, tsP).b; float sumWeight = 1.0; float roughness = 1.0 - tex2D(_CameraGBufferTexture1, tsP).a; float step = roughness * 3.0; /* for (float x = -step; x < step; x += 2 * step) { for (float y = -step; y < step; y += 2 * step) { float weight = 0.5; sumRayDistance += weight * tex2D(_HitPointTexture, (float2(x,y) * _InvScreenSize) + tsP).b; sumWeight += weight; } } */ return float4(sumRayDistance / sumWeight, 0.0, 0.0, 0.0); } static const int NUM_POISSON_TAPS = 12; // Same as used in CameraMotionBlur.shader static const float2 poissonSamples[NUM_POISSON_TAPS] = { float2(-0.326212,-0.40581), float2(-0.840144,-0.07358), float2(-0.695914,0.457137), float2(-0.203345,0.620716), float2(0.96234,-0.194983), float2(0.473434,-0.480026), float2(0.519456,0.767022), float2(0.185461,-0.893124), float2(0.507431,0.064425), float2(0.89642,0.412458), float2(-0.32194,-0.932615), float2(-0.791559,-0.59771) }; float4 fragFilterSharpReflections(v2f i) : SV_Target { // Could improve perf by not computing blur when we won't be sampling the highest level anyways float2 tsP = i.uv2.xy; float4 sum = 0.0; float sampleRadius = _MainTex_TexelSize.xy * 2.0; for (int i = 0; i < NUM_POISSON_TAPS; i++) { float2 p = tsP + poissonSamples[i] * sampleRadius; float4 tap = tex2Dlod(_MainTex, float4(p, 0, 0)); if (_HighlightSuppression) { tap.rgb = highlightCompression(tap.rgb); } sum += tap; } float4 result = sum / float(NUM_POISSON_TAPS); if (_HighlightSuppression) { result.rgb = highlightDecompression(result.rgb); } return result; } #line 1147 "" #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING #endif /* UNITY: Original start of shader */ #pragma exclude_renderers gles xbox360 ps3 #pragma vertex vert #pragma fragment fragFilterSharpReflections #pragma target 3.0 SAssets/Standard Assets/Effects/CinematicEffects(BETA)/ScreenSpaceReflection/ShadersÓ˙˙˙}küń'¨ô™!]{Ňô SHADOWS_SOFTDIRLIGHTMAP_COMBINEDDIRLIGHTMAP_SEPARATEDYNAMICLIGHTMAP_ON INSTANCING_ON