unity 5.x shaders and effects cookbook - netsitesnetsites.mx/unity3dtutorials/unity 5.x shaders and...
TRANSCRIPT
Unity5.xShadersandEffectsCookbook
TableofContents
Unity5.xShadersandEffectsCookbook
Credits
AbouttheAuthors
www.PacktPub.com
eBooks,discountoffers,andmore
Whysubscribe?
Preface
Whatthisbookcovers
Whatyouneedforthisbook
Whothisbookisfor
Sections
Gettingready
Howtodoit…
Howitworks…
There’smore…
Seealso
Conventions
Readerfeedback
Customersupport
Downloadingtheexamplecode
Downloadingthecolorimagesofthisbook
Errata
Piracy
Questions
1.CreatingYourFirstShader
Introduction
CreatingabasicStandardShader
Gettingready
Howtodoit…
Howitworks…
Seealso
MigratingLegacyShadersfromUnity4toUnity5
Gettingready
Howtodoit…
Upgradingautomatically
UsingStandardShaders
Migratingcustomshaders
Howitworks…
Seealso
Addingpropertiestoashader
Gettingready
Howtodoit…
Howitworks…
Seealso
UsingpropertiesinaSurfaceShader
Howtodoit…
Howitworks…
There’smore…
Seealso
2.SurfaceShadersandTextureMapping
Introduction
Diffuseshading
Gettingready
Howtodoit…
Howitworks…
Usingpackedarrays
Howtodoit…
Packedmatrices
Seealso
Addingatexturetoashader
Gettingready
Howtodoit…
Howitworks…
There’smore…
Seealso
ScrollingtexturesbymodifyingUVvalues
Gettingready
Howtodoit…
Howitworks…
Normalmapping
Gettingready
Howtodoit…
Howitworks…
There’smore…
Creatingatransparentmaterial
Gettingready
Howtodoit…
Howitworks…
CreatingaHolographicShader
Gettingready
Howtodoit…
Howitworks…
There’smore…
Seealso
Packingandblendingtextures
Gettingready
Howtodoit…
Howitworks…
Creatingacirclearoundyourterrain
Gettingready
Howtodoit…
Movingthecircle
Howitworks…
3.UnderstandingLightingModels
Introduction
Creatingacustomdiffuselightingmodel
Gettingready
Howtodoit…
Howitworks…
CreatingaToonShader
Gettingready
Howtodoit…
Howitworks…
There’smore…
CreatingaPhongSpeculartype
Gettingready
Howtodoit…
Howitworks…
CreatingaBlinnPhongSpeculartype
Gettingready
Howtodoit…
Howitworks…
Seealso
CreatinganAnisotropicSpeculartype
Gettingready
Howtodoit…
Howitworks…
4.PhysicallyBasedRenderinginUnity5
Introduction
Understandingthemetallicsetup
Gettingready
Howtodoit…
Howitworks…
Seealso
AddingtransparencytoPBR
Gettingready
Howtodoit…
Semi-transparentmaterials
Fadingobjects
Solidgeometrieswithholes
Seealso
Creatingmirrorsandreflectivesurfaces
Gettingready
Howtodoit…
Howitworks…
Seealso
Bakinglightsinyourscene
Gettingready
Howtodoit…
Configuringthestaticgeometry
Configuringthelightprobes
Bakingthelights
Howitworks…
Seealso
5.VertexFunctions
Introduction
AccessingavertexcolorinaSurfaceShader
Gettingready
Howtodoit…
Howitworks…
There’smore…
AnimatingverticesinaSurfaceShader
Gettingready
Howtodoit…
Howitworks…
Extrudingyourmodels
Gettingready
Howtodoit…
Howitworks…
There’smore…
Addingextrusionmaps
Implementingasnowshader
Gettingready
Howtodoit…
Howitworks…
Coloringthesurface
Alteringthegeometry
Seealso
Implementingavolumetricexplosion
Gettingready
Howtodoit…
Howitworks…
There’smore…
Seealso
6.FragmentShadersandGrabPasses
Introduction
UnderstandingVertexandFragmentShaders
Gettingready
Howtodoit…
Howitworks…
There’smore…
Inputsemantics
Outputsemantics
Seealso
Usinggrabpass
Gettingready
Howtodoit…
Howitworks…
There’smore…
ImplementingaGlassShader
Gettingready
Howtodoit…
Howitworks…
There’smore…
ImplementingaWaterShaderfor2Dgames
Gettingready
Howtodoit…
Howitworks…
7.MobileShaderAdjustment
Introduction
Whatisacheapshader?
Gettingready
Howtodoit…
Howitworks…
Profilingyourshaders
Gettingready
Howtodoit…
Howitworks…
There’smore…
Modifyingourshadersformobile
Gettingready
Howtodoit…
Howitworks…
8.ScreenEffectswithUnityRenderTextures
Introduction
Settingupthescreeneffectsscriptsystem
Gettingready
Howtodoit…
Howitworks…
There’smore…
Usingbrightness,saturation,andcontrastwithscreeneffects
Gettingready
Howtodoit…
Howitworks…
UsingbasicPhotoshop-likeBlendmodeswithscreeneffects
Gettingready
Howtodoit…
Howitworks…
There’smore…
UsingtheOverlayBlendmodewithscreeneffects
Gettingready
Howtodoit…
Howitworks…
9.GameplayandScreenEffects
Introduction
Creatinganoldmoviescreeneffect
Gettingready
Howtodoit…
Howitworks…
Seealso
Creatinganightvisionscreeneffect
Gettingready
Howtodoit…
Howitworks…
There’smore…
10.AdvancedShadingTechniques
Introduction
UsingCgIncludefilesthatarebuiltintoUnity
Gettingready
Howtodoit…
Howitworks…
MakingyourshaderworldmodularwithCgInclude
Gettingready
Howtodoit…
Howitworks…
ImplementingaFurShader
Gettingready
Howtodoit…
Howitworks…
There’smore…
Implementingheatmapswitharrays
Gettingready
Howtodoit…
Howitworks…
Index
Unity5.xShadersandEffectsCookbook
Unity5.xShadersandEffectsCookbookCopyright©2016PacktPublishing
Allrightsreserved.Nopartofthisbookmaybereproduced,storedinaretrievalsystem,ortransmittedinanyformorbyanymeans,withoutthepriorwrittenpermissionofthepublisher,exceptinthecaseofbriefquotationsembeddedincriticalarticlesorreviews.
Everyefforthasbeenmadeinthepreparationofthisbooktoensuretheaccuracyoftheinformationpresented.However,theinformationcontainedinthisbookissoldwithoutwarranty,eitherexpressorimplied.Neithertheauthors,norPacktPublishing,anditsdealersanddistributorswillbeheldliableforanydamagescausedorallegedtobecauseddirectlyorindirectlybythisbook.
PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthecompaniesandproductsmentionedinthisbookbytheappropriateuseofcapitals.However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.
FirstPublished:February2016
Productionreference:1220216
PublishedbyPacktPublishingLtd.
LiveryPlace
35LiveryStreet
BirminghamB32PB,UK.
ISBN978-1-78528-524-0
www.packtpub.com
CreditsAuthors
AlanZucconi
KennethLammers
Reviewer
KennethLammers
CommissioningEditor
PriyaSingh
AcquisitionEditors
RahulNair
ErolStaveley
ContentDevelopmentEditor
MehvashFatima
TechnicalEditors
PranilPathare
DanishShaikh
CopyEditor
TasneemFatehi
ProjectCoordinator
KinjalBari
Proofreader
SafisEditing
Indexer
MonicaAjmeraMehta
Graphics
KirkD’Penha
DishaHaria
ProductionCoordinator
NileshMohite
CoverWork
NileshMohite
AbouttheAuthorsAlanZucconiisapassionatedeveloper,author,andmotivationalspeaker,recognizedasoneofDevelop’s“30under30.”Hisexpertisehasbeenbuiltoverthepast10years,whilehededicatedhistimetoacademiaandthegamingindustry.Hestartedhisindependentcareertofullyexplorehiscreativity,tearingdownthewallbetweenartandgaming.Priortothat,heworkedatImperialCollegeLondon,wherehediscoveredhispassionforteachingandwriting.Histitlesincludethegravitypuzzle,0RBITALIS,andtheupcomingtimetravelplatformer,StillTime.
KennethLammershasover15yearsofexperienceinthegamingindustry,workingasacharacterartist,technicalartist,technicalartdirector,andprogrammer.Throughouthiscareer,hehasworkedontitlessuchasCallofDuty3,Crackdown2,AlanWake,andKinectStarWars.HecurrentlyownsandoperatesOzoneInteractivealongwithhisbusinesspartner,NoahKaarbo.Together,theyhaveworkedwithclientssuchasAmazon,ElineMedia,IGT,andMicrosoft.
KennyhasworkedforMicrosoftGamesStudios,Activision,andSurreal,andhasrecentlygoneoutonhisown,operatingCreativeTDandOzoneInteractive.
KennyauthoredthefirstversionofUnityShadersandEffectsCookbookbyPacktPublishing,andwasveryhappytobeapartofthewriting,updatingandreviewingofthisbook.
www.PacktPub.com
eBooks,discountoffers,andmoreDidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFandePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandasaprintbookcustomer,youareentitledtoadiscountontheeBookcopy.Getintouchwithusat<[email protected]>formoredetails.
Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signupforarangeoffreenewslettersandreceiveexclusivediscountsandoffersonPacktbooksandeBooks.
https://www2.packtpub.com/books/subscription/packtlib
DoyouneedinstantsolutionstoyourITquestions?PacktLibisPackt’sonlinedigitalbooklibrary.Here,youcansearch,access,andreadPackt’sentirelibraryofbooks.
Whysubscribe?FullysearchableacrosseverybookpublishedbyPacktCopyandpaste,print,andbookmarkcontentOndemandandaccessibleviaawebbrowser
PrefaceUnity5.xShadersandEffectsCookbookisyourguidetobecomingfamiliarwiththecreationofshadersandposteffectsinUnity5.Youwillstartyourjourneyatthebeginning,creatingthemostbasicshadersandlearninghowtheshadercodeisstructured.Thisfoundationalknowledgewillarmyouwiththemeanstoprogressfurtherthrougheachchapter,learningadvancedtechniquessuchasvolumetricexplosionsandfurshading.ThiseditionofthebookiswrittenspecificallyforUnity5andwillhelpyoutomasterphysically-basedrenderingandglobalilluminationtogetasclosetophotorealismaspossible.
Bytheendofeachchapter,youwillhavegainednewskillsetsthatwillincreasethequalityofyourshadersandevenmakeyourshaderwritingprocessmoreefficient.Thesechaptershavebeentailoredsothatyoucanjumpintoeachsectionandlearnaspecificskillfrombeginnertoexpert.ForthosewhoarenewtoshaderwritinginUnity,youcanprogressthrougheachchapter,oneatatime,tobuildonyourknowledge.Eitherway,youwilllearnthetechniquesthatmakemoderngameslookthewaytheydo.
Onceyouhavecompletedthisbook,youwillhaveasetofshadersthatyoucanuseinyourUnity3Dgamesaswellastheunderstandingtoaddtothem,accomplishneweffects,andaddressperformanceneeds.Solet’sgetstarted!
WhatthisbookcoversChapter1,CreatingYourFirstShader,introducesyoutotheworldofshadercodinginUnity4and5.
Chapter2,SurfaceShadersandTextureMapping,coversthemostcommonandusefultechniquesthatyoucanimplementwithSurfaceShaders,includinghowtousetexturesandnormalmapsforyourmodels.
Chapter3,UnderstandingLightingModels,givesyouanin-depthexplanationofhowshadersmodelthebehavioroflight.Thechapterteachesyouhowtocreatecustomlightingmodelsusedtosimulatespecialeffectssuchastoonshading.
Chapter4,PhysicallyBasedRenderinginUnity5,showsyouthatphysically-basedrenderingisthestandardtechnologyusedbyUnity5tobringrealismtoyourgames.Thischapterexplainshowtomakethemostoutofit,masteringtransparencies,reflectivesurfaces,andglobalillumination.
Chapter5,VertexFunctions,teachesyouhowshaderscanbeusedtoalterthegeometryofanobject;thischapterintroducesvertexmodifiersandusesthemtobringvolumetricexplosions,snowshaders,andothereffectstolife.
Chapter6,FragmentShadersandGrabPasses,explainshowtousegrabpassestomakematerialsthatemulatethedeformationsgeneratedbythesesemi-transparentmaterials.
Chapter7,MobileShaderAdjustment,helpsyouoptimizeyourshaderstogetthemostoutofanydevices.
Chapter8,ScreenEffectswithUnityRenderTextures,showsyouhowtocreatespecialeffectsandvisualsthatwouldotherwisebeimpossibletoachieve.
Chapter9,GameplayandScreenEffects,tellsyouhowpost-processingeffectscanbeusedtocomplementyourgameplay,simulating,forinstance,anightvisioneffect.
Chapter10,AdvancedShadingTechniques,introducesthemostadvancedtechniquesinthisbook,suchasfurshadingandheatmaprendering.
WhatyouneedforthisbookThefollowingisalistoftherequiredandoptionalsoftwaretocompletetherecipesinthisbook:
Unity5A3DapplicationsuchasMaya,Max,orBlender(optional)A2DimageeditingapplicationsuchasPhotoshoporGimp(optional)
WhothisbookisforThisbookiswrittenfordeveloperswhowanttocreatetheirfirstshadersinUnity5orwishtotaketheirgametoawholenewlevelbyaddingprofessionalpost-processingeffects.AsolidunderstandingofUnityisrequired.
SectionsInthisbook,youwillfindseveralheadingsthatappearfrequently(Gettingready,Howtodoit,Howitworks,There’smore,andSeealso).
Togiveclearinstructionsonhowtocompletearecipe,weusethesesectionsasfollows:
GettingreadyThissectiontellsyouwhattoexpectintherecipe,anddescribeshowtosetupanysoftwareoranypreliminarysettingsrequiredfortherecipe.
Howtodoit…Thissectioncontainsthestepsrequiredtofollowtherecipe.
Howitworks…Thissectionusuallyconsistsofadetailedexplanationofwhathappenedintheprevioussection.
There’smore…Thissectionconsistsofadditionalinformationabouttherecipeinordertomakethereadermoreknowledgeableabouttherecipe.
SeealsoThissectionprovideshelpfullinkstootherusefulinformationfortherecipe.
ConventionsInthisbook,youwillfindanumberoftextstylesthatdistinguishbetweendifferentkindsofinformation.Herearesomeexamplesofthesestylesandanexplanationoftheirmeaning.
Codewordsintext,databasetablenames,foldernames,filenames,fileextensions,pathnames,dummyURLs,userinput,andTwitterhandlesareshownasfollows:“EnterthefollowingcodeintothePropertiesblockofyourshader.”
Ablockofcodeissetasfollows:
voidsurf(InputIN,inoutSurfaceOutputo)
{
float4c;
c=pow((_EmissiveColor+_AmbientColor),_MySliderValue);
o.Albedo=c.rgb;
o.Alpha=c.a;
}
Whenwewishtodrawyourattentiontoaparticularpartofacodeblock,therelevantlinesoritemsaresetinbold:
voidsurf(InputIN,inoutSurfaceOutputStandardo){
fixed4c=pow((_Color+_AmbientColor),_MySliderValue);
o.Albedo=c.rgb;
o.Metallic=_Metallic;
o.Smoothness=_Glossiness;
o.Alpha=c.a;
}
Newtermsandimportantwordsareshowninbold.Wordsthatyouseeonthescreen,forexample,inmenusordialogboxes,appearinthetextlikethis:“IntheProjecttabinyourUnityeditor,right-clickontheAssetsfolderandselectCreate|Folder.”
NoteWarningsorimportantnotesappearinaboxlikethis.
TipTipsandtricksappearlikethis.
ReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook—whatyoulikedordisliked.Readerfeedbackisimportantforusasithelpsusdeveloptitlesthatyouwillreallygetthemostoutof.
Tosendusgeneralfeedback,simplye-mail<[email protected]>,andmentionthebook’stitleinthesubjectofyourmessage.
Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideatwww.packtpub.com/authors.
CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.
DownloadingtheexamplecodeYoucandownloadtheexamplecodefilesforthisbookfromyouraccountathttp://www.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.
Youcandownloadthecodefilesbyfollowingthesesteps:
1. Loginorregistertoourwebsiteusingyoure-mailaddressandpassword.2. HoverthemousepointerontheSUPPORTtabatthetop.3. ClickonCodeDownloads&Errata.4. EnterthenameofthebookintheSearchbox.5. Selectthebookforwhichyou’relookingtodownloadthecodefiles.6. Choosefromthedrop-downmenuwhereyoupurchasedthisbookfrom.7. ClickonCodeDownload.
Oncethefileisdownloaded,pleasemakesurethatyouunziporextractthefolderusingthelatestversionof:
WinRAR/7-ZipforWindowsZipeg/iZip/UnRarXforMac7-Zip/PeaZipforLinux
DownloadingthecolorimagesofthisbookWealsoprovideyouwithaPDFfilethathascolorimagesofthescreenshots/diagramsusedinthisbook.Thecolorimageswillhelpyoubetterunderstandthechangesintheoutput.Youcandownloadthisfilefromhttps://www.packtpub.com/sites/default/files/downloads/Unity5xShadersAndEffectsCookbook_SecondEdition_Graphics.pdf
ErrataAlthoughwehavetakeneverycaretoensuretheaccuracyofourcontent,mistakesdohappen.Ifyoufindamistakeinoneofourbooks—maybeamistakeinthetextorthecode—wewouldbegratefulifyoucouldreportthistous.Bydoingso,youcansaveotherreadersfromfrustrationandhelpusimprovesubsequentversionsofthisbook.Ifyoufindanyerrata,pleasereportthembyvisitinghttp://www.packtpub.com/submit-errata,selectingyourbook,clickingontheErrataSubmissionFormlink,andenteringthedetailsofyourerrata.Onceyourerrataareverified,yoursubmissionwillbeacceptedandtheerratawillbeuploadedtoourwebsiteoraddedtoanylistofexistingerrataundertheErratasectionofthattitle.
Toviewthepreviouslysubmittederrata,gotohttps://www.packtpub.com/books/content/supportandenterthenameofthebookinthesearchfield.TherequiredinformationwillappearundertheErratasection.
PiracyPiracyofcopyrightedmaterialontheInternetisanongoingproblemacrossallmedia.AtPackt,wetaketheprotectionofourcopyrightandlicensesveryseriously.IfyoucomeacrossanyillegalcopiesofourworksinanyformontheInternet,pleaseprovideuswiththelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.
Pleasecontactusat<[email protected]>withalinktothesuspectedpiratedmaterial.
Weappreciateyourhelpinprotectingourauthorsandourabilitytobringyouvaluablecontent.
QuestionsIfyouhaveaproblemwithanyaspectofthisbook,youcancontactusat<[email protected]>,andwewilldoourbesttoaddresstheproblem.
Chapter1.CreatingYourFirstShaderThischapterwillcoversomeofthemorecommondiffusetechniquesfoundintoday’sGameDevelopmentShadingPipelines.Inthischapter,youwilllearnaboutthefollowingrecipes:
CreatingabasicStandardShaderMigratingLegacyShadersfromUnity4toUnity5AddingpropertiestoashaderUsingpropertiesinaSurfaceShader
IntroductionLet’simagineacubethathasbeenpaintedwhiteuniformly.Evenifthecolorusedisthesameoneachface,theywillallhavedifferentshadesofwhitedependingonthedirectionthatthelightiscomingfromandtheanglethatwearelookingatit.Thisextralevelofrealismisachievedin3Dgraphicsbyshaders,specialprogramsthataremostlyusedtosimulatehowlightworks.Awoodencubeandametalonemaysharethesame3Dmodel,butwhatmakesthemlookdifferentistheshaderthattheyuse.Recipeafterrecipe,thisfirstchapterwillintroduceyoutoshadercodinginUnity.Ifyouhavelittletonopreviousexperiencewithshaders,thischapteriswhatyouneedtounderstandwhatshadersare,howtheywork,andhowtocustomizethem.
Bytheendofthischapter,youwillhavelearnedhowtobuildbasicshadersthatperformbasicoperations.Armedwiththisknowledge,youwillbeabletocreatejustaboutanySurfaceShader.
CreatingabasicStandardShaderEveryUnitygamedevelopershouldbefamiliarwiththeconceptofcomponents.Alltheobjectsthatarepartofagamecontainanumberofcomponentsthataffecttheirlookandbehavior.Whilescriptsdeterminehowobjectsshouldbehave,renderersdecidehowtheyshouldappearonthescreen.Unitycomeswithseveralrenderers,dependingonthetypeofobjectthatwearetryingtovisualise;every3DmodeltypicallyhasMeshRenderer.Anobjectshouldhaveonlyonerenderer,buttherendereritselfcancontainseveralmaterials.Eachmaterialisawrapperforasingleshader,thefinalringinthefoodchainof3Dgraphics.Therelationshipsbetweenthesecomponentscanbeseeninthefollowingdiagram:
Understandingthedifferencebetweenthesecomponentsisessentialtounderstandhowshaderswork.
GettingreadyTogetstartedwiththisrecipe,youwillneedtohaveUnity5runningandmusthavecreatedanewproject.TherewillalsobeaUnityprojectincludedwiththiscookbook,soyoucanusethatoneaswellandsimplyaddyourowncustomshaderstoitasyoustepthrougheachrecipe.Withthiscompleted,youarenowreadytostepintothewonderfulworldofreal-timeshading!
Howtodoit…Beforegettingintoourfirstshader,let’screateasmallsceneforustoworkwith.ThiscanbedonebynavigatingtoGameObject|CreateEmptyintheUnityeditor.Fromhere,youcancreateaplanetoactasaground,acoupleofspherestowhichwewillapplyourshader,andadirectionallighttogivethescenesomelight.Withourscenegenerated,wecanmoveontotheshaderwritingsteps:
1. IntheProjecttabinyourUnityeditor,right-clickontheAssetsfolderandselectCreate|Folder.
NoteIfyouareusingtheUnityprojectthatcamewiththecookbook,youcanskiptostep4.
2. RenamethefolderthatyoucreatedtoShadersbyright-clickingonitandselectingRenamefromthedrop-downlistorselectingthefolderandhittingF2onthekeyboard.
3. CreateanotherfolderandrenameittoMaterials.4. Right-clickontheShadersfolderandselectCreate|Shader.Thenright-clickonthe
MaterialsfolderandselectCreate|Material.5. RenameboththeshaderandmaterialtoStandardDiffuse.6. LaunchtheStandardDiffuseshaderinMonoDevelop(thedefaultscripteditorfor
Unity)bydouble-clickingonit.Thiswillautomaticallylaunchtheeditorforyouanddisplaytheshadercode.
NoteYouwillseethatUnityhasalreadypopulatedourshaderwithsomebasiccode.This,bydefault,willgetyouabasicDiffuseshaderthatacceptsonetexture.Wewillbemodifyingthisbasecodesothatyoucanlearnhowtoquicklystartdevelopingyourowncustomshaders.
7. Nowlet’sgiveourshaderacustomfolderfromwhichit’sselected.TheveryfirstlineofcodeintheshaderisthecustomdescriptionthatwehavetogivetheshadersothatUnitycanmakeitavailableintheshaderdrop-downlistwhenassigningtomaterials.WehaverenamedourpathtoShader"CookbookShaders/StandardDiffuse",butyoucannameittowhateveryouwantandrenameitatanytime.Sodon’tworryaboutanydependenciesatthispoint.SavetheshaderinMonoDevelopandreturntotheUnityeditor.Unitywillautomaticallycompiletheshaderwhenitrecognizesthatthefilehasbeenupdated.Thisiswhatyourshadershouldlooklikeatthispoint:
Shader"CookbookShaders/StandardDiffuse"{
Properties{
_Color("Color",Color)=(1,1,1,1)
_MainTex("Albedo(RGB)",2D)="white"{}
_Glossiness("Smoothness",Range(0,1))=0.5
_Metallic("Metallic",Range(0,1))=0.0
}
SubShader{
Tags{"RenderType"="Opaque"}
LOD200
CGPROGRAM
//PhysicallybasedStandardlightingmodel,andenableshadowson
alllighttypes
#pragmasurfacesurfStandardfullforwardshadows
//Useshadermodel3.0target,togetnicerlookinglighting
#pragmatarget3.0
sampler2D_MainTex;
structInput{
float2uv_MainTex;
};
half_Glossiness;
half_Metallic;
fixed4_Color;
voidsurf(InputIN,inoutSurfaceOutputStandardo){
//Albedocomesfromatexturetintedbycolor
fixed4c=tex2D(_MainTex,IN.uv_MainTex)*_Color;
o.Albedo=c.rgb;
//Metallicandsmoothnesscomefromslidervariables
o.Metallic=_Metallic;
o.Smoothness=_Glossiness;
o.Alpha=c.a;
}
ENDCG
}
FallBack"Diffuse"
}
8. Technicallyspeaking,thisisaSurfaceShaderbasedonphysically-basedrendering,whichUnity5hasadoptedasitsnewstandard.Asthenamesuggests,thistypeofshaderachievesrealismbysimulatinghowlightphysicallybehaveswhenhittingobjects.IfyouareusingapreviousversionofUnity(suchasUnity4),yourcodewilllookverydifferent.Priortotheintroductionofphysically-basedshaders,Unity4usedlesssophisticatedtechniques.Allthesedifferenttypesofshaderwillbefurtherexploredinthenextchaptersofthisbook.
9. Afteryourshaderiscreated,weneedtoconnectittoamaterial.SelectthematerialcalledStandardDiffusethatwecreatedinstep4andlookattheInspectortab.FromtheShaderdrop-downlist,selectCookbookShaders|StandardDiffuse.(Yourshaderpathmightbedifferentifyouchosetouseadifferentpathname.)Thiswillassignyourshadertoyourmaterialandmakeitreadyforyoutoassigntoanobject.
NoteToassignamaterialtoanobject,youcansimplyclickanddragyourmaterialfrom
theProjecttabtotheobjectinyourscene.YoucanalsodragamaterialontotheInspectortabofanobjectintheUnityeditortoassignamaterial.
Thescreenshotofanexampleisasfollows:
Notmuchtolookatatthispoint,butourshaderdevelopmentenvironmentissetupandwecannowstarttomodifytheshadertosuitourneeds.
Howitworks…Unityhasmadethetaskofgettingyourshaderenvironmentupandrunning,whichisveryeasyforyou.Itissimplyamatterofafewclicksandyouaregoodtogo.TherearealotofelementsworkinginthebackgroundwithregardtotheSurfaceShaderitself.UnityhastakentheCgshaderlanguageandmadeitmoreefficienttowritebydoingalotoftheheavyCgcodeliftingforyou.TheSurfaceShaderlanguageisamorecomponent-basedwayofwritingshaders.Taskssuchasprocessingyourowntexturecoordinatesandtransformationmatriceshavealreadybeendoneforyou,soyoudon’thavetostartfromscratchanymore.Inthepast,wewouldhavetostartanewshaderandrewritealotofcodeoverandoveragain.AsyougainmoreexperiencewithSurfaceShaders,youwillnaturallywanttoexploremoreoftheunderlyingfunctionsoftheCglanguageandhowUnityisprocessingallofthelow-levelgraphicsprocessingunit(GPU)tasksforyou.
NoteAllthefilesinaUnityprojectarereferencedindependentlyfromthefolderthattheyarein.Wecanmoveshadersandmaterialsfromwithintheeditorwithouttheriskofbreakinganyconnection.Files,however,shouldneverbemovedfromoutsidetheeditorasUnitywillnotbeabletoupdatetheirreferences.
So,bysimplychangingtheshader’spathnametoanameofourchoice,wehavegotourbasicDiffuseshaderworkingintheUnityenvironment,withlightsandshadowsandallthatbyjustchangingonelineofcode!
SeealsoThesourcecodeofthebuilt-inshadersistypicallyhiddeninUnity5.Youcannotopenthisfromtheeditorlikeyoudowithyourownshaders.
Formoreinformationonwheretofindalargeportionofthebuilt-inCgfunctionsforUnity,gotoyourUnityinstalldirectoryandnavigatetoUnity45\Editor\Data\CGIncludes.Inthisfolder,youcanfindthesourcecodeoftheshadersshippedwithUnity.Overtime,theyhavechangedalot;UNITYDOWNLOADARCHIVE(https://unity3d.com/get-unity/download/archive)istheplacetogoifyouneedtoaccessthesourcecodesofashaderusedinadifferentversionofUnity.Afterchoosingtherightversion,selectBuiltinshadersfromthedrop-downlist,asshowninthefollowingimage.Therearethreefilesthatareofnoteatthispoint—UnityCG.cginc,Lighting.cginc,andUnityShaderVariables.cginc.Ourcurrentshaderismakinguseofallthesefilesatthemoment:
Chapter10,AdvancedShadingTechniques,willexplorein-depthhowtouseGcIncludeforamodularapproachtoshadercoding.
MigratingLegacyShadersfromUnity4toUnity5Itisundeniablethatgraphicsinvideogameshavechangedmassivelyoverthelast10years.Everynewgamecomeswithcutting-edgetechniquesthataregettingusclosertoachievingreal-timephotorealism.WeshouldnotbesurprisedbythefactthatshadersthemselveshavechangedmassivelythroughoutthelifetimeofUnity.Thisisoneofthemajorsourcesofconfusionwhenapproachingshadersforthefirsttime.PriortoUnity5,mainlytwodifferentshaderswereadopted:DiffuseandSpecular.Asthenamessuggest,theywereusedformatteandshinymaterials,respectively.IfyouarealreadyusingUnity5,youcanskipthisrecipe.ThisrecipewillexplainhowtoreplicatetheseeffectsusingUnity5.
GettingreadyThestartingpointofthisrecipeishavingaworkspacemadeinUnity4,whichusessomeofthebuilt-inshadersthatwereoriginallyprovided.Ifyouaretostartanewgame,thereisnodoubtthatyoushouldusethelatestversionofUnity.However,ifyourprojectisalreadyinthelaterstagesofdevelopmentwithanolderversion,youshouldbeverycarefulbeforemigrating.Manythingshavechangedbehindthecurtainsoftheengine,andevenifyourbuilt-inshaderswillmostlikelyworkwithoutanyproblem,yourscriptsmightnot.Ifyouaretomigrateyourentireworkspace,thefirstthingthatyoushoulddoistakebackup.ItisimportanttorememberthatsavingassetsandscenesisnotenoughasmostoftheconfigurationinUnityisstoredinitsmetadatafiles.Thesafestoptiontosurviveamigrationistoduplicatetheentirefolderthatcontainsyourproject.ThebestwayofdoingthisisbyphysicallycopyingthefolderfromFileExplorer(Windows)orFinder(Mac).
Howtodoit…Therearetwomainoptionsifyouwanttomigrateyourbuilt-inshaders:upgradingyourprojectautomaticallyorswitchingtoStandardShadersinstead.
UpgradingautomaticallyThisoptionistheeasiestone.Unity5canimportaprojectmadewithanearlierversionandupgradeit.Youshouldnoticethatoncetheconversionisdone,youwillnotbeabletouseUnity4;evenifnoneofyourassetsmayhavechangeddirectly,Unitymetadatahasbeenconverted.Toproceedwiththis,openUnity5andclickonOPENOTHERtoselectthefolderofyouroldproject.Youwillbeaskedifyouwanttoconvertit;clickonUpgradetoproceed.Unitywillreimportallofyourassetsandrecompileallofyourscripts.Theprocessmightlastforseveralhoursifyourprojectisbig.Oncetheconversionisdone,yourbuilt-inshadersfromUnity4shouldhavebeenreplacedwiththeirlegacyequivalent.Youcancheckthisfromtheinspectorofyourmaterialsthatshouldhavechanged(forinstance)fromBumpedDiffusetoLegacyShader/BumpedDiffuse.
NoteEvenifDiffuse,Specular,andtheotherbuilt-inshadersfromUnity4arenowdeprecated,Unity5keepsthemforbackwardcompatibility.Theycanbefoundinthedrop-downmenuofamaterialundertheLegacyShadersfolder.
UsingStandardShadersInsteadofusingtheLegacyShaders,youmightdecidetoreplacethemwiththenewStandardShadersfromUnity5.Beforedoingthis,youshouldkeepinmindthatastheyarebasedonadifferentlightingmodel,yourmaterialswillmostlikelylookdifferent.Unity4camewithmorethaneightydifferentbuilt-inshadersdividedinsixdifferentfamilies(Normal,Transparent,TransparentCutout,Self-Illuminated,andReflective).InUnity5,theyareallreplacedbytheStandardShaderintroducedinthepreviousrecipe.Unfortunately,thereisnomagicrecipetoconvertyourshadersdirectly.However,youcanusethefollowingtableasastartingpointtounderstandhowtheStandardShadercanbeconfiguredtosimulateUnity4LegacyShaders:
Shader Unity4 Unity4(Legacy) Unity5
DiffuseDiffuse
Lambert
LegacyShader/Diffuse
Lambert
Standard
Physically-basedrendering:MetallicWorkflow
SpecularSpecular
Blinn-Phong
LegacyShader/Specular
Blinn-Phong
Standard(Specularsetup)
Physically-basedrendering:SpecularWorkflow
Transparent
TransparentVertex-Lit LegacyShader/TransparentVertex-LitStandard
RenderingMode:Transparent
TransparentCutoutVertex-Lit
LegacyShader/TransparentCutoutVertex-Lit
Standard
RenderingMode:Cutout
YoucanchangetheshaderusedbyyouroldmaterialusingtheShaderdrop-downmenuinInspector.AllyouneedtodoissimplyselecttheappropriateStandardShader.Ifyouroldshaderusedtextures,colours,andnormalmaps,theywillbeautomaticallyusedinthenewStandardShader.YoumightstillhavetoconfiguretheparametersoftheStandardShadertogetasclosetoyouroriginallightingmodelaspossible.ThefollowingpictureshowstheubiquitousStanfordbunnyrenderedwithaLegacyDiffuseShader(right),convertedStandardShader(left),andStandardShaderwithSmoothnesssettozero(middle):
MigratingcustomshadersIfyouhavewrittencustomshadersinUnity4,chancesarethatthiswillworkstraightawayinUnity5.Despitethis,Unityhasmadesomeminorchangesinthewayshaderswork,whichcancausebotherrorsandinconsistencies.Themostrelevantandimportantoneistheintensityofthelight.LightsinUnity5aretwiceasbright.AlltheLegacyShadershavebeenrewrittentotakethisintoaccount;ifyouhaveupgradedyourshadersorswitchedtoStandardShaders,youwillnotnoticeanydifference.Ifyouhavewrittenyourownlightingmodel,youwillhavetobesurethattheintensityofthelightisnotmultipliedbytwoanymore.Thefollowingcodeisusedtoensurethis:
//Unity4
c.rgb=s.Albedo*_LightColor0.rgb*(diff*atten*2);
//Unity5
c.rgb=s.Albedo*_LightColor0.rgb*(diff*atten);
Ifyouhaven’twrittenashaderyet,don’tpanic:lightingmodelswillbeextensivelyexplainedinChapter3,UnderstandingLightingModels.
NoteThereareseveralotherchangesinthewayUnity5handlesshaderscomparedtoUnity4.YoucanseealloftheminShadersinUnity5.0at
http://docs.unity3d.com/Manual/UpgradeGuide5-Shaders.html.
Howitworks…Writingshadersisalwaysatrade-offbetweenrealismandefficiency;realisticshadersrequireintensivecomputation,potentiallyintroducingasignificantlag.It’simportanttouseonlythoseeffectsthatarestrictlyrequired:ifamaterialdoesnotneedspecularreflections,thenthereisnoneedtouseashaderthatcalculatesthem.ThishasbeenthemainreasonwhyUnity4hasbeenshippedwithsomanydifferentshaders.ThenewStandardShaderofUnity5canpotentiallyreplaceallofthepreviousshadersasitincorporatesnormalmapping,transparency,andreflection.However,ithasbeencleverlyoptimizedsothatonlytheeffectsthatarereallynecessaryarecalculated.Ifyourstandardmaterialdoesnothavereflections,theywillnotbecalculated.
Despitethis,theStandardShaderismainlydesignedforrealisticmaterials.TheLegacyDiffuseandSpecularshaders,incomparison,werenotreallydesignedforrealisticmaterials.ThisisthereasonswitchingfromLegacytoStandardShaderswillmostlyintroduceslightchangesinthewayyourobjectsarerendered.
SeealsoChapter3,UnderstandingLightingModels,exploresin-depthhowtheDiffuseandSpecularshaderswork.EvenifdeprecatedinUnity5,understandingthemisessentialifyouwanttodesignnewlightingmodels.Chapter4,PhysicallyBasedRenderinginUnity5,willshowyouhowtounlockthepotentialoftheStandardShaderinUnity5.
AddingpropertiestoashaderPropertiesofashaderareveryimportantfortheshaderpipelineastheyarethemethodthatyouusetolettheartistoruseroftheshaderassigntexturesandtweakyourshadervalues.PropertiesallowyoutoexposeGUIelementsinamaterial’sInspectortabwithoutyouhavingtouseaseparateeditor,whichprovidesvisualwaystotweakashader.
WithyourshaderopenedinMonoDevelop,lookattheblockoflines2through7.ThisiscalledthePropertiesblock.Currently,itwillhaveonepropertyinitcalled_MainTex.Ifyoulookatyourmaterialthathasthisshaderappliedtoit,youwillnoticethatthereisonetextureGUIelementintheInspectortab.TheselinesofcodeinourshaderarecreatingthisGUIelementforus.
Again,Unityhasmadethisprocessveryefficientintermsofcodingandtheamountoftimeittakestoiteratethroughchangingyourproperties.
GettingreadyLet’sseehowthisworksinourcurrentshadercalledStandardDiffuse,bycreatingourownpropertiesandlearningmoreaboutthesyntaxinvolved.Forthisexample,wewillrefittheshaderpreviouslycreated.Insteadofusingatexture,itwillonlyuseitscolorandsomeotherpropertiesthatwewillbeabletochangedirectlyfromtheInspectortab.StartbyduplicatingtheStandardDiffuseshader.YoucandothisbyselectingitintheInspectortabandpressingCtrl+D.ThiswillcreateacopycalledStandardDiffuse2.
NoteYoucangiveafriendliernametoyourshaderinitsfirstline.Forinstance,Shader"CookbookShaders/StandardDiffuse"tellsUnitytocallthisStandardDiffuseshaderandmoveittoagroupcalledCookbookShaders.IfyouduplicateashaderusingCtrl+D,yournewfilewillsharethesamename.Toavoidconfusion,makesuretochangethefirstlineofeachnewshadersothatitusesauniquealias.
Howtodoit…OncetheStandardDiffuse2shaderisready,wecanstartchangingitsproperties:
1. InourPropertiesblockofourshader,removethecurrentpropertybydeletingthefollowingcodefromourcurrentshader:
_MainTex("Albedo(RGB)",2D)="white"{}
2. Aswehaveremovedanessentialproperty,thisshaderwillnotcompileuntiltheotherreferencesto_MainTexareremoved.Let’sremovethisotherline:
sampler2D_MainTex;
3. Theoriginalshaderused_MainTextocolorthemodel.Let’schangethisbyreplacingthefirstlineofcodeofthesurf()functionwiththis:
fixed4c=_Color;
4. WhenyousaveandreturntoUnity,theshaderwillcompile,andyouwillseethatnowourmaterial’sInspectortabdoesn’thaveatextureswatchanymore.Tocompletetherefitofthisshader,let’saddonemorepropertyandseewhathappens.Enterthefollowingcode:
_AmbientColor("AmbientColor",Color)=(1,1,1,1)
5. Wehaveaddedanothercolorswatchtothematerial’sInspectortab.Nowlet’saddonemoretogetafeelforotherkindsofpropertiesthatwecancreate.AddthefollowingcodetothePropertiesblock:
_MySliderValue("ThisisaSlider",Range(0,10))=2.5
6. WehavenowcreatedanotherGUIelementthatallowsustovisuallyinteractwithourshader.Thistime,wecreatedasliderwiththenameThisisaSlider,asshowninthefollowingscreenshot:
Propertiesallowyoutocreateavisualwaytotweakshaderswithouthavingtochangevaluesintheshadercodeitself.Thenextrecipewillshowyouhowthesepropertiescanactuallybeusedtocreateamoreinterestingshader.
Note
Whilepropertiesbelongtoshaders,thevaluesassociatedwiththemarestoredinmaterials.Thesameshadercanbesafelysharedbetweenmanydifferentmaterials.Ontheotherhand,changingthepropertyofamaterialwillaffectthelookofalltheobjectsthatarecurrentlyusingit.
Howitworks…EveryUnityshaderhasabuilt-instructurethatitislookingforinitscode.ThePropertiesblockisoneofthosefunctionsthatareexpectedbyUnity.Thereasonbehindthisistogiveyou,theshaderprogrammer,ameansofquicklycreatingGUIelementsthattiedirectlyintoyourshadercode.ThesepropertiesthatyoudeclareinthePropertiesblockcanthenbeusedinyourshadercodetochangevalues,colors,andtextures.Thesyntaxtodefineapropertyisasfollows:
Let’stakealookatwhatisgoingonunderthehoodhere.Whenyoufirststartwritinganewproperty,youwillneedtogiveitaVariableName.ThevariablenameisgoingtobethenamethatyourshadercodeisgoingtouseinordertogetthevaluefromtheGUIelement.Thissavesusalotoftimebecausewedon’thavetosetupthissystemourselves.
ThenextelementsofapropertyaretheInspectorGUINameandTypeoftheproperty,whichiscontainedwithinparentheses.TheInspectorGUINameisthenamethatisgoingtoappearinthematerial’sInspectortabwhentheuserisinteractingwithandtweakingtheshader.TheTypeisthetypeofdatathatthispropertyisgoingtocontrol.TherearemanytypesthatwecandefineforpropertiesinsideofUnityshaders.Thefollowingtabledescribesthetypesofvariablesthatwecanhaveinourshaders:
SurfaceShaderpropertytypes
Range(min,
max)Thiscreatesafloatpropertyasasliderfromtheminimumvaluetothemaximumvalue
ColorThiscreatesacolorswatchintheInspectortabthatopensupacolorpicker=(float,float,float,float)
2D Thiscreatesatextureswatchthatallowsausertodragatextureintheshader
Rect Thiscreatesanon-power-of-2textureswatchandfunctionsthesameasthe2DGUIelement
CubeThiscreatesacubemapswatchintheInspectortabandallowsausertodraganddropacubemapintheshader
Float ThiscreatesafloatvalueintheInspectortabbutwithoutaslider
Vector Thiscreatesafour-floatpropertythatallowsyoutocreatedirectionsorcolors
Finally,thereistheDefaultValue.Thissimplysetsthevalueofthispropertytothevaluethatyouplaceinthecode.So,inthepreviousexampleimage,thedefaultvalueforthe
propertynamed_AmbientColor,whichisoftheColortype,issettoavalueof1,1,1,1.AsthisisacolorpropertyexpectingacolorthatisRGBAorfloat4orr,g,b,a=x,y,z,w,thiscolorproperty,whenfirstcreated,issettowhite.
SeealsoThepropertiesaredocumentedintheUnitymanualathttp://docs.unity3d.com/Documentation/Components/SL-Properties.html.
UsingpropertiesinaSurfaceShaderNowthatwehavecreatedsomeproperties,let’sactuallyhookthemuptotheshadersothatwecanusethemastweakstoourshaderandmakethematerialprocessmuchmoreinteractive.
Wecanusetheproperties’valuesfromthematerial’sInspectortabbecausewehaveattachedavariablenametothepropertyitself,butintheshadercode,youhavetosetupacoupleofthingsbeforeyoucanstartcallingthevaluebyitsvariablename.
Howtodoit…ThefollowingstepsshowyouhowtousethepropertiesinaSurfaceShader:
1. Tobegin,let’sremovethefollowinglinesofcode,aswedeletedthepropertycalled_MainTexintheCreatingabasicStandardShaderrecipeofthischapter:
_MainTex("Albedo(RGB)",2D)="white"{}
sampler2D_MainTex;
fixed4c=tex2D(_MainTex,IN.uv_MainTex)*_Color;
2. Next,addthefollowinglinesofcodetotheshader,belowtheCGPROGRAMline:
float4_AmbientColor;
float_MySliderValue;
3. Withstep2complete,wecannowusethevaluesfromthepropertiesinourshader.Let’sdothisbyaddingthevaluefromthe_Colorpropertytothe_AmbientColorpropertyandgivingtheresultofthistotheo.Albedolineofcode.So,let’saddthefollowingcodetotheshaderinthesurf()function:
voidsurf(InputIN,inoutSurfaceOutputStandardo){
fixed4c=pow((_Color+_AmbientColor),_MySliderValue);
o.Albedo=c.rgb;
o.Metallic=_Metallic;
o.Smoothness=_Glossiness;
o.Alpha=c.a;
}
4. Finally,yourshadershouldlooklikethefollowingshadercode.IfyousaveyourshaderinMonoDevelopandre-enterUnity,yourshaderwillcompile.Iftherewerenoerrors,youwillnowhavetheabilitytochangetheambientandemissivecolorsofthematerialaswellasincreasethesaturationofthefinalcolorusingtheslidervalue.Prettyneat,huh!
Shader"CookbookShaders/StandardDiffuse3"{
//WedefinePropertiesinthepropertiesblock
Properties{
_Color("Color",Color)=(1,1,1,1)
_AmbientColor("AmbientColor",Color)=(1,1,1,1)
_MySliderValue("ThisisaSlider",Range(0,10))=2.5
}
SubShader{
Tags{"RenderType"="Opaque"}
LOD200
//Weneedtodeclarethepropertiesvariabletypeinsideofthe
//CGPROGRAMsowecanaccessitsvaluefromthepropertiesblock.
CGPROGRAM
#pragmasurfacesurfStandardfullforwardshadows
#pragmatarget3.0
structInput{
float2uv_MainTex;
};
fixed4_Color;
float4_AmbientColor;
float_MySliderValue;
voidsurf(InputIN,inoutSurfaceOutputStandardo){
//Wecanthenusethepropertiesvaluesinourshader
fixed4c=pow((_Color+_AmbientColor),_MySliderValue);
o.Albedo=c.rgb;
o.Alpha=c.a;
}
ENDCG
}
FallBack"Diffuse"
}
TipDownloadingtheexamplecode
YoucandownloadtheexamplecodefilesforallPacktbooksyouhavepurchasedfromyouraccountathttp://www.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.
NoteThepow(arg1,arg2)functionisabuilt-infunctionthatwillperformtheequivalentmathfunctionofpower.So,argument1isthevaluethatwewanttoraisetoapowerandargument2isthepowerthatwewanttoraiseitto.
Tofindoutmoreaboutthepow()function,lookattheCgtutorial.ItisagreatfreeresourcethatyoucanusetolearnmoreaboutshadingandgetaglossaryofallthefunctionsavailabletoyouintheCgshadinglanguage:
http://http.developer.nvidia.com/CgTutorial/cg_tutorial_appendix_e.html
Thefollowingscreenshotdemonstratestheresultobtainedusingourpropertiestocontrolourmaterial’scolorsandsaturationfromwithinthematerial’sInspectortab:
Howitworks…WhenyoudeclareanewpropertyinthePropertiesblock,youareprovidingawayfortheshadertoretrievethetweakedvaluefromthematerial’sInspectortab.Thisvalueisstoredinthevariablenameportionoftheproperty.Inthiscase,_AmbientColor,_Color,and_MySliderValuearethevariablesinwhichwearestoringthetweakedvalues.InorderforyoutobeabletousethevalueintheSubShader{}block,youneedtocreatethreenewvariableswiththesamenamesastheproperty’svariablename.Thisautomaticallysetsupalinkbetweenthesetwosothattheyknowtheyhavetoworkwiththesamedata.Additionally,itdeclaresthetypeofdatathatwewanttostoreinoursubshadervariables,whichwillcomeinhandywhenwelookatoptimizingshadersinalaterchapter.
Onceyouhavecreatedthesubshadervariables,youcanthenusethevaluesinthesurf()function.Inthiscase,wewanttoaddthe_Colorand_AmbientColorvariablestogetherandtakeittoapowerofwhateverthe_MySliderValuevariableisequaltointhematerial’sInspectortab.
ThevastmajorityofshadersstartoutasStandardShadersandgetmodifieduntiltheymatchthedesiredlook.WehavenowcreatedthefoundationforanySurfaceShaderyouwillcreatethatrequiresadiffusecomponent.
NoteMaterialsareassets.Thismeansthatanychangemadetothemwhileyourgameisrunningintheeditorarepermanent.Ifyouhavechangedthevalueofapropertybymistake,youcanundoitusingCtrl+Z.
There’smore…Likeanyotherprogramminglanguage,Cgdoesnotallowmistakes.Assuch,yourshaderwillnotworkifyouhaveatypoinyourcode.Whenthishappens,yourmaterialsarerenderedinunshadedmagenta:
Whenascriptdoesnotcompile,Unitypreventsyourgamefrombeingexportedorevenexecuted.Conversely,errorsinshadersdonotstopyourgamefrombeingexecuted.
Ifoneofyourshadersappearsasmagenta,itistimetoinvestigatewheretheproblemis.Ifyouselecttheincriminatedshader,youwillseealistoferrorsdisplayedinitsInspectortab:
Despiteshowingthelinethatraisedtheerror,itrarelymeansthatthisisthelinethathastobefixed.Theerrormessageshowninthepreviousimageisgeneratedbydeletingthesampler2D_MainTexvariablefromtheSubShader{}block.However,theerrorisraisedbythefirstlinethattriestoaccesssuchavariable.
Findingandfixingwhat’swrongwithcodeisaprocesscalleddebugging.Themostcommonmistakesthatyoushouldcheckforareasfollows:
Amissingbracket.Ifyouforgottoaddacurlybrackettocloseasection,thecompilerislikelytoraiseerrorsattheendofthedocument,atthebeginning,oranewsection.Amissingsemicolon.Oneofthemostcommonmistakesbutluckilyoneoftheeasiesttospotandfix.Errorsareoftenraisedbythefollowingline.ApropertythathasbeendefinedinthePropertiessectionbuthasnotbeencoupledwithavariableintheSubShader{}block.ConverselytowhatyoumightbeusedtoinC#scripts,floatingpointvaluesinCgdonotneedtothefollowedbyanf:it’s1.0,not1.0f.
Theerrormessagesraisedbyshaderscanbeverymisleading,especiallyduetotheirstrictsyntacticconstraints.Ifyouareindoubtabouttheirmeaning,it’sbesttosearchtheInternet.Unityforumsarefilledwithotherdeveloperswhoarelikelytohaveencountered(andfixed)yourproblembefore.
SeealsoMoreinformationonhowtomasterSurfaceShadersandtheirpropertiescanbefoundinChapter2,SurfaceShadersandTextureMapping.Ifyouarecurioustoseewhatshaderscanactuallydowhenusedattheirfullpotential,havealookatChapter10,AdvancedShadingTechniques,forsomeofthemostadvancedtechniquescoveredinthisbook.
Chapter2.SurfaceShadersandTextureMappingInthischapter,wewillexploreSurfaceShaders.Wewillstartfromaverysimplemattematerialandendwithholographicprojectionsandadvancedterrainsblending.Wecanalsousetexturestoanimate,blend,anddriveanyotherpropertythatwewant.Inthischapter,youwilllearnaboutthefollowingmethods:
DiffuseshadingUsingpackedarraysAddingatexturetoashaderScrollingtexturesbymodifyingUVvaluesNormalmappingCreatingatransparentmaterialCreatingaHolographicShaderPackingandblendingtexturesCreatingacirclearoundyourterrain
IntroductionSurfaceShadershavebeenintroducedinChapter1,CreatingYourFirstShader,asthemaintypeofshaderusedinUnity.Thischapterwillshowindetailwhattheseactuallyareandhowtheywork.Generallyspeaking,therearetwoessentialstepsineverySurfaceShader.First,youhavetospecifycertainphysicalpropertiesofthematerialthatyouwanttodescribe,suchasitsdiffusecolor,smoothness,andtransparency.Thesepropertiesareinitializedinafunctioncalledsurfacefunctionandstoredinastructurecalledsurfaceoutput.Secondly,thesurfaceoutputispassedtoalightingmodel.Thisisaspecialfunctionthatwillalsotakeinformationaboutthenearbylightsinthescene.Boththeseparametersarethenusedtocalculatethefinalcolorforeachpixelofyourmodel.Thelightingfunctioniswheretherealcalculationsofashadertakeplaceasit’sthepieceofcodethatdetermineshowlightshouldbehavewhenittouchesamaterial.
ThefollowingdiagramlooselysummarizeshowaSurfaceShaderworks.CustomlightingmodelswillbeexploredinChapter3,UnderstandingLightingModels,whileChapter5,VertexFunctions,willfocusonvertexmodifiers:
DiffuseshadingBeforestartingourjourneyintotexturemapping,itisimportanttounderstandhowdiffusematerialswork.Certainobjectsmighthaveauniformcolorandsmoothsurface,butnotsmoothenoughtoshineonreflectedlight.ThesemattematerialsarebestrepresentedwithaDiffuseshader.Whileintherealworld,purediffusematerialsdonotexist;Diffuseshadersarerelativelycheaptoimplementandfindalargeapplicationingameswithlow-polyaesthetics.
GettingreadyThereareseveralwaysinwhichyoucancreateyourownDiffuseshader.AquickwayistostartwiththeStandardShaderinUnity5andeditittoremoveanytexture,similarlytowhatwaspreviouslydoneinChapter1,CreatingYourFirstShader.
Howtodoit…Let’sstartwithourStandardShader,andapplythefollowingchanges:
1. Removeallthepropertiesexcept_Color:
_Color("Color",Color)=(1,1,1,1)
2. FromtheSubShader{}section,removethe_MainTex,_Glossiness,and_Metallicvariables.Youshouldnotremovethereferencetouv_MainTexasCgdoesnotallowtheInputstructtobeempty.Thevaluewillbesimplyignored.
3. Removethecontentofthesurf()functionandreplaceitwiththefollowing:
o.Albedo=_Color.rgb;
4. Yourshadershouldlookasfollows:
Shader"CookbookShaders/Diffuse"{
Properties{
_Color("Color",Color)=(1,1,1,1)
}
SubShader{
Tags{"RenderType"="Opaque"}
LOD200
CGPROGRAM
#pragmasurfacesurfStandardfullforwardshadows
#pragmatarget3.0
structInput{
float2uv_MainTex;
};
fixed4_Color;
voidsurf(InputIN,inoutSurfaceOutputStandardo){
o.Albedo=_Color.rgb;
}
ENDCG
}
FallBack"Diffuse"
}
AsthisshaderhasbeenrefittedfromaStandardShader,itwillusephysically-basedrenderingtosimulatehowlightbehavesonyourmodels.Ifyouaretryingtoachieveanon-photorealisticlook,youcanchangethefirst#pragmadirectivesothatitusesLambertratherthanStandard.Ifyoudoso,youshouldalsoreplaceSurfaceOutputStandardwithSurfaceOutput.
Howitworks…Thewayshadersallowyoutocommunicatetherenderingpropertiesofyourmaterialtotheirlightingmodelisviaasurfaceoutput.Itisbasicallyawrapperaroundalltheparametersthatthecurrentlightingmodelneeds.Itshouldnotsurpriseyouthatdifferentlightingmodelshavedifferentsurfaceoutputstructs.ThefollowingtableshowsthethreemainoutputstructsusedinUnity5andhowtheycanbeused:
Typeofshaders Unity4 Unity5
DiffuseAnySurfaceShaderSurfaceOutput
StandardSurfaceOutputStandard
SpecularAnySurfaceShaderSurfaceOutput
Standard(Specularsetup)SurfaceOutputStandardSpecular
TheSurfaceOutputstructhasthefollowingproperties:
fixed3Albedo;:Thisisthediffusecolorofthematerialfixed3Normal;:Thisisthetangentspacenormal,ifwrittenfixed3Emission;:Thisisthecolorofthelightemittedbythematerial(thispropertyisdeclaredashalf3intheStandardShaders)fixedAlpha;:ThisisthetransparencyofthematerialhalfSpecular;:Thisisthespecularpowerfrom0to1fixedGloss;:Thisisthespecularintensity
TheSurfaceOutputStandardstructhasthefollowingproperties:
fixed3Albedo;:Thisisthebasecolorofthematerial(whetherit’sdiffuseorspecular)fixed3Normal;
half3Emission;:Thispropertyisdeclaredashalf3,whileitwasdefinedasfixed3inSurfaceOutputfixedAlpha;
halfOcclusion;:Thisistheocclusion(default1)halfSmoothness;:Thisisthesmoothness(0=rough,1=smooth)halfMetallic;:0=non-metal,1=metal
TheSurfaceOutputStandardSpecularstructhasthefollowingproperties:
fixed3Albedo;.fixed3Normal;.half3Emission;.fixedAlpha;.halfOcclusion;.halfSmoothness;.fixed3Specular;:Thisisthespecularcolor.ThisisverydifferentfromtheSpecularpropertyinSurfaceOutputasitallowsspecifyingacolorratherthana
singlevalue.
UsingaSurfaceShadercorrectlyisamatterofinitializingthesurfaceoutputwiththecorrectvalues.
UsingpackedarraysLooselyspeaking,thecodeinsideashaderhastobeexecutedforatleasteverypixelinyourscreen.ThisisthereasonwhyGPUsarehighlyoptimizedforparallelcomputing.ThisphilosophyisalsoevidentinthestandardtypeofvariablesandoperatorsavailableinCg.Understandingthemisessentialnotjusttouseshaderscorrectly,butalsotowritehighlyoptimizedones.
Howtodoit…TherearetwotypesofvariablesinCg:singlevaluesandpackedarrays.Thelattercanbeidentifiedbecausetheirtypeendswithanumbersuchasfloat3orint4.Astheirnamessuggest,thesetypesofvariablesaresimilartostructs,whichmeansthattheyeachcontainseveralsinglevalues.Cgcallsthempackedarrays,thoughtheyarenotexactlyarraysinthetraditionalsense.
Theelementsofapackedarraycanbeaccessedasanormalstruct.Theyaretypicallycalledx,y,z,andw.However,Cgalsoprovidesyouwithanotheraliasforthem,thatis,r,g,b,anda.Despitetherebeingnodifferencebetweenusingxorr,itcanmakeahugedifferenceforthereaders.Shadercoding,infact,ofteninvolvescalculationwithpositionsandcolors.YoumighthaveseenthisintheStandardShaders:
o.Alpha=_Color.a;
Here,owasastructand_Colorwasapackedarray.ThisisalsowhyCgprohibitsthemixedusageofthesetwosyntaxes:youcannotuse_Color.xgz.
ThereisalsoanotherimportantfeatureofpackedarraysthathasnoequivalentinC#:swizzling.Cgallowsaddressingandreorderingelementswithinpackedarraysinjustasingleline.Onceagain,thisappearsintheStandardShader:
o.Albedo=_Color.rgb;
Albedoisfixed3,whichmeansthatitcontainsthreevaluesofthefixedtype.However,_Colorisdefinedasfixed4.Adirectassignmentwouldresultinacompilererroras_ColorisbiggerthanAlbedo.TheC#wayofdoingthiswouldbeasfollows:
o.Albedo.r=_Color.r;
o.Albedo.g=_Color.g;
o.Albedo.b=_Color.b;
However,itcanbecompressedinCg:
o.Albedo=_Color.rgb;
Cgalsoallowsreorderingelements,forinstance,using_Color.bgrtoswaptheredandbluechannels.
Lastly,whenasinglevalueisassignedtoapackedarray,itiscopiedtoallofitsfields:
o.Albedo=0;//Black=(0,0,0)
o.Albedo=1;//White=(1,1,1)
Thisisreferredtoassmearing.
Swizzlingcanalsobeusedontheleft-handsideofanexpression,allowingonlycertaincomponentsofapackedarraytobeoverwritten:
o.Albedo.rg=_Color.rg;
Inwhichcase,itiscalledmasking.
PackedmatricesWhereswizzlingreallyshowsitsfullpotentialiswhenappliedtopackedmatrices.Cgallowstypessuchasfloat4x4,whichrepresentsamatrixoffloatswithfourrowsandfourcolumns.Youcanaccessasingleelementofthematrixusingthe_mRCnotation,whereRistherowandCisthecolumn:
float4x4matrix;
//...
floatfirst=matrix._m00;
floatlast=matrix._m33;
The_mRCnotationcanalsobechained:
float4diagonal=matrix._m00_m11_m22_m33;
Anentirerowcanbeselectedusingsquaredbrackets:
float4firstRow=matrix[0];
//Equivalentto
float4firstRow=matrix._m00_m01_m02_m03;
SeealsoPackedarraysareoneofthenicestfeaturesofCg.Youcandiscovermoreaboutthemhere:
http://http.developer.nvidia.com/CgTutorial/cg_tutorial_chapter02.html
AddingatexturetoashaderTexturescanbringourshaderstolifeveryquicklyintermsofachievingveryrealisticeffects.Inordertoeffectivelyusetextures,weneedtounderstandhowa2Dimageismappedtoa3Dmodel.Thisprocessiscalledtexturemapping,anditrequiressomeworktobedoneontheshaderand3Dmodelthatwewanttouse.Models,infact,aremadeoutoftriangles;eachvertexcanstoredatathatshaderscanaccess.OneofthemostimportantinformationstoredinverticesistheUVdata.Itconsistsoftwocoordinates,UandV,rangingfrom0to1.TheyrepresenttheXYpositionofthepixelinthe2Dimagethatwillbemappedtothevertices.UVdataispresentonlyforvertices;whentheinnerpointsofatrianglehavetobetexture-mapped,theGPUinterpolatestheclosestUVvaluestofindtherightpixelinthetexturetobeused.Thefollowingimageshowsyouhowa2Dtextureismappedtoatrianglefroma3Dmodel:
TheUVdataisstoredinthe3Dmodelandrequiresamodelingsoftwaretobeedited.SomemodelslacktheUVcomponent,hencetheycannotsupporttexturemapping.TheStanfordbunny,forexample,wasnotoriginallyprovidedwithone.
GettingreadyForthisrecipe,you’llneeda3DmodelwithUVdataanditstexture.TheybothneedtobeimportedtoUnitybeforestarting.Youcandothissimplybydraggingthemtotheeditor.AstheStandardShadersupportstexturemappingbydefault,we’llusethisandthenexplainindetailhowitworks.
Howtodoit…AddingatexturetoyourmodelusingtheStandardShaderisincrediblysimple,asfollows:
1. CreateanewStandardShadercalledTexturedShader.2. CreateanewmaterialcalledTexturedMaterial.3. Assigntheshadertothematerialbydraggingoverit.4. Afterselectingthematerial,dragyourtexturetotheemptyrectanglecalledAlbedo
(RGB).Ifyouhavefollowedallthesestepscorrectly,yourmaterialInspectortabshouldlooklikethis:
TheStandardShaderknowshowtomapa2Dimagetoa3DmodelusingitsUVdata.
Howitworks…WhentheStandardShaderisusedfromtheinspectorofamaterial,theprocessbehindtexturemappingiscompletelytransparenttodevelopers.Ifwewanttounderstandhowitworks,it’snecessarytotakeacloserlookatTexturedShader.FromthePropertiessection,wecanseethattheAlbedo(RGB)textureisactuallyreferredtointhecodeas_MainTex:
_MainTex("Albedo(RGB)",2D)="white"{}
IntheCGPROGRAMsection,thistextureisdefinedassampler2D,thestandardtypefor2Dtextures:
sampler2D_MainTex;
ThenextlineshowsastructcalledInput.Thisistheinputparameterforthesurfacefunctionandcontainsapackedarraycalleduv_MainTex:
structInput{
float2uv_MainTex;
};
Everytimethesurf()surfacefunctioniscalled,theInputstructurewillcontaintheUVof_MainTexforthespecificpointofthe3Dmodelthatneedstoberendered.TheStandardShaderrecognizesthatthenameuv_MainTexrefersto_MainTexandinitializesitautomatically.IfyouareinterestedinunderstandinghowtheUVisactuallymappedfroma3Dspacetoa2Dtexture,youcancheckChapter3,UnderstandingLightingModels.
Finally,theUVdataisusedtosamplethetextureinthefirstlineofthesurfacefunction:
fixed4c=tex2D(_MainTex,IN.uv_MainTex)*_Color;
Thisisdoneusingthetex2D()functionofCg;ittakesatextureandUVandreturnsthecolorofthepixelatthatposition.
NoteTheUandVcoordinatesgofrom0to1,where(0,0)and(1,1)correspondtotwooppositecorners.DifferentimplementationsassociateUVwithdifferentcorners;ifyourtexturehappenstoappearreversed,tryinvertingtheVcomponent.
There’smore…WhenyouimportatexturetoUnity,youaresettingupsomeofthepropertiesthatsampler2Dwilluse.ThemostimportantistheFiltermode,whichdetermineshowcolorsareinterpolatedwhenthetextureissampled.ItisveryunlikelythattheUVdatawillpointexactlytothecenterofapixel;inalltheothercases,youmightwanttointerpolatebetweentheclosestpixelstogetamoreuniformcolor.ThefollowingisthescreenshotoftheInspectortabofanexampletexture:
Formostapplications,Bilinearprovidesaninexpensiveyeteffectivewaytosmooththetexture.Ifyouarecreatinga2Dgame,however,Bilinearmightproduceblurredtiles.Inthiscase,youcanusePointtoremoveanyinterpolationfromthetexturesampling.
Whenatextureisseenfromasteepangle,texturesamplingislikelytoproducevisuallyunpleasantartifacts.YoucanreducethembysettingAnisoLeveltoahighervalue.Thisisparticularusefulforfloorandceilingtextures,whereglitchescanbreaktheillusionofcontinuity.
SeealsoIfyouwouldliketoknowmoreabouttheinnerworkingofhowtexturesaremappedtoa3Dsurface,youcanreadtheinformationavailableathttp://http.developer.nvidia.com/CgTutorial/cg_tutorial_chapter03.html.
Foracompletelistoftheoptionsavailablewhenimportinga2Dtexture,youcanrefertothefollowingwebsite:
http://docs.unity3d.com/Manual/class-TextureImporter.html
ScrollingtexturesbymodifyingUVvaluesOneofthemostcommontexturetechniquesusedintoday’sgameindustryistheprocessofallowingyoutoscrollthetexturesoverthesurfaceofanobject.Thisallowsyoutocreateeffectssuchaswaterfalls,rivers,lavaflows,andsoon.It’salsoatechniquethatisthebasistocreateanimatedspriteeffects,butwewillcoverthisinasubsequentrecipeofthischapter.Let’sfirstseehowwewillcreateasimplescrollingeffectinaSurfaceShader.
GettingreadyTobeginthisrecipe,youwillneedtocreateanewshaderfileandmaterial.Thiswillsetusupwithanicecleanshaderthatwecanusetostudythescrollingeffectbyitself.
Howtodoit…Tobeginwith,wewilllaunchournewshaderfilethatwejustcreatedandenterthecodementionedinthefollowingsteps:
1. Theshaderwillneedtwonewpropertiesthatwillallowustocontrolthespeedofthetexturescrolling.So,let’saddaspeedpropertyfortheXdirectionandaspeedpropertyfortheYdirection,asshowninthefollowingcode:
Properties{
_MainTint("DiffuseTint",Color)=(1,1,1,1)
_MainTex("Base(RGB)",2D)="white"{}
_ScrollXSpeed("XScrollSpeed","Range(0,10))=2
_ScrollYSpeed("YScrollSpeed","Range(0,10))=2
}
2. ModifytheCgpropertiesintheCGPROGRAMsectionandcreatenewvariablessothatwecanaccessthevaluesfromourproperties:
fixed4_MainTint;
fixed_ScrollXSpeed;
fixed_ScrollYSpeed;
sampler2D_MainTex;
3. ModifythesurfacefunctiontochangetheUVsgiventothetex2D()function.Then,usethebuilt-in_TimevariabletoanimatetheUVsovertimewhentheplaybuttonispressedintheeditor:
voidsurf(InputIN,inoutSurfaceOutputo)
{
//CreateaseparatevariabletostoreourUVs
//beforewepassthemtothetex2D()function
fixed2scrolledUV=IN.uv_MainTex;
//Createvariablesthatstoretheindividualxandy
//componentsfortheUV'sscaledbytime
fixedxScrollValue=_ScrollXSpeed*_Time;
fixedyScrollValue=_ScrollYSpeed*_Time;
//ApplythefinalUVoffset
scrolledUV+=fixed2(xScrollValue,yScrollValue);
//Applytexturesandtint
half4c=tex2D(_MainTex,scrolledUV);
o.Albedo=c.rgb*_MainTint;
o.Alpha=c.a;
}
ThefollowingimagedemonstratestheresultofutilizingthescrollingUVsystemtocreateasimplerivermotionforyourenvironments.YoucannoticethiseffectinthescenecalledScrollingUVsfromthecodefilesprovidedwiththisbook:
Howitworks…Thescrollingsystemstartswiththedeclarationofacoupleofproperties,whichwillallowtheuserofthisshadertoincreaseordecreasethespeedofthescrollingeffectitself.Attheircore,theyarefloatvaluesbeingpassedfromthematerial’sInspectortabtothesurfacefunctionoftheshader.Formoreinformationonshaderproperties,seeChapter1,CreatingYourFirstShader.
Oncewehavethesefloatvaluesfromthematerial’sInspectortab,wecanusethemtooffsetourUVvaluesintheshader.
Tobeginthisprocess,wefirststoretheUVsinaseparatevariablecalledscrolledUV.Thisvariablehastobefloat2/fixed2becausetheUVvaluesarebeingpassedtousfromtheInputstructure:
structInput
{
float2uv_MainTex;
}
Oncewehaveaccesstothemesh’sUVs,wecanoffsetthemusingourscrollspeedvariablesandbuilt-in_Timevariable.Thisbuilt-invariablereturnsavariableofthefloat4type,meaningthateachcomponentofthisvariablecontainsdifferentvaluesoftimeasitpertainstogametime.
Acompletedescriptionoftheseindividualtimevaluesaredescribedatthefollowinglink:http://docs.unity3d.com/Manual/SL-UnityShaderVariables.html
This_TimevariablewillgiveusanincrementedfloatvaluebasedonUnity’sgametimeclock.So,wecanusethisvaluetomoveourUVsinaUVdirectionandscalethattimewithourscrollspeedvariables:
//Createvariablesthatstoretheindividualxandy
//componentsfortheuv'sscaledbytime
fixedxScrollValue=_ScrollXSpeed*_Time;
fixedyScrollValue=_ScrollYSpeed*_Time;
Withthecorrectoffsetbeingcalculatedbytime,wecanaddthenewoffsetvaluebacktotheoriginalUVposition.Thisiswhyweareusingthe+=operatorinthenextline.WewanttotaketheoriginalUVposition,addthenewoffsetvalue,andthenpassthistothetex2D()functionasthetexture’snewUVs.Thiscreatestheeffectofthetexturemovingonthesurface.WearereallymanipulatingtheUVs,sowearefakingtheeffectofthetexturemoving:
scrolledUV+=fixed2(xScrollValue,yScrollValue);
half4c=tex2D(_MainTex,scrolledUV);
NormalmappingEverytriangleofa3Dmodelhasafacingdirection,whichisthedirectionthatitispointingtoward.Itisoftenrepresentedwithanarrowplacedinthecenterofthetriangleandorthogonaltothesurface.Thefacingdirectionplaysanimportantroleinthewaylightreflectsonasurface.Iftwoadjacenttrianglesfacedifferentdirections,theywillreflectlightsatdifferentangles,hencethey’llbeshadeddifferently.Forcurvedobjects,thisisaproblem:itisobviousthatthegeometryismadeoutofflattriangles.
Toavoidthisproblem,thewaythelightreflectsonatriangledoesn’ttakeintoaccountitsfacingdirection,butitsnormaldirectioninstead.AsstatedinAddingatexturetoashaderrecipe,verticescanstoredata;thenormaldirectionisthemostusedinformationaftertheUVdata.Thisisavectorofunitlengththatindicatesthedirectionfacedbythevertex.Regardlessofthefacingdirection,everypointwithinatrianglehasitsownnormaldirectionthatisalinearinterpolationoftheonesstoredinitsvertices.Thisgivesustheabilitytofaketheeffectofhigh-resolutiongeometryonalow-resolutionmodel.Thefollowingimageshowsthesamegeometricshaperenderedwithdifferentper-vertexnormals.Intheimageontheleft,normalsareorthogonaltothefacerepresentedbyitsvertices;thisindicatesthatthereisaclearseparationbetweeneachface.Ontheright,normalsareinterpolatedalongthesurface,indicatingthatevenifthesurfaceisrough,lightshouldreflectasifit’ssmooth.It’seasytoseethatevenifthethreeobjectsinthefollowingimagesharethesamegeometry,theyreflectlightdifferently.Despitebeingmadeoutofflattriangles,theobjectontherightreflectslightasifitssurfacewasactuallycurved:
Smoothobjectswithroughedgesareaclearindicationthatper-vertexnormalshavebeeninterpolated.Thiscanbeseenifwedrawthedirectionofthenormalstoredineveryvertex,asshowninthefollowingimage.Youshouldnotethateverytrianglehasonlythreenormals,butasmultipletrianglescansharethesamevertex,morethanonelinecancomeoutofit:
Calculatingthenormalsfromthe3Dmodelisatechniquethathasrapidlydeclinedinfavorofamoreadvancedone—normalmapping.Similartowhathappenswithtexturemapping,thenormaldirectionscanbeprovidedusinganadditionaltexture,usuallycallednormalmaporbumpmap.NormalmapsareusuallyRGBimages,wheretheRGBcomponentsareusedtoindicatetheX,Y,andZcomponentsofthenormaldirection.Therearemanywaystocreatenormalmapsthesedays.SomeapplicationssuchasCrazyBump(http://www.crazybump.com/)andNDOPainter(http://quixel.se/ndo/)willtakein2Ddataandconvertittonormaldataforyou.OtherapplicationssuchasZbrush4R7(http://www.pixologic.com/)andAUTODESK(http://usa.autodesk.com)willtake3Dsculpteddataandcreatenormalmapsforyou.Theactualprocessofcreatingnormalmapsisdefinitelyoutofthescopeofthisbook,butthelinksintheprevioustextshouldhelpyougetstarted.
UnitymakestheprocessofaddingnormalstoyourshadersquiteaneasyprocessintheSurfaceShaderrealmusingtheUnpackNormals()function.Let’sseehowthisisdone.
GettingreadyCreateanewmaterialandshaderandsetthemuponanewobjectintheSceneview.Thiswillgiveusacleanworkspaceinwhichwecanlookatjustthenormalmappingtechnique.
Youwillneedanormalmapforthisrecipe,butthereisalsooneintheUnityprojectincludedwiththisbook.
Anexamplenormalmapincludedwiththisbook’scontentsisshownhere:
Howtodoit…Thefollowingarethestepstocreateanormalmapshader:
1. Let’sgetthePropertiesblocksetupinordertohaveacolortintandtexture:
Properties
{
_MainTint("DiffuseTint",Color)=(1,1,1,1)
_NormalTex("NormalMap",2D)="bump"{}
}
NoteByinitializingthetextureasbump,wearetellingUnitythat_NormalTexwillcontainanormalmap.Ifthetextureisnotset,itwillbereplacedbyagreytexture.Thecolorused(0.5,0.5,0.5,1)indicatesnobumpatall.
2. LinkthepropertiestotheCgprogrambydeclaringtheminSubShader{}belowtheCGPROGRAMstatement:
CPROGRAM
#pragmasurfacesurfLambert
//LinkthepropertytotheCGprogram
sampler2D_NormalTex;
float4_MainTint;
3. WeneedtomakesurethatweupdatetheInputstructwiththepropervariablenamesothatwecanusethemodel’sUVsforthenormalmaptexture:
//MakesureyougettheUVsforthetextureinthestruct
structInput
{
float2uv_NormalTex;
}
4. Finally,weextractthenormalinformationfromthenormalmaptextureusingthebuilt-inUnpackNormal()function.Then,youonlyhavetoapplythesenewnormalstotheoutputoftheSurfaceShader:
//Getthenormaldataoutofthenormalmaptexture
//usingtheUnpackNormalfunction
float3normalMap=UnpackNormal(tex2D(_NormalTex,IN.uv_NormalTex));
//Applythenewnormaltothelightingmodel
o.Normal=normalMap.rgb;
Thefollowingimagedemonstratestheresultofournormalmapshader:
NoteShaderscanhavebothatexturemapandnormalmap.ItisnotuncommontousethesameUVdatatoaddressboth.However,itispossibletoprovideasecondarysetofUVsinthevertexdata(UV2)specificallyusedforthenormalmap.
Howitworks…Theactualmathtoperformthenormalmappingeffectisdefinitelybeyondthescopeofthischapter,butUnityhasdoneitallforusalready.Ithascreatedthefunctionsforussothatwedon’thavetokeepdoingitoverandoveragain.ThisisanotherreasonwhySurfaceShadersareareallyefficientwaytowriteshaders.
IfyoulookintheUnityCG.cgincfilefoundintheDatafolderinyourUnityinstallationdirectory,youwillfindthedefinitionsfortheUnpackNormal()function.WhenyoudeclarethisfunctioninyourSurfaceShader,Unitytakestheprovidednormalmapandprocessesitforyouandgivesyouthecorrecttypeofdatasothatyoucanuseitinyourper-pixellightingfunction.It’sahugetime-saver!Whensamplingatexture,yougetRGBvaluesfrom0to1;however,thedirectionsofanormalvectorrangefrom-1to+1.UnpackNormal()bringsthesecomponentsintherightrange.
OnceyouhaveprocessedthenormalmapwiththeUnpackNormal()function,yousenditbacktoyourSurfaceOutputstructsothatitcanbeusedinthelightingfunction.Thisisdonebyo.Normal=normalMap.rgb;.WewillseehowthenormalisactuallyusedtocalculatethefinalcolorofeachpixelinChapter3,UnderstandingLightingModels.
There’smore…Youcanalsoaddsomecontrolstoyournormalmapshaderthatletsauseradjusttheintensityofthenormalmap.Thisiseasilydonebymodifyingthexandycomponentsofthenormalmapvariableandthenaddingitallbacktogether.AddanotherpropertytothePropertiesblockandnameit_NormalMapIntensity:
_NormalMapIntensity("Normalintensity",Range(0,1))=1
Multiplythexandycomponentsoftheunpackednormalmapandreapplythisvaluetothenormalmapvariable:
fixed3n=UnpackNormal(tex2D(_BumpTex,IN.uv_uv_MainTex)).rgb;
n.x*=_NormalMapIntensity;
n.y*=_NormalMapIntensity;
o.Normal=normalize(n);
NoteNormalvectorsaresupposedtohavelengthsequaltoone.Multiplyingthemfor_NormalMapIntensitychangestheirlength,makingnormalizationnecessary.
Now,youcanletauseradjusttheintensityofthenormalmapinthematerial’sInspectortab.Thefollowingimageshowstheresultofmodifyingthenormalmapwithourscalarvalues:
CreatingatransparentmaterialAlltheshadersseensofarhavesomethingincommon—theyareusedforsolidmaterials.Ifyouwanttoimprovethelookofyourgame,transparentmaterialsareoftenagoodwaytostart.Theycanbeusedforanythingfromafireeffecttoawindowglass.Workingwiththem,unfortunately,isslightlymorecomplicated.Beforerenderingsolidmodels,Unityordersthemaccordingtothedistancefromthecamera(Zordering)andskipsallthetrianglesthatarefacingawayfromthecamera(culling).Whenrenderingtransparentgeometries,thereareinstancesinwhichthesetwoaspectscancauseproblems.ThisrecipewillshowyouhowtosolvesomeoftheseissueswhenitcomestocreatingatransparentSurfaceShader.ThistopicwillbeheavilyrevisitedinChapter6,FragmentShadersandGrabPasses,whererealisticglassandwatershaderswillbeprovided.
GettingreadyThisreciperequiresanewshader,whichwe’llbecallingTransparent,andanewmaterialsothatitcanbeattachedtoanobject.Asthisisgoingtobeatransparentglasswindow,aquadorplaneisperfect.Wewillalsoneedseveralothernon-transparentobjectstotesttheeffect.Inthisexample,wewilluseaPNGfortheglasstexture.Thealphachanneloftheimagewillbeusedtodeterminethetransparencyoftheglass.Theprocessofcreatingsuchanimagedependsonthesoftwarethatyouareusing.However,thesearethemainstepsthatyouwillneedtofollow:
1. Findtheimageoftheglassyouwantforyourwindows.2. Openitwithaphotoeditingsoftware,suchasGIMPorPhotoshop.3. Selectthepartsoftheimagethatyouwanttobesemi-transparent.4. Createawhite(fullopacity)layermaskonyourimage.5. Usetheselectionpreviouslymadetofillthelayermaskwithadarkercolor.6. SavetheimageandimportittoUnity.
ThetoyimageusedinthisrecipeisapictureofastainedglassfromtheMeauxCathedralinFrance(https://en.wikipedia.org/wiki/Stained_glass).Ifyouhavefollowedallthesteps,yourimageshouldlooklikethis(RGBchannelsontheleft,andAchannelontheright):
Howtodoit…Asmentionedpreviously,thereareafewaspectsthatweneedtotakecareofwhileusingaTransparentShader:
1. IntheSubShader{}sectionoftheshader,addthefollowingtagsthatsignaltheshaderistransparent:
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
}
2. Asthisshaderisdesignedfor2Dmaterials,makesurethatthebackgeometryofyourmodelisnotdrawnbyaddingthefollowing:
CullBack
3. Telltheshaderthatthismaterialistransparentandneedstobeblendedwithwhatwasdrawnonthescreenbefore:
#pragmasurfacesurfStandardalpha:fade
4. UsethisSurfaceShadertodeterminethefinalcolorandtransparencyoftheglass:
voidsurf(InputIN,inoutSurfaceOutputStandardo)
{
float4c=tex2D(_MainTex,IN.uv_MainTex)*_Color;
o.Albedo=c.rgb;
o.Alpha=c.a;
}
Howitworks…Thisshaderintroducesseveralnewconcepts.Firstofall,Tagsareusedtoaddinformationabouthowtheobjectisgoingtoberendered.ThereallyinterestingonehereisQueue.Unity,bydefault,willsortyourobjectsforyoubasedonthedistancefromthecamera.So,asanobjectgetsnearertothecamera,itisgoingtobedrawnoveralltheobjectsthatarefurtherawayfromthecamera.Formostcases,thisworksoutjustfineforgames,butyouwillfindcertainsituationswhereyouwillwanttohavemorecontroloverthesortingofyourobjectsinyourscene.Unityhasprovideduswithsomedefaultrenderqueues,eachwithauniquevaluethatdirectsUnitywhentodrawtheobjecttothescreen.Thesebuilt-inrenderqueuesarecalledBackground,Geometry,AlphaTest,Transparent,andOverlay.Thesequeuesweren’tjustcreatedarbitrarily;theyactuallyserveapurposetomakeourliveseasierwhenwritingshadersandinteractingwiththereal-timerenderer.Refertothefollowingtablefordescriptionsontheusageofeachoftheseindividualrenderqueues:
Renderqueue Renderqueuedescription
Renderqueuevalue
Background Thisrenderqueueisrenderedfirst.Itisusedforskyboxesandsoon. 1000
GeometryThisisthedefaultrenderqueue.Thisisusedformostobjects.Opaquegeometryusesthisqueue.
2000
AlphaTestAlpha-testedgeometryusesthisqueue.It’sdifferentfromtheGeometryqueueasit’smoreefficienttorenderalpha-testedobjectsafterallthesolidobjectsaredrawn.
2450
Transparent
ThisrenderqueueisrenderedafterGeometryandAlphaTestqueuesinback-to-frontorder.Anythingalpha-blended(thatis,shadersthatdon’twritetothedepthbuffer)shouldgohere,forexample,glassandparticleeffects.
3000
OverlayThisrenderqueueismeantforoverlayeffects.Anythingrenderedlastshouldgohere,forexample,lensflares.
4000
So,onceyouknowwhichrenderqueueyourobjectbelongsto,youcanassignitsbuilt-inrenderqueuetag.OurshaderusedtheTransparentqueue,sowewroteTags{"Queue"="Trasparent"}.
NoteThefactthattheTransparentqueueisrenderedafterGeometrydoesnotmeanthatourglasswillappearontopofalltheothersolidobjects.Unitywilldrawtheglasslast,butitwillnotrenderpixelsthatbelongtopiecesofgeometryhiddenbehindsomethingelse.ThiscontrolisdoneusingatechniquecalledZBuffering.Moreinformationonhowmodelsarerenderedcanbefoundathttp://docs.unity3d.com/Manual/SL-CullAndDepth.html.
TheIgnoreProjectortagmakesthisobjectunaffectedbyUnity’sprojectors.Lastly,RenderTypeplaysaroleinshaderreplacement,atopicthatwillbecoveredbrieflyinChapter9,GameplayandScreenEffects.
Thelastconceptintroducedisalpha:fade.Thisindicatesthatallthepixelsfromthismaterialhavetobeblendedwithwhatwasonthescreenbeforeaccordingtotheiralphavalues.Withoutthisdirective,thepixelswillbedrawninthecorrectorder,buttheywon’thaveanytransparency.
CreatingaHolographicShaderMoreandmorespace-themedgamesarebeingreleasedeveryyear.Animportantpartofagoodsci-figameisthewayfuturistictechnologyispresentedandintegratedinthegameplay.There’snothingthatscreamsfuturisticmorethanholograms.Despitebeingpresentinmanyflavors,hologramsareoftenrepresentedassemi-transparent,thinprojectionsofanobject.Thisrecipeshowsyouhowtocreateashaderthatsimulatessucheffects.Takethisasastartingpoint:youcanaddnoise,animatedscanlines,andvibrationstocreateatrulyoutstandingholographiceffect.Thefollowingimageshowsanexampleofaholographiceffect:
GettingreadyAstheholographiceffectsshowsonlytheoutlinesofanobject,we’llcallthisshaderSilhouette.Attachittoamaterialandassignittoyour3Dmodel.
Howtodoit…Thefollowingchangeswillmodifyourexistingshaderintoaholographicone:
1. Addthefollowingpropertytotheshader:
_DotProduct("Rimeffect",Range(-1,1))=0.25
2. AdditsrespectivevariabletotheCGPROGRAMsection:
float_DotProduct;
3. Asthismaterialistransparent,addthefollowingtags:
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
}
NoteAccordingtothetypeofobjectthatyouwilluse,youmightwantitsbacksidetoappear.Ifthisisthecase,addCullOffsothatthebackofthemodelwon’tberemoved(culled).
4. Thisshaderisnottryingtosimulatearealisticmaterial,sothereisnoneedtousethePBRlightingmodel.TheLambertianreflectance,whichisverycheap,isusedinstead.Additionally,weshoulddisableanylightingwithnolightingandsignaltoCgthatthisisaTransparentShaderusingalpha:fade:
#pragmasurfacesurfLambertalpha:fadenolighting
5. ChangetheInputstructuresothatUnitywillfillitwiththecurrentviewdirectionandworldnormaldirection:
structInput
{
float2uv_MainTex;
float3worldNormal;
float3viewDir;
};
6. Usethefollowingsurfacefunction.RememberthatasthisshaderisusingtheLambertianreflectanceasitslightingfunction,thenameofthesurfaceoutputstructureshouldbechangedaccordinglytoSurfaeOutputinsteadofSurfaceOutputStandard:
voidsurf(InputIN,inoutSurfaceOutputo)
{
float4c=tex2D(_MainTex,IN.uv_MainTex)*_Color;
o.Albedo=c.rgb;
floatborder=1-(abs(dot(IN.viewDir,IN.worldNormal)));
floatalpha=(border*(1-_DotProduct)+_DotProduct);
o.Alpha=c.a*alpha;
}
YoucannowusetheRimeffectslidertochoosethestrengthoftheholographiceffect.
Howitworks…Asmentionedbefore,thisshaderworksbyshowingonlythesilhouetteofanobject.Ifwelookattheobjectfromanotherangle,itsoutlinewillchange.Geometricallyspeaking,theedgesofamodelareallthosetriangleswhosenormaldirectionisorthogonal(90degrees)tothecurrentviewdirection.TheInputstructuredeclarestheseparameters,worldNormalandviewDir,respectively.
Theproblemofunderstandingwhentwovectorsareorthogonalcanbesolvedusingthedotproduct.It’sanoperatorthattakestwovectorsandreturnszeroiftheyareorthogonal.Weuse_DotProducttodeterminehowclosetozerothedotproducthastobeforthetriangletofadecompletely.
Thesecondaspectthatisusedinthisshaderisthegentlefadingbetweentheedgeofthemodel(fullyvisible)andtheangledeterminedby_DotProduct(invisible).Thislinearinterpolationisdoneasfollows:
floatalpha=(border*(1-_DotProduct)+_DotProduct);
Finally,theoriginalalphafromthetextureismultipliedwiththenewlycalculatedcoefficienttoachievethefinallook.
There’smore…Thistechniqueisverysimpleandrelativelyinexpensive.Yet,itcanbeusedforalargevarietyofeffects,suchasthefollowing:
Theslightlycoloredatmosphereofaplanetinsci-figamesTheedgeofanobjectthathasbeenselectedoriscurrentlyunderthemouseAghostorspecterSmokecomingoutofanengineTheshockwaveofanexplosionThebubbleshieldofaspaceshipunderattack
SeealsoThedotproductplaysanimportantroleinthewayreflectionsarecalculated.Chapter3,UnderstandingLightingModels,willexplainindetailhowitworksandwhyitiswidelyusedinsomanyshaders.
PackingandblendingtexturesTexturesareusefultostorenotonlyloadsofdata,notjustpixelcolorsaswegenerallytendtothinkofthem,butalsoformultiplesetsofpixelsinboththexandydirectionsandRGBAchannels.WecanactuallypackmultipleimagesintoonesingleRGBAtextureanduseeachoftheR,G,B,andAcomponentsasindividualtexturesthemselvesbyextractingeachofthesecomponentsintheshadercode.
TheresultofpackingindividualgrayscaleimagesintoasingleRGBAtexturecanbeseeninthefollowingimage:
Whyisthishelpful?Well,intermsoftheamountofactualmemorythatyourapplicationtakesup,texturesarealargeportionofyourapplication’ssize.So,tobeginreducingthesizeofyourapplication,wecanlookatalloftheimagesthatweareusinginourshaderandseeifwecanmergethesetexturesintoasingletexture.
AnytexturethatisgrayscalecanbepackedintooneoftheRGBAchannelsofanothertexture.Thismightsoundabitoddatfirst,butthisrecipeisgoingtodemonstrateoneoftheusesofpackingatextureandusingthesepackedtexturesinashader.
Oneexampleofusingthesepackedtexturesiswhenyouwanttoblendasetoftexturestogetherontoasinglesurface.Youseethismostofteninterraintypeshaders,whereyouneedtoblendintoanothertexturenicelyusingsomesortofcontroltextureorthepackedtexture,inthiscase.Thisrecipecoversthistechniqueandshowsyouhowyoucanconstructthebeginningsofanicefour-textureblendedterrainshader.
GettingreadyLet’screateanewshaderfileinyourShadersfolderandthencreateanewmaterialforthisshader.Thenamingconventionisentirelyuptoyouforyourshaderandmaterialfiles,sotryyourbesttokeepthemorganizedandeasytoreferencelateron.
Onceyouhaveyourshaderandmaterialready,createanewsceneinwhichwecantestourshader.
Youwillalsoneedtogatherupfourtexturesthatyouwouldwanttoblendtogether.Thesecanbeanything,butforaniceterrainshader,youwillwantgrass,dirt,rockydirt,androcktextures.
Thesearethecolortexturesthatwewillbeusingforthisrecipe,whichareincludedwiththisbook.
Finally,wewillalsoneedablendingtexturethatispackedwithgrayscaleimages.Thiswillgiveusthefourblendingtexturesthatwecanusetodirecthowthecolortextureswillbeplacedontheobjectsurface.
Wecanuseveryintricateblendingtexturestocreateaveryrealisticdistributionofterraintexturesoveraterrainmesh,asseeninthefollowingimage:
Howtodoit…Let’slearnhowtousepackedtexturesbyenteringthecodeshowninthefollowingsteps:
1. WeneedtoaddafewpropertiestoourPropertiesblock.Wewillneedfivesampler2Dobjects,ortextures,andtwocolorproperties:
Properties
{
_MainTint("DiffuseTint",Color)=(1,1,1,1)
//Addthepropertiesbelowsowecaninputallofourtextures
_ColorA("TerrainColorA",Color)=(1,1,1,1)
_ColorB("TerrainColorB",Color)=(1,1,1,1)
_RTexture("RedChannelTexture",2D)=""{}
_GTexture("GreenChannelTexture",2D)=""{}
_BTexture("BlueChannelTexture",2D)=""{}
_ATexture("AlphaChannelTexture",2D)=""{}
_BlendTex("BlendTexture",2D)=""{}
}
2. WethenneedtocreatetheSubShader{}sectionvariablesthatwillbeourlinktothedatainthePropertiesblock:
CGPROGRAM
#pragmasurfacesurfLambert
float4_MainTint;
float4_ColorA;
float4_ColorB;
sampler2D_RTexture;
sampler2D_GTexture;
sampler2D_BTexture;
sampler2D_BlendTex;
sampler2D_ATexture;
3. So,nowwehaveourtexturepropertiesandwearepassingthemtoourSubShader{}function.Inordertoallowtheusertochangethetilingratesonaper-texturebasis,wewillneedtomodifyourInputstruct.Thiswillallowustousethetilingandoffsetparametersoneachtexture:
structInput
{
float2uv_RTexture;
float2uv_GTexture;
float2uv_BTexture;
float2uv_ATexture;
float2uv_BlendTex;
};
4. Inthesurf()function,getthetextureinformationandstorethemintheirownvariablessothatwecanworkwiththedatainaclean,easy-to-understandway:
//Getthepixeldatafromtheblendtexture
//weneedafloat4herebecausethetexture
//willreturnR,G,B,andAorX,Y,Z,andW
float4blendData=tex2D(_BlendTex,IN.uv_BlendTex);
//Getthedatafromthetextureswewanttoblend
float4rTexData=tex2D(_RTexture,IN.uv_RTexture);
float4gTexData=tex2D(_GTexture,IN.uv_GTexture);
float4bTexData=tex2D(_BTexture,IN.uv_BTexture);
float4aTexData=tex2D(_ATexture,IN.uv_ATexture);
5. Let’sblendeachofourtexturestogetherusingthelerp()function.Ittakesthreearguments,lerp(value:a,value:b,blend:c).Thelerp()functiontakesintwotexturesandblendsthemwiththefloatvaluegiveninthelastargument:
//NoweneedtocontructanewRGBAvalueandaddall
//thedifferentblendedtexturebacktogether
float4finalColor;
finalColor=lerp(rTexData,gTexData,blendData.g);
finalColor=lerp(finalColor,bTexData,blendData.b);
finalColor=lerp(finalColor,aTexData,blendData.a);finalColor.a=
1.0;
6. Finally,wemultiplyourblendedtextureswiththecolortintvaluesandusetheredchanneltodeterminewherethetwodifferentterraintintcolorsgo:
//Addonourterraintintingcolors
float4terrainLayers=lerp(_ColorA,_ColorB,blendData.r);
finalColor*=terrainLayers;
finalColor=saturate(finalColor);
o.Albedo=finalColor.rgb*_MainTint.rgb;
o.Alpha=finalColor.a;
Theresultofblendingtogetherfourterraintexturesandcreatingaterraintintingtechniquecanbeseeninthefollowingimage:
Howitworks…Thismightseemlikequiteafewlinesofcode,buttheconceptbehindblendingisactuallyquitesimple.Forthetechniquetowork,wehavetoemploythebuilt-inlerp()functionfromtheCgFXstandardlibrary.Thisfunctionallowsustopickavaluebetweenargumentoneandargumenttwousingargumentthreeastheblendamount:
Function Description
lerp(a,b,
f)
Thisinvolveslinearinterpolation:(1–f)*a+b*f
Here,aandbarematchingvectororscalartypes.Thefparametercanbeeitherascalarorvectorofthesametypeasaandb.
So,forexample,ifwewantedtofindthemid-valuebetween1and2,wecouldfeedthevalue0.5asthethirdargumenttothelerp()functionanditwouldreturnthevalue1.5.ThisworksperfectlyforourblendingneedsasthevaluesofanindividualchannelinanRGBAtexturearesinglefloatvalues,usuallyintherangeof0to1.
Intheshader,wesimplytakeoneofthechannelsfromourblendtextureanduseittodrivethecolorthatispickedinalerp()functionforeachpixel.Forinstance,wetakeourgrasstextureanddirttexture,usetheredchannelfromourblendingtexture,andfeedthistoalerp()function.Thiswillgiveusthecorrectblendedcolorresultforeachpixelonthesurface.
Amorevisualrepresentationofwhatishappeningwhenusingthelerp()functionisshowninthefollowingimage:
Theshadercodesimplyusesthefourchannelsoftheblendtextureandallthecolortexturestocreateafinalblendedtexture.Thisfinaltexturethenbecomesourcolorthatwecanmultiplywithourdiffuselighting.
CreatingacirclearoundyourterrainManyRTSgamesdisplaydistances(rangeattack,movingdistance,sight,andsoon)bydrawingacirclearoundtheselectedunit.Iftheterrainisflat,thiscanbedonesimplybystretchingaquadwiththetextureofacircle.Ifthat’snotthecase,thequadwillmostlikelybeclippedbehindahilloranotherpieceofgeometry.Thisrecipewillshowyouhowtocreateashaderthatallowsyoutodrawcirclesaroundanobjectofarbitrarycomplexity.Ifyouwanttobeabletomoveoranimateyourcircle,wewillneedbothashaderandC#script.Thefollowingimageshowsanexampleofdrawingacircleinahillyregionusingashader:
GettingreadyDespiteworkingwitheverypieceofgeometry,thistechniqueisorientedtoterrains.Hence,thefirststepissettingupaterraininUnity.
1. Let’sstartbycreatinganewshadercalledRadiusShaderandtherespectivematerial,Radius.
2. Havethecharacterforyourobjectready;wewilldrawacirclearoundit.3. Fromthemenu,navigatetoGameObject|3DObject|Terraintocreateanew
terrain.4. Createthegeometryforyourterrain.Youcaneitherimportanexistingoneordraw
yourownusingthetoolsavailable(Raise/LowerTerrain,PaintHeight,SmoothHeight).
5. TerrainsarespecialobjectsinUnity,andthewaytexturemappingworksonthemisdifferentfromtraditional3Dmodels.Youcannotprovide_MainTexfromashaderasitneedstobeprovideddirectlyfromtheterrainitself.Todothis,selectPaintTextureandthenclickonAddTexture…:
6. Nowthatthetextureisset,youhavetochangethematerialoftheterrainsothatacustomshadercanbeprovided.FromTerrainSettings,changetheMaterialpropertytoCustom,andthendragtheRadiusmaterialtotheCustomMaterialbox.
Youarenowreadytocreateyourshader.
Howtodoit…Let’sstartbyeditingtheRadiusShaderfile:
1. Inthenewshader,addthesefourproperties:
_Center("Center",Vector)=(0,0,0,0)
_Radius("Radius",Float)=0.5
_RadiusColor("RadiusColor",Color)=(1,0,0,1)
_RadiusWidth("RadiusWidth",Float)=2
2. AddtheirrespectivevariablestotheCGPROGRAMsection:
float3_Center;
float_Radius;
fixed4_RadiusColor;
float_RadiusWidth;
3. InputtooursurfacefunctionrequiresnotonlytheUVofthetexture,butalsotheposition(inworldcoordinates)ofeverypointoftheterrain.WecanretrievethisparameterbychangingtheInputstructasfollows:
structInput
{
float2uv_MainTex;//TheUVoftheterraintexture
float3worldPos;//Thein-worldposition
};
4. Lastly,weusethissurfacefunction:
voidsurf(InputIN,inoutSurfaceOutputStandardo)
{
floatd=distance(_Center,IN.worldPos);
if(d>_Radius&&d<_Radius+_RadiusWidth)
o.Albedo=_RadiusColor;
else
o.Albedo=tex2D(_MainTex,IN.uv_MainTex).rgb;
}
Thesestepsareallittakestodrawacircleonyourterrain.Youcanusethematerial’sInspectortabtochangetheposition,radius,andcolorofthecircle.
MovingthecircleIfyouwantthecircletofollowyourcharacter,otherstepsarenecessary:
1. CreateanewC#scriptcalledRadius.2. Addthesepropertiestothescript:
publicMaterialradiusMaterial;
publicfloatradius=1;
publicColorcolor=Color.white;
3. IntheUpdate()method,addtheselinesofcode:
radiusMaterial.SetVector("_Center",transform.position);
radiusMaterial.SetFloat("_Radius",radius);
radiusMaterial.SetColor("_RadiusColor",color);
4. Attachthescripttoyourcharacter.5. Finally,dragtheRadiusmaterialtotheRadiusMaterialslotofthescript.
Youcannowmoveyourcharacteraroundandthiswillcreateanicecirclearoundit.ChangingthepropertiesoftheRadiusscriptwillchangetheradiusaswell.
Howitworks…Therelevantparameterstodrawacircleareitscenter,radius,andcolor.Theyareallavailableintheshaderwiththenames_Center,_Radius,and_RadiusColor.ByaddingtheworldPosvariabletotheInputstructure,weareaskingUnitytoprovideuswiththepositionofthepixelthatwearedrawingexpressedinworldcoordinates.Thisistheactualpositionofanobjectintheeditor.
Thesurf()functioniswherethecircleisactuallydrawn.Itcalculatesthedistancefromthepointbeingdrawnandcenteroftheradius,thenitcheckswhetheritisbetween_Radiusand_Radius+_RadiusWidth;ifthisisthecase,itusesthechosencolor.Intheothercase,itjustsamplesthetexturemaplikealltheothershadersseensofar.
Chapter3.UnderstandingLightingModelsInthepreviouschapters,weintroducedSurfaceShadersandexplainedhowwecanchangephysicalproperties(suchasAlbedoandSpecular)tosimulatedifferentmaterials.Howdoesthisreallywork?AttheheartofeverySurfaceShader,thereisitslightingmodel.It’sthefunctionthattakesthesepropertiesandcalculatesthefinalshadeofeachpixel.Unityusuallyhidesthisfromthedevelopersbecauseinordertowritealightingmodel,youhavetounderstandhowlightreflectsandrefractsontosurfaces.Thischapterwillfinallyshowyouhowlightingmodelsworkandgiveyouthebasicstocreateyourown.
Inthischapter,youwilllearnthefollowingrecipes:
CreatingacustomdiffuselightingmodelCreatingaToonShaderCreatingaPhongSpeculartypeCreatingaBlinnPhongSpeculartypeCreatinganAnisotropicSpeculartype
IntroductionSimulatingthewaylightworksisaverychallengingandresource-consumingtask.Formanyyears,videogameshaveusedverysimplelightingmodelsthat,despitelackingrealism,wereverybelievable.Evenifmost3Denginesarenowusingphysically-basedrenderers,itisworthexploringsomesimplertechniques.Theonespresentedinthischapterarereasonablyrealisticandwidelyadoptedondeviceswithlowresourcessuchasmobilephones.Understandingthesesimplelightingmodelsisalsoessentialifyouwanttocreateyourownone.
CreatingacustomdiffuselightingmodelIfyouarefamiliarwithUnity4,youmayknowthatthedefaultshaderitprovidedwasbasedonalightingmodelcalledLambertianreflectance.Thisrecipewillshowyouhowitispossibletocreateashaderwithacustomlightingmodelandexplainthemathematicsandimplementationbehindit.ThefollowingimageshowsthesamegeometryrenderedwithaStandardShader(right)anddiffuseLambertone(left):
ShadersbasedontheLambertianreflectanceareclassifiedasnon-photorealistic;noobjectintherealworldreallylookslikethis.However,LambertShadersarestilloftenusedinlowpolygamesastheyproduceaneatcontrastbetweenthefacesofcomplexgeometries.ThelightingmodelusedtocalculatetheLambertianreflectanceisalsoveryefficient,makingitperfectformobilegames.
Unityhasalreadyprovideduswithalightingfunctionthatwecanuseforourshaders.ItiscalledtheLambertianlightingmodel.Itisoneofthemorebasicandefficientformsofreflectance,whichyoucanfindinalotofgameseventoday.AsitisalreadybuiltintheUnitySurfaceShaderlanguage,wethoughtitisbesttostartwiththisfirstandbuildonit.YoucanalsofindanexampleintheUnityreferencemanual,butwewillgointomoredepthwithitandexplainwherethedataiscomingfromandwhyitisworkingthewayitis.Thiswillhelpyougetanicegroundinginsettingupcustomlightingmodelssothatwecanbuildonthisknowledgeinthefuturerecipesinthischapter.
GettingreadyLet’sstartbycarryingoutthefollowingsteps:
1. Createanewshaderandgiveitaname.2. Createanewmaterial,giveitaname,andassignthenewshadertoitsshader
property.3. Then,createasphereobjectandplaceitroughlyinthecenterofthescene.4. Finally,let’screateadirectionallighttocastsomelightonourobject.
WhenyourassetshavebeensetupinUnity,youshouldhaveascenethatresemblesthefollowingscreenshot:
Howtodoit…TheLambertianreflectancecanbeachievedwiththefollowingchangestotheshader:
1. Beginbyaddingthefollowingpropertiestotheshader’sPropertiesblock:
_MainTex("Texture",2D)="white"
2. Changethe#pragmadirectiveoftheshadersothat,insteadofStandard,itusesourcustomlightingmodel:
#pragmasurfacesurfSimpleLambert
3. Useaverysimplesurfacefunction,whichjustsamplesthetextureaccordingtoitsUVdata:
voidsurf(InputIN,inoutSurfaceOutputo){
o.Albedo=tex2D(_MainTex,IN.uv_MainTex).rgb;
}
4. AddafunctioncalledLightingSimpleLambert()thatwillcontainthefollowingcodefortheLambertianreflectance:
half4LightingSimpleLambert(SurfaceOutputs,half3lightDir,half
atten){
halfNdotL=dot(s.Normal,lightDir);
half4c;
c.rgb=s.Albedo*_LightColor0.rgb*(NdotL*atten*1);
c.a=s.Alpha;
returnc;
}
Howitworks…AspreviouslyseeninChapter1,CreatingYourFirstShader,the#pragmadirectiveisusedtospecifywhichsurfacefunctiontouse.Choosingadifferentlightingmodelworksinasimilarfashion:SimpleLambertforcesCgtolookforafunctioncalledLightingSimpleLambert().NoteLightingatthebeginning,whichisomittedinthedirective.
Thelightingfunctiontakesthreeparameters:thesurfaceoutput(whichcontainsthephysicalpropertiessuchasthealbedoandtransparency),thedirectionthelightiscomingfrom,anditsattenuation.
AccordingtotheLambertianreflectance,theamountoflightasurfacereflectsdependsontheanglebetweentheincidentlightandsurfacenormal.Ifyouhaveplayedpoolbilliards,youaresurelyfamiliarwiththisconcept;thedirectionofaballdependsonitsincidentangleagainstthewall.Ifyouhitawallata90degreeangle,theballwillcomebackatyou;ifyouhititwithaverylowangle,itsdirectionwillbemostlyunchanged.TheLambertianmodelmakesthesameassumption;ifthelighthitsatrianglewitha90degreeangle,allthelightgetsreflectedback.Thelowertheangle,thelesslightisreflectedbacktoyou.Thisconceptisshowninthefollowingimage:
Thissimpleconcepthastobetranslatedintoamathematicalform.Invectoralgebra,theanglebetweentwounitvectorscanbecalculatedviaanoperatorcalleddotproduct.Whenthedotproductisequaltozero,twovectorsareorthogonal,whichmeansthattheymakea90degreeangle.Whenitisequaltoone(orminusone),theyareparalleltoeachother.Cghasafunctioncalleddot(),whichimplementsthedotproductextremelyefficiently.
Thefollowingpictureshowsalightsource(sun)shiningonacomplexsurface.Lindicatesthelightdirection(calledlightDirintheshader)andNisthenormaltothesurface.Thelightisreflectedwiththesameanglethatithitsthesurface:
TheLambertianreflectancesimplyusestheNdotLdotproductasamultiplicativecoefficientfortheintensityoflight:
WhenNandLareparallel,allthelightisreflectedbacktothesource,causingthegeometrytoappearbrighter.The_LightColor0variablecontainsthecolorofthelightthatiscalculated.
NotePriortoUnity5,theintensityofthelightsweredifferent.IfyouareusinganoldDiffuseshaderbasedontheLambertianmodel,youmaynoticethatNdotLwasmultipliedbytwo:(NdotL*atten*2)ratherthan(NdotL*atten).IfyouareimportingacustomshaderfromUnity4,youwillneedtocorrectthismanually.LegacyShaders,however,havealreadybeendesignedtakingthisaspectintoaccount.
Whenthedotproductisnegative,thelightiscomingfromtheoppositesideofthetriangle.Thisisnotaproblemforopaquegeometriesastrianglesthatarenotfacingthecamerafrontallyareculled(discarded)andnotrendered.
ThisbasicLambertisagreatstartingpointwhenyouareprototypingyourshadersasyoucangetalotaccomplishedintermsofwritingthecorefunctionalityoftheshaderwhilenothavingtoworryaboutthebasiclightingfunctions.
UnityhasprovideduswithalightingmodelthathasalreadytakenthetaskofcreatingaLambertlightingforyou.IfyoulookattheUnityCG.cgincfilefoundinyourUnity’sinstallationdirectoryundertheDatafolder,youwillnoticethatyouhaveLambertandBlinnPhonglightingmodelsavailableforyoutouse.Themomentyoucompileyourshaderwith#pragmasurfacesurfLambert,youaretellingtheshadertoutilizeUnity’simplementationoftheLambertlightingfunctionintheUnityCG.cgincfilesothatwedon’thavetowritethatcodeoverandoveragain.WewillexplorehowtheBlinnPhongmodelworkslaterinthischapter.
CreatingaToonShaderOneofthemostusedeffectsingamesisthetoonshading,whichisalsoknownascelshading(shortforcelluloid).Itisanon-photorealisticrenderingtechniquethatmakes3Dmodelsappearflat.Manygamesuseittogivetheillusionthatthegraphicsarebeinghand-drawnratherthanbeing3D-modeled.Youcansee,inthefollowingpicture,asphererenderedwithaStandardShader(right)andToonShader(left):
Achievingthiseffectusingjustsurfacefunctionsisnotimpossible,butitwouldbeextremelyexpensiveandtime-consuming.Thesurfacefunction,infact,onlyworksonthepropertiesofthematerial,notitsactuallightingcondition.Astoonshadingrequirestochangethewaylightreflects,weneedtocreateourcustomlightingmodelinstead.
GettingreadyLet’sstartthisrecipebycreatingashaderanditsmaterialandimportingaspecialtexture,asfollows:
1. Startbycreatinganewshader;inthisexample,wewillextendtheonemadeinthepreviousrecipe.
2. Createanewmaterialfortheshaderandattachittoa3Dmodel.Thetoonshadingworksbestoncurvedsurfaces.
3. Thisreciperequiresanadditionaltexturecalledrampmap.ItisimportantthatyouchangeitsWrapModetoClamp.Ifyouwanttheedgesbetweenthecolorstobesharp,theFilterModeshouldalsobesettoPoint:
Howtodoit…Thetoonaestheticcanbeachievedwiththefollowingchangestotheshader:
1. Addanewpropertyforatexturecalled_RampTex:
_RampTex("Ramp",2D)="white"{}
2. AdditsrelativevariableintheCGPROGRAMsection:
sampler2D_RampTex;
3. Changethe#pragmadirectivesothatitpointstoafunctioncalledLightingToon():
#pragmasurfacesurfToon
4. Usethislightingmodel:
fixed4LightingToon(SurfaceOutputs,fixed3lightDir,fixedatten)
{
halfNdotL=dot(s.Normal,lightDir);
NdotL=tex2D(_RampTex,fixed2(NdotL,0.5));
fixed4c;
c.rgb=s.Albedo*_LightColor0.rgb*NdotL*atten;
c.a=s.Alpha;
returnc;
}
Howitworks…Themaincharacteristicofthetoonshadingisthewaythelightisrendered;surfacesarenotshadeduniformly.Toachievethiseffect,weneedarampmap.ItspurposeistoremaptheLambertianlightintensityNdotLtoanothervalue.Usingarampmapwithoutagradient,wecanforcethelightingtoberenderedinsteps.Thefollowingimageshowshowtherampmapisusedtocorrectthelightintensity:
There’smore…Therearemanydifferentwaysonecanachieveatoonshadingeffect.Usingdifferentrampscanproducedramaticchangesinthewayyourmodelslook,soyoushouldexperimentinordertofindthebestone.
AnalternativetoramptexturesistosnapthelightintensityNdotLsothatitcanonlyassumeacertainnumberofvaluesequidistantlysampledfrom0to1:
half4LightingCustomLambert(SurfaceOutputs,half3lightDir,half3
viewDir,halfatten){
halfNdotL=dot(s.Normal,lightDir);
halfcel=floor(NdotL*_CelShadingLevels)/(_CelShadingLevels-0.5);//
Snap
half4c;
c.rgb=s.Albedo*_LightColor0.rgb*cel*atten;
c.a=s.Alpha;
returnc;
}
ThesnappingcodemultipliesNdotLtimes_CelShadingLevels,roundsittoaninteger,andthendividesitback.Bydoingthis,thecelquantityisforcedtoassumeoneofthe_CelShadingLevelsequidistantvaluesfrom0to1.Thisremovestheneedforaramptextureandmakesallthecolorstepsofthesamesize.Ifyouaregoingforthisimplementation,remembertoaddapropertycalled_CelShadingLevelstoyourshader.
CreatingaPhongSpeculartypeThespecularityofanobjectsurfacesimplydescribeshowshinyitis.Thesetypesofeffectsareoftenreferredtoasview-dependenteffectsintheshaderworld.ThisisbecauseinordertoachievearealisticSpeculareffectinyourshaders,youneedtoincludethedirectionthatthecameraoruserisfacingtheobject’ssurface.Themostbasicandperformance-friendlySpeculartypeisthePhongSpeculareffect.Itisthecalculationofthelightdirectionreflectingoffofthesurfacecomparedtotheuser’sviewdirection.ItisaverycommonSpecularmodelusedinmanyapplications,fromgamestomovies.Whileitisn’tthemostrealisticintermsofaccuratelymodelingthereflectedSpecular,itgivesagreatapproximationthatperformswellinmostsituations.Additionally,ifyourobjectisfurtherawayfromthecameraandthereisnoneedforaveryaccurateSpecular,thisisagreatwaytoprovideaSpeculareffecttoyourshaders.
Inthisrecipe,wewillbecoveringhowtoimplementtheper-vertexversionoftheshaderandalsotheper-pixelversionusingsomenewparametersintheSurfaceShader’sInputstruct.Wewillseethedifferenceanddiscusswhenandwhytousethesetwodifferentimplementationsfordifferentsituations.
GettingreadyTostartwiththisrecipe,performthefollowingsteps:
1. Createanewshader,material,andobject,andgivethemappropriatenamessothatyoucanfindthemlater.
2. Attachtheshadertothematerialandthematerialtotheobject.Tofinishoffyournewscene,createanewdirectionallightsothatwecanseeourSpeculareffectaswecodeit.
Howtodoit…FollowthefollowingstepstocreateaPhonglightingmodel:
1. Youmightbeseeingapatternatthispoint,butwealwaysliketostartoutwithourmostbasicpartoftheshaderwritingprocess:thecreationofproperties.So,let’saddthefollowingpropertiestotheshader:
Properties
{
_MainTint("DiffuseTint",Color)=(1,1,1,1)
_MainTex("Base(RGB)",2D)="white"{}
_SpecularColor("SpecularColor",Color)=(1,1,1,1)
_SpecPower("SpecularPower",Range(0,30))=1
}
2. WethenhavetomakesuretoaddthecorrespondingvariablestoourCGPROGRAMblockinourSubShader{}block:
float4_SpecularColor;
sampler2D_MainTex;
float4_MainTint;
float_SpecPower;
3. NowwehavetoaddourcustomlightingmodelsothatwecancomputeourownPhongSpecular.Don’tworryifitdoesn’tmakesenseatthispoint;wewillcovereachlineofcodeintheHowitworks…section.Addthefollowingcodetotheshader’sSubShader{}function:
fixed4LightingPhong(SurfaceOutputs,fixed3lightDir,half3viewDir,
fixedatten)
{
//Reflection
floatNdotL=dot(s.Normal,lightDir);
float3reflectionVector=normalize(2.0*s.Normal*NdotL-
lightDir);
//Specular
floatspec=pow(max(0,dot(reflectionVector,viewDir)),_SpecPower);
float3finalSpec=_SpecularColor.rgb*spec;
//Finaleffect
fixed4c;
c.rgb=(s.Albedo*_LightColor0.rgb*max(0,NdotL)*atten)+
(_LightColor0.rgb*finalSpec);
c.a=s.Alpha;
returnc;
}
4. Finally,wehavetotelltheCGPROGRAMblockthatitneedstouseourcustomlightingfunctioninsteadofoneofthebuilt-inones.Wedothisbychangingthe#pragmastatementtothefollowing:
CPROGRAM
#pragmasurfacesurfPhong
ThefollowingscreenshotdemonstratestheresultofourcustomPhonglightingmodelusingourowncustomreflectionvector:
Howitworks…Let’sbreakdownthelightingfunctionbyitself,astherestoftheshadershouldbeprettyfamiliartoyouatthispoint.
Inthepreviousrecipes,wehaveusedalightingfunctionthatprovidedonlythelightdirection,lightDir.Unitycomeswithasetoflightingfunctionsthatyoucanuse,includingonethatprovidestheviewdirection,viewDir.Refertothefollowingtableorgotohttp://docs.unity3d.com/Documentation/Components/SL-SurfaceShaderLighting.html:
Notview-dependent
half4LightingNameYouchoose(SurfaceOutputs,half3lightDir,halfatten);
View-dependent half4LightingNameYouchoose(SurfaceOutputs,half3lightDir,half3viewDir,
halfatten);
Inourcase,wearedoingaSpecularshader,soweneedtohavetheview-dependentlightingfunctionstructure.So,wehavetowritethefollowing:
CPROGRAM
#pragmasurfacesurfPong
fixed4LightingPhong(SurfaceOutputs,fixed3lightDir,half3viewDir,
fixedatten)
{
//...
}
Thiswilltelltheshaderthatwewanttocreateourownview-dependentshader.Alwaysmakesurethatyourlightingfunctionnameisthesameinyourlightingfunctiondeclarationandthe#pragmastatement,orUnitywillnotbeabletofindyourlightingmodel.
ThecomponentsthatplayaroleinthePhongmodelaredescribedinthefollowingimage.WehavethelightdirectionL(coupledwithitsperfectreflectionR)andnormaldirectionN.TheyhaveallbeenencounteredbeforeintheLambertianmodel,withtheexceptionofV,whichistheviewdirection:
ThePhongmodelassumesthatthefinallightintensityofareflectivesurfaceisgivenbytwocomponents:itsdiffusecolorandSpecularvalue,asfollows:
ThediffusecomponentDremainsunchangedfromtheLambertianmodel:
TheSpecularcomponentSisdefinedasfollows:
Here,pistheSpecularpowerdefinedas_SpecPowerintheshader.TheonlyunknownparameterisR,whichisthereflectionofLaccordingtoN.Invectoralgebra,thiscanbecalculatedasfollows:
Thisisexactlywhatiscalculatedinthefollowing:
float3reflectionVector=normalize(2.0*s.Normal*NdotL-lightDir);
Thishastheeffectofbendingthenormaltowardsthelight;asavertexnormalispointingawayfromthelight,itisforcedtolookatthelight.Refertothefollowingscreenshotforamorevisualrepresentation.Thescriptthatproducesthisdebugeffectisincludedinthebook’ssupportpageathttps://www.packtpub.com/books/content/support:
ThefollowingscreenshotdisplaysthefinalresultofourPhongSpecularcalculationisolatedintheshader:
CreatingaBlinnPhongSpeculartypeBlinnisanothermoreefficientwayofcalculatingandestimatingspecularity.Itisdonebygettingthehalfvectorfromtheviewdirectionandlightdirection.ItwasbroughtintotheworldofCgbyJimBlinn.Hefoundthatitwasmuchmoreefficienttojustgetthehalfvectorinsteadofcalculatingourownreflectionvectors.Itcutdownonthecodeandprocessingtime.Ifyouactuallylookatthebuilt-inBlinnPhonglightingmodelincludedintheUnityCG.cgincfile,youwillnoticethatitisusingthehalfvectoraswell,henceitisnamedBlinnPhong.ItisjustasimplerversionofthefullPhongcalculation.
GettingreadyTostartwiththisrecipe,performthefollowingsteps:
1. Thistime,insteadofcreatingawholenewscene,let’sjustusetheobjectsandscenethatwehave,andcreateanewshaderandmaterialandnamethemBlinnPhong.
2. Onceyouhaveanewshader,double-clickonittolaunchMonoDevelopsothatwecanstarteditingourshader.
Howtodoit…PerformthefollowingstepstocreateaBlinnPhonglightingmodel:
1. First,weneedtoaddourownpropertiestothePropertiesblocksothatwecancontrolthelookoftheSpecularhighlight:
Properties
{
_MainTint("DiffuseTint",Color)=(1,1,1,1)
_MainTex("Base(RGB)",2D)="white"{}
_SpecularColor("SpecularColor",Color)=(1,1,1,1)
_SpecPower("SpecularPower",Range(0.1,60))=3
}
2. Then,weneedtomakesurethatwehavecreatedthecorrespondingvariablesinourCGPROGRAMblocksothatwecanaccessthedatafromourPropertiesblock,inoursubshader:
sampler2D_MainTex;
float4_MainTint;
float4_SpecularColor;
float_SpecPower;
3. Nowit’stimetocreateourcustomlightingmodelthatwillprocessourDiffuseandSpecularcalculations.Thecodeisasfollows:
fixed4LightingCustomBlinnPhong(SurfaceOutputs,fixed3lightDir,
half3viewDir,fixedatten)
{
floatNdotL=max(0,dot(s.Normal,lightDir));
float3halfVector=normalize(lightDir+viewDir);
floatNdotH=max(0,dot(s.Normal,halfVector));
floatspec=pow(NdotH,_SpecPower)*_SpecularColor;
float4c;
c.rgb=(s.Albedo*_LightColor0.rgb*NdotL)+(_LightColor0.rgb*
_SpecularColor.rgb*spec)*atten;
c.a=s.Alpha;
returnc;
}
4. Tocompleteourshader,wewillneedtotellourCGPROGRAMblocktouseourcustomlightingmodelratherthanabuilt-inonebymodifyingthe#pragmastatementwiththefollowingcode:
CPROGRAM
#pragmasurfacesurfCustomBlinnPhong
ThefollowingscreenshotdemonstratestheresultsofourBlinnPhonglightingmodel:
Howitworks…TheBlinnPhongSpecularisalmostexactlylikethePhongSpecular,exceptthatitismoreefficientbecauseituseslesscodetoachievealmostthesameeffect.Beforetheintroductionofphysically-basedrendering,thisapproachwasthedefaultchoiceforSpecularreflectioninUnity4.
CalculatingthereflectionvectorRisgenerallyexpensive.TheBlinnPhongSpecularreplacesitwiththehalfvectorHbetweentheviewdirectionVandlightdirectionL:
Insteadofcalculatingourownreflectionvector,wearesimplygoingtogetthevectorhalfwaybetweentheviewdirectionandlightdirection,basicallysimulatingthereflectionvector.Ithasactuallybeenfoundthatthisapproachismorephysicallyaccuratethanthelastapproach,butwethoughtitnecessarytoshowyouallthepossibilities:
Accordingtovectoralgebra,thehalfvectorcanbecalculatedasfollows:
Here, isthelengthofthevector .InCg,wesimplyneedtoaddtheviewdirectionandlightdirectiontogetherandthennormalizetheresulttoaunityvector:
float3halfVector=normalize(lightDir+viewDir);
Then,wesimplyneedtodotthevertexnormalwiththisnewhalfvectortogetourmainSpecularvalue.Afterthis,wejusttakeittoapowerof_SpecPowerandmultiplyitbytheSpecularcolorvariable.It’smuchlighteronthecodeandmath,butstillgivesusaniceSpecularhighlightthatwillworkforalotofreal-timesituations.
SeealsoThelightmodelsseeninthischapterareextremelysimple;norealmaterialisperfectlymatteorperfectlyspecular.Moreover,itisnotuncommonforcomplexmaterialssuchasclothing,wood,andskintorequireknowledgeofhowlightscattersinthelayersbeneaththesurface.
Usethefollowingtabletorecapthedifferentlightingmodelsencounteredsofar:
Technique Type Unity5shader LightIntensity(I)
Lambertian Diffuse LegacyShaders|Diffuse
Phong Specular
BlinnPhong Specular LegacyShaders|Specular
ThereareotherinterestingmodelssuchastheOren-Nayarlightingmodelforroughsurfaces:https://en.wikipedia.org/wiki/Oren%E2%80%93Nayar_reflectance_model
CreatinganAnisotropicSpeculartypeAnisotropicisatypeofSpecularorreflectionthatsimulatesthedirectionalityofgroovesinasurfaceandmodifies/stretchestheSpecularintheperpendiculardirection.Itisveryusefulwhenyouwanttosimulatebrushedmetals,notametalwithaclear,smooth,andpolishedsurface.ImaginetheSpecularthatyouseewhenyoulookatthedatasideofaCDorDVDorthewaySpecularisshapedatthebottomofapotorpan.Youwillnoticethatifyoucarefullyexaminethesurface,youwillseethatthereisadirectiontothegroovesinthesurface,usuallyinthewaythemetalwasbrushed.WhenyouapplyaSpeculartothissurface,yougetaSpecularstretchedintheperpendiculardirection.
ThisrecipewillintroduceyoutotheconceptofaugmentingyourSpecularhighlightstoachievedifferenttypesofbrushedsurfaces.Infuturerecipes,wewilllookatwaysinwhichwecanusetheconceptsofthisrecipetoachieveothereffectssuchasstretchedreflectionsandhair,buthere,youaregoingtolearnthefundamentalsofthetechniquefirst.WewillbeusingthisshaderasareferenceforourowncustomAnisotropicShader:
http://wiki.unity3d.com/index.php?title=Anisotropic_Highlight_Shader
ThefollowingscreenshotshowsexamplesofdifferenttypesofSpeculareffectsonecanachieveusingAnisotropicShadersinUnity:
GettingreadyLet’sstartthisrecipebycreatingashader,itsmaterial,andsomelightsforourscene:
1. Createanewscenewithsomeobjectsandlightssothatwecanvisuallydebugourshader.
2. Thencreateanewshaderandmaterial,andhookthemuptoourobjects.3. Lastly,wewillneedsomesortofnormalmapthatwillindicatethedirectionalityof
ourAnisotropicSpecularhighlight.
ThefollowingscreenshotshowstheAnisotropynormalmapwewillbeusingforthisrecipe.Itisavailablefromthebook’ssupportpageathttps://www.packtpub.com/books/content/support:
Howtodoit…TocreateanAnisotropiceffect,weneedtomakethefollowingchangestotheshaderpreviouslycreated:
1. Wefirstneedtoaddthepropertiesthatwearegoingtoneedforourshader.Thesewillallowalotofartisticcontroloverthefinalappearanceofthesurface:
Properties
{
_MainTint("DiffuseTint",Color)=(1,1,1,1)
_MainTex("Base(RGB)",2D)="white"{}
_SpecularColor("specularColor",Color)=(1,1,1,1)
_Specular("SpecularAmount",Range(0,1))=0.5
_SpecPower("SpecularPower",Range(0,1))=0.5
_AnisoDir("AnisotropicDirection",2D)=""{}
_AnisoOffset("AnisotropicOffset",Range(-1,1))=-0.2
}
2. WethenneedtomaketheconnectionbetweenourPropertiesblockandourSubShader{}blocksothatwecanusethedatabeingprovidedbythePropertiesblock:
sampler2D_MainTex;
sampler2D_AnisoDir;
float4_MainTint;
float4_SpecularColor;
float_AnisoOffset;
float_Specular;
float_SpecPower;
3. NowwecancreateourlightingfunctionthatwillproducethecorrectAnisotropiceffectonoursurface.Wewillusethefollowingcodeforthis:
fixed4LightingAnisotropic(SurfaceAnisoOutputs,fixed3lightDir,half3
viewDir,fixedatten)
{
fixed3halfVector=normalize(normalize(lightDir)+
normalize(viewDir));
floatNdotL=saturate(dot(s.Normal,lightDir));
fixedHdotA=dot(normalize(s.Normal+s.AnisoDirection),
halfVector);
floataniso=max(0,sin(radians((HdotA+_AnisoOffset)*180)));
floatspec=saturate(pow(aniso,s.Gloss*128)*s.Specular);
fixed4c;
c.rgb=((s.Albedo*_LightColor0.rgb*NdotL)+(_LightColor0.rgb*
_SpecularColor.rgb*spec))*atten;
c.a=s.Alpha;
returnc;
}
4. Inordertousethisnewlightingfunction,weneedtotellthesubshader’s#pragma
statementtolookforitinsteadofusingoneofthebuilt-inlightingfunctions.Wearealsotellingtheshadertotargetshadermodel3.0sothatwecanhavemorespacefortexturesinourprogram:
CGPROGRAM
#pragmasurfacesurfAnisotropic
#pragmatarget3.0
5. WehavealsogiventheAnisotropicnormalmapitsownUVsbydeclaringthefollowingcodeintheInputstruct.Thisisn’tentirelynecessaryaswecouldjustusetheUVsfromthemaintexture,butthisgivesusindependentcontroloverthetilingofourbrushedmetaleffectsothatwecanscaleittoanysizewewant:
structInput
{
float2uv_MainTex;
float2uv_AnisoDir;
};
6. WealsoneedtoaddtheSurfaceAnisoOutputstruct:
structSurfaceAnisoOutput
{
fixed3Albedo;
fixed3Normal;
fixed3Emission;
fixed3AnisoDirection;
halfSpecular;
fixedGloss;
fixedAlpha;
};
7. Finally,weneedtousethesurf()functiontopassthecorrectdatatoourlightingfunction.So,wewillgettheper-pixelinformationfromourAnisotropicnormalmapandsetourSpecularparametersasfollows:
voidsurf(InputIN,inoutSurfaceAnisoOutputo)
{
half4c=tex2D(_MainTex,IN.uv_MainTex)*_MainTint;
float3anisoTex=UnpackNormal(tex2D(_AnisoDir,IN.uv_AnisoDir));
o.AnisoDirection=anisoTex;
o.Specular=_Specular;
o.Gloss=_SpecPower;
o.Albedo=c.rgb;
o.Alpha=c.a;
}
TheAnisotropicnormalmapallowsustogivethesurfacedirectionandhelpsusdispersetheSpecularhighlightaroundthesurface.ThefollowingscreenshotdemonstratestheresultofourAnisotropicShader:
Howitworks…Let’sbreakdownthisshaderintoitscorecomponentsandexplainwhywearegettingtheeffect.Wewillmostlybecoveringthecustomlightingfunctionhere,astherestoftheshadershouldbeprettyself-explanatoryatthispoint.
WefirststartbydeclaringourownSurfaceAnisoOutputstruct.Weneedtodothisinordertogettheper-pixelinformationfromtheAnisotropicnormalmap,andtheonlywaywecandothisinaSurfaceShaderistouseatex2D()functioninthesurf()function.Thefollowingcodeshowsthecustomsurfaceoutputstructureusedinourshader:
structSurfaceAnisoOutput
{
fixed3Albedo;
fixed3Normal;
fixed3Emission;
fixed3AnisoDirection;
halfSpecular;
fixedGloss;
fixedAlpha;
};
WecanusetheSurfaceAnisoOutputstructasawayofinteractingbetweenthelightingfunctionandsurfacefunction.Inourcase,wearestoringtheper-pixeltextureinformationinthevariablecalledanisoTexinoursurf()functionandthenpassingthisdatatotheSurfaceAnisoOutputstructbystoringitintheAnisoDirectionvariable.Oncewehavethis,wecanusetheper-pixelinformationinthelightingfunctionusings.AnisoDirection.
Withthisdataconnectionsetup,wecanmoveontoouractuallightingcalculations.Thisbeginsbygettingtheusualoutoftheway,thehalfvector,sothatwedon’thavetodothefullreflectioncalculationanddiffuselighting,whichisthevertexnormaldottedwiththelightvectorordirection.ThisisdoneinCgwiththefollowinglines:
fixed3halfVector=normalize(normalize(lightDir)+normalize(viewDir));
floatNdotL=saturate(dot(s.Normal,lightDir));
Then,westarttheactualmodificationtotheSpeculartogettherightlook.Wefirstdotthenormalizedsumofthevertexnormalandper-pixelvectorsfromourAnisotropicnormalmapwithhalfVectorcalculatedinthepreviousstep.Thisgivesusafloatvaluethatgivesavalueof1asthesurfacenormal,whichismodifiedbytheAnisotropicnormalmapasitbecomesparallelwithhalfVectorand0asitisperpendicular.Finally,wemodifythisvaluewithasin()functionsothatwecanbasicallygetadarkermiddlehighlightandultimatelyaringeffectbasedoffofhalfVector.AllthepreviouslymentionedoperationsaresummarizedinthefollowingtwolinesofCgcode:
fixedHdotA=dot(normalize(s.Normal+s.AnisoDirection),halfVector);
floataniso=max(0,sin(radians((HdotA+_AnisoOffset)*180)));
Finally,wescaletheeffectoftheanisovaluebytakingittoapowerofs.Gloss,andthengloballydecreaseitsstrengthbymultiplyingitbys.Specular:
floatspec=saturate(pow(aniso,s.Gloss*128)*s.Specular);
Thiseffectisgreattocreatemoreadvancedmetaltypesurfaces,especiallytheonesthatarebrushedandseemtohavedirectionalitytothem.Italsoworkswellforhairoranysortofsoftsurfacewithdirectionalitytoit.ThefollowingscreenshotshowstheresultofdisplayingthefinalAnisotropiclightingcalculation:
Chapter4.PhysicallyBasedRenderinginUnity5OneofthebiggestchangesintroducedinUnity5isphysically-basedrendering,whichisalsoknownasPBR.Previouschaptershaverepeatedlymentioneditwithoutrevealingtoomuchaboutit.IfyouwanttounderstandnotonlyhowPBRworks,buthowtomakethemostoutofit,thisisthechapteryoushouldread.
Inthischapter,youwilllearnthefollowingrecipes:
UnderstandingthemetallicsetupAddingtransparencytoPBRCreatingmirrorsandreflectivesurfacesBakinglightsinyourscene
IntroductionAllthelightingmodelsencounteredinChapter3,UnderstandingLightingModels,wereveryprimitivedescriptionsofhowlightbehaves.Themostimportantaspectduringtheirmakingwasefficiency.Real-timeshadingisexpensive,andtechniquessuchasLambertianorBlinnPhongareacompromisebetweencomputationalcostandrealism.Havingamorepowerfulgraphicsprocessingunit(GPU)hasallowedustowriteprogressivelymoresophisticatedlightingmodelsandrenderingengines,withtheaimofsimulatinghowlightactuallybehaves.Thisis,inanutshell,thephilosophybehindPBR.Asthenamesuggests,ittriestogetascloseaspossibletothephysicsbehindtheprocessesthatgiveauniquelooktoeachmaterial.Despitethis,thetermPBRhasbeenwidelyusedinmarketingcampaignsandismoreofasynonymforstate-of-the-artrenderingratherthanawell-definedtechnique.Unity5implementsPBRbyintroducingtwoimportantchanges.Thefirstisacompletelynewlightingmodel(calledStandard).SurfaceShadersallowdeveloperstospecifythephysicalpropertiesofamaterial,buttheydonotimposeactualphysicalconstraintsonthem.PBRfillsthisgapusingalightingmodelthatenforcesprinciplesofphysicssuchasenergyconservation(anobjectcannotreflectmorelightthantheamountitreceives),microsurfacescattering(roughsurfacesreflectlightmoreerraticallycomparedtosmoothones),Fresnelreflectance(specularreflectionsappearatgrazingangles),andsurfaceocclusion(thedarkeningofcornersandothergeometriesthatarehardtolight).Alltheseaspects,andmanyothers,areusedtocalculatetheStandardlightingmodel.ThesecondaspectthatmakesPBRsorealisticiscalledGlobalIllumination(GI)andisthesimulationofphysically-basedlighttransport.Itmeansthatobjectsarenotdrawninthesceneasiftheywereseparateentities.Theyallcontributetothefinalrenderingaslightcanreflectonthembeforehittingsomethingelse.Thisaspectisnotcapturedintheshadersthemselvesbutisanessentialpartofhowtherenderingengineworks.Unfortunately,accuratelysimulatinghowlightraysactuallybounceoversurfacesinrealtimeisbeyondthecapabilitiesofmodernGPUs.Unity5makessomecleveroptimizationsthatallowretainingvisualfidelitywithoutsacrificingperformance.Someofthemostadvancedtechniques(suchasreflections),however,requiretheuserinput.Alloftheseaspectswillbecoveredinthischapter.ItisimportanttorememberthatPBRandGIdonotautomaticallyguaranteethatyourgamewillbephotorealistic.Achievingphotorealismisaverychallengingtaskand,likeeveryart,itrequiresgreatexpertizeandexceptionalskills.
UnderstandingthemetallicsetupUnity5providestwodifferenttypesofPBRshaders;theyarereferredtointhedrop-downmenuofthematerial’sInspectortabasStandardandStandard(Specularsetup).ThemaindifferenceisthattheformerexposestheMetallicproperty,whilethelatterreplacesitwithSpecular.BoththesemetallicandspecularsetupsrepresentdifferentwaysinwhichonecaninitializePBRmaterials.OneoftheconceptsthathasdrivenPBRistheabilitytoprovidemeaningful,physically-relatedpropertiesthatartistsanddeveloperscantweakandplaywith.Thepropertiesofsomematerialsareeasiertorepresentindicatinghowmetallictheyare,whileforsome,theotherismoreimportantinordertodefinehowtheyreflectlightsdirectly.IfyouhaveusedUnity4inthepast,Standard(Specularsetup)mightlookmorefamiliartoyou.Thisrecipewillshowyouhowtousethemetallicsetupeffectively.It’simportanttorememberthatthemetallicworkflowisnotjustformetallicmaterials;itisawaytodefinehowmaterialswilllookaccordingtohowmetallicornon-metallictheirsurfaceis.Despitebeingpresentedastwodifferenttypesofshaders,bothMetallicandSpecularsetupsaregenerallyequallyexpressive.AsshownintheUnitydocumentationathttp://docs.unity3d.com/Manual/StandardShaderMetallicVsSpecular.html,thesamematerialscanusuallyberecreatedwithbothsetups(seethefollowingimage):
GettingreadyThisrecipewillusetheStandardShaderprovidedinUnity5,sothereisnoneedtocreateanewone.Thestepstostarttherecipeareasfollows:
1. Createanewmaterial.2. FromitsInspector,makesurethatStandardisselectedfromitsShaderdrop-down
menu.
Youwillalsoneedatextured3Dmodel.
Howtodoit…TherearetwomaintexturesthatneedtobeconfiguredintheStandardShader:AlbedoandMetallic.Tousethemetallicworkfloweffectively,weneedtoinitializethesemapscorrectly:
1. TheAlbedomapshouldbeinitializedwiththeunlittextureofthe3Dmodel.2. TocreatetheMetallicmap,startbyduplicatingthefileforyourAlbedomap.You
candothisbyselectingthemapfromtheProjecttabandpressingCtrl+D.3. Usewhite(#ffffff)tocolortheregionsofthemapthatcorrespondtomaterialsthat
aremadeofpuremetal.Useblack(#000000)foralltheothercolors.Shadesofgreyshouldbeusedfordusty,weathered,orwornoutmetalsurfaces,rust,scratchedpaint,andsoon.Asamatteroffact,Unityusesonlytheredchanneltostorethemetallicvalue;thegreenandblueonesareignored.
4. UsethealphachanneloftheimagetoprovideinformationabouttheSmoothnessofthematerial.
5. AssigntheMetallicmaptothematerial.BothMetallicandSmoothnesssliderswilldisappearasthesetwopropertiesarenowcontrolledbythemap.
Howitworks…LegacyShadersallowartiststocreatematerialsthateasilybreaktheillusionofphotorealismbyhavinglightingconditionsthatareimpossibleinreality.ThishappensbecauseallthepropertiesofamaterialexposedinaLegacySurfaceShaderareuncorrelated.Byintroducingthemetallicworkflow,Unity5imposesmoreconstraintsonthewayobjectslook,makingitharderforartiststocreateillogicalmaterials.
Metalsareknownfortheconductingofelectricity;lightisintheformofelectromagneticwaves,meaningthatalmostallmetalsbehaveinasimilarwaycomparedtonon-conductors(oftenreferredasinsulators).Conductorstendtoreflectmostphotons(70-100%),resultinginhighreflectance.Theremaininglightisabsorbed,ratherthandiffused,suggestingthatconductorshaveaverydarkdiffusecomponent.Insulators,conversely,havealowreflectance(4%);therestofthelightisscatteredonthesurface,contributingtotheirdiffusedlooks.
IntheStandardShader,purelymetallicmaterialshavedarkdiffusecomponentsandthecoloroftheirspecularreflectionsisdeterminedbytheAlbedomap.Conversely,thediffusecomponentofpurelynon-metallicmaterialsisdeterminedbytheAlbedomap;thecoloroftheirspecularhighlightsisdeterminedbythecoloroftheincominglight.FollowingtheseprinciplesallowsthemetallicworkflowtocombinethealbedoandspecularintotheAlbedomap,enforcingphysically-accuratebehaviors.Thisalsoallowssavingmorespace,resultinginasignificantspeedupattheexpensesofreducedcontroloverthelookofyourmaterials.
SeealsoFormoreinformationaboutthemetallicsetup,youcanrefertotheselinks:
Calibrationchart:Howtocalibrateametallicmaterial(http://blogs.unity3d.com/wp-content/uploads/2014/11/UnityMetallicChart.png)Materialchart:HowtoinitializetheStandardShaderparametersforcommonmaterials(http://docs.unity3d.com/Manual/StandardShaderMaterialCharts.html)QuixelMEGASCANS:Avastlibraryofmaterials,includingtexturesandPBRparameters(http://quixel.se/megascans)PBRTextureConversion:HowtraditionalshaderscanbeconvertedtoPBRshaders(http://www.marmoset.co/toolbag/learn/pbr-conversion)SubstanceDesigner:Anode-basedsoftwaretoworkwithPBR(https://www.allegorithmic.com/products/substance-designer)TheTheoryofPhysically-basedRendering:AcompleteguideaboutPBR(https://www.allegorithmic.com/pbr-guide)
AddingtransparencytoPBRTransparencyissuchanimportantaspectingamesthattheStandardShadersupportsthreedifferentwaysofdoingit.Thisrecipeisusefulifyouneedtohaverealisticmaterialswithtransparentorsemi-transparentproperties.Glasses,bottles,windows,andcrystalsaregoodcandidatesforPBRtransparentshaders.ThisisbecauseyoucanstillhavealltherealismintroducedbyPBRwiththeadditionofatransparentortranslucenteffect.IfyouneedtransparencyforsomethingdifferentsuchasUIelementsorpixelart,therearemoreefficientalternativesthatareexploredintheCreatingatransparentmaterialrecipeinChapter2,SurfaceShadersandTextureMapping.
NoteInordertohaveatransparentStandardmaterial,changingthealphachannelofitsAlbedocolorpropertyisnotenough.UnlessyouproperlysetitsRenderingMode,yourmaterialwillnotappeartransparent.
GettingreadyThisrecipewillusetheStandardShader,sothereisnoneedtocreateanewone:
1. Createanewmaterial.2. MakesurethattheShaderpropertyissettoeitherStandardorStandard(Specular
setup)fromthematerial’sInspectortab.3. Assignthenewlycreatedmaterialtothe3Dobjectthatyouwanttobetransparent.
Howtodoit…TheStandardShaderprovidesthreedifferenttypesoftransparencies.Despitebeingverysimilar,theyhavesubtledifferencesandfitdifferentcontexts.
Semi-transparentmaterialsSomematerialssuchasclearplastics,crystal,andglassaresemi-transparent.ThismeansthattheybothrequirealltherealisticeffectsofPBR(suchasspecularhighlightsandFresnelrefractionandreflection)butallowthegeometrybehindtobeseen.Ifthisiswhatyouneed,performthefollowingsteps:
1. Fromthematerial’sInspectortab,setRenderingModetoTransparent.2. TheamountoftransparencyisdeterminedbythealphachanneloftheAlbedocolor
ortheAlbedomap(ifany).
ThefollowingpictureshowstheUnity5calibrationscenewithfourdifferenthighlypolishedplasticspheres.Fromlefttoright,theirtransparencyisincreased.Thelastsphereisfullytransparent,butretainsalltheaddedeffectsofPBR:
TheTransparentrenderingmodeisperfectforwindows,bottles,gems,andheadsets.
NoteYoushouldnoticethatmanytransparentmaterialsdon’tusuallyprojectshadows.Ontopofthis,theMetallicandSmoothnesspropertiesofamaterialcaninterferewiththetransparencyeffect.Amirror-likesurfacecanhavethealphasettozero,butifitreflectsalltheincominglight,itwon’tappeartransparent.
Fadingobjects
Sometimes,youwantanobjecttofullydisappearwithafadingeffect.Inthiscase,specularreflectionsandFresnelrefractionandreflectionshoulddisappearaswell.Whenafadingobjectisfullytransparent,itshouldalsobeinvisible.Todothis,performthefollowingsteps:
1. Fromthematerial’sInspectortab,setRenderingModetoFade.2. Asbefore,usethealphachanneloftheAlbedocolorormaptodeterminethefinal
transparency.
Thefollowingpictureshowsfadingspheres.ItisclearfromthepicturethatthePBReffectsfadewiththesphereaswell.Asyoucanseeinthefollowingimage,thelastoneontherightisalmostinvisible:
Thisrenderingmodeworksbestfornon-realisticobjects,suchasholograms,laserrays,fauxlights,ghosts,andparticleeffects.
SolidgeometrieswithholesMostofthematerialsencounteredinagamearesolid,meaningthattheydon’tallowlighttopassthroughthem.Atthesametime,manyobjectshaveaverycomplex(yetflat)geometry.Modelingleavesandgrasswith3Dobjectsisoftenoverkill.Amoreefficientapproachistouseaquad(rectangle)withaleaftexture.Whiletheleafitselfissolid,therestofthetextureshouldbefullytransparent.Ifthisiswhatyouwant,thenperformthefollowingsteps:
1. Fromthematerial’sInspectortab,setRenderingModetoCutout.2. UsetheAlphaCutoffslidertodeterminethecutoffthreshold.Allthepixelsinthe
AlbedomapwithanalphavalueequaltoorlessthanAlphaCutoffwillbehidden.
Thefollowingimage,takenfromtheUnityOfficialTutorialsonPBR(https://www.youtube.com/watch?v=fD_ho_ofY6A),showsyouhowtheeffectoftheCutoutrenderingmodecanbeusedtocreateaholeinthegeometry:
It’sworthnoticingthatCutoutdoesnotallowthebackofthegeometrytobeseen.Inthepreviousexample,youcouldnotseetheinnervolumeofthesphere.Ifyourequiresuchaneffect,youneedtocreateyourownshaderandmakesurethatthebackgeometryisnotculled.
SeealsoTheexamplesintheserecipehavebeencreatedusingtheUnity5ShaderCalibrationScene,whichisfreelyavailableintheAssetStoreathttps://www.assetstore.unity3d.com/en/#!/content/25422.Moreinformationaboutalbedoandtransparencycanbefoundathttp://docs.unity3d.com/Manual/StandardShaderMaterialParameterAlbedoColor.html.
CreatingmirrorsandreflectivesurfacesSpecularmaterialsreflectlightswhenobjectsareviewedfromcertainangles.Unfortunately,eventheFresnelreflection,whichisoneofthemostaccuratemodels,doesnotcorrectlyreflectlightsfromnearbyobjects.Thelightingmodelsexaminedinthepreviouschapterstookintoaccountonlylightsources,butignoredlightthatisreflectedfromothersurfaces.Withwhatyou’velearnedaboutshaderssofar,makingamirrorissimplynotpossible.GlobalilluminationmakesthispossiblebyprovidingPBRshaderswithinformationabouttheirsurroundings.Thisallowsobjectstohavenotjustspecularhighlights,butalsorealreflections,whichdependontheotherobjectsaroundthem.Real-timereflectionsareverycostlyandrequiremanualsettingupandtweakinginordertowork.Whendoneproperly,theycanbeusedtocreatemirror-likesurfaces,asseeninthefollowingpicture:
GettingreadyThisrecipewillnotfeatureanynewshader.Quitetheopposite;mostoftheworkisdonedirectlyintheeditor.Performthefollowingsteps:
1. Createanewscene.2. Createaquad,whichwillserveasamirror.3. Createanewmaterialandattachittothemirror.4. Placethequadinascenewithotherobjects.5. CreateanewreflectionprobefromGameObject|Light|ReflectionProbeand
placeitinfrontofthequad.
Howtodoit…Iftheprecedingstepshavebeenfollowedcorrectly,youshouldhaveaquadinthemiddleofyourscene,closetoareflectionprobe.Inordertomakeitinamirror,somechangesneedtobemade:
1. ChangetheshaderofthematerialtoStandardanditsRenderingModetoOpaque.2. ChangeitsMetallicandSmoothnesspropertiestoone.Youshouldseethematerial
reflectingtheskymoreclearly.3. SelectthereflectionprobeandchangeitsSizeandProbeOriginuntilitisinfrontof
thequadanditenclosesalltheobjectsthatyouwanttoreflect.4. Finally,changeitsTypetoRealtime.MakesurethatCullingMaskissetto
Everything.
Yourreflectionprobeshouldbeconfigured,asshowninthefollowingimage:
Ifyourprobeisusedforarealmirror,youshouldchecktheBoxProjectionflag.Ifitisusedforotherreflectivesurfaces,suchasshinypiecesofmetalorglasstables,youcanuncheckit.
Howitworks…Whenashaderwantsinformationaboutitssurroundings,itisusuallyprovidedinastructurecalledcubemaps.TheyhavebeenbrieflymentionedinChapter1,CreatingYourFirstShader,asoneoftheshaderpropertytypes,amongColor,2D,Float,andVector.Looselyspeaking,cubemapsarethe3Dequivalentof2Dtextures;theyrepresenta360-degreeviewoftheworld,asseenfromacenterpoint.Unity5previewscubemapswithasphericalprojection,asseeninthefollowingpicture:
Whencubemapsareattachedwithacamera,theyarereferredtoasskyboxesastheyareusedtoprovideawaytoreflectthesky.Theycanbeusedtoreflectgeometriesthatarenotintheactualscene,suchasnebulae,clouds,stars,andsoon.
Thereasonwhytheyarecalledcubemapsisbecauseofthewaytheyarecreated:acubemapismadeupofsixdifferenttextures,eachoneattachedtothefaceofacube.Youcancreateacubemapmanuallyordelegateittoareflectionprobe.Youcanimagineareflectionprobeasacollectionofsixcameras,creatinga360mappingofthesurroundingarea.Thisalsogivesyouanideawhyprobesaresoexpensive.Bycreatingoneinourscene,weallowUnitytoknowwhichobjectsarearoundthemirror.Ifyouneedmorereflectivesurfaces,youcanaddmultipleprobes.Youneednofurtheractionforthereflectionprobestowork.TheStandardShaderswillusethemautomatically.
YoushouldnoticethatwhentheyaresettoRealtime,theyrendertheircubemapatthebeginningofeveryframe.Thereisatricktomakethisfaster;ifyouknowthatpartofthegeometrythatyouwanttoreflectdoesnotmove,youcanbakethereflection.ThismeansthatUnitycancalculatethereflectionbeforestartingthegame,allowingmoreprecise(andcomputationallyexpensive)calculations.Inordertodothis,yourreflectionprobemustbesettoBakedandwillworkonlyforobjectsthatareflaggedasStatic.Staticobjectscannotmoveorchange,whichmakesthemperfectforterrains,buildings,andprops.Everytimeastaticobjectismoved,Unitywillregeneratethecubemapsforitsbakedreflectionprobes.Thismighttakeafewminutestoseveralhours.
YoucanmixRealtimeandBakedprobestoincreasetherealismofyourgame.Bakedprobeswillprovideveryhigh-qualityreflectionsenvironmentalreflections,whilethereal-timeonescanbeusedtomoveobjectssuchascarsormirrors.ThenextBakinglightsinyourscenerecipewillexplainindetailhowlightbakingworks.
SeealsoIfyouareinterestedinlearningmoreaboutreflectionprobes,youshouldchecktheselinks:
Unity5manualaboutReflectionProbe:http://docs.unity3d.com/Manual/class-ReflectionProbe.html
BakinglightsinyoursceneRenderinglightingisaveryexpensiveprocess.Evenwithstate-of-the-artGPUs,accuratelycalculatingthelighttransport(whichishowlightbouncesbetweensurfaces)cantakehours.Inordertomakethisprocessfeasibleforgames,real-timerenderingisessential.Modernenginescompromisebetweenrealismandefficiency;mostofthecomputationisdonebeforehandinaprocesscalledlightbaking.Thisrecipewillexplainhowlightbakingworksandhowyoucangetthemostoutofit.
GettingreadyLightbakingrequiresyoutohaveasceneready.Itshouldhavegeometriesand,obviously,lights.Forthisrecipe,wewillrelyonUnity’sstandardfeaturessothereisnoneedtocreateadditionalshadersormaterials.Forabettercontrol,youmightwanttoaccesstheLightingwindow.Ifyoudon’tseeit,selectWindow|Lightingfromthemenuanddockitwhereitismoreconvenientforyou.
Howtodoit…Lightbakingrequiressomemanualconfiguration.Therearethreeessential,yetindependent,stepsthatyouneedtotake.
ConfiguringthestaticgeometryThesestepsmustbefollowedfortheconfiguration:
1. Identifyalltheobjectsinyourscenethatdonotchangeposition,size,andmaterial.Possiblecandidatesarebuildings,walls,terrains,props,trees,andothers.
2. Selecttheseobjects,andchecktheStaticboxfromtheInspectortab,asshowninthefollowingimage.Ifanyoftheselectedobjectshaschildren,Unitywillaskifyouwantthemtobeconsideredstaticaswell.Iftheymeettherequirements(fixedposition,size,andmaterial),selectYes,changechildreninthepop-upbox:
3. Ifalightqualifiesasastaticobjectbutilluminatesnon-staticgeometry,makesurethatitsBakingpropertyissettoMixed.Ifitwillaffectonlystaticobjects,setittoBaked.
ConfiguringthelightprobesThereareobjectsinyourgamethatwillmove,suchasthemaincharacter,enemies,andtheothernon-playablecharacters(NPCs).Iftheyenterastaticregionthatisilluminated,youmightwanttosurrounditwithlightprobes.Todothis,followthegivensteps:
1. Fromthemenu,navigatetoGameObject|Light|LightProbeGroup.AnewobjectcalledLightProbeGroupwillappearinHierarchy.
2. Onceselected,fourinterconnectedsphereswillappear.Clickandmovethemaroundthescenesothattheyenclosethestaticregioninwhichyourcharacterscanenter.Thefollowingpictureshowsanexampleofhowlightprobescanbeusedtoenclosethevolumeofastaticofficespace:
3. Selectthemovingobjectsthatwillenterthelightproberegion.4. FromtheirInspector,expandtheirrenderercomponent(usuallyMeshRenderer)
andmakesurethatUseLightProbesischecked(seethefollowingimage):
Decidingwhereandwhentouselightprobesisacriticalproblem;moreinformationaboutthiscanbefoundintheHowitworks…section.
BakingthelightsTobakethelights,followthegivensteps:
1. Tofinallybakethelights,opentheLightingwindowandselectitsLightmapstab.2. IftheAutocheckboxisenabled,Unitywillautomaticallyexecutethebakingprocess
inthebackground.Ifnot,clickonBuild.
NoteLightbakingcantakeseveralhoursevenforarelativelysmallscene.Ifyouareconstantlymovingstaticobjectsorlights,Unitywillrestarttheprocessfromscratchcausingasevereslowdownintheeditor.YoucanunchecktheAutocheckboxfromtheLighting|Lightmapstabtopreventthissothatyoucandecidewhentostarttheprocessmanually.
Howitworks…Themostcomplicatedpartoftherenderingisthelighttransport.Duringthisphase,theGPUcalculateshowtheraysoflightbouncebetweenobjects.Ifanobjectanditslightsdon’tmove,thiscalculationcanbedoneonlyonceasitwillneverchangeduringthegame.FlagginganobjectasStaticishowyouaretellingUnitythatsuchanoptimizationcanbemade.
Looselyspeaking,lightbakingreferstotheprocessofcalculatingtheglobalilluminationofastaticobjectandsavingitinwhatiscalledalightmap.Oncebakingiscompleted,lightmapscanbeseenintheLightmapstaboftheLightingwindow:
Lightbakingcomesatagreatexpense:memory.Everystaticsurfaceis,infact,retexturedsothatitalreadyincludesitslightingcondition.Let’simaginethatyouhaveaforestoftrees,allsharingthesametexture.Oncetheyaremadestatic,eachtreewillhaveitsveryowntexture.Lightbakingnotonlyincreasesthesizeofyourgame,butcantakealotoftexturememoryifusedindiscriminately.
Thesecondaspectintroducedinthisrecipeislightprobing.Lightbakingproducesextremelyhigh-qualityresultsforstaticgeometriesbutdoesnotworkonmovingobjects.Ifyourcharacterisenteringinastaticregion,itcanlooksomehowdetachedfromtheenvironment.Itsshadingwillnotmatchthesurrounding,resultinginanaestheticallyunpleasantresult.Otherobjects,suchasskinnedmeshrenderers,willnotreceiveglobalilluminationevenifmadestatic.Bakinglightsinrealtimeisnotpossible,althoughlight
probesofferaneffectivealternative.Everylightprobesamplestheglobalilluminationataspecificpointinspace.Alightprobegroupcansampleseveralpointsinspace,allowingtointerpolateglobalilluminationwithinaspecificvolume.Thisallowsustocastabetterlightonmovingobjects,evendespitethefactthatglobalilluminationhasbeencalculatedonlyforafewpoints.Itisimportanttorememberthatlightprobesneedtoencloseavolumeinordertowork.Itisbesttoplacelightprobesinregionswherethereisasuddenchangeinthelightcondition.Similartolightmaps,probesconsumememoryandshouldbeplacedwisely;rememberthattheyexistonlyfornon-staticgeometry.
Evenwhileusinglightprobes,thereareafewaspectsthatUnity’sglobalilluminationcannotcapture.Non-staticobjects,forinstance,cannotreflectlightonotherobjects.
SeealsoYoucanreadmoreaboutlightprobesathttp://docs.unity3d.com/Manual/LightProbes.html.
Chapter5.VertexFunctionsThetermshaderoriginatesfromthefactthatCghasbeenusedmainlytosimulaterealisticlightingconditions(shadows)on3Dmodels.Despitethis,shadersarenowmuchmorethanthat.Theynotonlydefinethewaytheobjectsaregoingtolook,theycanalsoredefinetheirshapesentirely.Ifyouwanttolearnhowtomanipulatethegeometryofa3Dobjectviashaders,thisisthechapterforyou.
Inthischapter,youwilllearnthefollowingrecipes:
AccessingavertexcolorinaSurfaceShaderAnimatingverticesinaSurfaceShaderExtrudingyourmodelsImplementingasnowshaderImplementingavolumetricexplosion
IntroductionInChapter1,CreatingYourFirstShader,weexplainedthat3Dmodelsarenotjustacollectionoftriangles.Eachvertexcancontaindatathatisessentialtorenderthemodelitselfcorrectly.Thischapterwillexplorehowtoaccessthisinformationinordertouseitinashader.WewillalsoexploreindetailhowthegeometryofanobjectcanbedeformedsimplyusingCgcode.
AccessingavertexcolorinaSurfaceShaderLet’sbeginthischapterbytakingalookathowwecanaccesstheinformationofamodel’svertexusingthevertexfunctioninaSurfaceShader.Thiswillarmuswiththeknowledgetostartutilizingtheelementscontainedwithinamodel’svertextocreatereallyusefulandvisuallyappealingeffects.
Avertexinavertexfunctioncanreturninformationaboutitselfthatweneedtobeawareof.Youcanactuallyretrievethevertices’normaldirectionsasafloat3value,thepositionofthevertexasfloat3,andyoucanevenstorecolorvaluesineachvertexandreturnthatcolorasfloat4.Thisiswhatwewilltakealookatinthisrecipe.WeneedtoseehowtostorecolorinformationandretrievethisstoredcolorinformationinsideeachvertexofaSurfaceShader.
GettingreadyInordertowritethisshader,weneedtoprepareafewassets.ThefollowingstepswillsetusuptocreatethisVertexShader:
1. Inordertoviewthecolorsofavertex,weneedtohaveamodelthathashadcolorappliedtoitsvertices.WhileyoucoulduseUnitytoapplycolors,youwouldhavetowriteatooltoallowanindividualtoapplythecolorsorwritesomescriptstoachievethecolorapplication.Inthecaseofthisrecipe,wesimplyutilizedMayatoapplythecolorstoourmodel.Thismodelisavailableonthebook’sSupportpageathttps://www.packtpub.com/books/content/support.
2. Createanewsceneandplacetheimportedmodelinthescene.3. Createanewshaderandmaterial.Whencompleted,assigntheshadertothematerial
andthenthematerialtotheimportedmodel.
Yoursceneshouldnowlooksimilartothefollowingscreenshot:
Howtodoit…Withourscene,shader,andmaterialcreatedandreadytogo,wecanbegintowritethecodeforourshader.Launchtheshaderbydouble-clickingonitintheProjecttabintheUnityeditor.Performthefollowingsteps:
1. Aswearecreatingaverysimpleshader,wewillnotneedtoincludeanypropertiesinourPropertiesblock.Wewillstillincludeaglobaltintcolor,justtostayconsistentwiththeothershadersinthisbook.EnterthefollowingcodeinthePropertiesblockofyourshader:
Properties
{
_MainTint("GlobalColorTint",Color)=(1,1,1,1)
}
2. ThisnextsteptellsUnitythatwewillbeincludingavertexfunctioninourshader:
CGPROGRAM
#pragmasurfacesurfLambertvertex:vert
3. Asusual,ifwehaveincludedpropertiesinourPropertiesblock,wemustmakesuretocreateacorrespondingvariableinourCGPROGRAMstatement.Enterthefollowingcodejustbelowthe#pragmastatement:
float4_MainTint;
4. WenowturnourattentiontotheInputstruct.Weneedtoaddanewvariableinorderforoursurf()functiontoaccessthedatagiventousbyourvert()function:
structInput
{
float2uv_MainTex;
float4vertColor;
};
5. Now,wecanwriteoursimplevert()functiontogainaccesstothecolorsstoredineachvertexofourmesh:
voidvert(inoutappdata_fullv,outInputo)
{
o.vertColor=v.color;
}
6. Finally,wecanusethevertexcolordatafromourInputstructtobeassignedtotheo.Albedoparametersinthebuilt-inSurfaceOutputstruct:
voidsurf(InputIN,inoutSurfaceOutputo)
{
o.Albedo=IN.vertColor.rgb*_MainTint.rgb;
}
7. Withourcodecompleted,wecannowre-entertheUnityeditorandlettheshadercompile.Ifallgoeswell,youshouldseesomethingsimilartothefollowing
screenshot:
Howitworks…Unityprovidesuswithawaytoaccessthevertexinformationofthemodeltowhichashaderisattached.Thisgivesusthepowertomodifythingssuchasthevertices’positionandcolor.Withthisrecipe,wehaveimportedameshfromMaya(thoughjustaboutany3Dsoftwareapplicationcanbeused),wherevertexcolorswereaddedtoVerts.You’llnoticethatbyimportingthemodel,thedefaultmaterialwillnotdisplaythevertexcolors.Weactuallyhavetowriteashadertoextractthevertexcoloranddisplayitonthesurfaceofthemodel.Unityprovidesuswithalotofbuilt-infunctionalitywhenusingSurfaceShaders,whichmaketheprocessofextractingthisvertexinformationquickandefficient.
OurfirsttaskistotellUnitythatwewillbeusingavertexfunctionwhencreatingourshader.Wedothisbyaddingthevertex:vertparametertothe#pragmastatementofCGPROGRAM.ThisautomaticallymakesUnitylookforavertexfunctionnamedvert()whenitgoestocompiletheshader.Ifitdoesn’tfindone,Unitywillthrowacompilingerrorandaskyoutoaddavert()functiontoyourshader.
Thisbringsustoournextstep.Wehavetoactuallycodethevert()function,asseeninstep5.Byhavingthisfunction,wecanaccessthebuilt-indatastructcalledappdata_full.Thisbuilt-instructiswherethevertexinformationisstored.So,wethenextractthevertexcolorinformationbypassingittoourInputstructbyaddingthecode,o.vertColor=v.color.
TheovariablerepresentsourInputstructandthevvariableisourappdata_fullvertexdata.Inthiscase,wearesimplytakingthecolorinformationfromtheappdata_fullstructandputtingitinourInputstruct.OncethevertexcolorisinourInputstruct,wecanuseitinoursurf()function.Inthecaseofthisrecipe,wesimplyapplythecolortotheo.Albedoparametertothebuilt-inSurfaceOutputstruct.
There’smore…Onecanalsoaccessafourthcomponentfromthevertcolordata.Ifyounotice,thevertColorvariablewedeclaredintheInputstructisofthefloat4type.Thismeansthatwearealsopassingthealphavalueofthevertexcolors.Knowingthis,youcanuseittoyouradvantageforthepurposeofstoringafourthvertexcolortoperformeffectssuchastransparencyorgivingyourselfonemoremasktoblendtwotextures.It’sreallyuptoyouandyourproductiontodetermineifyoureallyneedtousethefourthcomponent,butitisworthmentioninghere.
WithUnity5,wenowhavetheabilitytotargetshaderstoDirectX11.Thisisgreat,butitmeansthatthecompilingprocessfortheshadersisnowabitpickier.Thismeansthatweneedtoincludeonemorelineofcodetoourshadertoinitializetheoutputofthevertexinformationproperly.Thefollowingcodeshowswhatthevertexfunctioncodelookslike,ifyouareusingDirectX11inyourshader:
voidvert(inoutappdata_fullv,outInputo)
{
UNITY_INITIALIZE_OUTPUT(Input,o);
o.vertColor=v.color;
}
Byincludingthislineofcode,yourVertexShaderwillnotthrowanywarnings,whichsaythatitwon’tcompiletoDirectX11appropriately.
AnimatingverticesinaSurfaceShaderNowthatweknowhowtoaccessdataonaper-vertexbasis,let’sexpandourknowledgesettoincludeothertypesofdataandpositionofavertex.
Usingavertexfunction,wecanaccessthepositionofeachvertexinamesh.Thisallowsustoactuallymodifyeachindividualvertexwhiletheshaderdoestheprocessing.
Inthisrecipe,wewillcreateashaderthatwillallowustomodifythepositionsofeachvertexonameshwithasinewave.Thistechniquecanbeusedtocreateanimationsforobjectssuchasflagsorwavesonanocean.
GettingreadyLet’sgatherourassetstogethersothatwecancreatethecodeforourVertexShader:
1. Createanewsceneandplaceaplanemeshinthecenterofthescene.2. Thencreateanewshaderandmaterial.3. Finally,assigntheshadertothematerialandthematerialtotheplanemesh.
Yoursceneshouldlooksimilartothefollowingscreenshot:
Howtodoit…Withourscenereadytogo,let’sdouble-clickonournewlycreatedshadertoopenitinMonoDevelop:
1. Let’sbeginwithourshaderbypopulatingthePropertiesblock:
Properties
{
_MainTex("Base(RGB)",2D)="white"{}
_tintAmount("TintAmount",Range(0,1))=0.5
_ColorA("ColorA",Color)=(1,1,1,1)
_ColorB("ColorB",Color)=(1,1,1,1)
_Speed("WaveSpeed",Range(0.1,80))=5
_Frequency("WaveFrequency",Range(0,5))=2
_Amplitude("WaveAmplitude",Range(-1,1))=1
}
2. WenowneedtotellUnitythatwearegoingtobeusingavertexfunctionbyaddingthefollowingtothe#pragmastatement:
CGPROGRAM
#pragmasurfacesurfLambertvertex:vert
3. Inordertoaccessthevaluesthathavebeengiventousbyourproperties,weneedtodeclareacorrespondingvariableinourCGPROGRAMblock:
sampler2D_MainTex;
float4_ColorA;
float4_ColorB;
float_tintAmount;
float_Speed;
float_Frequency;
float_Amplitude;
float_OffsetVal;
4. Wewillbeusingthevertexpositionmodificationasavertcoloraswell.Thiswillallowustotintourobject:
structInput
{
float2uv_MainTex;
float3vertColor;
}
5. Atthispoint,wecanperformourvertexmodificationusingasinewaveandvertexfunction.EnterthefollowingcodeaftertheInputstruct:
voidvert(inoutappdata_fullv,outInputo)
{
floattime=_Time*_Speed;
floatwaveValueA=sin(time+v.vertex.x*_Frequency)*_Amplitude;
v.vertex.xyz=float3(v.vertex.x,v.vertex.y+waveValueA,
v.vertex.z);
v.normal=normalize(float3(v.normal.x+waveValueA,v.normal.y,
v.normal.z));
o.vertColor=float3(waveValueA,waveValueA,waveValueA);
}
6. Finally,wecompleteourshaderbyperformingalerp()functionbetweentwocolorssothatwecantintthepeaksandvalleysofournewmesh,modifiedbyourvertexfunction:
voidsurf(InputIN,inoutSurfaceOutputo)
{
half4c=tex2D(_MainTex,IN.uv_MainTex);
float3tintColor=lerp(_ColorA,_ColorB,IN.vertColor).rgb;
o.Albedo=c.rgb*(tintColor*_tintAmount);
o.Alpha=c.a;
}
Aftercompletingthecodeforyourshader,switchbacktoUnityandlettheshadercompile.Oncecompiled,youshouldseesomethingsimilartothefollowingscreenshot:
Howitworks…Thisparticularshaderusesthesameconceptfromthelastrecipe,exceptthatthistime,wearemodifyingthepositionsoftheverticesinthemesh.Thisisreallyusefulifyoudon’twanttorigupsimpleobjects,suchasaflag,andthenanimatethemusingaskeletonstructureorhierarchyoftransforms.
Wesimplycreateasinewavevalueusingthesin()functionthatisbuiltintotheCglanguage.Aftercalculatingthisvalue,weaddittotheyvalueofeachvertexposition,creatingawave-likeeffect.
Wealsodidalittlebitofmodificationtothenormalonthemeshjusttogiveitamorerealisticshadingbasedonthesinewavevalue.
Youwillseehoweasyitistoperformmorecomplexvertexeffectsbyutilizingthebuilt-invertexparametersthatSurfaceShadersgiveus.
ExtrudingyourmodelsOneofthebiggestproblemsingamesisrepetitions.Creatingnewcontentisatime-consumingtask,andwhenyouhavetofacethousandsofenemies,chancesarethattheywillalllookthesame.Arelativelycheaptechniquetoaddvariationstoyourmodelsisusingashaderthataltersitsbasicgeometry.Thisrecipewillshowyouatechniquecallednormalextrusion,whichcanbeusedtocreateachubbierorskinnierversionofamodel,asshowninthefollowingpicturewiththesoldierfromtheUnitycampdemo:
GettingreadyForthisrecipe,weneedtohaveaccesstotheshaderusedbythemodelthatyouwanttoalter.Onceyouhaveit,weduplicateitsothatwecanedititsafely.Itcanbedoneasfollows:
1. Findtheshaderyourmodelisusingandonceselected,duplicateitbypressingCtrl+D.
2. Duplicatetheoriginalmaterialofthemodelandassigntheclonedshadertoit.3. Assignthenewmaterialtoyourmodel,andstarteditingit.
Inorderforthiseffecttowork,yourmodelshouldhavenormals.
Howtodoit…Tocreatethiseffect,startbymodifyingtheduplicatedshader:
1. Let’sstartbyaddingapropertytoourshader,whichwillbeusedtomodulateitsextrusion.Therangepresentedheregoesfrom-1to+1,butyoumighthavetoadjustthisaccordingtoyourownneeds:
_Amount("ExtrusionAmount",Range(-1,+1))=0
2. Couplethepropertywithitsrespectivevariable:
float_Amount;
3. Changethe#pragmadirectivesothatitnowusesavertexmodifier.Youcandothisbyaddingvertex:function_nameattheendofit.Inourcase,wehavecalledthefunction,vert:
#pragmasurfacesurfLambertvertex:vert
4. Addthefollowingvertexmodifier:
voidvert(inoutappdata_fullv){
v.vertex.xyz+=v.normal*_Amount;
}
5. Theshaderisnowready;youcanusetheExtrusionAmountsliderinthematerial’sInspectortabtomakeyourmodelskinnierorchubbier.
Howitworks…SurfaceShadersworksintwosteps.Inallthepreviouschapters,weonlyexploreditslastone:thesurfacefunction.Thereisanotherfunctionthatcanbeused:thevertexmodifier.Ittakesthedatastructureofavertex(whichisusuallycalledappdata_full)andappliesatransformationtoit.Thisgivesusthefreedomtodovirtuallyeverythingwiththegeometryofourmodel.Wesignalthegraphicsprocessingunit(GPU)thatsuchafunctionexistsbyaddingvertex:verttothe#pragmadirectiveoftheSurfaceShader.YoucanrefertoChapter6,FragmentShadersandGrabPasses,tolearnhowvertexmodifierscanbedefinedinaVertexandFragmentShaderinstead.
Oneofthemostsimple,yeteffective,techniquesthatcanbeusedtoalterthegeometryofamodeliscallednormalextrusion.Itworksbyprojectingavertexalongitsnormaldirection.Thisisdonebythefollowinglineofcode:
v.vertex.xyz+=v.normal*_Amount;
Thepositionofavertexisdisplacedby_Amountunitstowardthevertexnormal.If_Amountgetstoohigh,theresultscanbequiteunpleasant.Withsmallervalues,however,youcanaddalotofvariationstoyourmodels.
There’smore…Ifyouhavemultipleenemiesandwanteachonetohaveitsownweight,youhavetocreateadifferentmaterialforeachoneofthem.Thisisnecessaryasmaterialsarenormallysharedbetweenmodelsandchangingonewillchangeallofthem.Thereareseveralwaysinwhichyoucandothis;thequickestoneistocreateascriptthatautomaticallydoesitforyou.Thefollowingscript,onceattachedtoanobjectwithaRenderer,willduplicateitsfirstmaterialandsetthe_Amountpropertyautomatically:
usingUnityEngine;
publicclassNormalExtruder:MonoBehaviour{
[Range(-0.0001f,0.0001f)]
publicfloatamount=0;
//Usethisforinitialization
voidStart(){
Materialmaterial=GetComponent<Renderer>().sharedMaterial;
MaterialnewMaterial=newMaterial(material);
newMaterial.SetFloat("_Amount",amount);
GetComponent<Renderer>().material=newMaterial;
}
}
AddingextrusionmapsThistechniquecanactuallybeimprovedevenfurther.Wecanaddanextratexture(orusethealphachannelofthemainone)toindicatetheamountoftheextrusion.Thisallowsamuchbettercontroloverwhichpartsareraisedorlowered.Thefollowingcodeshowsyouhowitispossibletoachievesuchaneffect:
sampler2D_ExtrusionTex;
voidvert(inoutappdata_fullv){
float4tex=tex2Dlod(_ExtrusionTex,float4(v.texcoord.xy,0,0));
floatextrusion=tex.r*2-1;
v.vertex.xyz+=v.normal*_Amount*extrusion;
}
Theredchannelof_ExtrusionTexisusedasamultiplyingcoefficientfornormalextrusion.Avalueof0.5leavesthemodelunaffected;darkerorlightershadesareusedtoextrudeverticesinwardoroutward,respectively.Youshouldnoticethattosampleatexturewithinavertexmodifier,tex2Dlodshouldbeusedinsteadoftex2D.
NoteInshaders,colorchannelsgofrom0to1,althoughsometimesyouneedtorepresentnegativevaluesaswell(suchasinwardextrusion).Whenthisisthecase,treat0.5aszero,havingsmallervaluesconsideredasnegativeandhighervaluesaspositive.Thisisexactlywhathappenswithnormals,whichareusuallyencodedinRGBtextures.TheUnpackNormal()functionisusedtomapavalueintherange(0,1)ontherange(-1,+1).Mathematicallyspeaking,thisisequivalenttotex.r*2-1.
Extrusionmapsareperfecttozombifycharactersbyshrinkingtheskintohighlighttheshapeofthebonesunderneath.Thefollowingpictureshowsyouhowahealthysoldiercanbetransformedintoacorpseusingjustashaderandextrusionmap.Comparedtothepreviousexample,youcannoticehowtheclothingisunaffected.Theshaderusedinthefollowingpicturealsodarkenstheextrudedregionstogiveanevenmoreemaciatedlooktothesoldier:
ImplementingasnowshaderThesimulationofsnowhasalwaysbeenachallengeingames.Thevastmajorityofgamessimplyincludessnowdirectlyinthemodel’stexturessothattheirtopslookwhite.However,whatifoneoftheseobjectsstartsrotating?Snowisnotjustalickofpaintonasurface;itisaproperaccumulationofmaterialandshouldbetreatedassuch.Thisrecipeshowsyouhowtogiveasnowylooktoyourmodelsusingjustashader.
Thiseffectisachievedintwosteps.First,awhitecolorisusedforallthetrianglesfacingthesky.Second,theirverticesareextrudedtosimulatetheeffectofsnowaccumulation.Youcanseetheresultinthefollowingpicture:
NoteKeepinmindthatthisrecipedoesnotaimtocreateaphotorealisticsnoweffect.Itprovidesagoodstartingpoint,butitisuptoanartisttocreatetherighttexturesandfind
therightparameterstomakeitfityourgame.
GettingreadyThiseffectispurelybasedonshaders.Wewillneedthefollowing:
1. Createanewshaderforthesnoweffect.2. Createanewmaterialfortheshader.3. Assignthenewlycreatedmaterialtotheobjectthatyouwanttobesnowy.
Howtodoit…Tocreateasnowyeffect,openyourshaderandmakethefollowingchanges:
1. Replacethepropertiesoftheshaderwiththefollowingones:
_MainColor("MainColor",Color)=(1.0,1.0,1.0,1.0)
_MainTex("Base(RGB)",2D)="white"{}
_Bump("Bump",2D)="bump"{}
_Snow("Levelofsnow",Range(1,-1))=1
_SnowColor("Colorofsnow",Color)=(1.0,1.0,1.0,1.0)
_SnowDirection("Directionofsnow",Vector)=(0,1,0)
_SnowDepth("Depthofsnow",Range(0,1))=0
2. Completethemwiththeirrelativevariables:
sampler2D_MainTex;
sampler2D_Bump;
float_Snow;
float4_SnowColor;
float4_MainColor;
float4_SnowDirection;
float_SnowDepth;
3. ReplacetheInputstructurewiththefollowingone:
structInput{
float2uv_MainTex;
float2uv_Bump;
float3worldNormal;
INTERNAL_DATA
};
4. Replacethesurfacefunctionwiththefollowingone.Itwillcolorthesnowypartsofthemodelwhite:
voidsurf(InputIN,inoutSurfaceOutputStandardo){
half4c=tex2D(_MainTex,IN.uv_MainTex);
o.Normal=UnpackNormal(tex2D(_Bump,IN.uv_Bump));
if(dot(WorldNormalVector(IN,o.Normal),_SnowDirection.xyz)>=
_Snow)
o.Albedo=_SnowColor.rgb;
else
o.Albedo=c.rgb*_MainColor;
o.Alpha=1;
}
5. Configurethe#pragmadirectivesothatitusesvertexmodifiers:
#pragmasurfacesurfStandardvertex:vert
6. Addthefollowingvertexmodifiers,whichextrudetheverticescoveredinsnow:
voidvert(inoutappdata_fullv){
float4sn=mul(UNITY_MATRIX_IT_MV,_SnowDirection);
if(dot(v.normal,sn.xyz)>=_Snow)
v.vertex.xyz+=(sn.xyz+v.normal)*_SnowDepth*_Snow;
}
Youcannowusethematerial’sInspectortabtoselecthowmuchofyourmodelisgoingtobecoveredandhowthickthesnowshouldbe.
Howitworks…Thisshaderworksintwosteps.
ColoringthesurfaceThefirstonealtersthecolorofthetrianglesthatarefacingthesky.Itaffectsallthetriangleswithanormaldirectionsimilarto_SnowDirection.AsseenbeforeinChapter3,UnderstandingLightingModels,comparingunitvectorscanbedoneusingthedotproduct.Whentwovectorsareorthogonal,theirdotproductiszero;itisone(orminusone)whentheyareparalleltoeachother.The_Snowpropertyisusedtodecidehowalignedtheyshouldbetobeconsideredfacingthesky.
Ifyoulookcloselyatthesurfacefunction,youcanseethatwearenotdottingthenormalandsnowdirectiondirectly.Thisisbecausetheyareusuallydefinedinadifferentspace.Thesnowdirectionisexpressedinworldcoordinates,whiletheobjectnormalsareusuallyrelativetothemodelitself.Ifwerotatethemodel,itsnormalswillnotchange,whichisnotwhatwewant.Tofixthis,weneedtoconvertthenormalsfromtheirobjectcoordinatestoworldcoordinates.ThisisdonewiththeWorldNormalVector()function,asseeninthefollowingcode:
if(dot(WorldNormalVector(IN,o.Normal),_SnowDirection.xyz)>=_Snow)
o.Albedo=_SnowColor.rgb;
else
o.Albedo=c.rgb*_MainColor;
Thisshadersimplycolorsthemodelwhite;amoreadvancedoneshouldinitializetheSurfaceOutputStandardstructurewithtexturesandparametersfromarealisticsnowmaterial.
AlteringthegeometryThesecondeffectofthisshaderaltersthegeometrytosimulatetheaccumulationofsnow.Firstly,weidentifywhichtriangleshavebeencoloredwhitebytestingthesameconditionusedinthesurfacefunction.Thistime,unfortunately,wecannotrelyonWorldNormalVector()astheSurfaceOutputStandardstructureisnotyetinitializedinthevertexmodifier.Weusethisothermethodinstead,whichconverts_SnowDirectiontoobjectcoordinates:
float4sn=mul(UNITY_MATRIX_IT_MV,_SnowDirection);
Then,wecanextrudethegeometrytosimulatetheaccumulationofsnow:
if(dot(v.normal,sn.xyz)>=_Snow)
v.vertex.xyz+=(sn.xyz+v.normal)*_SnowDepth*_Snow;
Onceagain,thisisaverybasiceffect.Onecoulduseatexturemaptocontroltheaccumulationofsnowmorepreciselyorgiveapeculiar,unevenlook.
SeealsoIfyouneedhigh-qualitysnoweffectsandpropsforyourgame,youcanalsochecktheseresourcesontheUnityAssetStore:
WinterSuite($30):Amuchmoresophisticatedversionofthesnowshaderpresentedinthisrecipecanbefoundathttps://www.assetstore.unity3d.com/en/#!/content/13927WinterPack($60):Averyrealisticsetofpropsandmaterialsforsnowyenvironmentscanbefoundathttps://www.assetstore.unity3d.com/en/#!/content/13316
ImplementingavolumetricexplosionTheartofgamedevelopmentisaclevertrade-offbetweenrealismandefficiency.Thisisparticularlytrueforexplosions;theyareattheheartofmanygames,yetthephysicsbehindthemisoftenbeyondthecomputationalpowerofmodernmachines.Explosionsare,essentially,nothingmorethanveryhotballsofgas;hence,theonlywaytocorrectlysimulatethemisbyintegratingafluidsimulationintoyourgame.Asyoucanimagine,thisisinfeasibleforruntimeapplication,andmanygamessimulatethemsimplywithparticles.Whenanobjectexplodes,itiscommontosimplyinstantiatemanyfire,smoke,anddebrisparticlesthat,together,canachievebelievableresults.Thisapproach,unfortunately,isnotveryrealisticandiseasytospot.Thereisanintermediatetechniquethatcanbeusedtoachieveamuchmorerealisticeffect:volumetricexplosions.Theideabehindthisconceptisthatexplosionsarenottreatedanymorelikeabunchofparticles;theyareevolving3Dobjects,notjustflat2Dtextures.
GettingreadyStartthisrecipewiththefollowingsteps:
1. Createanewshaderforthiseffect.2. Createanewmaterialtohosttheshader.3. Attachthematerialtoasphere.Youcancreateonedirectlyfromtheeditor,
navigatingtoGameObject|3DObject|Sphere.
NoteThisrecipeworkswellwiththestandardUnitySphere,butifyouneedbigexplosions,youmightneedtouseamorehigh-polysphere.Infact,avertexfunctioncanonlymodifytheverticesofamesh.Alltheotherpointswillbeinterpolatedusingthepositionsofthenearbyvertices.Fewerverticesmeanalowerresolutionforyourexplosions.
4. Forthisrecipe,youwillalsoneedaramptexturethathas,inagradient,allthecolorsyourexplosionswillhave.YoucancreateatexturelikethefollowingimageusingGIMPorPhotoshop:
5. Onceyouhavethepicture,importittoUnity.Then,fromitsInspector,makesurethatFilterModeissettoBilinearandWrapModetoClamp.Thesetwosettingsmakesurethattheramptextureissampledsmoothly.
6. Lastly,youwillneedanoisytexture.YoucansearchontheInternetforfreelyavailablenoisetextures.ThemostcommonlyusedonesaregeneratedusingPerlinnoise.
Howtodoit…Thiseffectworksintwosteps:avertexfunctiontochangethegeometryandsurfacefunctiontogiveittherightcolor.Thestepsareasfollows:
1. Addthefollowingpropertiestotheshader:
_RampTex("ColorRamp",2D)="white"{}
_RampOffset("Rampoffset",Range(-0.5,0.5))=0
_NoiseTex("Noisetex",2D)="gray"{}
_Period("Period",Range(0,1))=0.5
_Amount("_Amount",Range(0,1.0))=0.1
_ClipRange("ClipRange",Range(0,1))=1
2. AddtheirrelativevariablessothattheCgcodeoftheshadercanactuallyaccessthem:
sampler2D_RampTex;
half_RampOffset;
sampler2D_NoiseTex;
float_Period;
half_Amount;
half_ClipRange;
3. ChangetheInputstructuresothatitreceivestheUVdataoftheramptexture:
structInput{
float2uv_NoiseTex;
};
4. Addthefollowingvertexfunction:
voidvert(inoutappdata_fullv){
float3disp=tex2Dlod(_NoiseTex,float4(v.texcoord.xy,0,0));
floattime=sin(_Time[3]*_Period+disp.r*10);
v.vertex.xyz+=v.normal*disp.r*_Amount*time;
}
5. Addthefollowingsurfacefunction:
voidsurf(InputIN,inoutSurfaceOutputo){
float3noise=tex2D(_NoiseTex,IN.uv_NoiseTex);
floatn=saturate(noise.r+_RampOffset);
clip(_ClipRange-n);
half4c=tex2D(_RampTex,float2(n,0.5));
o.Albedo=c.rgb;
o.Emission=c.rgb*c.a;
}
6. Wespecifythevertexfunctioninthe#pragmadirective,addingthenolightmapparametertopreventUnityfromaddingrealisticlightingstoourexplosion:
#pragmasurfacesurfLambertvertex:vertnolightmap
7. Thelaststepisselectingthematerial,andfromitsInspector,attachingthetwotexturesintherelativeslots.Thisisananimatedmaterial,meaningthatitevolvesovertime.YoucanwatchthematerialchangingintheeditorbyclickingonAnimatedMaterialsfromtheScenewindow:
Howitworks…Ifyouarereadingthisrecipe,youshouldalreadybefamiliarwithhowSurfaceShadersandvertexmodifierswork.Themainideabehindthiseffectistoalterthegeometryofthesphereinaseeminglychaoticway,exactlylikeithappensinarealexplosion.Thefollowingpictureshowsyouhowsuchanexplosionwilllookinsidetheeditor.Youcanseethattheoriginalmeshhasbeenheavilydeformed:
ThevertexfunctionisavariantofthetechniquecallednormalextrusionintroducedintheExtrudingyourmodelsrecipeofthischapter.Thedifferencehereisthattheamountoftheextrusionisdeterminedbothbythetimeandnoisetexture.
NoteWhenyouneedarandomnumberinUnity,youcanrelyontheRandom.Range()function.Thereisnostandardwaytogetrandomnumbersinashader,sotheeasiestwayistosampleanoisetexture.
Thereisnostandardwaytodothis,sotakethisasanexampleonly:
floattime=sin(_Time[3]*_Period+disp.r*10);
Thebuilt-in_Time[3]variableisusedtogetthecurrenttimefromwithintheshader,andtheredchannelofthenoisetexturedisp.risusedtomakesurethateachvertexmovesindependently.Thesin()functionmakestheverticesgoupanddown,simulatingthechaoticbehaviorofanexplosion.Then,thenormalextrusiontakesplace:
v.vertex.xyz+=v.normal*disp.r*_Amount*time;
Youshouldplaywiththesenumbersandvariablesuntilyoufindapatternofmovementthatyouarehappywith.
Thelastpartoftheeffectisachievedbythesurfacefunction.Here,thenoisetextureisusedtosamplearandomcolorfromtheramptexture.However,therearetwomoreaspectsthatareworthnoticing.Thefirstoneistheintroductionof_RampOffset.Itsusage
forcestheexplosiontosamplecolorsfromtheleftorrightsideofthetexture.Withpositivevalues,thesurfaceoftheexplosiontendstoshowmoregreytones;exactlywhathappenswhenitisdissolving.Youcanuse_RampOffsettodeterminehowmuchfireorsmokethereshouldbeinyourexplosion.Thesecondaspectintroducedinthesurfacefunctionistheusageofclip().Whatclip()doesisitclips(removes)pixelsfromtherenderingpipeline.Wheninvokedwithanegativevalue,thecurrentpixelisnotdrawn.Thiseffectiscontrolledby_ClipRange,whichdetermineswhichpixelsofthevolumetricexplosionsaregoingtobetransparent.
Bycontrollingboth_RampOffsetand_ClipRange,youhavefullcontroltodeterminehowtheexplosionbehavesanddissolves.
There’smore…Theshaderpresentedinthisrecipemakesaspherelooklikeanexplosion.Ifyoureallywanttouseit,youshouldcoupleitwithsomescriptsinordertogetthemostoutofit.Thebestthingtodoistocreateanexplosionobjectandmakeitintoaprefabsothatyoucanreuseiteverytimeyouneed.YoucandothisbydraggingthespherebackintotheProjectwindow.Onceitisdone,youcancreateasmanyexplosionsasyouwantusingtheInstantiate()function.
Itisworthnoticing,however,thatalltheobjectswiththesamematerialsharethesamelook.Ifyouhavemultipleexplosionsatthesametime,theyshouldnotusethesamematerial.Whenyouareinstantiatinganewexplosion,youshouldalsoduplicateitsmaterial.Youcandothiseasilywiththispieceofcode:
GameObjectexplosion=Instantiate(explosionPrefab)asGameObject;
Rendererrenderer=explosion.GetComponent<Renderer>();
Materialmaterial=newMaterial(renderer.sharedMaterial);
renderer.material=material;
Lastly,ifyouaregoingtousethisshaderinarealisticway,youshouldattachascripttoitthatchangesitssize,_RampOffset,and_ClipRangeaccordingtothetypeofexplosionthatyouwanttorecreate.
SeealsoMuchmorecanbedonetomakeexplosionsrealistic.Theapproachpresentedinthisrecipeonlycreatesanemptyshell;insideit,theexplosionisactuallyempty.Aneasytricktoimprovethisistocreateparticlesinsideit.However,youcanonlygothatfarwiththis.Theshortmovie,TheButterflyEffect(http://unity3d.com/pages/butterfly)createdbyUnityTechnologiesincollaborationwithPassionPicturesandNvidia,istheperfectexample.Itisbasedonthesameconceptofalteringthegeometryofasphere,butitrendersitwithatechniquecalledvolumeraycasting.Inanutshell,itrendersthegeometryasifit’sfull.Youcanseeanexampleinthefollowingpicture:
Ifyouarelookingforhigh-qualityexplosions,checkoutPyroTechnix(https://www.assetstore.unity3d.com/en/#!/content/16925)ontheAssetStore.Itincludesvolumetricexplosionsandcouplesthemwithrealisticshockwaves.
Chapter6.FragmentShadersandGrabPassesSofar,wehavereliedonSurfaceShaders.Theyhavebeendesignedtosimplifythewayshadercodingworks,providingmeaningfultoolsforartists.Ifwewanttopushourknowledgeofshadersfurther,weneedtoventureintotheterritoryofVertexandFragmentShaders.
Inthischapter,youwilllearnthefollowingrecipes:
UnderstandingVertexandFragmentShadersUsinggrabpassImplementingaGlassShaderImplementingaWaterShaderfor2Dgames
IntroductionComparedtoSurfaceShaders,VertexandFragmentShaderscomewithlittletonoinformationaboutthephysicalpropertiesthatdeterminehowlightreflectsonsurfaces.Whattheylackinexpressivity,theycompensatewithpower:VertexandFragmentShadersarenotlimitedbyphysicalconstraintsandareperfectfornon-photorealisticeffects.Thischapterwillfocusonatechniquecalledgrabpass,whichallowstheseshaderstosimulatedeformations.
UnderstandingVertexandFragmentShadersThebestwaytounderstandhowVertexandFragmentShadersworkisbycreatingoneyourself.Thisrecipewillshowyouhowtowriteoneoftheseshaders,whichwillsimplyapplyatexturetoamodelandmultiplyitbyagivencolor,asshowninthefollowingimage:
Theshaderpresentedhereisverysimple,anditwillbeusedasastartingbaseforalltheotherVertexandFragmentShaders.
GettingreadyForthisrecipe,wewillneedanewshader.Followthesesteps:
1. Createanewshader.2. Createanewmaterialandassigntheshadertoit.
Howtodoit…Inallthepreviouschapters,wehavealwaysbeenabletorefitSurfaceShaders.ThisisnotthecaseanymoreasSurfaceandFragmentShadersarestructurallydifferent.Wewillneedthefollowingchanges:
1. Deleteallthepropertiesoftheshader,replacingthemwiththefollowing:
_Color("Color",Color)=(1,0,0,1)//Red
_MainTex("Basetexture",2D)="white"{}
2. DeleteallthecodeintheSubShaderblockandreplaceitwiththisone:
Pass{
CGPROGRAM
#pragmavertexvert
#pragmafragmentfrag
half4_Color;
sampler2D_MainTex;
structvertInput{
float4pos:POSITION;
float2texcoord:TEXCOORD0;
};
structvertOutput{
float4pos:SV_POSITION;
float2texcoord:TEXCOORD0;
};
vertOutputvert(vertInputinput){
vertOutputo;
o.pos=mul(UNITY_MATRIX_MVP,input.pos);
o.texcoord=input.texcoord;
returno;
}
half4frag(vertOutputoutput):COLOR{
half4mainColour=tex2D(_MainTex,output.texcoord);
returnmainColour*_Color;
}
ENDCG
}
ThiswillalsobethebaseforallfutureVertexandFragmentShaders.
Howitworks…Asthenamesuggests,VertexandFragmentShadersworkintwosteps.Themodelisfirstpassedthroughavertexfunction;theresultistheninputtedtoafragmentfunction.Boththesefunctionsareassignedusingpragmadirectives:
#pragmavertexvert
#pragmafragmentfrag
Inthiscase,theyaresimplycalledvertandfrag.
Conceptuallyspeaking,fragmentsarecloselyrelatedtopixels;thetermfragmentisoftenusedtorefertothecollectionofdatanecessarytodrawapixel.ThisisalsowhyVertexandFragmentShadersareoftencalledPixelShaders.
ThevertexfunctiontakestheinputdatainastructurethatisdefinedasvertInputintheshader:
structvertInput{
float4pos:POSITION;
float2texcoord:TEXCOORD0;
};
Itsnameistotallyarbitrary,butitscontentisnot.Eachfieldofstructmustbedecoratedwithabindingsemantic.ThisisafeatureofCgthatallowsustomarkvariablessothattheywillbeinitializedwithcertaindata,suchasnormalvectorsandvertexposition.ThebindingsemanticPOSITIONindicatesthatwhenvertInputisinputtedtothevertexfunction,poswillcontainthepositionofthecurrentvertex.Thisissimilartothevertexfieldoftheappdata_fullstructureinaSurfaceShader.Themaindifferenceisthatposisrepresentedinmodelcoordinates(relativetothe3Dobject),whichweneedtoconverttoviewcoordinatesmanually(relativetothepositiononthescreen).
NoteThevertexfunctioninaSurfaceShaderisusedtoalterthegeometryofthemodelonly.InaVertexandFragmentShader,instead,thevertexfunctionisnecessarytoprojectthecoordinatesofthemodeltothescreen.
Themathematicsbehindthisconversionisbeyondthescopeofthischapter.However,thistransformationcanbeachievedbymultiplyingposbyaspecialmatrixprovidedbyUnity:UNITY_MATRIX_MVP.Itisoftenreferredtoasthemodel-view-projectionmatrix,anditisessentialtofindthepositionofavertexonthescreen:
vertOutputo;
o.pos=mul(UNITY_MATRIX_MVP,input.pos);
Theotherpieceofinformationinitializedistextcoord,whichusestheTEXCOORD0bindingsemanticstogettheUVdataofthefirsttexture.Nofurtherprocessingisrequiredandthisvaluecanbepasseddirectlytothefragmentfunction:
o.texcoord=input.texcoord;
WhileUnitywillinitialisevertInputforus,weareresponsiblefortheinitializationofvertOutput.Despitethis,itsfieldsstillneedtobedecoratedwithbindingsemantics:
structvertOutput{
float4pos:SV_POSITION;
float2texcoord:TEXCOORD0;
};
OncethevertexfunctionhasinitialisedvertOutput,thestructureispassedtothefragmentfunction.Thissamplesthemaintextureofthemodelandmultipliesitbythecolorprovided.
Asyoucansee,theVertexandFragmentShaderhasnoknowledgeofthephysicalpropertiesofthematerial;comparedtoaSurfaceShader,itworksclosertothearchitectureofthegraphicsprocessingunit(GPU).
There’smore…OneofthemostconfusingaspectsofVertexandFragmentShadersisbindingsemantics.Therearemanyothersthatyoucanuseandtheirmeaningdependsonthecontext.
InputsemanticsThebindingsemanticsinthefollowingtablecanbeusedinvertInput,whichisthestructurethatUnityprovidestothevertexfunction.Thefieldsdecoratedwiththesesemanticswillbeinitializedautomatically:
Bindingsemantics Description
POSITION,SV_POSITION Thepositionofavertexinworldcoordinates(objectspace)
NORMAL Thenormalofavertex,relativetotheworld(nottothecamera)
COLOR,COLOR0,DIFFUSE,SV_TARGET Thecolorinformationstoredinthevertex
COLOR1,SPECULAR Thesecondarycolorinformationstoredinthevertex(usuallythespecular)
TEXCOORD0,TEXCOORD1,…,TEXCOORDi Thei-thUVdatastoredinthevertex
OutputsemanticsWhenbinding,semanticsareusedinvertOutput;theydonotautomaticallyguaranteethatfieldswillbeinitialized.Quitetheopposite;it’sourresponsibilitytodoso.Thecompilerwillmakeitsbesttoensurethatthefieldsareinitializedwiththerightdata:
Bindingsemantics Description
POSITION,SV_POSITION,HPOSThepositionofavertexincameracoordinates(clipspace,fromzerotooneforeachdimension)
COLOR,COLOR0,COL0,COL,
SV_TARGETThefrontprimarycolor
COLOR1,COL1 Thefrontsecondarycolor
TEXCOORD0,TEXCOORD1,…,
TEXCOORDi,TEXiThei-thUVdatastoredinthevertex
WPOS Theposition,inpixels,inthewindow(origininthelowerleftcorner)
If,foranyreason,youneedafieldthatwillcontainadifferenttypeofdata,youcandecorateitwithoneofthemanyTEXCOORDdataavailable.Thecompilerwillnotallowfieldstobeleftundecorated.
SeealsoYoucanrefertotheNVIDIAReferenceManualtochecktheotherbindingsemanticsthatareavailableinCg:
http://developer.download.nvidia.com/cg/Cg_3.1/Cg-3.1_April2012_ReferenceManual.pdf
UsinggrabpassIntheAddingtransparencytoPBRrecipeofChapter4,CreatingTestCasesandWritingScenariosforBehaviorDrivenDevelopmentinSymfony,wehaveseenhowamaterialcanbemadetransparent.Evenifatransparentmaterialcandrawoverascene,itcannotchangewhathasbeendrawnunderneathit.ThismeansthatthoseTransparentShaderscannotcreatedistortionssuchastheonestypicallyseeninglassorwater.Inordertosimulatethem,weneedtointroduceanothertechniquecalledgrabpass.Thisallowsustoaccesswhathasbeendrawnonscreensofarsothatashadercanuseit(oralterit)withnorestrictions.Tolearnhowtousegrabpasses,wewillcreateamaterialthatgrabswhat’srenderedbehinditanddrawsitagainonthescreen.It’sashaderthat,paradoxically,usesseveraloperationstoshownochangesatall.
GettingreadyThisreciperequiresthefollowingoperations:
1. Createashaderthatwewillinitializelater.2. Createamaterialtohosttheshader.3. Attachthematerialtoaflatpieceofgeometry,suchasaquad.Placeitinfrontof
someotherobjectsothatyoucannotseethroughit.Thequadwillappeartransparentassoonastheshaderiscomplete.
Howtodoit…Tousegrabpass,youneedtofollowthesesteps:
1. RemovethePropertiessection;thisshaderwillnotuseanyofthem.2. IntheSubShadersection,addgrabpass:
GrabPass{}
3. Afterthegrabpass,wewillneedtoaddthisextrapass:
Pass{
CGPROGRAM
#pragmavertexvert
#pragmafragmentfrag
#include"UnityCG.cginc"
sampler2D_GrabTexture;
structvertInput{
float4vertex:POSITION;
};
structvertOutput{
float4vertex:POSITION;
float4uvgrab:TEXCOORD1;
};
//Vertexfunction
vertInputvert(vertexInputv){
vertexOutputo;
o.vertex=mul(UNITY_MATRIX_MVP,v.vertex);
o.uvgrab=ComputeGrabScreenPos(o.vertex);
returno;
}
//Fragmentfunction
half4frag(vertexOutputi):COLOR{
fixed4col=tex2Dproj(_GrabTexture,
UNITY_PROJ_COORD(i.uvgrab));
returncol+half4(0.5,0,0,0);
}
ENDCG
}
Howitworks…ThisrecipenotonlyintroducesgrabpassesbutalsoVertexandFragmentShaders;forthisreason,wehavetoanalyzetheshaderindetail.
Sofar,allthecodehasalwaysbeenplaceddirectlyintheSubShadersection.Thisisbecauseourpreviousshadersrequiredonlyasinglepass.Thistime,twopassesarerequired.Thefirstoneisthegrabpass,whichisdefinedsimplybyGrabPass{}.Therestofthecodeisplacedinthesecondpass,whichiscontainedinaPassblock.
Thesecondpassisnotstructurallydifferentfromtheshadershowninthefirstrecipeofthischapter;weusethevertexfunctionverttogetthepositionofthevertexandthenwegiveitacolorinthefragmentfunctionfrag.Thedifferenceisthatvertcalculatesanotherimportantdetail:theUVdataforthegrabpass.Thegrabpassautomaticallycreatesatexturethatcanbereferredtoasfollows:
sampler2D_GrabTexture;
Inordertosamplethistexture,weneeditsUVdata.TheComputeGrabScreenPosfunctionreturnsdatathatwecanuselatertosamplethegrabtexturecorrectly.ThisisdoneintheFragmentShaderusingthefollowingline:
fixed4col=tex2Dproj(_GrabTexture,UNITY_PROJ_COORD(i.uvgrab));
Thisisthestandardwayinwhichatextureisgrabbedandappliedtothescreeninitscorrectposition.Ifeverythinghasbeendonecorrectly,thisshaderwillsimplyclonewhathasbeenrenderedbehindthegeometry.Wewillseeinthefollowingrecipeshowthistechniquecanbeusedtocreatematerialssuchaswaterandglass.
There’smore…EverytimeyouuseamaterialwithGrabPass{},Unitywillhavetorenderthescreentoatexture.Thisoperationisveryexpensiveandlimitsthenumberofgrabpassesthatyoucanuseinagame.Cgoffersaslightlydifferentvariation:
GrabPass{"TextureName"}
Thislinenotonlyallowsyoutogiveanametothetexture,butitalsosharesthetexturewithallthematerialsthathaveagrabpasscalledTextureName.Thismeansthatifyouhavetenmaterials,Unitywillonlydoasinglegrabpassandsharethetexturetoallofthem.Themainproblemofthistechniqueisthatitdoesn’talloweffectsthatcanbestacked.Ifyouarecreatingaglasswiththistechnique,youwon’tbeabletohavetwoglassesoneaftertheother.
ImplementingaGlassShaderGlassisaverycomplicatedmaterial;itshouldnotbeasurprisethatotherchaptershavealreadycreatedshaderstosimulateitintheAddingtransparencytoPBRrecipeofChapter4,CreatingTestCasesandWritingScenariosforBehaviorDrivenDevelopmentinSymfony.However,thereisaneffectthattransparencycannotreproducedeformations.Mostglassesarenotperfect,hencetheycreatedistortionswhenwelookthroughthem.Thisrecipewillteachyouhowtodothis.TheideabehindthiseffectistouseaVertexandFragmentShaderwithagrabpass,andthensamplethegrabtexturewithalittlechangetoitsUVdatatocreateadistortion.Youcanseetheeffectinthefollowingimage,usingtheglass-stainedtexturesfromtheUnityStandardAssets:
GettingreadyThesetupforthisrecipeissimilartotheonepresentedinthepreviouschapter:
1. CreatenewVertexandFragmentShaders.Youcanstartbycopyingtheoneusedinthepreviousrecipe,Usinggrabpass,asabase.
2. Createamaterialthatwillusetheshader.3. Assignthematerialtoaquadoranotherflatgeometrythatwillsimulateyourglass.4. Placesomeobjectsbehinditsothatyoucanseethedistortioneffect.
Howtodoit…Let’sstartbyeditingtheVertexandFragmentShaders:
1. AddthesetwopropertiestothePropertiesblock:
_MainTex("Base(RGB)Trans(A)",2D)="white"{}
_BumpMap("Noisetext",2D)="bump"{}
_Magnitude("Magnitude",Range(0,1))=0.05
2. Addtheirvariablesinthesecondpass:
sampler2D_MainTex;
sampler2D_BumpMap;
float_Magnitude;
3. Addthetextureinformationintheinputandoutputstructures:
float2texcoord:TEXCOORD0;
4. TransfertheUVdatafromtheinputtotheoutputstructure:
o.texcoord=v.texcoord;
5. Usethefollowingfragmentfunction:
half4frag(vertOutputi):COLOR{
half4mainColour=tex2D(_MainTex,i.texcoord);
half4bump=tex2D(_BumpMap,i.texcoord);
half2distortion=UnpackNormal(bump).rg;
i.uvgrab.xy+=distortion*_Magnitude;
fixed4col=tex2Dproj(_GrabTexture,UNITY_PROJ_COORD(i.uvgrab));
returncol*mainColour*_Colour;
}
6. ThismaterialistransparentsoitchangesitstagsintheSubShaderblock:
Tags{"Queue"="Transparent""IgnoreProjector"="True""RenderType"=
"Opaque"}
7. What’sleftnowistosetthetexturefortheglassandanormalmaptodisplacethegrabtexture.
Howitworks…Thecorethatthisshaderusesisagrabpasstotakewhathasalreadybeenrenderedonthescreen.Thepartwherethedistortiontakesplaceisinthefragmentfunction.Here,anormalmapisunpackedandusedtooffsettheUVdataofthegrabtexture:
half4bump=tex2D(_BumpMap,i.texcoord);
half2distortion=UnpackNormal(bump).rg;
i.uvgrab.xy+=distortion*_Magnitude;
The_Magnitudeslideisusedtodeterminehowstrongtheeffectis.
There’smore…Thiseffectisverygeneric;itgrabsthescreenandcreatesadistortionbasedonanormalmap.Thereisnoreasonwhyitshouldn’tbeusedtosimulatemoreinterestingthings.Manygamesusedistortionsaroundexplosionsorothersci-fidevices.Thismaterialcanbeappliedtoasphereand,withadifferentnormalmap,itwouldsimulatetheheatwaveofanexplosionperfectly.
ImplementingaWaterShaderfor2DgamesTheGlassShaderintroducedinthepreviousrecipeisstatic;itsdistortionneverchanges.Ittakesjustafewchangestoconvertittoananimatedmaterial,makingitperfectfor2Dgames,whichfeaturewater.ThisrecipeusesasimilartechniquetotheoneshowninChapter5,AnimatingVerticesinaSurfaceShader:
GettingreadyThisrecipeisbasedontheVertexandFragmentShadersdescribedintheUsinggrabpassrecipeasitwillrelyheavilyongrabpass.
1. Createanewgrabpassshader;youcanwriteyourownorstartwiththeonepresentedintheUsinggrabpassrecipe.
2. Createanewmaterialforyourshader.3. Assignthematerialtoaflatgeometrythatwillrepresentyour2Dwater.Inorderfor
thiseffecttowork,youshouldhavesomethingrenderedbehinditsothatyoucanseethewater-likedisplacement.
4. Thisreciperequiresanoisetexture,whichisusedtogetpseudo-randomvalues.Itisimportantthatyouchooseaseamlessnoisetexture,suchastheonesgeneratedbytileable2DPerlinnoise,asshowninthefollowingimage.Thisensuresthatwhenthematerialisappliedtoalargeobject,youwillnotseeanydiscontinuity.Inorderforthiseffecttowork,thetexturehastobeimportedintheRepeatmode.Ifyouwantasmoothandcontinuouslookforyourwater,youshouldalsosetittoBilinearfromInspector.Thesesettingsensurethatthetextureissampledcorrectlyfromtheshader:
Howtodoit…Tocreatethisanimatedeffect,youcanstartbyrefittingtheshader.Followthesesteps:
1. Addthefollowingproperties:
_NoiseTex("Noisetext",2D)="white"{}
_Colour("Colour",Color)=(1,1,1,1)
_Period("Period",Range(0,50))=1
_Magnitude("Magnitude",Range(0,0.5))=0.05
_Scale("Scale",Range(0,10))=1
2. Addtheirrespectivevariablestothesecondpassoftheshader:
sampler2D_NoiseTex;
fixed4_Colour;
float_Period;
float_Magnitude;
float_Scale;
3. Definethefollowingoutputstructureforthevertexfunction:
structvertInput{
float4vertex:POSITION;
fixed4color:COLOR;
float2texcoord:TEXCOORD0;
float4worldPos:TEXCOORD1;
float4uvgrab:TEXCOORD2;
};
4. Thisshaderneedstoknowtheexactpositionofthespaceofeveryfragment.Todothis,addthefollowinglinetothevertexfunction:
o.worldPos=mul(_Object2World,v.vertex);
5. Usethefollowingfragmentfunction:
fixed4frag(vertInputi):COLOR{
floatsinT=sin(_Time.w/_Period);
float2distortion=float2(
tex2D(_NoiseTex,i.worldPos.xy/_Scale+float2(sinT,0)).r-0.5,
tex2D(_NoiseTex,i.worldPos.xy/_Scale+float2(0,sinT)).r-
0.5
);
i.uvgrab.xy+=distortion*_Magnitude;
fixed4col=tex2Dproj(_GrabTexture,UNITY_PROJ_COORD(i.uvgrab));
returncol*_Colour;
}
Howitworks…ThisshaderisverysimilartotheoneintroducedintheImplementingaGlassShaderrecipe.Themajordifferenceisthatthisisananimatedmaterial;thedisplacementisnotgeneratedfromanormalmapbuttakesintoaccountthecurrenttimeinordertocreateaconstantanimation.ThecodethatdisplacestheUVdataofthegrabtextureseemsquitecomplicated;let’strytounderstandhowithasbeengenerated.Theideabehinditisthatasinusoidfunctionisusedtomakethewateroscillate.Thiseffectneedstoevolveovertime;toachievethiseffect,thedistortiongeneratedbytheshaderdependsonthecurrenttimethatisretrievedwiththebuilt-invariable,_Time.The_Periodvariabledeterminestheperiodofthesinusoid,whichmeanshowfastthewavesappear:
float2distortion=float2(sin(_Time.w/_Period),sin(_Time.w/_Period))–
0.5;
TheproblemwiththiscodeisthatyouhavethesamedisplacementontheXandYaxes;asaresult,theentiregrabtexturewillrotateinacircularmotion,whichlooksnothinglikewater.Weobviouslyneedtoaddsomerandomnesstothis.
Themostcommonwaytoaddrandombehaviorstoshadersisbyincludinganoisetexture.Theproblemnowistofindawaytosamplethetextureatseeminglyrandompositions.ThebestwaytoavoidseeinganobvioussinusoidpatternistousethesinewavesasanoffsetintheUVdataofthenoisetexture:
floatsinT=sin(_Time.w/_Period);
float2distortion=float2
(tex2D(_NoiseTex,i.texcoord/_Scale+float2(sinT,0)).r-0.5,
tex2D(_NoiseTex,i.texcoord/_Scale+float2(0,sinT)).r-0.5
);
The_Scalevariabledeterminesthesizeofthewaves.Thissolutionisclosertothefinalversion,buthasasevereissue—ifthewaterquadmoves,theUVdatafollowsitandyoucanseethewaterwavesfollowingthematerialratherthanbeinganchoredtothebackground.Tosolvethis,weneedtousetheworldpositionofthecurrentfragmentastheinitialpositionfortheUVdata:
floatsinT=sin(_Time.w/_Period);
float2distortion=float2
(tex2D(_NoiseTex,i.worldPos.xy/_Scale+float2(sinT,0)).r-0.5,
tex2D(_NoiseTex,i.worldPos.xy/_Scale+float2(0,sinT)).r-0.5
);
i.uvgrab.xy+=distortion*_Magnitude;
Theresultisapleasant,seamlessdistortion,whichdoesn’tmoveinanycleardirection.
NoteAsithappenswithallthesespecialeffects,thereisnoperfectsolution.Thisrecipeshowsyouatechniquetocreatewater-likedistortion,butyouareencouragedtoplaywithituntilyoufindaneffectthatfitstheaestheticsofyourgame.
Chapter7.MobileShaderAdjustmentInthenexttwochapters,wearegoingtotakealookatmakingtheshadersthatwewriteperformance-friendlyfordifferentplatforms.Wewon’tbetalkingaboutanyoneplatformspecifically,butwearegoingtobreakdowntheelementsofshaderswecanadjusttomakethemmoreoptimizedformobilesandefficientonanyplatformingeneral.ThesetechniquesrangefromunderstandingwhatUnityoffersintermsofbuilt-invariablesthatreducetheoverheadoftheshadersmemorytolearningaboutwaysinwhichwecanmakeourownshadercodemoreefficient.Thischapterwillcoverthefollowingrecipes:
WhatisacheapshaderProfilingyourshadersModifyingourshadersformobile
IntroductionLearningtheartofoptimizingyourshaderswillcomeupinjustaboutanygameprojectthatyouworkon.Therewillalwayscomeapointinanyproductionwhereashaderneedstobeoptimized,ormaybeitneedstouselesstexturesbutproducethesameeffect.Asatechnicalartistorshaderprogrammer,youhavetounderstandthesecorefundamentalstooptimizeyourshaderssothatyoucanincreasetheperformanceofyourgamewhilestillachievingthesamevisualfidelity.Havingthisknowledgecanalsohelpinsettingthewayinwhichyouwriteyourshaderfromthestart.Forinstance,byknowingthatthegamebuiltusingyourshaderwillbeplayedonamobiledevice,wecanautomaticallysetallourlightingfunctionstouseahalfvectorastheviewdirectionorsetallofourfloatvariabletypestofixedorhalf.These,andmanyothertechniques,allcontributetoyourshadersrunningefficientlyonyourtargethardware.Let’sbeginourjourneyandstartlearninghowtooptimizeourshaders.
Whatisacheapshader?Whenfirstaskedthequestion,whatisacheapshader,itmightbealittletoughtoanswerastherearemanyelementsthatgointomakingamoreefficientshader.Itcouldbetheamountofmemoryusedupbyyourvariables.Itcouldbetheamountoftexturestheshaderisusing.Itcouldalsobethatourshaderisworkingfine,butwecanactuallyproducethesamevisualeffectwithhalftheamountofdatabyreducingtheamountofcodeweareusingordatawearecreating.Wearegoingtoexploreafewofthesetechniquesinthisrecipeandshowhowtheycanbecombinedtomakeyourshaderfastandefficientbutstillproducethehigh-qualityvisualseveryoneexpectsfromgamestoday,whetheronamobileorPC.
GettingreadyInordertogetthisrecipestarted,weneedtogatherafewresourcestogether.Solet’sperformthefollowingtasks:
1. Createanewsceneandfillitwithasimplesphereobjectandsingledirectionallight.2. Createanewshaderandmaterialandassigntheshadertothematerial.3. Wethenneedtoassignthematerialwejustcreatedtooursphereobjectinournew
scene.4. Finally,modifytheshadersothatitusesadiffusetextureandnormalmapand
includesyourowncustomlightingfunction.Thefollowingimageshowstheresultofmodifyingourdefaultshaderthatwecreatedinstep1:
Youshouldnowhaveasetupsimilartothefollowingimage.Thissetupwillallowusto
takealookatsomeofthebasicconceptsthatgointooptimizingshadersusingSurfaceShadersinUnity:
Howtodoit…WearegoingtobuildasimpleDiffuseshadertotakealookatafewwaysinwhichyoucanoptimizeyourshadersingeneral.
First,we’lloptimizeourvariabletypessothattheyuselessmemorywhentheyareprocessingdata:
1. Let’sbeginwiththestructInputinourshader.Currently,ourUVsarebeingstoredinavariableofthefloat2type.Weneedtochangethistousehalf2instead:
2. Wecanthenmovetoourlightingfunctionandreducethevariable’smemoryfootprintbychangingtheirtypestothefollowing:
3. Finally,wecancompletethisoptimizationpassbyupdatingthevariablesinoursurf()function:
Nowthatwehaveourvariablesoptimized,wearegoingtotakeadvantageofabuilt-inlightingfunctionvariablesothatwecancontrolhowlightsareprocessedbythisshader.Bydoingthis,wecangreatlyreducetheamountoflightstheshaderprocesses.Modifythe#pragmastatementinyourshaderwiththefollowingcode:
WecanoptimizethisfurtherbysharingUVsbetweenthenormalmapanddiffusetexture.Todothis,wesimplychangetheUVlookupinourUnpackNormal()functiontouse_MainTexUVsinsteadoftheUVsof_NormalMap:
4. AswehaveremovedtheneedforthenormalmapUVs,weneedtomakesurethatweremovethenormalmapUVcodefromtheInputstruct:
5. Finally,wecanfurtheroptimizethisshaderbytellingtheshaderthatitonlyworkswithcertainrenderers:
Theresultofouroptimizationpassesshowusthatwereallydon’tnoticeadifferenceinthevisualquality,butwehavereducedtheamountoftimeittakesforthisshadertobedrawntothescreen.Youwilllearnaboutfindingouthowmuchtimeittakesforashadertorenderinthenextrecipe,buttheideatofocusonhereisthatweachievethesameresultwithlessdata.Sokeepthisinmindwhencreatingyourshaders.Thefollowingimageshowsusthefinalresultofourshader:
Howitworks…Nowthatwehaveseenthewaysinwhichwecanoptimizeourshaders,let’sdiveinabitdeeperandreallyunderstandwhyallofthesetechniquesareworkingandlookatacoupleofothertechniquesthatyoucantryforyourself.
Let’sfirstfocusourattentiononthesizeofthedataeachofourvariablesisstoringwhenwedeclarethem.Ifyouarefamiliarwithprogramming,thenyouwillunderstandthatyoucandeclarevaluesorvariableswithdifferentsizesoftypes.Thismeansthatafloatactuallyhasamaximumsizeinmemory.Thefollowingdescriptionwilldescribethesevariabletypesinmuchmoredetail:
Float:Afloatisafull32-bitprecisionvalueandistheslowestofthethreedifferenttypesweseehere.Italsohasitscorrespondingvaluesoffloat2,float3,andfloat4.Half:Thehalfvariabletypeisareduced16-bitfloatingpointvalueandissuitabletostoreUVvaluesandcolorvaluesandismuchfasterthanusingafloatvalue.Ithasitscorrespondingvalueslikethefloattype,whicharehalf2,half3,andhalf4.Fixed:Afixedvalueisthesmallestinsizeofthethreetypes,butcanbeusedforlightingcalculationsandcolorsandhasthecorrespondingvaluesoffixed2,fixed3,andfixed4.
Oursecondphaseofoptimizingoursimpleshaderwastodeclarethenoforwardaddvaluetoour#pragmastatement.ThisisbasicallyaswitchthatautomaticallytellsUnitythatanyobjectwiththisparticularshaderreceivesonlyper-pixellightfromasingledirectionallight.Anyotherlightsthatarecalculatedbythisshaderwillbeforcedtobeprocessedasper-vertexlightsusingSphericalHarmonicvaluesproducedinternallybyUnity.Thisisespeciallyobviouswhenweplaceanotherlightinthescenetolightoursphereobjectbecauseourshaderisdoingaper-pixeloperationusingthenormalmap.
Thisisgreat,butwhatifyouwantedtohaveabunchofdirectionallightsinthesceneandcontroloverwhichoftheselightsisusedforthemainper-pixellight?Well,ifyounotice,eachlighthasaRenderModedrop-down.Ifyouclickonthisdrop-down,youwillseeacoupleofflagsthatcanbeset.TheseareAuto,Important,andNotImportant.Byselectingalight,youcantellUnitythatalightshouldbeconsideredmoreasaper-pixellightthanaper-vertexlight,bysettingitsrendermodetoImportantandviceversa.IfyouleavealightsettoAuto,thenyouwillletUnitydecidethebestcourseofaction.
Placeanotherlightinyoursceneandremovethetexturethatiscurrentlyinthemaintextureforourshader.Youwillnoticethatthesecondpointlightdoesnotreactwiththenormalmap,onlythedirectionallightthatwecreatedfirst.Theconcepthereisthatyousaveonper-pixeloperationsbyjustcalculatingallextralightsasvertexlights,andsaveperformancebyjustcalculatingthemaindirectionallightasaper-pixellight.Thefollowingimagevisuallydemonstratesthisconceptasthepointlightisnotreactingwiththenormalmap:
Finally,wedidabitofcleaningupandsimplytoldthenormalmaptexturetousethemaintexture’sUVvalues,andwegotridofthelineofcodethatpulledinaseparatesetofUVvaluesspecificallyforthenormalmap.Thisisalwaysanicewaytosimplifyyourcodeandcleanupanyunwanteddata.
Wealsodeclaredexclude_pass:prepassinour#pragmastatementsothatthisshaderwouldn’tacceptanycustomlightingfromthedeferredrenderer.Thismeansthatwecanreallyusethisshadereffectivelyintheforwardrendereronly,whichissetinthemaincamera’ssettings.
Bytakingabitoftime,youwillbeamazedathowmuchashadercanbeoptimized.YouhaveseenhowwecanpackgrayscaletexturesintoasingleRGBAtextureaswellasuselookuptexturestofakelighting.Therearemanywaysinwhichashadercanbeoptimized,whichiswhyitisalwaysanambiguousquestiontoaskinthefirstplace,butknowingthesedifferentoptimizationtechniques,youcancateryourshaderstoyourgameandtargetplatform,ultimatelyresultinginverystreamlinedshadersandanicesteadyframerate.
ProfilingyourshadersNowthatweknowhowwecanreducetheoverheadthatourshadersmighttake,let’stakealookathowtofindproblematicshadersinascenewhereyoumighthavealotofshadersoratonofobjects,shaders,andscripts,allrunningatthesametime.Tofindasingleobjectorshaderamongawholegamecanbequitedaunting,butUnityprovidesuswithitsbuilt-inProfiler.Thisallowsustoactuallysee,onaframe-by-framebasis,whatishappeninginthegameandeachitembeingusedbytheGPUandCPU.
UsingtheProfiler,wecanisolateitemssuchasshaders,geometry,andgeneralrenderingitemsusingitsinterfacetocreateblocksofprofilingjobs.Wecanfilteroutitemstillwearelookingattheperformanceofjustasingleobject.ThisthenletsusseetheeffectsontheCPUandGPUthattheobjecthaswhileitisperformingitsfunctionsatruntime.
Let’stakealookthroughthedifferentsectionsoftheProfilerandlearnhowtodebugourscenesand,mostimportantly,ourshaders.
GettingreadyLet’suseourProfilerbygettingafewassetsreadyandlaunchingtheProfilerwindow:
1. Let’susethescenefromthelastrecipeandlaunchtheUnityProfilerfromWindow|ProfilerorCtrl+7.
2. Let’salsoduplicateoursphereacouplemoretimestoseehowthataffectsourrendering.
Youshouldseesomethingsimilartothefollowingimage:
Howtodoit…TousetheProfiler,wewilltakealookatsomeoftheUIelementsofthiswindow.Beforewehitplay,let’stakealookathowtogettheinformationweneedfromtheprofiler:
1. First,clickonthelargerblocksintheProfilerwindowcalledGPUUsage,CPUUsage,andRendering.Youwillfindtheseblocksontheleft-handsideoftheupperwindow:
Usingtheseblocks,wecanseedifferentdataspecifictothosemajorfunctionsofourgame.TheCPUUsageisshowinguswhatmostofourscriptsaredoingaswellasphysicsandoverallrendering.TheGPUUsageblockisgivingusdetailedinformationabouttheelementsthatarespecifictoourlighting,shadows,andrenderqueues.Finally,theRenderingblockisgivingusinformationaboutthedrawcallsandamountofgeometrywehaveinoursceneatanyoneframe.
Byclickingoneachoftheseblocks,wecanisolatethetypeofdataweseeduringourprofilingsession.
2. Now,clickonthetinycoloredblocksinoneoftheseProfileblocksandhitplayorCtrl+Ptorunthescene.
Thisletsusdivedownevendeeperintoourprofilingsessionsothatwecanfilteroutwhatisbeingreportedbackforus.Whilethesceneisrunning,uncheckalloftheboxes,exceptforOpaqueintheGPUUsageblock.NoticethatwecannowseejusthowmuchtimeisbeingusedtorendertheobjectsthataresettotheRenderQueueof
Opaque:
3. AnothergreatfunctionoftheProfilerwindowistheactionofclickinganddragginginthegraphview.Thiswillautomaticallypauseyourgamesothatyoucanfurtheranalyzeacertainspikeinthegraphtofindoutexactlywhichitemiscausingtheperformanceproblem.Clickanddragaroundinthegraphviewtopausethegameandseetheeffectofusingthisfunctionality:
4. TurningourattentionnowtowardsthelowerhalfoftheProfilerwindow,youwillnoticethatthereisadrop-downitemavailablewhenwehavetheGPUBlockselected.Wecanexpandthistogetevenmoredetailedinformationaboutthecurrentactiveprofilingsessionand,inthiscase,moreinformationaboutwhatthecameraiscurrentlyrenderingandhowmuchtimeitistakingup:
ThisgivesusacompletelookattheinnerworkingsofwhatUnityisprocessinginthisparticularframe.Inthiscase,wecanseethatourthreesphereswithouroptimizedshaderaretakingroughly0.14millisecondstodrawtothescreen,theyaretakingupsevendrawcalls,andthisprocessistaking3.1percentoftheGPU’stimeineveryframe.It’sthistypeofinformationwecanusetodiagnoseandsolveperformanceissueswithregardtoshaders.Let’sconductatesttoseetheeffectsofaddingonemoretexturetoourshaderandblendingtwodiffusetexturestogetherusingalerpfunction.Youwillseetheeffectsintheprofilerprettyclearly.
5. ModifythePropertiesblockofyourshaderwiththefollowingcodetogiveusanothertexturetouse:
6. Thenlet’sfeedourtexturetoCGPROGRAM:
7. Nowit’stimetoupdateoursurf()functionaccordinglysothatweblendourtexturediffusetexturestogether:
OnceyousaveyourmodificationsinyourshaderandreturntoUnity’seditor,wecanrunourgameandseetheincreaseinmillisecondsofournewshader.PressplayonceyouhavereturnedtoUnityandlet’stakealookattheresultsinourprofiler:
YoucanseenowthattheamountoftimetorenderourOpaqueShadersinthissceneistaking0.179milliseconds,upfrom0.140milliseconds.Byaddinganothertextureandusingthelerp()function,weincreasedtherendertimeforourspheres.Whileit’sasmall
change,imaginehaving20shadersallworkingindifferentwaysondifferentobjects.
Usingtheinformationgivenhere,youcanpinpointareasthatarecausingperformancedecreasesmorequicklyandsolvetheseissuesusingthetechniquesfromthepreviousrecipe.
Howitworks…Whileit’scompletelyoutofscopeofthisbooktodescribehowthistoolactuallyworksinternally,wecansurmisethatUnityhasgivenusawaytoviewthecomputer’sperformancewhileourgameisrunning.Basically,thiswindowistiedverytightlytotheCPUandGPUtogiveusreal-timefeedbackofhowmuchtimeisbeingtakenforeachofourscripts,objects,andrenderqueues.Usingthisinformation,wehaveseenthatwecantracktheefficiencyofourshaderwritingtoeliminateproblematicareasandcode.
There’smore…Itisalsopossibletoprofilespecificallyformobileplatforms.UnityprovidesuswithacoupleofextrafeatureswhentheAndroidorIOSbuildtargetissetintheBuildSettings.Wecanactuallygetreal-timeinformationfromourmobiledeviceswhilethegameisrunning.Thisbecomesveryusefulbecauseyouareabletoprofiledirectlyonthedeviceitselfinsteadofprofilingdirectlyinyoureditor.Tofindoutmoreaboutthisprocess,refertoUnity’sdocumentationatthefollowinglink:
http://docs.unity3d.com/Documentation/Manual/MobileProfiling.html
ModifyingourshadersformobileNowthatwehaveseenquiteabroadsetoftechniquestomakereallyoptimizedshaders,let’stakealookatwritinganice,high-qualityshadertargetedforamobiledevice.Itisactuallyquiteeasytomakeafewadjustmentstotheshaderswehavewrittensothattheyrunfasteronamobiledevice.Thisincludeselementssuchasusingtheapproxvieworhalfasviewlightingfunctionvariables.Wecanalsoreducetheamountoftexturesweneedandevenapplybettercompressionforthetexturesweareusing.Bytheendofthisrecipe,wewillhaveanicelyoptimizednormal-mapped,Specularshaderforuseinourmobilegames.
GettingreadyBeforewebegin,let’sgetafreshnewsceneandfillitwithsomeobjectstoapplyourMobileshader:
1. Createanewsceneandfillitwithadefaultsphereandsingledirectionallight.2. Createanewmaterialandshader,andassigntheshadertothematerial.3. Finally,assignthematerialtooursphereobjectinourscene.
Whencompleted,youshouldhaveascenesimilartotheoneinthefollowingimage:
Howtodoit…Forthisrecipe,wewillwriteamobile-friendlyshaderfromscratchanddiscusstheelementsthatmakeitmoremobile-friendly:
1. Let’sfirstpopulateourPropertiesblockwiththeneededtextures.Inthiscase,wearegoingtouseasingleDiffusetexturewiththeglossmapinitsalphachannel,normalmap,andsliderforspecularintensity:
2. Ournexttaskistosetupour#pragmadeclarations.ThiswillsimplyturncertainfeaturesoftheSurfaceShaderonandoff,ultimatelymakingtheshadercheaperormoreexpensive:
3. WethenneedtomaketheconnectionbetweenourPropertiesblockandCGPROGRAM.Thistime,wearegoingtousethefixedvariabletypeforourspecularintensityslidertoreduceitsmemoryusage:
4. Inorderforustomapourtexturestothesurfaceofourobject,weneedtogetsomeUVs.Inthiscase,wearegoingtogetonlyonesetofUVstokeeptheamountofdatainourshaderdowntoaminimum:
5. Thenextstepistofillinourlightingfunctionusingafewnewinputvariablesthatareavailabletoususingthenew#pragmadeclarations:
6. Finally,wecompletetheshaderbycreatingthesurf()functionandprocessingthefinalcolorofoursurface:
Whencompletedwiththecodeportionofthisrecipe,saveyourshaderandreturntotheUnityeditortolettheshadercompile.Ifnoerrorsoccurred,youshouldseearesultsimilartothefollowingimage:
Howitworks…So,let’sbeginthedescriptionofthisshaderbyexplainingwhatitdoesanddoesn’tdo.First,itexcludesthedeferredlightingpass.Thismeansthatifyoucreatedalightingfunctionthatwasconnectedtothedeferredrenderer’sprepass,itwouldn’tusethatparticularlightingfunctionandwouldlookforthedefaultlightingfunctionliketheonesthatwehavebeencreatingthusfarinthisbook.
ThisparticularshaderdoesnotsupportLightmappingbyUnity’sinternallight-mappingsystem.Thisjustkeepstheshaderfromtryingtofindlightmapsfortheobjectthattheshaderisattachedto,makingtheshadermoreperformancefriendlybecauseitisnothavingtoperformthelightmappingcheck.
Weincludedthenoforwardadddeclarationsothatweprocessonlyper-pixeltextureswithasingledirectionallight.Allotherlightsareforcedtobecomeper-vertexlightsandwillnotbeincludedinanyper-pixeloperationsyoumightdointhesurf()function.
Finally,weareusingthehalfasviewdeclarationtotellUnitythatwearen’tgoingtousethemainviewDirparameterfoundinanormallightingfunction.Instead,wearegoingtousethehalfvectorastheviewdirectionandprocessourspecularwiththis.Thisbecomesmuchfasterfortheshadertoprocessasitwillbedoneonaper-vertexbasis.Itisn’tcompletelyaccuratewhenitcomestosimulatingspecularintherealworld,butvisuallyonamobiledevice,itlooksjustfineandtheshaderismoreoptimized.
Itstechniqueslikethesethatmakeashadermoreefficientandcleaner,codewise.Alwaysmakesurethatyouareusingonlythedatayouneedwhileweighingthisagainstyourtargethardwareandthevisualqualitythatthegamerequires.Intheend,itbecomesacocktailofthesetechniquesthatultimatelymakeupyourshadersforyourgames.
Chapter8.ScreenEffectswithUnityRenderTexturesInthischapter,youwilllearnthefollowingrecipes:
SettingupthescreeneffectsscriptsystemUsingbrightness,saturation,andcontrastwithscreeneffectsUsingbasicPhotoshop-likeBlendmodeswithscreeneffectsUsingtheOverlayBlendmodewithscreeneffects
IntroductionOneofthemostimpressiveaspectsoflearningtowriteshadersistheprocessofcreatingyourownscreeneffects,alsoknownasposteffects.Withthesescreeneffects,wecancreatestunningreal-timeimageswithBloom,MotionBlur,HDReffects,andsoon.MostmoderngamesoutinthemarkettodaymakeheavyuseoftheseScreeneffectsfortheirdepthoffieldeffects,bloomeffects,andevencolorcorrectioneffects.
Throughoutthischapter,youwilllearnhowtobuildupthescriptsystemthatgivesusthecontroltocreatethesescreeneffects.WewillcoverRenderTextures,whatthedepthbufferis,andhowtocreateeffectsthatgiveyouPhotoshop-likecontroloverthefinalrenderedimageofyourgame.Byutilizingscreeneffectsforyourgames,younotonlyroundoutyourshaderwritingknowledge,butyouwillalsohavethepowertocreateyourownincrediblereal-timerenderswithUnity.
SettingupthescreeneffectsscriptsystemTheprocessofcreatingscreeneffectsisoneinwhichwegrabafullscreenimage(ortexture),useashadertoprocessitspixelsontheGPU,andthensenditbacktoUnity’srenderertoapplyittothewholerenderedimageofthegame.Thisallowsustoperformper-pixeloperationsontherenderedimageofthegameinrealtime,givingusamoreglobalartisticcontrol.
Imagineifyouhadtogothroughandadjusteachmaterialoneachobjectinyourgametojustadjustthecontrastofthefinallookofyourgame.Whilenotimpossible,thiswouldtakeabitoflabortoperform.Byutilizingascreeneffect,wecanadjustthescreen’sfinallookasawhole,therebygivingusamorePhotoshop-likecontroloverourgame’sfinalappearance.
InordertogetaScreeneffectsystemupandrunning,wehavetosetupasinglescripttoactasthecourierofthegame’scurrentrenderedimageor,whatUnitycalls,theRenderTexture.ByutilizingthisscripttopasstheRenderTexturetoashader,wecancreateaflexiblesystemtocreatescreeneffects.Forourfirstscreeneffect,wearegoingtocreateaverysimplegrayscaleeffect,wherewecanmakeourgamelookblackandwhite.Let’stakealookathowthisisdone.
GettingreadyInordertogetourScreenEffectssystemupandrunning,weneedtocreateafewassetsforourcurrentUnityproject.Bydoingthis,wewillsetourselvesupforthestepsinthefollowingsections:
1. Inthecurrentproject,weneedtocreateanewC#scriptandcallitTestRenderImage.cs.
2. CreateanewshaderandcallitImageEffect.shader.3. Createasimplesphereinthesceneandassignitanewmaterial.Thisnewmaterial
canbeanything,butforourexample,wewillmakeasimplered,specularmaterial.4. Finally,createanewdirectionallightandsavethescene.
Withallofourassetsready,youshouldhaveasimplescenesetup,whichlookssimilartothefollowingimage:
Howtodoit…Inordertomakeourgrayscalescreeneffectwork,weneedascriptandshader.So,wewillcompletethesetwonewitemshereandfilltheminwiththeappropriatecodetoproduceourfirstscreeneffect.OurfirsttaskistocompletetheC#script.Thiswillgetthewholesystemrunning.Afterthis,wewillcompletetheshaderandseetheresultsofourScreenEffect.Let’scompleteourscriptandshaderwiththefollowingsteps:
1. OpentheTestRenderImage.csC#scriptandlet’sbeginbyenteringafewvariablesthatwewillneedtostoreimportantobjectsanddata.EnterthefollowingcodeattheverytopoftheTestRenderImageclass:
2. InorderforustoedittheScreenEffectinrealtime,whentheUnityeditorisn’tplaying,weneedtoenterthefollowinglineofcodejustabovethedeclarationoftheTestRenderImageclass:
3. AsourScreenEffectisusingashadertoperformthepixeloperationsonourScreenimage,wehavetocreateamaterialtoruntheshader.Withoutthis,wecan’taccessthepropertiesoftheshader.Forthis,wewillcreateaC#propertytocheckforamaterial,andcreateoneifitdoesn’tfindone.Enterthefollowingcodejustafterthedeclarationofthevariablesfromstep1:
4. WenowwanttosetupsomechecksinourscripttoseeifthecurrenttargetplatformthatwearebuildingtheUnitygameonactuallysupportsimageeffects.Ifitdoesn’tfindanythingatthestartofthisscript,thenthescriptwilldisableitself:
5. ToactuallygrabtheRenderedImagefromtheUnityRenderer,weneedtomakeuseofthefollowingbuilt-infunctionthatUnityprovidesus,calledOnRenderImage().EnterthefollowingcodesothatwecanhaveaccesstothecurrentRenderTexture:
6. OurScreeneffecthasavariablecalledgrayScaleAmountwithwhichwecancontrolhowmuchgrayscalewewantforourfinalScreenEffect.So,inthiscase,weneedtomakethevaluegofrom0–1,where0isnograyscaleeffectand1isfullgrayscaleeffect.WewillperformthisoperationintheUpdate()functionsothatitsetseveryframethisscriptisrunning:
7. Finally,wecompleteourscriptbydoingalittlebitofcleanuponobjectswecreatedwhenthescriptstarted:
Atthispoint,wecannowapplythisscripttothecamera,ifitcompiledwithouterrors,inUnity.Let’sapplytheTestRenderImage.csscripttoourmaincamerainourscene.YoushouldseethegrayScaleAmountvalueandafieldforashader,butthescriptthrowsanerrortotheconsolewindow.Itsaysthatitismissinganinstancetoanobjectandsowon’tprocessappropriately.Ifyourecallfromstep4,wearedoingsomecheckstoseewhetherwehaveashaderandthecurrentplatformsupportstheshader.Aswehaven’tgiventheScreenEffectscriptashadertoworkwith,thenthecurShadervariableisjustnull,whichthrowstheerror.Let’scontinueourScreenEffectssystembycompletingtheshader.
8. Tobeginourshader,wewillpopulateourpropertieswithsomevariablessothatwecansenddatatothisshader:
9. OurshaderisnowgoingtoutilizepureCGshadercodeinsteadofutilizingUnity’sbuilt-inSurfaceShadercode.ThiswillmakeourScreenEffectmoreoptimizedasweneedtoworkonlywiththepixelsoftheRenderTexture.So,wewillcreateanewPassblockinourshaderandfillitwithsomenew#pragmastatementsthatwehaven’tseenbefore:
10. InordertoaccessthedatabeingsenttotheshaderfromtheUnityeditor,weneedtocreatethecorrespondingvariablesinourCGPROGRAM:
11. Finally,allweneedtodoissetupourpixelfunction,inthiscase,calledfrag().ThisiswherethemeatoftheScreenEffectis.ThisfunctionwillprocesseachpixeloftheRenderTextureandreturnanewimagetoourTestRenderImage.csscript:
Oncetheshaderiscomplete,returntoUnityandletitcompiletoseeifanyerrorsoccurred.Ifnot,assignthenewshadertotheTestRenderImage.csscriptandchangethevalueofthegrayscaleamountvariable.Youshouldseethegameviewgofromacoloredversionofthegametoagrayscaleversionofthegame.ThefollowingimagedemonstratesthisScreenEffect:
Withthiscomplete,wenowhaveaneasywaytotestoutnewScreenEffectshaderswithouthavingtowriteourwholeScreenEffectsystemoverandoveragain.Let’sdiveinalittledeeperandlearnaboutwhat’sgoingonwiththeRenderTextureandhowitisprocessedthroughoutitsexistence.
Howitworks…TogetascreeneffectupandrunninginsideofUnity,weneedtocreateascriptandshader.Thescriptdrivesthereal-timeupdateintheeditorandisalsoresponsibleforcapturingtheRenderTexturefromthemaincameraandpassingittotheshader.Oncetherendertexturegetstotheshader,wecanusetheshadertoperformper-pixeloperations.
Atthestartofthescript,weperformafewcheckstomakesurethatthecurrentselectedbuildplatformactuallysupportsscreeneffectsandtheshaderitself.ThereareinstanceswhereacurrentplatformwillnotsupportScreenEffectsortheshaderthatweareusing.SothechecksthatwedointheStart()functionmakesurewedon’tgetanyerrorsiftheplatformdoesn’tsupportthescreensystem.
Oncethescriptpassesthesechecks,weinitiatetheScreenEffectssystembycallingthebuilt-infunction,OnRenderImage().ThisfunctionisresponsibleforgrabbingtherenderTexture,givingittotheshaderusingtheGraphics.Blit()function,andreturningtheprocessedimagetotheUnityrenderer.YoucanfindmoreinformationonthesetwofunctionsatthefollowingURLs:
OnRenderImage:http://docs.unity3d.com/Documentation/ScriptReference/MonoBehaviour.OnRenderImage.htmlGraphics.Blit:http://docs.unity3d.com/Documentation/ScriptReference/Graphics.Blit.html
Oncethecurrentrendertexturereachestheshader,theshadertakesit,processesitthroughthefrag()function,andreturnsthefinalcolorforeachpixel.
YoucanseehowpowerfulthisbecomesasitgivesusPhotoshop-likecontroloverthefinalrenderedimageofourgame.ThesescreeneffectsworksequentiallylikePhotoshoplayersinthecamera.Whenyouplacethesescreeneffectsoneaftertheother,theywillbeprocessedinthatorder.Thesearejustthebarebonesstepstogetascreeneffectworking,butitisthecoreofhowthescreeneffectssystemworks.
There’smore…NowthatwehaveoursimpleScreenEffectsystemupandrunning,let’stakealookatsomeoftheotherusefulinformationwecanobtainfromUnity’srenderer:
WecanactuallygetthedepthofeverythinginourcurrentgamebyturningonUnity’sbuilt-inDepthmode.Oncethisisturnedon,wecanusethedepthinformationforatonofdifferenteffects.Let’stakealookathowthisisdone:
1. CreateanewshaderandcallitSceneDepth_Effect.Thendouble-clickonthisshadertoopenitintheMonoDevelopeditor.
2. WewillcreatetheMainTexturepropertyandapropertytocontrolthepowerofthescenedeptheffect.Enterthefollowingcodeinyourshader:
3. NowweneedtocreatethecorrespondingvariablesinourCGPROGRAM.Wearegoingtoaddonemorevariablecalled_CameraDepthTexture.Thisisabuilt-invariablethatUnityhasprovideduswiththroughtheuseoftheUnityCGcgincludefile.Itgivesusthedepthinformationfromthecamera:
4. Wewillcompleteourdepthshaderbyutilizingacoupleofbuilt-infunctionsthatUnityprovidesuswith,theUNITY_SAMPLE_DEPTH()andlinear01Depth()functions.Thefirstfunctionactuallygetsthedepthinformationfromour_CameraDepthTextureandproducesasinglefloatvalueforeachpixel.TheLinear01Depth()functionthenmakessurethatthevaluesarewithinthe0-1rangebytakingthisfinaldepthvaluetoapowerwecancontrol,wherethemid-valueonthe0-1rangesitsinthescenebasedoffofthecameraposition:
5. Withourshadercomplete,let’sturnourattentiontoourScreenEffectsscript.WeneedtoaddthedepthPowervariabletothescriptsothatwecanletuserschangethevalueintheeditor:
6. OurOnRenderImage()functionthenneedstobeupdatedsothatitispassingtherightvaluetoourshader:
7. TocompleteourdepthScreeneffect,weneedtotellUnitytoturnonthedepthrenderinginthecurrentcamera.Thisisdonebysimplysettingthemaincamera’sdepthTextureMode:
Withallthecodesetup,saveyourscriptandshaderandreturntoUnitytoletthembothcompile.Ifnoerrorsareencountered,youshouldseearesultsimilartothefollowingimage:
Usingbrightness,saturation,andcontrastwithscreeneffectsNowthatwehaveourscreeneffectssystemupandrunning,wecanexplorehowtocreatemoreinvolvedpixeloperationstoperformsomeofthemorecommonScreenEffectsfoundingamestoday.
Tobegin,usingascreeneffecttoadjusttheoverallfinalcolorsofyourgameiscrucialingivingartistsaglobalcontroloverthefinallookofthegame.Techniquessuchascoloradjustmentsliderstoadjusttheintensityforthereds,blues,andgreensofthefinalrenderedgameortechniqueslikeputtingacertaintoneofcoloroverthewholescreenasseeninsomethinglikeasepiafilmeffect.
Forthisparticularrecipe,wearegoingtocoversomeofthemorecorecoloradjustmentoperationswecanperformonanimage.Thesearebrightness,saturation,andcontrast.Learninghowtocodethesecoloradjustmentsgivesusanicebasetolearntheartofscreeneffects.
GettingreadyWewillneedtocreateacoupleofnewassets.Wecanutilizethesamesceneasourtestscene,butwewillneedanewscriptandshader:
1. CreateanewscriptandcallitBSC_ImageEffect.2. CreateanewshadercalledBSC_Effect.3. NowwesimplyneedtocopythecodewehadfromtheC#scriptintheprevious
recipetoournewC#script.Thiswillallowustofocusjustonthemathematicsforthebrightness,saturation,andcontrasteffect.
4. Copythecodefromtheshaderinthepreviousrecipetoournewshader.5. Createacoupleofnewobjectsinthescene,setupsomedifferentcoloreddiffuse
materials,andrandomlyassignthemtothenewobjectsinthescene.Thiswillgiveusagoodrangeofcolorstotestwithournewscreeneffect.
Whencompleted,youshouldhaveascenesimilartothefollowingimage:
Howtodoit…Nowthatwehavecompletedourscenesetupandcreatedournewscriptandshader,wecanbegintofillinthecodenecessarytoachievethebrightness,saturation,andcontrastScreenEffect.Wewillbefocusingonjustthepixeloperationandvariablesetupforourscriptandshader,asgettingaScreenEffectsystemupandrunningisdescribedintheSettingupthescreeneffectsscriptsystemrecipe:
1. Let’sbeginbylaunchingournewshaderandscriptinMonoDevelop.Simplydouble-clickonthetwofilesintheprojectviewtoperformthisaction.
2. EditingtheshaderfirstmakesmoresensesothatweknowwhatkindofvariableswewillneedforourC#script.Let’sbeginthisbyenteringtheappropriatepropertiesforourbrightness,saturation,andcontrasteffect.Remember,weneedtokeepthe_MainTexpropertyinourshaderasthisisthepropertythattheRenderTexturetargetswhencreatingScreenEffects:
3. Asusual,inorderforustoaccessthedatacominginfromourpropertiesinourCGPROGRAM,weneedtocreatethecorrespondingvariablesintheCGPROGRAM:
4. Nowweneedtocreatetheoperationsthatwillperformthebrightness,saturation,andcontrasteffects.Enterthefollowingnewfunctioninourshader,justabovethefrag()function.Don’tworryifitdoesn’tmakesensejustyet;allthecodewillbeexplainedinthenextrecipe:
5. Finally,wejustneedtoupdateourfrag()functiontoactuallyusetheContrastSaturationBrightness()function.ThiswillprocessallthepixelsofourRenderTextureandpassitbacktoourscript:
Withthecodeenteredintheshader,returntotheUnityeditortoletthenewshadercompile.Iftherearenoerrors,wecanreturntoMonoDeveloptoworkonourscript.Let’sbeginthisbycreatingacoupleofnewlinesofcodethatwillsendtheproperdatatoourshader:
1. OurfirststepinmodifyingourscriptistoaddthepropervariablesthatwilldrivethevaluesofourScreenEffect.Inthiscase,wewillneedasliderforbrightness,asliderforsaturation,andasliderforcontrast:
2. Withourvariablessetup,wenowneedtotellthescripttopasstheirdatatotheshader.WedothisintheOnRenderImage()function:
3. Finally,allweneedtodoisclampthevaluesofthevariableswithinarangethatisreasonable.Theseclampvaluesareentirelypreferential,soyoucanusewhichevervaluesyouseefit:
Withthescriptcompletedandshaderfinished,wesimplyassignourscripttoourmaincameraandourshadertothescript,andyoushouldseetheeffectsofbrightness,saturation,andcontrastbymanipulatingtheslidervalues.Thefollowingimageshowsaresultyoucanachievewiththisscreeneffect:
Thefollowingimageshowsanotherexampleofwhatcanbedonebyadjustingthecolorsoftherenderimage:
Howitworks…SincewenowknowhowthebasicScreenEffectssystemworks,let’sjustcovertheper-pixeloperationswecreatedintheContrastSaturationBrightness()function.
Thefunctionstartsbytakingafewarguments.Thefirstandmostimportantisthecurrentrendertexture.Theotherargumentssimplyadjusttheoveralleffectofthescreeneffectandarerepresentedbyslidersinthescreeneffects’Inspectortab.Oncethefunctionreceivestherendertextureandtheadjustmentvalues,itdeclaresafewconstantvaluesthatweusetomodifyandcompareagainsttheoriginalrendertexture.
TheluminanceCoeffvariablestoresthevaluesthatwillgiveustheoverallbrightnessofthecurrentimage.ThesecoefficientsarebasedontheCIEcolormatchingfunctionsandareprettystandardthroughouttheindustry.Wecanfindtheoverallbrightnessoftheimagebygettingthedotproductofthecurrentimagedottedwiththeseluminancecoefficients.Oncewehavethebrightness,wesimplyuseacoupleoflerpfunctionstoblendfromthegrayscaleversionofthebrightnessoperationandtheoriginalimagemultipliedbythebrightnessvalue,beingpassedintothefunction.
Thescreeneffects,likethisone,arecrucialtoachievehigh-qualitygraphicsforyourgamesasitletsyoutweakthefinallookofyourgamewithouthavingtoediteachmaterialinyourcurrentgamescene.
UsingbasicPhotoshop-likeBlendmodeswithscreeneffectsThescreeneffectsaren’tjustlimitedtoadjustingthecolorsofarenderedimagefromourgame.WecanalsousethemtocombineotherimageswithourRenderTexture.ThistechniqueisnodifferentthancreatinganewlayerinPhotoshopandchoosingablendmodetoblendtwoimagestogetheror,inourcase,atexturewithaRenderTexture.ThisbecomesaverypowerfultechniqueasitgivestheartistsinaproductionenvironmentawaytosimulatetheirblendingmodesinthegameratherthanjustinPhotoshop.
Forthisparticularrecipe,wearegoingtotakealookatsomeofthemorecommonblendmodes,suchasMultiply,Add,andOverlay.YouwillseehowsimpleitistohavethepowerofPhotoshopBlendmodesinyourgame.
GettingreadyTobegin,wehavetogetourassetsready.Solet’sfollowthenextfewstepstogetourscreeneffectssystemupandrunningforournewBlendmodescreeneffect:
1. CreateanewscriptandcallitBlendMode_ImageEffect.2. CreateanewshadercalledBlendMode_Effect.3. NowwesimplyneedtocopythecodewehadfromtheC#scriptinthefirstrecipeof
thischaptertoournewC#script.Thiswillallowustofocusonjustthemathematicsforthebrightness,saturation,andcontrasteffect.
4. Copythecodefromtheshaderinthefirstrecipeinthischaptertoournewshader.5. Finally,wewillneedanothertexturetoperformourblendmodeeffect.Inthisrecipe,
wewilluseagrungetypetexture.Thiswillmaketheeffectveryobviouswhenwearetestingitout.
Thefollowingimageisthegrungemapusedinthemakingofthiseffect.Findingatexturewithenoughdetailandanicerangeofgrayscalevalueswillmakeforanicetexturetotestourneweffect:
Howtodoit…OurfirstblendmodethatwewillimplementistheMultiplyblendmodeasseeninPhotoshop.Let’sbeginbymodifyingthecodeinourshaderfirst.
1. LaunchtheshaderinMonoDevelopbydouble-clickingonitinUnity’sprojectview.2. Weneedtoaddsomenewpropertiessothatwehaveatexturetoblendwithanda
sliderforanopacityvalue.Enterthefollowingcodeinyournewshader:
3. EnterthecorrespondingvariablesinourCGPROGRAMsothatwecanaccessthedatafromourPropertiesblock:
4. Finally,wemodifyourfrag()functionsothatitperformsthemultiplyoperationonourtwotextures:
5. SavetheshaderandreturntotheUnityeditortoletthenewshadercodecompileandcheckforerrors.Ifnoerrorsoccurred,thendouble-clickontheC#scriptfiletolaunchitintheMonoDevelopeditor.
6. Inourscriptfileaswell,weneedtocreatethecorrespondingvariables.Sowewillneedatexturesothatwecanassignonetotheshaderandaslidertoadjustthefinalamountoftheblendmodewewanttouse:
7. WethenneedtosendourvariabledatatotheshaderthroughtheOnRenderImage()function:
8. Tocompletethescript,wesimplyfillinourUpdate()functionsothatwecanclampthevalueoftheblendOpacityvariablebetweenavalueof0.0and1.0:
Withthiscomplete,weassignthescreeneffectscripttoourmaincameraandourscreeneffectshadertoourscriptsothatithasashadertousefortheper-pixeloperations.Finally,inorderfortheeffecttobefullyfunctional,thescriptandshaderislookingforatexture.YoucanassignanytexturetothetexturefieldintheInspectorforthescreeneffectscript.Oncethistextureisinplace,youwillseetheeffectofmultiplyingthistextureoverthegame’srenderedimage.Thefollowingimagedemonstratesthescreeneffect:
Thefollowingimagedemonstratesahigherintensityofopacity,makingthemultipliedimagemuchmoreapparentoverourrenderimage:
Withourfirstblendmodesetup,wecanbegintoaddacoupleofsimplerblendmodestogetabetterunderstandingofhoweasyitistoaddmoreeffectsandreallyfine-tunethefinalresultinyourgame.However,firstlet’sbreakdownwhatishappeninghere.
Howitworks…NowwearestartingtogainatonofpowerandflexibilityinourScreenEffectsprogramming.IamsurethatyouarenowstartingtounderstandhowmuchonecandowiththissimplesysteminUnity.WecanliterallyreplicatetheeffectsofPhotoshoplayerblendingmodesinourgametogiveartiststheflexibilitytheyneedtoachievehigh-qualitygraphicsinashortamountoftime.
Withthisparticularrecipe,welookedathowtomultiplytwoimagestogether,addtwoimagestogether,andperformascreenblendingmode,usingjustalittlebitofmathematics.Whenworkingwithblendmodes,onehastothinkonaper-pixellevel.Forinstance,whenweareusingamultiplyblendmode,weliterallytakeeachpixelfromtheoriginalrendertextureandmultiplythemwitheachpixeloftheblendtexture.Thesamegoesfortheaddblendmode.Itisjustasimplemathematicaloperationofaddingeachpixelfromthesourcetexture,orrendertexture,totheblendtexture.
Thescreenblendmodeisdefinitelyabitmoreinvolved,butitisactuallydoingthesamething.Ittakeseachimage,rendertexture,andblendtexture,invertsthem,thenmultipliesthemtogether,andinvertsthemagaintoachievethefinallook.JustlikePhotoshopblendsitstexturestogetherusingblendmodes,wecandothesamewithscreeneffects.
There’smore…Let’scontinuethisrecipebyaddingacoupleofmoreblendmodestoourscreeneffect.
Inthescreeneffectshader,let’saddthefollowingcodetoourfrag()functionandchangethevaluewearereturningtoourscript.Wewillalsoneedtocommentoutthemultiplyblendsothatwedon’treturnthataswell:
1. SavetheshaderfileinMonoDevelopandreturntotheUnityeditortolettheshadercompile.Ifnoerrorsoccurred,youshouldseearesultsimilartothefollowingimage.Thisisasimpleaddblendingmode:
Asyoucansee,thishastheoppositeeffectofmultiplybecauseweareaddingthetwoimagestogether.
2. Finally,let’saddonemoreblendmodecalledaScreenBlend.Thisoneisalittlebit
moreinvolved,fromamathematicalstandpoint,butstillsimpletoimplement.Enterthefollowingcodeinthefrag()functionofourshader:
ThefollowingimagedemonstratestheresultsofusingaScreentypeblendmodetoblendtwoimagestogetherinascreeneffect:
UsingtheOverlayBlendmodewithscreeneffectsForourfinalrecipe,wearegoingtotakealookatanothertypeofblendmode,theOverlayBlendmode.Thisblendingactuallymakesuseofsomeconditionalstatementsthatdeterminethefinalcolorofeachpixelineachchannel.So,theprocessofusingthistypeofblendmodeneedsabitmorecodingtowork.Let’stakealookathowthisisdoneinthenextfewrecipes.
GettingreadyForthislastScreenEffect,wewillneedtosetupourtwoscriptsaswehaveinthepreviousrecipesinthischapter.Forthisrecipe,wewillbeusingthesamescenewehavebeenusing,sowedon’thavetocreateanewone:
1. CreateanewscriptfilecalledOverlay_ImageEffectandshaderfilecalledOverlay_Effect.
2. CopythecodefromthepreviousC#scriptfiletoournewscriptfile.3. Copythecodefromthepreviousshaderfiletoournewshaderfile.4. AssigntheOverlay_ImageEffectscripttothemaincameraandOverlay_Effectto
thescriptcomponentintheInspector.5. Finally,double-clickonthescriptandshaderfilestoopenthemintheMonoDevelop
editor.
Howtodoit…TobeginourOverlayScreenEffect,wewillneedtogetthecodeofourshaderupandrunningwithouterrors.Wecanthenmodifyourscriptfiletofeedthecorrectdatatotheshader.
1. WefirstneedtosetupourpropertiesinourPropertiesblock.Wewillusethesamepropertiesfromthepreviousfewrecipesinthischapter:
2. WethenneedtocreatethecorrespondingvariablesinourCGPROGRAM:
3. InorderfortheOverlayBlendeffecttowork,wewillhavetoprocesseachpixelfromeachchannelindividually.Todothisinashader,wehavetowriteacustomfunctionthatwilltakeinasinglechannel,forinstance,theredchannel,andperformtheOverlayoperation.Enterthefollowingcodeintheshaderjustbelowthevariabledeclarations:
4. Finally,weneedtoupdateourfrag()functiontoprocesseachchannelofourtexturestoperformtheblending:
5. Withthecodecompletedintheshader,oureffectshouldbeworking.SavetheshaderandreturntotheUnityeditortolettheshadercompile.Ourscriptisalreadysetup,sowedon’thavetomodifyitanyfurther.Oncetheshadercompiles,youshouldseearesultsimilartothefollowingimage:
Howitworks…OurOverlayblendmodeisdefinitelyalotmoreinvolved,butifyoureallybreakdownthefunction,youwillnoticethatitissimplyamultiplyblendmodeandscreenblendmode.It’sjustthat,inthiscase,wearedoingaconditionalchecktoapplyoneortheotherblendmodetoapixel.
WiththisparticularScreenEffect,whentheOverlayfunctionreceivesapixel,itcheckstoseewhetheritislessthan0.5.Ifitis,thenweapplyamodifiedmultiplyblendmodetothatpixel;ifit’snot,thenweapplyamodifiedscreenblendmodetothepixel.Wedothisforeachpixelforeachchannel,givingusthefinalRGBpixelvaluesforourScreeneffect.
Asyoucansee,therearemanythingsthatcanbedonewithscreeneffects.Itreallyjustdependsontheplatformandamountofmemoryyouhaveallocatedforscreeneffects.Usually,thisisdeterminedthroughoutthecourseofagameproject,sohavefunandgetcreativewithyourscreeneffects.
Chapter9.GameplayandScreenEffectsWhenitcomestocreatingbelievableandimmersivegames,materialdesignisnottheonlyaspectthatweneedtotakeintoaccount.Theoverallfeelingcanbealteredusingscreeneffects.Thisisverycommoninmovies,forinstance,whencolorsarecorrectedinthepost-productionphase.Youcanimplementthesetechniquesinyourgamestoo,usingtheknowledgefromChapter8,ScreenEffectswithUnityRenderTexture.Twointerestingeffectsarepresentedinthischapter;youcan,however,adaptthemtofityourneedsandcreateyourveryownscreeneffect.
Inthischapter,youwilllearnthefollowingrecipes:
CreatinganoldmoviescreeneffectCreatinganightvisionscreeneffect
IntroductionIfyouarereadingthisbook,youaremostlikelyapersonwhohasplayedagameortwoinyourtime.Oneoftheaspectsofreal-timegamesistheeffectofimmersingaplayerintoaworldtomakeitfeelasiftheywereactuallyplayingintherealworld.Themoremoderngamesmakeheavyuseofscreeneffectstoachievethisimmersion.
Withscreeneffects,wecanturnthemoodofacertainenvironmentfromcalmtoscary,justbychangingthelookofthescreen.Imaginewalkingintoaroomthatiscontainedwithinalevel,thenthegametakesoverandgoesintoacinematicmoment.Manymoderngameswillturnondifferentscreeneffectstochangethemoodofthecurrentmoment.Understandinghowtocreateeffectstriggeredbygameplayisnextinourjourneyofshaderwriting.
Inthischapter,wearegoingtotakealookatsomeofthemorecommongameplayscreeneffects.Youaregoingtolearnhowtochangethelookofthegamefromnormaltoanoldmovieeffect,andwearegoingtotakealookathowmanyfirst-personshootergamesapplytheirnightvisioneffectstothescreen.Witheachoftheserecipes,wearegoingtolookathowtohooktheseuptogameeventssothattheyareturnedonandoffasthegame’scurrentpresentationneeds.
CreatinganoldmoviescreeneffectManygamesaresetindifferenttimes.Sometakeplaceinfantasyworldsorfuturesci-fiworlds,andsomeeventakeplaceintheoldwest,wherefilmcameraswerejustbeingdevelopedandthemoviesthatpeoplewatchedwereblackandwhiteorsometimestintedwithwhatiscalledasepiaeffect.Thelookisverydistinct,andwearegoingtoreplicatethislookusingascreeneffectinUnity.
Thereareafewstepstoachievethislook,andjusttomakethewholescreenblackandwhiteorgrayscale,weneedtobreakdownthiseffectintoitscomponentparts.Ifweanalyzesomereferencefootageofanoldmovie,wecanbegintodothis.Let’stakealookatthefollowingimageandbreakdowntheelementsthatmakeuptheoldmovielook:
Weconstructedthisimageusingafewreferenceimagesfoundonline.ItisalwaysagoodideatotryandutilizePhotoshoptoconstructimageslikethistoaidyouincreatingaplanforyournewscreeneffect.Performingthisprocessnotonlytellsustheelementswewillhavetocodein,butitalsogivesusaquickwaytoseewhichblendingmodesworkandhowwewillconstructthelayersofourscreeneffect.ThePhotoshopfilewecreatedforthisrecipeisincludedinthisbook’ssupportpageatwww.packtpub.com/supportandiscalledOldFilmEffect_Research_Layout.psd.
GettingreadyNowthatweknowwhatwehavetomake,let’stakealookathoweachofthelayersiscombinedtocreatethefinaleffectandgathersomeresourcesforourshaderandscreeneffectscript.
Sepiatone:Thisisarelativelysimpleeffecttoachieve,aswejustneedtobringallthepixelcolorsoftheoriginalrendertexturetoasinglecolorrange.Thisiseasilyachievedusingtheluminanceoftheoriginalimageandaddingaconstantcolor.Ourfirstlayerwilllooklikethefollowingimage:
Vignetteeffect:Wecanalwaysseesomesortofsoftborderaroundoldfilmswhentheyarebeingprojectedwithanoldmovieprojector.Thisiscausedbecausethebulbbeingusedforthemovieprojectorhasmorebrightnessinthemiddlethanitdoesattheedgesofthefilm.Thiseffectisgenerallycalledthevignetteeffectandisoursecondlayerinourscreeneffect.Wecanachievethiswithanoverlaidtextureoverthewholescreen.Thefollowingimagedemonstrateswhatthislayerlookslike,isolatedasatexture:
Dustandscratches:Thethirdandfinallayerinouroldmoviescreeneffectisdustandscratches.Thislayerwillutilizetwodifferenttiledtextures,oneforscratchesandonefordust.Thereasonisthatwewillwanttoanimatethesetwotexturesovertimeatdifferenttilingrates.Thiswillgivetheeffectthatthefilmismovingalongandtherearesmallscratchesanddustoneachframeoftheoldfilm.Thefollowingimagedemonstratesthiseffectisolatedtoitsowntexture:
Let’sgetourscreeneffectsystemreadywiththeprecedingtextures.Performthefollowingsteps:
1. Gatherupavignettetextureanddustandscratchestexture,liketheoneswejustsaw.2. CreateanewscriptcalledOldFilmEffect.csandanewshadercalled
OldFilmEffectShader.shader.3. Withournewfilescreated,fillinthecodenecessarytogetthescreeneffectsystem
upandrunning.Forreferencesonhowtodothis,seeChapter8,ScreenEffectswithUnityRenderTextures.
Finally,withourscreeneffectsystemupandrunningandourtexturesgathered,wecanbegintheprocessofrecreatingthisoldfilmeffect.
Howtodoit…Ourindividuallayersforouroldfilmscreeneffectarequitesimple,butwhencombined,wegetsomeveryvisuallystunningeffects.Let’srunthroughhowtoconstructthecodeforourscriptandshader,thenwecanstepthrougheachlineofcodeandlearnwhythingsareworkingthewaytheyare.Atthispoint,youshouldhavethescreeneffectssystemupandrunning,aswewillnotbecoveringhowtosetthisupinthisrecipe.
1. Wewillbeginbyenteringthecodeinourscript.OurfirstblockofcodethatwewillenterwilldefineourvariablethatwewanttoexposetoInspectorinordertolettheuserofthiseffectadjustitastheyseefit.Wecanalsouseourmocked-upPhotoshopfileasareferencewhendecidingwhatwewillneedtoexposetotheInspectorofthiseffect.Enterthefollowingcodeinyoureffectscript:
#regionVariables
publicShaderoldFilmShader;
publicfloatOldFilmEffectAmount=1.0f;
publicColorsepiaColor=Color.white;
publicTexture2DvignetteTexture;
publicfloatvignetteAmount=1.0f;
publicTexture2DscratchesTexture;
publicfloatscratchesYSpeed=10.0f;
publicfloatscratchesXSpeed=10.0f;
publicTexture2DdustTexture;
publicfloatdustYSpeed=10.0f;
publicfloatdustXSpeed=10.0f;
privateMaterialcurMaterial;
privatefloatrandomValue;
#endregion
2. Next,weneedtofillinthecontentsofourOnRenderImage()function.Here,wewillbepassingthedatafromourvariablestoourshadersothattheshadercanthenusethisdataintheprocessingoftherendertexture:
voidOnRenderImage(RenderTexturesourceTexture,RenderTexture
destTexture)
{
if(oldFilmShader!=null)
{
material.SetColor("_SepiaColor",sepiaColor);
material.SetFloat("_VignetteAmount",vignetteAmount);
material.SetFloat("_EffectAmount",OldFilmEffectAmount);
if(vignetteTexture)
{
material.SetTexture("_VignetteTex",vignetteTexture);
}
if(scratchesTexture)
{
material.SetTexture("_ScratchesTex",scratchesTexture);
material.SetFloat("_ScratchesYSpeed",scratchesYSpeed);
material.SetFloat("_ScratchesXSpeed",scratchesXSpeed);
}
if(dustTexture)
{
material.SetTexture("_DustTex",dustTexture);
material.SetFloat("_dustYSpeed",dustYSpeed);
material.SetFloat("_dustXSpeed",dustXSpeed);
material.SetFloat("_RandomValue",randomValue);
}
Graphics.Blit(sourceTexture,destTexture,material);
}
else
{
Graphics.Blit(sourceTexture,destTexture);
}
}
3. Tocompletethescriptportionofthiseffect,wesimplyneedtomakesurethatweclampthevaluesofthevariablesthatneedtohaveaclampedrangeinsteadofbeinganyvalue:
voidUpdate()
{
vignetteAmount=Mathf.Clamp01(vignetteAmount);
OldFilmEffectAmount=Mathf.Clamp(OldFilmEffectAmount,0f,
1.5f);
randomValue=Random.Range(-1f,1f);
}
4. Withourscriptcomplete,let’sturnourattentiontoourshaderfile.Weneedtocreatethecorrespondingvariables,whichwecreatedinourscriptinourshader.Thiswillallowthescriptandshadertocommunicatewithoneanother.EnterthefollowingcodeinthePropertiesblockoftheshader:
Properties
{
_MainTex("Base(RGB)",2D)="white"{}
_VignetteTex("VignetteTexture",2D)="white"{}
_ScratchesTex("ScartchesTexture",2D)="white"{}
_DustTex("DustTexture",2D)="white"{}
_SepiaColor("SepiaColor",Color)=(1,1,1,1)
_EffectAmount("OldFilmEffectAmount",Range(0,1))=1.0
_VignetteAmount("VignetteOpacity",Range(0,1))=1.0
_ScratchesYSpeed("ScratchesYSpeed",Float)=10.0
_ScratchesXSpeed("ScratchesXSpeed",Float)=10.0
_dustXSpeed("DustXSpeed",Float)=10.0
_dustYSpeed("DustYSpeed",Float)=10.0
_RandomValue("RandomValue",Float)=1.0
_Contrast("Contrast",Float)=3.0
}
5. Then,asusual,weneedtoaddthesesamevariablenamestoourCGPROGRAMblocksothatthePropertiesblockcancommunicatewiththeCGPROGRAMblock:
CGPROGRAM
#pragmavertexvert_img
#pragmafragmentfrag
#pragmafragmentoptionARB_precision_hint_fastest
#include"UnityCG.cginc"
uniformsampler2D_MainTex;
uniformsampler2D_VignetteTex;
uniformsampler2D_ScratchesTex;
uniformsampler2D_DustTex;
fixed4_SepiaColor;
fixed_VignetteAmount;
fixed_ScratchesYSpeed;
fixed_ScratchesXSpeed;
fixed_dustXSpeed;
fixed_dustYSpeed;
fixed_EffectAmount;
fixed_RandomValue;
fixed_Contrast;
6. Now,wesimplyfillinthegutsofourfrag()functionsothatweprocessthepixelsforourscreeneffect.Tostartwith,let’sgettherendertextureandvignettetexturepassedtousbythescript:
fixed4frag(v2f_imgi):COLOR
{
//GetthecolorsfromtheRenderTextureandtheuv's
//fromthev2f_imgstruct
half2distortedUV=barrelDistortion(i.uv);
distortedUV=half2(i.uv.x,i.uv.y+(_RandomValue*_SinTime.z*
0.005));
fixed4renderTex=tex2D(_MainTex,i.uv);
//GetthepixelsfromtheVignetteTexture
fixed4vignetteTex=tex2D(_VignetteTex,i.uv);
7. Wethenneedtoaddtheprocessforthedustandscratchesbyenteringthefollowingcode:
//ProcesstheScratchesUVandpixels
half2scratchesUV=half2(i.uv.x+(_RandomValue*_SinTime.z*
_ScratchesXSpeed),i.uv.y+(_Time.x*_ScratchesYSpeed));
fixed4scratchesTex=tex2D(_ScratchesTex,scratchesUV);
//ProcesstheDustUVandpixels
half2dustUV=half2(i.uv.x+(_RandomValue*(_SinTime.z*
_dustXSpeed)),i.uv.y+(_RandomValue*(_SinTime.z*_dustYSpeed)));
fixed4dustTex=tex2D(_DustTex,dustUV);
8. Thesepiatoneprocessisnextonourlist:
//gettheluminosityvaluesfromtherendertextureusingtheYIQ
values.
fixedlum=dot(fixed3(0.299,0.587,0.114),renderTex.rgb);
//Addtheconstantcolortothelumvalues
fixed4finalColor=lum+lerp(_SepiaColor,_SepiaColor+
fixed4(0.1f,0.1f,0.1f,1.0f),_RandomValue);
finalColor=pow(finalColor,_Contrast);
9. Finally,wecombineallofourlayersandcolorsandreturnthefinalscreeneffecttexture:
//Createaconstantwhitecolorwecanusetoadjustopacityofeffects
fixed3constantWhite=fixed3(1,1,1);
//CompositetogetherthedifferentlayerstocreatefinslScreenEffect
finalColor=lerp(finalColor,finalColor*vignetteTex,
_VignetteAmount);
finalColor.rgb*=lerp(scratchesTex,constantWhite,(_RandomValue));
finalColor.rgb*=lerp(dustTex.rgb,constantWhite,(_RandomValue*
_SinTime.z));
finalColor=lerp(renderTex,finalColor,_EffectAmount);
returnfinalColor;
10. Withallofourcodeenteredandnoerrors,youshouldhavearesultverysimilartothefollowingimage.HitplayintheUnityeditortoseetheeffectsofthedustandscratchesandtheslightimageshiftthatwegavethescreeneffect:
Howitworks…Now,let’swalkthrougheachofthelayersinthisscreeneffect,breakdownwhyeachofthelinesofcodeisworkingthewayitis,andgetmoreinsightastohowwecanaddmoretothisscreeneffect.
Nowthatouroldfilmscreeneffectisworking,let’sstepthroughthelinesofcodeinourfrag()functionasalltheothercodeshouldbeprettyself-explanatoryatthispointinthebook.
JustlikeourPhotoshoplayers,ourshaderisprocessingeachlayerandthencompositingthemtogether,sowhilewegothrougheachlayer,trytoimaginehowthelayersinPhotoshopwork.Keepingthisconceptinmindalwayshelpswhendevelopingnewscreeneffects.
Here,wehavethefirstsetoflinesofcodeinourfrag()function:
fixed4frag(v2f_imgi):COLOR
{
//GetthecolorsfromtheRenderTextureandtheuv's
//fromthev2f_imgstruct
half2distortedUV=barrelDistortion(i.uv);
fixed4renderTex=tex2D(_MainTex,i.uv);
fixed4vignetteTex=tex2D(_VignetteTex,i.uv);
Thefirstlineofcode,justafterthefrag()functiondeclaration,isthedefinitionofhowtheUVsshouldworkforourmainrendertextureortheactualrenderedframeofourgame.Aswearelookingtofaketheeffectofanoldfilmstyle,wewanttoadjusttheUVsofourrendertexture,everyframe,suchthattheyflicker.Thisflickeringsimulateshowthewindingofthefilm’sprojectorisjustabitoff.ThistellsusthatweneedtoanimatetheUVsandthisiswhatthisfirstlineofcodeisdoing.
Weusedthebuilt-in_SinTimevariable,whichUnityprovides,togetavaluebetween-1and1.Wethenmultiplythisbyaverysmallnumber,inthiscase,0.005,toreducetheintensityoftheeffect.Thefinalvalueisthenmultipliedagainbythe_RandomValuevariable,whichwegeneratedintheeffectscript.Thisvaluebouncesbackandforthbetween-1and1tobasicallyflipthedirectionofthemotionbackandforth.
OnceourUVsarebuiltandstoredintherenderTexUVvariable,wecansampletherendertextureusingatex2D()function.Thisoperationthengivesusourfinalrendertexture,whichwecanusetoprocessfurtherintherestoftheshader.
Movingontothelastlineinthepreviousimage,wesimplydoastraightsampleofthevignettetextureusingthetex2D()function.Wedon’tneedtousetheanimatedUVswealreadycreated,asthevignettetexturewillbetiedtothemotionofthecameraitselfandnottotheflickeringofthecamerafilm.
Thefollowingcodesnippetillustratesthesecondsetoflinesofcodeinourfrag()function:
//ProcesstheScratchesUVandpixels
half2scratchesUV=half2(i.uv.x+(_RandomValue*_SinTime.z*
_ScratchesXSpeed),
i.uv.y+(_Time.x*_ScratchesYSpeed));
fixed4scratchesTex=tex2D(_ScratchesTex,scratchesUV);
//ProcesstheDustUVandpixels
half2dustUV=half2(i.uv.x+(_RandomValue*(_SinTime.z*_dustXSpeed)),
i.uv.y+(_RandomValue*(_SinTime.z*_dustYSpeed)));
fixed4dustTex=tex2D(_DustTex,dustUV);
TheselinesofcodearealmostexactlylikethepreviouslinesofcodeinwhichweneedtogenerateuniqueanimatedUVvaluestomodifythepositionofourscreeneffectlayers.Wesimplyusethebuilt-in_SinTimevaluetogetavaluebetween-1and1,multiplyitbyourrandomvalue,andthenbyanothermultipliertoadjusttheoverallspeedoftheanimation.OncetheseUVvaluesaregenerated,wecanthensampleourdustandscratchestextureusingthesenewanimatedvalues.
Ournextsetofcodehandlesthecreationofthecolorizingeffectforouroldfilmscreeneffect.Thefollowingcodesnippetdemonstratestheselines:
//gettheluminosityvaluesfromtherendertextureusingtheYIQvalues.
fixedlum=dot(fixed3(0.299,0.587,0.114),renderTex.rgb);
//Addtheconstantcolortothelumvalues
fixed4finalColor=lum+lerp(_SepiaColor,_SepiaColor
+fixed4(0.1f,0.1f,0.1f,1.0f),_RandomValue);
Withthissetofcode,wearecreatingtheactualcolortintingoftheentirerendertexture.Toaccomplishthis,wefirstneedtoturntherendertextureintothegrayscaleversionofitself.Todothis,wecanusetheluminosityvaluesgiventousbytheYIQvalues.YIQvaluesarethecolorspaceusedbytheNTSCcolorTVsystem.EachletterinYIQactuallystorescolorconstantsthatareusedbyTVstoadjustthecolorforreadability.
Whileitisnotnecessarytoactuallyknowthereasonsforthiscolorscale,itshouldbeknownthattheYvalueinYIQistheconstantluminancevalueforanyimage.So,wecangenerateagrayscaleimageofourrendertexturebytakingeachpixeloftherendertextureanddottingitwithourluminancevalues.Thisiswhatthefirstlineinthissetisdoing.
Oncewehavetheluminancevalues,wecansimplyaddthecolorwewanttotinttheimagewith.Thiscolorispassedfromourscripttoourshader,thentoourCGPROGRAMblock,wherewecanaddittoourgrayscalerendertexture.Oncecompleted,wewillhaveaperfectlytintedimage.
Finally,wecreatetheblendingbetweeneachofourlayersinourscreeneffect.Thefollowingcodesnippetshowsthesetofcodewearelookingat:
//Createaconstantwhitecolorwecanusetoadjustopacityofeffects
fixed3constantWhite=fixed3(1,1,1);
//CompositetogetherthedifferentlayerstocreatefinslScreenEffect
finalColor=lerp(finalColor,finalColor*vignetteTex,_VignetteAmount);
finalColor.rgb*=lerp(scratchesTex,constantWhite,(_RandomValue));
finalColor.rgb*=lerp(dustTex.rgb,constantWhite,(_RandomValue*
_SinTime.z));
finalColor=lerp(renderTex,finalColor,_EffectAmount);
returnfinalColor
Ourlastsetofcodeisrelativelysimpleanddoesn’treallyneedatonofexplanation.Inshort,itissimplymultiplyingallthelayerstogethertoreachourfinalresult.JustlikewemultipliedourlayerstogetherinPhotoshop,wemultiplythemtogetherinourshader.Eachlayerisprocessedthroughalerp()functionsothatwecanadjusttheopacityofeachlayer,whichgivesmoreartisticcontroloverthefinaleffect.Themoretweaksonecanoffer,thebetterwhenitcomestoscreeneffects.
SeealsoFormoreinformationontheYIQvalues,refertothefollowinglinks:
http://en.wikipedia.org/wiki/YIQhttp://www.blackice.com/colorspaceYIQ.htm
CreatinganightvisionscreeneffectOurnextscreeneffectisdefinitelyamorepopularone.ThenightvisionscreeneffectisseeninCallofDutyModernWarfare,Halo,andjustaboutanyfirst-personshooteroutinthemarkettoday.Itistheeffectofbrighteningthewholeimageusingthatverydistinctlimegreencolor.
Inordertoachieveournightvisioneffect,weneedtobreakdownoureffectusingPhotoshop.Itisasimpleprocessoffindingsomereferenceimagesonlineandcomposingalayeredimagetoseewhatkindofblendingmodesyouwillneedorinwhichorderwewillneedtocombineourlayers.ThefollowingimageshowstheresultofperformingjustthisprocessinPhotoshop:
Let’sbegintobreakdownourroughPhotoshopcompositeimageintoitscomponentpartssothatwecanbetterunderstandtheassetswewillhavetogather.Inthenextrecipe,wewillcovertheprocessofdoingthis.
GettingreadyLet’sbeginthisscreeneffectbyagainbreakingdownoureffectintoitscomponentlayers.UsingPhotoshop,wecanconstructalayeredimagetobetterillustratehowwecangoaboutcapturingtheeffectofnightvision:
Tintedgreen:Ourfirstlayerinourscreeneffectistheiconicgreencolor,foundinjustabouteverynightvisionimage.Thiswillgiveoureffectthatsignaturenightvisionlook,asshowninthefollowingimage:
Scanlines:Toincreasetheeffectofthisbeinganewtypeofdisplayfortheplayer,weincludescanlinesoverthetopofourtintedlayer.Forthis,wewilluseatexturecreatedinPhotoshopandlettheusertileitsothatthescanlinescanbebiggerorsmaller.Noise:Ournextlayerisasimplenoisetexturethatwetileoverthetintedimageandscanlinestobreakuptheimageandaddevenmoredetailtooureffect.Thislayersimplyemphasizesthatdigitalread-outlook:
Vignette:Thelastlayerinournightvisioneffectisthevignette.IfyoulookatthenightvisioneffectinCallofDutyModernWarfare,youwillnoticethatitusesavignettethatfakestheeffectoflookingdownascope.Wewilldothatforthisscreeneffect:
Let’screateascreeneffectsystembygatheringourtextures.Performthefollowingsteps:
1. Gatherupavignettetexture,noisetexture,andscanlinetexture,liketheoneswejustsaw.
2. CreateanewscriptcalledNightVisionEffect.csandanewshadercalledNightVisionEffectShader.shader.
3. Withournewfilescreated,fillinthecodenecessarytogetthescreeneffectsystemupandrunning.Forinstructionsonhowtodothis,refertoChapter8,ScreenEffectswithUnityRenderTextures.
Finally,withourscreeneffectsystemupandrunningandourtexturesgathered,wecanbegintheprocessofrecreatingthisoldfilmeffect.
Howtodoit…Withallofourassetsgatheredandscreeneffectsystemrunningsmoothly,let’sbegintoaddthecodenecessarytoboththescriptandshader.WewillbeginourcodingwiththeNightVisionEffect.csscript,sodouble-clickonthisfilenowtoopenitinMonoDevelop.
1. Weneedtocreateafewvariablesthatwillallowtheuserofthiseffecttoadjustitinthescript’sInspector.EnterthefollowingcodeintheNightVisionEffect.csscript:
#regionVariables
publicShadernightVisionShader;
publicfloatcontrast=2.0f;
publicfloatbrightness=1.0f;
publicColornightVisionColor=Color.white;
publicTexture2DvignetteTexture;
publicTexture2DscanLineTexture;
publicfloatscanLineTileAmount=4.0f;
publicTexture2DnightVisionNoise;
publicfloatnoiseXSpeed=100.0f;
publicfloatnoiseYSpeed=100.0f;
publicfloatdistortion=0.2f;
publicfloatscale=0.8f;
privatefloatrandomValue=0.0f;
privateMaterialcurMaterial;
#endregion
2. Next,weneedtocompleteourOnRenderImage()functionsothatwearepassingtherightdatatotheshaderinorderfortheshadertoprocessthescreeneffectproperly.CompletetheOnRenderImage()functionwiththefollowingcode:
voidOnRenderImage(RenderTexturesourceTexture,RenderTexture
destTexture)
{
if(nightVisionShader!=null)
{
material.SetFloat("_Contrast",contrast);
material.SetFloat("_Brightness",brightness);
material.SetColor("_NightVisionColor",nightVisionColor);
material.SetFloat("_RandomValue",randomValue);
material.SetFloat("_distortion",distortion);
material.SetFloat("_scale",scale);
if(vignetteTexture)
{
material.SetTexture("_VignetteTex",vignetteTexture);
}
if(scanLineTexture)
{
material.SetTexture("_ScanLineTex",scanLineTexture);
material.SetFloat("_ScanLineTileAmount",
scanLineTileAmount);
}
if(nightVisionNoise)
{
material.SetTexture("_NoiseTex",nightVisionNoise);
material.SetFloat("_NoiseXSpeed",noiseXSpeed);
material.SetFloat("_NoiseYSpeed",noiseYSpeed);
}
Graphics.Blit(sourceTexture,destTexture,material);
}
else
{
Graphics.Blit(sourceTexture,destTexture);
}
}
3. TocompletetheNightVisionEffect.csscript,wesimplyneedtomakesurethatweclampcertainvariablessothattheystaywithinarange.Theserangesarearbitraryandcanbechangedatalatertime.Thesearejustvaluesthatworkedwell:
voidUpdate()
{
contrast=Mathf.Clamp(contrast,0f,4f);
brightness=Mathf.Clamp(brightness,0f,2f);
randomValue=Random.Range(-1f,1f);
distortion=Mathf.Clamp(distortion,-1f,1f);
scale=Mathf.Clamp(scale,0f,3f);
}
4. Wecannowturnourattentionovertotheshaderportionofthisscreeneffect.Opentheshader,ifyouhaven’talready,andbeginbyenteringthefollowingpropertiesinthePropertiesblock:
Properties
{
_MainTex("Base(RGB)",2D)="white"{}
_VignetteTex("VignetteTexture",2D)="white"{}
_ScanLineTex("ScanLineTexture",2D)="white"{}
_NoiseTex("NoiseTexture",2D)="white"{}
_NoiseXSpeed("NoiseXSpeed",Float)=100.0
_NoiseYSpeed("NoiseYSpeed",Float)=100.0
_ScanLineTileAmount("ScanLineTileAmount",Float)=4.0
_NightVisionColor("NightVisionColor",Color)=(1,1,1,1)
_Contrast("Contrast",Range(0,4))=2
_Brightness("Brightness",Range(0,2))=1
_RandomValue("RandomValue",Float)=0
_distortion("Distortion",Float)=0.2
_scale("Scale(Zoom)",Float)=0.8
}
5. TomakesurethatwearepassingthedatafromourPropertiesblocktoourCGPROGRAMblock,weneedtomakesuretodeclarethemwiththesamenameintheCGPROGRAMblock:
CGPROGRAM
#pragmavertexvert_img
#pragmafragmentfrag
#pragmafragmentoptionARB_precision_hint_fastest
#include"UnityCG.cginc"
uniformsampler2D_MainTex;
uniformsampler2D_VignetteTex;
uniformsampler2D_ScanLineTex;
uniformsampler2D_NoiseTex;
fixed4_NightVisionColor;
fixed_Contrast;
fixed_ScanLineTileAmount;
fixed_Brightness;
fixed_RandomValue;
fixed_NoiseXSpeed;
fixed_NoiseYSpeed;
fixed_distortion;
fixed_scale;
6. Oureffectisalsogoingtoincludealensdistortiontofurtherconveytheeffectthatwearelookingthroughalensandtheedgesoftheimagearebeingdistortedbytheangleofthelens.EnterthefollowingfunctionjustafterthevariabledeclarationsintheCGPROGRAMblock:
float2barrelDistortion(float2coord)
{
//lensdistortionalgorithm
//Seehttp://www.ssontech.com/content/lensalg.htm
float2h=coord.xy-float2(0.5,0.5);
floatr2=h.x*h.x+h.y*h.y;
floatf=1.0+r2*(_distortion*sqrt(r2));
returnf*_scale*h+0.5;
}
7. WecannowconcentrateonthemeatofourNightVisionEffectshader.Let’sstartthisbyenteringthecodethatisnecessarytogettherendertextureandvignettetexture.Enterthefollowingcodeinthefrag()functionofourshader:
fixed4frag(v2f_imgi):COLOR
{
//GetthecolorsfromtheRenderTextureandtheuv's
//fromthev2f_imgstruct
half2distortedUV=barrelDistortion(i.uv);
fixed4renderTex=tex2D(_MainTex,distortedUV);
fixed4vignetteTex=tex2D(_VignetteTex,i.uv);
8. Thenextstepinourfrag()functionistoprocessthescanlinesandnoisetexturesandapplytheproperanimatedUVstothem:
//Processscanlinesandnoise
half2scanLinesUV=half2(i.uv.x*_ScanLineTileAmount,i.uv.y
*_ScanLineTileAmount);
fixed4scanLineTex=tex2D(_ScanLineTex,scanLinesUV);
half2noiseUV=half2(i.uv.x+(_RandomValue*_SinTime.z*
_NoiseXSpeed),
i.uv.y+(_Time.x*
_NoiseYSpeed));
fixed4noiseTex=tex2D(_NoiseTex,noiseUV);
9. Tocompleteallofourlayersinthescreeneffect,wesimplyneedtoprocesstheluminancevalueofourrendertexture,andthenapplythenightvisioncolortoittoachievethaticonicnightvisionlook:
//gettheluminosityvaluesfromtherendertextureusingtheYIQ
values.
fixedlum=dot(fixed3(0.299,0.587,0.114),renderTex.rgb);
lum+=_Brightness;
fixed4finalColor=(lum*2)+_NightVisionColor;
10. Lastly,wewillcombineallthelayerstogetherandreturnthefinalcolorofournightvisioneffect:
//Finaloutput
finalColor=pow(finalColor,_Contrast);
finalColor*=vignetteTex;
finalColor*=scanLineTex*noiseTex;
returnfinalColor;
Whenyouhavefinishedenteringthecode,returntotheUnityeditortoletthescriptandshadercompile.Iftherearenoerrors,hitplayintheeditortoseetheresults.Youshouldseesomethingsimilartothefollowingimage:
Howitworks…Thenightvisioneffectisactuallyverysimilartotheoldfilmscreeneffect,whichshowsusjusthowmodularwecanmakethesecomponents.Justbysimplyswappingthetexturesthatweareusingforoverlaysandchangingthespeedatwhichourtilingratesarebeingcalculated,wecanachieveverydifferentresultsusingthesamecode.
Theonlydifferencewiththiseffectisthefactthatweareincludingalensdistortiontoourscreeneffect.Solet’sbreakthisdownsothatwecangetabetterunderstandingofhowitworks.
Thefollowingcodesnippetillustratesthecodeusedinprocessingourlensdistortion.ItisasnippetofcodeprovidedtousbythemakersofSynthEyes,andthecodeisfreelyavailabletouseinyourowneffects:
float2barrelDistortion(float2coord)
{
//lensdistortionalgorithm
//Seehttp://www.ssontech.com/content/lensalg.htm
float2h=coord.xy-float2(0.5,0.5);
floatr2=h.x*h.x+h.y*h.y;
floatf=1.0+r2*(_distortion*sqrt(r2));
returnf*_scale*h+0.5;
}
There’smore…Itisnotuncommoninvideogamestohavetheneedtohighlightcertainobjects.Forinstance,athermalvisorshouldapplyapost-processingeffectonlytopeopleandothersourcesofheat.Doingthisisalreadypossiblewiththeknowledgegatheredsofarinthisbook;youcan,infact,changetheshaderormaterialofanobjectbycode.However,thisisoftenlaboriousandhastobereplicatedonalltheobjects.
Amoreeffectivewayisusingreplacedshaders.EachshaderhasatagcalledRenderTypethathasneverbeenusedsofar.Thispropertycanbeusedtoforceacameratoapplyashaderonlytocertainobjects.Youcandothisbyattachingthefollowingscripttothecamera:
usingUnityEngine;
publicclassReplacedShader:MonoBehaviour{
publicShadershader;
voidStart(){
GetComponent<Camera>().SetReplacementShader(shader,"Heat");
}
}
Afterenteringtheplaymode,thecamerawillqueryalltheobjectsthatithastorender.Iftheydon’thaveashaderdecoratedwithRenderType="Heat",theywillnotberendered.Objectswithsuchatagwillberenderedwiththeshaderattachedtothescript.
Chapter10.AdvancedShadingTechniquesInthischapter,youwilllearnthefollowingrecipes:
UsingCgIncludefilesthatarebuiltintoUnityMakingyourshaderworldmodularwithCgIncludeImplementingaFurShaderImplementingheatmapswitharrays
IntroductionThisfinalchaptercoverssomeadvancedshadertechniquesthatyoucanuseforyourgame.Youshouldrememberthatmanyofthemosteye-catchingeffectsyoucanseeingamesaremadebytestingthelimitofwhatshaderscando.Thisbookprovidesyouwiththetechnicalbasistomodifyandcreateshaders,butyouarestronglyencouragedtoplayandexperimentwiththemasmuchasyoucan.Makingagoodgameisnotaquestforphotorealism;youshouldnotapproachshaderswiththeintentionofreplicatingrealitybecausethisisunlikelytohappen.Instead,youshouldtrytouseshadersasatooltomakeyourgametrulyunique.Withtheknowledgeofthisfinalchapter,youwillbeabletocreatethematerialsthatyouwant.
UsingCgIncludefilesthatarebuiltintoUnityOurfirststepinwritingourownCgIncludefilesistounderstandwhatUnityisalreadyprovidinguswithforshaders.BywritingSurfaceShaders,thereisalothappeningunderthehood,whichmakestheprocessofwritingSurfaceShaderssoefficient.WecanseethiscodeintheincludedCgIncludefilesfoundinyourUnityinstallfolderatEditor|Data|CGIncludes.Allthefilescontainedwithinthisfolderdotheirparttorenderourobjectswithourshaderstothescreen.Someofthesefilestakecareofshadowsandlighting,sometakecareofhelperfunctions,andsomemanageplatformdependencies.Withoutthem,ourshaderwritingexperiencewouldbemuchmorelaborious.
YoucanfindalistofinformationthatUnityhasprovideduswithatthefollowinglink:http://docs.unity3d.com/Documentation/Components/SL-BuiltinIncludes.html
Let’sbegintheprocessofunderstandingthesebuilt-inCgIncludefiles,usingsomeofthebuilt-inhelperfunctionsfromtheUnityCG.cgincfile.
GettingreadyBeforewestartdivingintothemeatofwritingtheshader,weneedtogetafewitemssetupinourscene.Let’screatethefollowingandthenopentheshaderinMonoDevelop:
1. Createanewsceneandfillitwithasimplespheremodel.2. Createanewshaderandmaterial.3. Attachthenewshadertothenewmaterialandassignthematerialtothesphere.4. Then,let’screateadirectionallightandpositionitaboveoursphere.5. Finally,wearegoingtowanttoopentheUnityCG.cgincfilefromUnity’s
CgIncludefolderlocatedinUnity’sinstalldirectory.Thiswillletusanalyzesomeofthehelperfunction’scodesothatwecanunderstandbetterwhatishappeningwhenweusethem.
6. Youshouldhaveasimplescenesetuptoworkontheshader.Refertothefollowingscreenshotasanexample:
Howtodoit…Withthesceneprepared,wecannowbegintheprocessofexperimentingwithsomeofthebuilt-inhelperfunctionsincludedwiththeUnityCG.cgincfile.Double-clickontheshaderthatwascreatedforthissceneinordertoopenitinMonoDevelopandinsertthecodegiveninthefollowingsteps:
1. AddthefollowingcodetothePropertiesblockofthenewshaderfile.Wewillneedasingletextureandslideforourexampleshader:
Properties
{
_MainTex("Base(RGB)",2D)="white"{}
_DesatValue("Desaturate",Range(0,1))=0.5
}
2. WethenneedtomakesurethatwecreatethedataconnectionbetweenourPropertiesandCGPROGRAMblocks,withthefollowingcodeplacedaftertheCGPROGRAMdeclarationand#pragmadirectives:
sampler2D_MainTex;
fixed_DesatValue;
3. Finally,wejusthavetoupdateoursurf()functiontoincludethefollowingcode.Weintroduceanewfunctionthatwehaven’tseenyet,whichisbuiltintoUnity’sUnityCG.cgincfile:
voidsurf(InputIN,inoutSurfaceOutputo)
{
half4c=tex2D(_MainTex,IV.uv_MainTex);
c.rgb=lerp(c.rgb,Luminance(r.rgb),_DesatValue);
o.Albedo=c.rgb;
o.Alpha=c.a;
}
Withtheshadercodemodified,youshouldseesomethingsimilartothefollowingscreenshot.Wehavesimplyusedahelperfunction,builtintoUnity’sCgIncludefile,togiveusaneffectofdesaturatingthemaintextureofourshader:
Howitworks…Usingthebuilt-inhelperfunctionnamedLuminance(),weareabletoquicklygetadesaturationorgrayscaleeffectonourshaders.ThisisallpossiblebecausetheUnityCG.cgincfileisbroughtautomaticallytoourshaderasweareusingaSurfaceshader.
IfyousearchthroughtheUnityCG.cgincfile,openedinMonoDevelop,youwillfindtheimplementationofthisfunctionatline276.Thefollowingsnippetistakenfromthefile:
inlinefixedLuminance(fixed3c)
{
returndot(c,fixed3(0.22,0.707,0.071));
}
AsthisfunctionisincludedinthefileandUnityautomaticallycompileswiththisfile,wecanusethefunctioninourcodeaswell,therebyreducingtheamountofcodethatwehavetowriteoverandoveragain.
IfyounoticethereisalsoaLighting.cgincfilethatUnitycomeswith.Thisfilehousesallthelightingmodelsthatweusewhenwedeclaresomethinglike#pragmaSurfacesurfLambert.Siftingthroughthisfilerevealsthatallthebuilt-inlightingmodelsaredefinedhereforreuseandmodularity.
MakingyourshaderworldmodularwithCgIncludeKnowingaboutthebuilt-inCgIncludefilesisgreat,butwhatifwewantedtobuildourownCgIncludefilestostoreourownlightingmodelsandhelperfunctions?Wecan,infact,createourownCgIncludefiles,butweneedtolearnalittlemorecodesyntaxbeforewecanstartusingthemefficientlyinourshaderwritingpipelines.Let’stakealookattheprocessofcreatinganewCgIncludefilefromscratch.
GettingreadyLet’swalkthroughtheprocessofgeneratinganewitemforthisrecipe.
1. BeginbycreatinganewtextfileandcallitsomethinglikeMyCgInclude.txt.2. Thenchangeitsfileextensionto.cginc.Windowswillgiveyouawarningmessage
sayingthatthefilemaybecomeunusable,butitwillstillwork.3. Importthisnew.cgincfiletoyourUnityprojectandletitcompile.Ifallgoeswell,
youwillseethatUnityknewtocompileittoaCgIncludefile.
WearenowreadytobegincreatingourowncustomCgIncludecode.Simplydouble-clickontheCgIncludefilethatyoucreatedinordertoopenitinMonoDevelop.
Howtodoit…WithourCgIncludefileopen,wecanbegintoenterthecodethatwillgetitworkingwithourSurfaceShaders.ThefollowingcodewillgetourCgIncludefilereadyforusewithinourSurfaceShadersandallowustocontinuallyaddmorecodetoitaswedevelopmoreshaders:
1. WebeginourCgIncludefilewithwhatiscalledapreprocessordirective.Thesearestatementssuchas#pragmaand#include.Inthiscase,wewanttodefineanewsetofcodethatwillbeexecutedifourshaderincludesthisfileinitscompilerdirectives.EnterthefollowingcodeatthetopofyourCgIncludefile:
#ifndefMY_CG_INCLUDE
#defineMY_CG_INCLUDE
2. Wealwaysneedtomakesurethatweclose#ifndefor#ifdefwith#endiftoclosethedefinitioncheck,justlikeanifstatementneedstobeclosedwithtwobracketsinC#.Enterthefollowingcodejustafterthe#definedirective:
#endif
3. Atthispoint,wejustneedtofillinthegutsoftheCgIncludefile.SowefinishoffourCgIncludefilebyenteringthefollowingcode:
fixed4_MyColor;
inlinefixed4LightingHalfLamber(SurfaceOutputs,fixed3lightDir,
fixedatten)
{
fixeddiff=max(0,dot(s.Normal,lightDir));
diff=(diff+0.5)*0.5;
fixedc;
c.rgb=s.Albedo*_LightColor0.rgb*((diff*_MyColor.rgb)*
atten);
c.a=s.Alpha;
returnc;
}
#endif
4. Withthiscompleted,younowhaveyourveryfirstCgIncludefile.Withjustthislittlebitofcode,wecangreatlyreducetheamountofcodethatwehavetorewrite,andwecanbegintostorelightingmodelsthatweuseallthetimeheresothatweneverlosethem.YourCgIncludefileshouldlooksimilartothefollowingcodeshown:
#ifndefMY_CG_INCLUDE
#defineMY_CG_INCLUDE
fixed4_MyColor;
inlinefixed4LightingHalfLamber(SurfaceOutputs,fixed3lightDir,
fixedatten)
{
fixeddiff=max(0,dot(s.Normal,lightDir));
diff=(diff+0.5)*0.5;
fixedc;
c.rgb=s.Albedo*_LightColor0.rgb*((diff*_MyColor.rgb)*
atten);
c.a=s.Alpha;
returnc;
}
#endif
ThereareacouplemorestepsthatweneedtocompletebeforewecanfullyutilizethisCgIncludefile.Wesimplyneedtotellthecurrentshaderweareworkingwithtousethisfileanditscode.TocompletetheprocessofcreatingandusingCgIncludefiles,let’scompletethenextsetofsteps:
1. Ifweturnourattentiontoourshader,weneedtotellourCGPROGRAMblocktoincludeournewCgIncludefilesothatwecanaccessthecodeitcontains.ModifythedirectivesofourCGPROGRAMblocktoincludethefollowingcode:
CGPROGRAM
#include"MyCGInclude.cginc"
#pragmasurfacesurfLambert
2. Ourcurrentshaderiscurrentlyusingthebuilt-inLambertlightingmodel,butwewanttousetheHalfLambertlightingmodelthatwecreatedinourCgInclude.AsweincludedthecodefromourCgIncludefile,wecanusetheHalfLambertlightingmodelwiththefollowingcode:
CGPROGRAM
#include"MyCGInclude.cginc"
#pragmasurfacesurfHalfLambert
3. Finally,wehavealsodeclaredacustomvariableinourCgIncludefiletoshowthatwecansetupdefaultvariablesforourshaderstouse.Toseethisinaction,enterthefollowingcodeinthePropertiesblockofyourshader:
Properties
{
_MainTex("Base(RGB)",2D)="white"{}
_DesatValue("Desaturate",Range(0,1))=0.5
_MyColor("MyColor",Color)=(1,1,1,1)
}
4. WhenwereturntoUnity,theshaderandCgIncludefilewillcompile,andifyoudonotseeanyerrors,youwillnoticethatinfactweareusingournewHalfLambertlightingmodelandanewcolorswatchappearsinourmaterial’sInspector.ThefollowingscreenshotshowstheresultofusingourCgIncludefile:
Howitworks…Whenusingshaders,wecanincludeothersetsofcodeusingthe#includepreprocessordirective.ThistellsUnitythatwewanttoletthecurrentshaderusethecodefromwithintheincludedfileintheshader;thisisthereasonwhythesefilesarecalledCgIncludefiles.WeareincludingsnippetsofCgcodeusingthe#includedirective.
Oncewedeclarethe#includedirectiveandUnityisabletofindthefileintheproject,Unitywillthenlookforcodesnippetsthathavebeendefined.Thisiswherewestarttousethe#ifndefand#endifdirectives.Whenwedeclarethe#ifndefdirective,wearesimplysaying,ifnotdefined,definesomethingwithaname.Inthisrecipe’scase,wesaidwewantedto#defineMY_CG_INCLUDE.SoifUnitydoesn’tfindadefinitioncalledMY_CG_INCLUDE,itgoesandcreatesitwhentheCgIncludefileiscompiled,therebygivingusaccesstothecodethatfollows.The#endifmethodsimplysaysthatthisistheendofthisdefinition,sostoplookingformorecode.
Youcannowseehowpowerfulthisbecomesaswecannowstoreallofourlightingmodelsandcustomvariablesinonefileandgreatlyreducetheamountofcodethatwehavetowrite.TherealpoweriswhenyoucanbegintogiveyourshaderstheflexibilitybydefiningmultiplestatesoffunctionsintheCgIncludefiles.
ImplementingaFurShaderThelookofamaterialdependsonitsphysicalstructure.Theshadersattempttosimulatethem,butindoingso,theyoversimplifythewaylightbehaves.Materialswithacomplexmacroscopicstructureareparticularlyhardtorender.Thisisthecaseformanytextilefabricsandanimalfurs.Thisrecipewillshowyouhowitispossibletosimulatefurandothermaterials(suchasgrass)thataremorethanjustaflatsurface.Inordertodothis,thesamematerialisdrawnmultipletimesoverandover,increasingitssizeeverytime.Thiscreatestheillusionoffur.
TheshaderpresentedhereisbasedontheworkofJonathanCzeckandArasPranckevičius:
GettingreadyInorderforthisrecipetowork,youwillneedtwothings.Thefirstoneisthetextureofthefurasitappearsfromtheoutside.Thesecondtexturewillbeusedtocontrolthedistributionofthefurandisdeeplyconnectedtotheoriginalone.Thefollowingimageshowsaleopardfur(left)andpossiblecontrolmask(right):
Thewhitepixelsinthecontrolmaskwillbeextrudedfromtheoriginalmaterial,simulatingafur.Itisimportantthatthedistributionofthesewhitepixelsissparseinordertogiveanillusionthatthematerialismadeoutofmanysmallhairstrands.Aloosewaytocreatesuchatextureisasfollows:
1. Applyathresholdtoyouroriginaltexturetobettercapturepatcheswherethefurislessdense.
2. Applyanoisefilterthatpixelatestheimage.TheRGBchannelsofnoisemustnotbedependentinordertoproduceablackandwhiteresult.
3. Foramorerealisticlook,overlayaPerlinnoisefilterthataddstothevariabilityofthefur.
4. Finally,applyathresholdfilteragaintobetterseparatethepixelsinyourtexture.
Likealltheothershadersbefore,youwillneedtocreateanewstandardshaderandmaterialtohostit.
Howtodoit…Forthisrecipe,wecanstartmodifyingaStandardshader:
1. AddthefollowingProperties:
_FurLength("FurLength",Range(.0002,1))=.25
_Cutoff("Alphacutoff",Range(0,1))=0.5
_CutoffEnd("Alphacutoffend",Range(0,1))=0.5
_EdgeFade("EdgeFade",Range(0,1))=0.4
_Gravity("Gravitydirection",Vector)=(0,0,1,0)
_GravityStrength("Gstrenght",Range(0,1))=0.25
2. Thisshaderrequiresyoutorepeatthesamepassseveraltimes.WewillusethetechniqueintroducedintheMakingyourshaderworldmodularwithCgIncludessectiontogroupallthecodenecessaryfromasinglepassinanexternalfile.Let’sstartcreatinganewCgIncludefilecalledFurPass.cgincwiththefollowingcode:
#pragmatarget3.0
fixed4_Color;
sampler2D_MainTex;
half_Glossiness;
half_Metallic;
uniformfloat_FurLength;
uniformfloat_Cutoff;
uniformfloat_CutoffEnd;
uniformfloat_EdgeFade;
uniformfixed3_Gravity;
uniformfixed_GravityStrength;
voidvert(inoutappdata_fullv)
{
fixed3direction=lerp(v.normal,_Gravity*_GravityStrength+
v.normal*(1-_GravityStrength),FUR_MULTIPLIER);
v.vertex.xyz+=direction*_FurLength*FUR_MULTIPLIER*
v.color.a;
}
structInput{
float2uv_MainTex;
float3viewDir;
};
voidsurf(InputIN,inoutSurfaceOutputStandardo){
fixed4c=tex2D(_MainTex,IN.uv_MainTex)*_Color;
o.Albedo=c.rgb;
o.Metallic=_Metallic;
o.Smoothness=_Glossiness;
//o.Alpha=step(_Cutoff,c.a);
o.Alpha=step(lerp(_Cutoff,_CutoffEnd,FUR_MULTIPLIER),c.a);
floatalpha=1-(FUR_MULTIPLIER*FUR_MULTIPLIER);
alpha+=dot(IN.viewDir,o.Normal)-_EdgeFade;
o.Alpha*=alpha;
}
3. GetbacktoyouroriginalshaderandaddthisextrapassaftertheENDCGsection:
CGPROGRAM
#pragmasurfacesurfStandardfullforwardshadowsalpha:blend
vertex:vert
#defineFUR_MULTIPLIER0.05
#include"FurPass.cginc"
ENDCG
4. Addmorepasses,progressivelyincreasingFUR_MULTIPLIER.Youcangetdecentresultswith20passes,from0.05to0.95.
Oncetheshaderiscompiledandattachedtoamaterial,youcanchangeitsappearancefromtheInspector.TheFurLengthpropertydeterminesthespacebetweenthefurshells,whichwillbealteringthelengthofthefur.Alongerfurmightrequiremorepassestolookrealistic.AlphaCutoffandAlphaCutoffEndareusedtocontrolthedensityofthefurandhowitgetsprogressivelythinner.EdgeFadedeterminesthefinaltransparencyofthefur,resultinginafuzzierlook.SoftermaterialsshouldhaveahighEdgeFade.Finally,GravityDirectionandGravityStrengthcurvethefurshellstosimulatetheeffectofgravity.
Howitworks…ThetechniquepresentedinthisrecipeisknownasLengyel’sconcentricfurshelltechniqueor,simply,shelltechnique.Itworksbycreatingprogressivelybiggercopiesofthegeometrythatneedstoberendered.Withtherighttransparency,itgivestheillusionofacontinuousthreadofhair:
Theshelltechniqueisextremelyversatileandrelativelyeasytoimplement.Realistic,realfurrequiresnotonlyextrudingthegeometryofthemodel,butalsoalteringitsvertices.Thisispossiblewithtessellationshaders,whicharemuchmoreadvancedandnotcoveredinthisbook.
EachpassinthisFurShaderiscontainedinFurPass.cginc.Thevertexfunctioncreatesaslightlybiggerversionofthemodel,whichisbasedontheprincipleofnormalextrusion.Additionally,theeffectofgravityistakenintoaccountsothatitgetsmoreintensethefurtherwearefromthecentre:
voidvert(inoutappdata_fullv)
{
fixed3direction=lerp(v.normal,_Gravity*_GravityStrength+
v.normal*(1-_GravityStrength),FUR_MULTIPLIER);
v.vertex.xyz+=direction*_FurLength*FUR_MULTIPLIER*v.color.a;
}
Inthisexample,thealphachannelisusedtodeterminethefinallengthofthefur.Thisallowsforamoreprecisecontrol.
Finally,thesurfacefunctionreadsthecontrolmaskfromthealphachannel.Itusesthecutoffvaluetodeterminewhichpixelstoshowandwhichonestohide.ThisvaluechangesfromthefirsttothefinalfurshelltomatchAlphaCutoffandAlphaCutoffEnd:
o.Alpha=step(lerp(_Cutoff,_CutoffEnd,FUR_MULTIPLIER),c.a);
floatalpha=1-(FUR_MULTIPLIER*FUR_MULTIPLIER);
alpha+=dot(IN.viewDir,o.Normal)-_EdgeFade;
o.Alpha*=alpha;
Thefinalalphavalueofthefuralsodependsonitsanglefromthecamera,givingitasofterlook.
There’smore…TheFurShaderhasbeenusedtosimulatefur.However,itcanbeusedforavarietyofothermaterials.Itworksverywellformaterialsthatarenaturallymadeofmultiplelayers,suchasforestcanopies,fuzzyclouds,humanhair,andevengrass.
Therearemanyotherimprovementsthatcandramaticallyincreaseitsrealism.Youcanaddaverysimplewindanimationbychangingthedirectionofthegravitydependingonthecurrenttime.Ifcalibratedcorrectly,thiscangivetheimpressionthatthefurismovingbecauseofthewind.
Additionally,youcanmakeyourfurmovewhenthecharacterismoving.Alltheselittletweakscontributetothebelievabilityofyourfur,givingtheillusionthatitisnotjustastaticmaterialdrawnonthesurface.Unfortunately,thisshadercomesataprice:20passesareveryheavytocompute.Thenumberofpassesroughlydetermineshowbelievablethematerialis.Youshouldplaywithfurlengthandpassesinordertogettheeffectthatworksbestforyou.Giventheperformanceimpactofthisshader,itisadvisabletohaveseveralmaterialswithdifferentnumbersofpasses;youcanusethematdifferentdistancesandsavealotofcomputation.
ImplementingheatmapswitharraysOnecharacteristicthatmakesshadershardtomasteristhelackofaproperdocumentation.Mostdeveloperslearnshadersbymessingupwiththecode,withouthavingadeepknowledgeofwhat’sgoingon.TheproblemisamplifiedbythefactthatCg/HLSLmakesalotofassumptions,someofwhicharenotproperlyadvertised.Unity3DallowsC#scriptstocommunicatewithshadersusingmethodssuchasSetFloat,SetInt,SetVector,andsoon.Unfortunately,Unity3Ddoesn’thaveaSetArraymethod,whichledmanydeveloperstobelievethatCg/HLSLdoesn’tsupportarrayseither.Thisisnottrue.Thispostwillshowyouhowit’spossibletopassarraystoshaders.JustrememberthatGPUsarehighlyoptimizedforparallelcomputations,andusingforloopsinashaderwilldramaticallydropitsperformance.
Forthisrecipe,wewillimplementaheatmap,asshowninthefollowingimage:
GettingreadyTheeffectinthisrecipecreatesaheatmapfromasetofpoints.Thisheatmapcanbeoverlaidontopofanotherpicture,likeintheprecedingimage.Thefollowingstepsarenecessary:
1. Createaquadwiththetexturethatyouwanttousefortheheatmap.Inthisexample,amapofLondonhasbeenused.
2. Createanotherquad,andplaceitontopofthepreviousone.Ourheatmapwillappearonthisquad.
3. Attachanewmaterialandshadertothesecondquad.
Howtodoit…Thisshaderisquitedifferentfromtheonescreatedbefore,yetitisrelativelyshort.Forthisreason,theentirecodeisprovidedinthefollowingpoints:
1. Copythiscodetothenewlycreatedshader:
shader"Heatmap"{
Properties{
_HeatTex("Texture",2D)="white"{}
}
Subshader{
Tags{"Queue"="Transparent"}
BlendSrcAlphaOneMinusSrcAlpha//Alphablend
Pass{
CGPROGRAM
#pragmavertexvert
#pragmafragmentfrag
structvertInput{
float4pos:POSITION;
};
structvertOutput{
float4pos:POSITION;
fixed3worldPos:TEXCOORD1;
};
vertOutputvert(vertInputinput){
vertOutputo;
o.pos=mul(UNITY_MATRIX_MVP,input.pos);
o.worldPos=mul(_Object2World,input.pos).xyz;
returno;
}
uniformint_Points_Length=0;
uniformfloat3_Points[20];//(x,y,z)=position
uniformfloat2_Properties[20];//x=radius,y=
intensity
sampler2D_HeatTex;
half4frag(vertOutputoutput):COLOR{
//Loopsoverallthepoints
halfh=0;
for(inti=0;i<_Points_Length;i++)
{
//Calculatesthecontributionofeachpoint
halfdi=distance(output.worldPos,
_Points[i].xyz);
halfri=_Properties[i].x;
halfhi=1-saturate(di/ri);
h+=hi*_Properties[i].y;
}
//Converts(0-1)accordingtotheheattexture
h=saturate(h);
half4color=tex2D(_HeatTex,fixed2(h,0.5));
returncolor;
}
ENDCG
}
}
Fallback"Diffuse"
}
2. Onceyouhaveattachedthisscripttoyourmaterial,youshouldprovidearamptexturefortheheatmap.It’simportanttoconfigureitsothatitsWrapModeissettoClamp.Thefollowingonehasbeenusedforthisexample:
NoteIfyourheatmapisgoingtobeusedasanoverlay,thenmakesurethattheramptexturehasanalphachannelandthetextureisimportedwiththeoption,AlphaisTransparency.
3. CreateanewscriptcalledHeatmapsusingthefollowingcode:
usingUnityEngine;
usingSystem.Collections;
publicclassHeatmap:MonoBehaviour{
publicVector3[]positions;
publicfloat[]radiuses;
publicfloat[]intensities;
publicmaterialmaterial;
voidStart()
{
material.SetInt("_Points_Length",positions.Length);
for(inti=0;i<positions.Length;i++)
{
material.SetVector("_Points"+i.ToString(),positions[i]);
Vector2properties=newVector2(radiuses[i],
intensities[i]);
material.SetVector("_Properties"+i.ToString(),
properties);
}
}
}
4. Attachthescripttoanobjectinyourscene,preferablytothequad.Then,dragthematerialcreatedforthiseffecttothematerialslotofthescript.Bydoingthis,thescriptwillbeabletoaccessthematerialandinitializeit.
5. Lastly,expandthepositions,radiuses,andintensitiesfieldsofyourscriptandfillthemwiththevaluesofyourheatmap.Positionsindicatethepoints(inworldcoordinates)ofyourheatmaps,radiiindicatetheirsize,andintensitiesindicatehowstronglytheyaffectthesurroundingarea:
Howitworks…Thisshaderreliesonthingsthathaveneverbeenintroducedbeforeinthisbook;thefirstoneisarrays.Cgallowsarraysthatcanbecreatedwiththefollowingsyntax:
uniformfloat3_Points[20];
Cgdoesn’tsupportarrayswithanunknownsize:youmustpreallocateallthespacethatyouneedbeforehand.Theprecedinglineofcodecreatesanarrayof20elements.
Unitydoesnotexposeanymethodtoinitializethesearraysdirectly.However,singleelementsareaccessibleusingthenameofthearray(_Points)followedbytheposition,suchas_Points0or_Points10.Thiscurrentlyworksonlyforcertaintypesofarrays,suchasfloat3andfloat2.Thescriptattachedtothequadinitializestheshader’sarrays,elementbyelement.
Inthefragmentfunctionoftheshader,thereisasimilarforloopthat,foreachpixelofthematerial,queriesallthepointstofindtheircontributiontotheheatmap:
halfh=0;
for(inti=0;i<_Points_Length;i++)
{
//Calculatesthecontributionofeachpoint
halfdi=distance(output.worldPos,_Points[i].xyz);
halfri=_Properties[i].x;
halfhi=1-saturate(di/ri);
h+=hi*_Properties[i].y;
}
Thehvariablestorestheheatfromallthepoints,giventheirradiiandintensities.Itisthenusedtolookupwhichcolortousefromtheramptexture.
Theshadersandarraysareawinningcombination,especiallyasveryfewgamesareusingthemattheirfullpotential.However,theyintroduceasignificancebottleneckasforeachpixel,theshaderhastoloopthroughallthepoints.
IndexA
albedoandtransparencyURL/Seealso
AnisotropicSpeculartypecreating/CreatinganAnisotropicSpeculartype,Gettingready,Howtodoit…,Howitworks…URL/CreatinganAnisotropicSpeculartype
arraysheatmaps,implementingwith/Implementingheatmapswitharrays,Gettingready,Howtodoit…,Howitworks…
assets/Howitworks…AUTODESK
URL/Normalmapping
BbasicStandardShader
creating/CreatingabasicStandardShader,Howtodoit…,Howitworks…,Seealso
bindingsemantic/Howitworks…bindingsemantics
URL/SeealsoBlinn/CreatingaBlinnPhongSpeculartypeBlinnPhongSpeculartype
creating/CreatingaBlinnPhongSpeculartype,Howtodoit…,Howitworks…,Seealso
ButterflyEffectURL/Seealso
Ccalibrationchart
URL/SeealsoCelShading/CreatingaToonShaderCgInclude
used,formakingshaderworldmodular/MakingyourshaderworldmodularwithCgInclude,Howtodoit…,Howitworks…
CgIncludefilesbuiltintoUnity,using/UsingCgIncludefilesthatarebuiltintoUnity,Gettingready,Howtodoit…,Howitworks…
CgshadinglanguageURL/Howtodoit…
cheapshaderabout/Whatisacheapshader?,Gettingready,Howtodoit…,Howitworks…
circlecreating,aroundterrain/Creatingacirclearoundyourterrain,Gettingready,Howtodoit…moving/Movingthecircle
component/CreatingabasicStandardShaderCrazyBump
URL/Normalmappingcubemaps/Howitworks…culling/Creatingatransparentmaterialcustomdiffuselightingmodel
creating/Creatingacustomdiffuselightingmodel,Gettingready,Howitworks…
customshadersmigrating/Migratingcustomshaders
D2Dgames
waterShader,implementingfor/ImplementingaWaterShaderfor2Dgames,Gettingready,Howtodoit…,Howitworks…
2DtextureURL/Seealso
3DsurfaceURL/Seealso
debugging/There’smore…DefaultValue/Howitworks…Diffuseshader/MigratingLegacyShadersfromUnity4toUnity5diffuseshader
about/Howtodoit…,Howitworks…Diffuseshading
about/Diffuseshading,Howtodoit…,Howitworks…dotproduct/Howitworks…,Howitworks…,Coloringthesurface
Eextrusionmaps
adding/Addingextrusionmaps
Ffurshader
implementing/ImplementingaFurShader,Gettingready,Howtodoit…,Howitworks…,There’smore…
GGIMP/GettingreadyGimp/GettingreadyglassShader
implementing/ImplementingaGlassShader,Gettingready,Howtodoit…,There’smore…
GlobalIllumination(GI)/Introductiongrabpass
about/Introductionusing/Usinggrabpass,Howtodoit…,Howitworks…,There’smore…
Graphicalprocessingunit(GPU)/Howitworks…Graphics.Blit
URL/Howitworks…graphicsprocessingunit(GPU)/Howitworks…,Introduction,Howitworks…
Hheatmaps
implementing,witharrays/Implementingheatmapswitharrays,Gettingready,Howtodoit…,Howitworks…
holographicshadercreating/CreatingaHolographicShader,Howtodoit…,Seealso
Iindividualtimevalues
URL/Howitworks…InspectorGUIName/Howitworks…insulators/Howitworks…
LLambertianreflectance/Howtodoit…,Howtodoit…,Howitworks…
about/CreatingacustomdiffuselightingmodelLegacyShaders
migrating,fromUnity4toUnity5/MigratingLegacyShadersfromUnity4toUnity5automaticupgradeoption/UpgradingautomaticallyStandardShaders,using/UsingStandardShaderscustomshaders,migrating/Migratingcustomshaders
lightbaking/Bakinglightsinyourscenelightingfunctions
URL/Howitworks…lightingmodel/Introductionlightmap/Howitworks…Lightmapping/Howitworks…lightprobes
configuring/ConfiguringthelightprobesURL/Seealso
lightprobing/Howitworks…lights
baking,inscene/Bakinglightsinyourscene,Configuringthestaticgeometrystaticgeometry,configuring/Configuringthestaticgeometrylightprobes,configuring/Configuringthelightprobesbaking/Bakingthelights,Howitworks…
lighttransport/Bakinglightsinyourscene
Mmasking/Howtodoit…materialchart
URL/Seealsomaterials/CreatingabasicStandardShaderMaya/Gettingreadymetallicsetup
about/Understandingthemetallicsetup,Gettingready,Howitworks…mirrors
creating/Creatingmirrorsandreflectivesurfaces,Gettingreadymodel-view-projectionmatrix/Howitworks…models
extruding/Extrudingyourmodels,Howtodoit…,There’smore…extrusionmaps,adding/Addingextrusionmaps
MonoDevelop/Howtodoit…
NNDOPainter
URL/Normalmappingnightvisionscreeneffect
creating/Creatinganightvisionscreeneffect,Howtodoit…,Howitworks…,There’smore…tintedgreen/Gettingreadyscanlines/Gettingreadynoisetexture/Gettingreadyvignetteeffect/Gettingready
noisetextures/Gettingreadynon-playablecharacters(NPCs)/Configuringthelightprobesnormalextrusion/Extrudingyourmodels,Howitworks…normalmapping
about/Normalmapping,Howtodoit…,Howitworks…,There’smore…normals/GettingreadyNvidia/Seealso
Ooldmoviescreeneffect
creating/Creatinganoldmoviescreeneffect,Gettingready,Howtodoit…,Howitworks…sepiatone/Gettingreadyvignetteeffect/Gettingreadydustandscratches/Gettingready
OnRenderImageURL/Howitworks…
Oren-NayarlightingmodelURL/Seealso
OverlayBlendmodewithscreeneffects/UsingtheOverlayBlendmodewithscreeneffects,Howtodoit…,Howitworks…
Ppackedarrays
using/Usingpackedarrays,Howtodoit…about/Howtodoit…URL/Seealso
packedmatricesabout/Packedmatrices,Seealso
PassionPictures/SeealsoPBRTextureConversion
URL/SeealsoPerlinnoise/GettingreadyPhongSpeculartype
creating/CreatingaPhongSpeculartype,Howtodoit…,Howitworks…Photoshop/Gettingready,Gettingreadyphysically-basedrendering/Howtodoit…physically-basedrendering(PBR)
URL/Seealsotransparency,adding/AddingtransparencytoPBR,Howtodoit…
pixelShaders/Howitworks…posteffects/IntroductionProfiler
using/Gettingready,Howtodoit…URL/There’smore…
propertiesadding,toshader/Addingpropertiestoashader,Howtodoit…,Howitworks…URL/Seealsousing,inSurfaceShader/UsingpropertiesinaSurfaceShader,Howtodoit…,Howitworks…,There’smore…,Seealso
PyroTechnixURL/Seealso
QQuixelMEGASCANS
URL/Seealso
Rrampmap/Gettingreadyreal-timeshading/Introductionreflectionprobe
URL/Gettingready,Seealso/Howitworks…reflectivesurfaces
creating/Creatingmirrorsandreflectivesurfaces,Gettingreadyrenderercomponent/Configuringthelightprobesrenderers/CreatingabasicStandardShaderrenderqueues/Howitworks…RGBchannels/Gettingready
Sscene
lights,baking/Bakinglightsinyourscene,Configuringthestaticgeometryscreeneffects
brightness/Usingbrightness,saturation,andcontrastwithscreeneffects,Howtodoit…,Howitworks…saturation/Usingbrightness,saturation,andcontrastwithscreeneffects,Howtodoit…,Howitworks…contrast/Usingbrightness,saturation,andcontrastwithscreeneffects,Howtodoit…,Howitworks…blendmodes,basicPhotoshoplike/UsingbasicPhotoshop-likeBlendmodeswithscreeneffects,Howtodoit…,Howitworks…,There’smore…overlayBlendmodewith/UsingtheOverlayBlendmodewithscreeneffects,Howtodoit…,Howitworks…
screeneffectsscriptsystemsettingup/Settingupthescreeneffectsscriptsystem,Howtodoit…,Howitworks…,There’smore…
scripts/CreatingabasicStandardShadershader
URL/Migratingcustomshadersproperties,adding/Addingpropertiestoashader,Howtodoit…,Howitworks…textures,adding/Addingatexturetoashader,Gettingready,Howitworks…,There’smore…,Seealsomakingmodular,CgIncludeused/MakingyourshaderworldmodularwithCgInclude,Howtodoit…,Howitworks…furshader,implenenting/ImplementingaFurShader,Gettingready,Howtodoit…,Howitworks…,There’smore…
ShaderCalibrationSceneURL/Seealso
Shaderreplacement/Howitworks…shaders/Introduction
profiling/Profilingyourshaders,Howtodoit…,There’smore…modifying,formobile/Modifyingourshadersformobile,Howtodoit…,Howitworks…
skinnedmeshrenderers/Howitworks…skyboxes/Howitworks…smearing/Howtodoit…snowshader
implementing/Implementingasnowshader,Howtodoit…surface,coloring/Coloringthesurfacegeometry,altering/Alteringthegeometry
Specularshader/MigratingLegacyShadersfromUnity4toUnity5
standard/IntroductionStandardDiffuse/Howtodoit…StandardShaders
using/UsingStandardShadersstaticgeometry
configuring/Configuringthestaticgeometrysubstancedesigner
URL/Seealsosurfacefunction/Introductionsurfaceoutput/IntroductionSurfaceOutputStandardSpecularstruct
properties/Howitworks…SurfaceOutputStandardstruct
properties/Howitworks…SurfaceOutputstruct
properties/Howitworks…SurfaceShader
about/Howtodoit…properties,using/UsingpropertiesinaSurfaceShader,Howtodoit…,Howitworks…,There’smore…,Seealsoworking/Introductionvertexcolor,accessing/AccessingavertexcolorinaSurfaceShader,Gettingready,Howtodoit…,Howitworks…vertices,animating/AnimatingverticesinaSurfaceShader,Howtodoit…,Howitworks…
swizzling/Howtodoit…
Tterrain
circle,creatingaround/Creatingacirclearoundyourterrain,GettingreadytextureGUIelement/Addingpropertiestoashadertexturemapping/Addingatexturetoashadertextures
adding,toshader/Addingatexturetoashader,Gettingready,Howitworks…,There’smore…,Seealsoscrolling,bymodifyingUVvalues/ScrollingtexturesbymodifyingUVvalues,Howtodoit…,Howitworks…packing/Packingandblendingtextures,Gettingready,Howtodoit…,Howitworks…blending/Packingandblendingtextures,Gettingready,Howtodoit…,Howitworks…
toonshadercreating/CreatingaToonShader,Gettingready,Howitworks…,There’smore…
toonshading/CreatingaToonShadertransparency
adding,toPBR/AddingtransparencytoPBR,Howtodoit…semi-transparentmaterials/Semi-transparentmaterialsobjects,fading/Fadingobjectssolidgeometries,withholes/Solidgeometrieswithholes
transparentmaterialcreating/Creatingatransparentmaterial,Gettingready,Howtodoit…,Howitworks…
Type/Howitworks…
UUnity
documentation,URL/UnderstandingthemetallicsetupURL/UsingCgIncludefilesthatarebuiltintoUnity
Unity4toUnity5,LegacyShadersmigratingfrom/MigratingLegacyShadersfromUnity4toUnity5
UnityAssetStoreURL/Seealso
UNITYDOWNLOADARCHIVEURL/Seealso
UnityOfficialTutorialsURL/Solidgeometrieswithholes
UVdataabout/Addingatexturetoashader
UVvaluesmodifying,toscrolltextures/ScrollingtexturesbymodifyingUVvalues,Howtodoit…,Howitworks…
VVariableName/Howitworks…VertexandFragmentShaders
about/UnderstandingVertexandFragmentShaders,Howtodoit…,Howitworks…fragmentfunction/Howitworks…fragment/Howitworks…pixelShaders/Howitworks…bindingsemantic/Howitworks…model-view-projectionmatrix/Howitworks…Graphicalprocessingunit(GPU)/Howitworks…bindingsemantics/There’smore…,Outputsemanticsinputsemantics/Inputsemanticsoutputsemantics/Outputsemantics
vertexcolorinSurfaceShader,accessing/AccessingavertexcolorinaSurfaceShader,Howtodoit…,Howitworks…
vertexmodifier/Howitworks…vertices
inSurfaceShader,animating/AnimatingverticesinaSurfaceShader,Gettingready,Howtodoit…,Howitworks…
volumeraycasting/Seealsovolumetricexplosion
implementing/Implementingavolumetricexplosion,Gettingready,Howtodoit…,Howitworks…,Seealso
volumetricexplosionsabout/Implementingavolumetricexplosion
WwaterShader
implementing,for2Dgames/ImplementingaWaterShaderfor2Dgames,Howtodoit…,Howitworks…
YYIQvalues
URL/Seealso
ZZbrush4R7
URL/NormalmappingZBuffering/Howitworks…Zordering/Creatingatransparentmaterial