unity 5.x shaders and effects cookbook - netsitesnetsites.mx/unity3dtutorials/unity 5.x shaders and...

Post on 17-Jun-2020

45 Views

Category:

Documents

7 Downloads

Preview:

Click to see full reader

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<customercare@packtpub.com>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<feedback@packtpub.com>,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<copyright@packtpub.com>withalinktothesuspectedpiratedmaterial.

Weappreciateyourhelpinprotectingourauthorsandourabilitytobringyouvaluablecontent.

QuestionsIfyouhaveaproblemwithanyaspectofthisbook,youcancontactusat<questions@packtpub.com>,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

top related