Clipping & Reflections Using The Stencil Buffer - NeHe ...

文章推薦指數: 80 %
投票人數:10人

It will ONLY run on cards that support the stencil buffer. ... The trick behind creating a real looking reflection that reflects in the floor and nowhere ... HOME TWITTER FACEBOOK RSS ATOM FORUM Clipping&ReflectionsUsingTheStencilBuffer Welcometoanotherexcitingtutorial.ThecodeforthistutorialwaswrittenbyBanuOctavian.Thetutorialwasofcoursewrittenbymyself(NeHe).InthistutorialyouwilllearnhowtocreateEXTREMELYrealisticreflections.Nothingfakehere!Theobjectsbeingreflectedwillnotshowupunderneaththefloororontheothersideofawall.Truereflections! Averyimportantthingtonoteaboutthistutorial:BecausetheVoodoo1,2andsomeothercardsdonotsupportthestencilbuffer,thisdemowillNOTrunonthosecards.ItwillONLYrunoncardsthatsupportthestencilbuffer.Ifyou'renotsureifyourcardsupportsthestencilbuffer,downloadthecode,andtryrunningthedemo.Also,thisdemorequiresafairlydecentprocessorandgraphicscard.EvenonmyGeForceInoticethereisalittleslowdownattimes.Thisdemorunsbestin32bitcolormode! Asvideocardsgetbetter,andprocessorsgetfaster,Icanseethestencilbufferbecomingmorepopular.Ifyouhavethehardwareandyou'rereadytoreflect,readon! Thefirstpartofthecodeisfairlystandard.Weincludeallnecessaryheaderfiles,andsetupourDeviceContext,RenderingContext,etc. #include //HeaderFileForWindows #include //HeaderFileForTheOpenGL32Library #include //HeaderFileForTheGLu32Library #include //HeaderFileForTheGlauxLibrary #include //HeaderFileForStandardInput/Output HDC hDC=NULL; //PrivateGDIDeviceContext HGLRC hRC=NULL; //PermanentRenderingContext HWND hWnd=NULL; //HoldsOurWindowHandle HINSTANCE hInstance=NULL; //HoldsTheInstanceOfTheApplication Nextwehavethestandardvariablestokeeptrackofkeypresses(keys[]),whetherornottheprogramisactive(active),andifweshouldusefullscreenmodeorwindowedmode(fullscreen). bool keys[256]; //ArrayUsedForTheKeyboardRoutine bool active=TRUE; //WindowActiveFlagSetToTRUEByDefault bool fullscreen=TRUE; //FullscreenFlagSetToFullscreenModeByDefault Nextwesetupourlightingvariables.LightAmb[]willsetourambientlight.Wewilluse70%red,70%greenand70%blue,creatingalightthatis70%brightwhite.LightDif[]willsetthediffuselighting(theamountoflightevenlyreflectedoffthesurfaceofourobject).Inthiscasewewanttoreflectfullintensitylight.LastlywehaveLightPos[]whichwillbeusedtopositionourlight.Inthiscasewewantthelight4unitstotheright,4unitsup,and6unitstowardstheviewer.Ifwecouldactuallyseethelight,itwouldbefloatinginfrontofthetoprightcornerofourscreen. //LightParameters staticGLfloat LightAmb[]={0.7f,0.7f,0.7f,1.0f}; //AmbientLight staticGLfloat LightDif[]={1.0f,1.0f,1.0f,1.0f}; //DiffuseLight staticGLfloat LightPos[]={4.0f,4.0f,6.0f,1.0f}; //LightPosition Wesetupavariablecalledqforourquadraticobject,xrotandyrottokeeptrackofrotation.xrotspeedandyrotspeedcontrolthespeedourobjectrotatesat.zoomisusedtozoominandoutofthescene(westartat-7whichshowsustheentirescene)andheightistheheightoftheballabovethefloor. Wethenmakeroomforour3textureswithtexture[3],anddefineWndProc(). GLUquadricObj *q; //QuadraticForDrawingASphere GLfloat xrot =0.0f; //XRotation GLfloat yrot =0.0f; //YRotation GLfloat xrotspeed =0.0f; //XRotationSpeed GLfloat yrotspeed =0.0f; //YRotationSpeed GLfloat zoom =-7.0f; //DepthIntoTheScreen GLfloat height =2.0f; //HeightOfBallFromFloor GLuint texture[3]; //3Textures LRESULT CALLBACKWndProc(HWND,UINT,WPARAM,LPARAM); //DeclarationForWndProc TheReSizeGLScene()andLoadBMP()codehasnotchangedsoIwillskipoverbothsectionsofcode. GLvoidReSizeGLScene(GLsizeiwidth,GLsizeiheight) //ResizeAndInitializeTheGLWindow AUX_RGBImageRec*LoadBMP(char*Filename) //LoadsABitmapImage Theloadtexturecodeisprettystandard.You'veuseditmanytimesbeforeintheprevioustutorials.Wemakeroomfor3textures,thenweloadthethreeimages,andcreatelinearfilteredtexturesfromtheimagedata.ThebitmapfilesweusearelocatedintheDATAdirectory. intLoadGLTextures() //LoadBitmapsAndConvertToTextures { intStatus=FALSE; //StatusIndicator AUX_RGBImageRec*TextureImage[3]; //CreateStorageSpaceForTheTextures memset(TextureImage,0,sizeof(void*)*3); //SetThePointerToNULL if((TextureImage[0]=LoadBMP("Data/EnvWall.bmp"))&& //LoadTheFloorTexture (TextureImage[1]=LoadBMP("Data/Ball.bmp"))&& //LoadtheLightTexture (TextureImage[2]=LoadBMP("Data/EnvRoll.bmp"))) //LoadtheWallTexture { Status=TRUE; //SetTheStatusToTRUE glGenTextures(3,&texture[0]); //CreateTheTexture for(intloop=0;loop<3;loop++) //LoopThrough5Textures { glBindTexture(GL_TEXTURE_2D,texture[loop]); glTexImage2D(GL_TEXTURE_2D,0,3,TextureImage[loop]->sizeX,TextureImage[loop]->sizeY,0,GL_RGB,GL_UNSIGNED_BYTE,TextureImage[loop]->data); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); } for(loop=0;loop<3;loop++) //LoopThrough5Textures { if(TextureImage[loop]) //IfTextureExists { if(TextureImage[loop]->data) //IfTextureImageExists { free(TextureImage[loop]->data); //FreeTheTextureImageMemory } free(TextureImage[loop]); //FreeTheImageStructure } } } returnStatus; //ReturnTheStatus } AnewcommandcalledglClearStencilisintroducedintheinitcode.Passing0asaparametertellsOpenGLtodisableclearingofthestencilbuffer.Youshouldbefamiliarwiththerestofthecodebynow.Weloadourtexturesandenablesmoothshading.Theclearcolorissettoanoffblueandthecleardepthissetto1.0f.Thestencilclearvalueissetto0.Weenabledepthtesting,andsetthedepthtestvaluetolessthanorequalto.Ourperspectivecorrectionissettonicest(verygoodquality)and2dtexturemappingisenabled. intInitGL(GLvoid) //AllSetupForOpenGLGoesHere { if(!LoadGLTextures()) //IfLoadingTheTexturesFailed { returnFALSE; //ReturnFalse } glShadeModel(GL_SMOOTH); //EnableSmoothShading glClearColor(0.2f,0.5f,1.0f,1.0f); //Background glClearDepth(1.0f); //DepthBufferSetup glClearStencil(0); //ClearTheStencilBufferTo0 glEnable(GL_DEPTH_TEST); //EnablesDepthTesting glDepthFunc(GL_LEQUAL); //TheTypeOfDepthTestingToDo glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST); //ReallyNicePerspectiveCalculations glEnable(GL_TEXTURE_2D); //Enable2DTextureMapping Nowit'stimetosetuplight0.ThefirstlinebelowtellsOpenGLtousethevaluesstoredinLightAmbfortheAmbientlight.Ifyourememberatthebeginningofthecode,thergbvaluesofLightAmbwereall0.7f,givingusawhitelightat70%fullintensity.WethensettheDiffuselightusingthevaluesstoredinLightDifandpositionthelightusingthex,y,zvaluesstoredinLightPos. AfterwehavesetthelightupwecanenableitwithglEnable(GL_LIGHT0).Eventhoughthelightisenabled,youwillnotseeituntilweenablelightingwiththelastlineofcode. Note:IfwewantedtoturnoffalllightsinascenewewoulduseglDisable(GL_LIGHTING).IfwewantedtodisablejustoneofourlightswewoulduseglDisable(GL_LIGHT{0-7}).Thisgivesusalotofcontroloverthelightingandwhatlightsareonandoff.JustrememberifGL_LIGHTINGisdisabled,youwillnotseelights! glLightfv(GL_LIGHT0,GL_AMBIENT,LightAmb); //SetTheAmbientLightingForLight0 glLightfv(GL_LIGHT0,GL_DIFFUSE,LightDif); //SetTheDiffuseLightingForLight0 glLightfv(GL_LIGHT0,GL_POSITION,LightPos); //SetThePositionForLight0 glEnable(GL_LIGHT0); //EnableLight0 glEnable(GL_LIGHTING); //EnableLighting Inthefirstlinebelow,wecreateanewquadraticobject.ThesecondlinetellsOpenGLtogeneratesmoothnormalsforourquadraticobject,andthethirdlinetellsOpenGLtogeneratetexturecoordinatesforourquadratic.Withoutthesecondandthirdlinesofcode,ourobjectwoulduseflatshadingandwewouldn'tbeabletotextureit. ThefourthandfifthlinestellOpenGLtousetheSphereMappingalgorithmtogeneratethetexturecoordinates.Thisallowsustospheremapthequadraticobject. q=gluNewQuadric(); //CreateANewQuadratic gluQuadricNormals(q,GL_SMOOTH); //GenerateSmoothNormalsForTheQuad gluQuadricTexture(q,GL_TRUE); //EnableTextureCoordsForTheQuad glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP); //SetUpSphereMapping glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP); //SetUpSphereMapping returnTRUE; //InitializationWentOK } Thecodebelowwilldrawourobject(whichisacoollookingenvironmentmappedbeachball). WesetthecolortofullintensitywhiteandbindtoourBALLtexture(theballtextureisaseriesofred,whiteandbluestripes). Afterselectingourtexture,wedrawaQuadraticSpherewitharadiusof0.35f,32slicesand16stacks(upanddown). voidDrawObject() //DrawOurBall { glColor3f(1.0f,1.0f,1.0f); //SetColorToWhite glBindTexture(GL_TEXTURE_2D,texture[1]); //SelectTexture2(1) gluSphere(q,0.35f,32,16); //DrawFirstSphere Afterdrawingthefirstsphere,weselectanewtexture(EnvRoll),setthealphavalueto40%andenableblendingbasedonthesourcealphavalue.glEnable(GL_TEXTURE_GEN_S)andglEnable(GL_TEXTURE_GEN_T)enablesspheremapping. Afterdoingallthat,weredrawthesphere,disablespheremappinganddisableblending. Thefinalresultisareflectionthatalmostlookslikebrightpointsoflightmappedtothebeachball.Becauseweenablespheremapping,thetextureisalwaysfacingtheviewer,evenastheballspins.Weblendsothatthenewtexturedoesn'tcancelouttheoldtexture(aformofmultitexturing). glBindTexture(GL_TEXTURE_2D,texture[2]); //SelectTexture3(2) glColor4f(1.0f,1.0f,1.0f,0.4f); //SetColorToWhiteWith40%Alpha glEnable(GL_BLEND); //EnableBlending glBlendFunc(GL_SRC_ALPHA,GL_ONE); //SetBlendingModeToMixBasedOnSRCAlpha glEnable(GL_TEXTURE_GEN_S); //EnableSphereMapping glEnable(GL_TEXTURE_GEN_T); //EnableSphereMapping gluSphere(q,0.35f,32,16); //DrawAnotherSphereUsingNewTexture //TexturesWillMixCreatingAMultiTextureEffect(Reflection) glDisable(GL_TEXTURE_GEN_S); //DisableSphereMapping glDisable(GL_TEXTURE_GEN_T); //DisableSphereMapping glDisable(GL_BLEND); //DisableBlending } Thecodebelowdrawsthefloorthatourballhoversover.Weselectthefloortexture(EnvWall),anddrawasingletexturemappedquadonthez-axis.Prettysimple! voidDrawFloor() //DrawsTheFloor { glBindTexture(GL_TEXTURE_2D,texture[0]); //SelectTexture1(0) glBegin(GL_QUADS); //BeginDrawingAQuad glNormal3f(0.0,1.0,0.0); //NormalPointingUp glTexCoord2f(0.0f,1.0f); //BottomLeftOfTexture glVertex3f(-2.0,0.0,2.0); //BottomLeftCornerOfFloor glTexCoord2f(0.0f,0.0f); //TopLeftOfTexture glVertex3f(-2.0,0.0,-2.0); //TopLeftCornerOfFloor glTexCoord2f(1.0f,0.0f); //TopRightOfTexture glVertex3f(2.0,0.0,-2.0); //TopRightCornerOfFloor glTexCoord2f(1.0f,1.0f); //BottomRightOfTexture glVertex3f(2.0,0.0,2.0); //BottomRightCornerOfFloor glEnd(); //DoneDrawingTheQuad } Nowforthefunstuff.Here'swherewecombinealltheobjectsandimagestocreateourreflectivescene. Westartoffbyclearingthescreen(GL_COLOR_BUFFER_BIT)toourdefaultclearcolor(offblue).Thedepth(GL_DEPTH_BUFFER_BIT)andstencil(GL_STENCIL_BUFFER_BIT)buffersarealsocleared.Makesureyouincludethestencilbuffercode,it'snewandeasytooverlook!It'simportanttonotewhenweclearthestencilbuffer,wearefillingitwith0's. Afterclearingthescreenandbuffers,wedefineourclippingplaneequation.Theplaneequationisusedforclippingthereflectedimage. Theequationeqr[]={0.0f,-1.0f,0.0f,0.0f}willbeusedwhenwedrawthereflectedimage.Asyoucansee,thevalueforthey-planeisanegativevalue.Meaningwewillonlyseepixelsiftheyaredrawnbelowthefloororatanegativevalueonthey-axis.Anythingdrawnabovethefloorwillnotshowupwhenusingthisequation. Moreonclippinglater...readon. intDrawGLScene(GLvoid) //DrawEverything { //ClearScreen,DepthBuffer&StencilBuffer glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); //ClipPlaneEquations doubleeqr[]={0.0f,-1.0f,0.0f,0.0f}; //PlaneEquationToUseForTheReflectedObjects Sowehaveclearedthescreen,anddefinedourclippingplanes.Nowforthefunstuff! Westartoffbyresettingthemodelviewmatrix.Whichofcoursestartsalldrawinginthecenterofthescreen.Wethentranslatedown0.6funits(toaddasmallperspectivetilttothefloor)andintothescreenbasedonthevalueofzoom.Tobetterexplainwhywetranslatedown0.6funits,I'llexplainusingasimpleexample.Ifyouwerelookingatthesideofapieceofpaperatexactlyeyelevel,youwouldbarelybeabletoseeit.Itwouldmorethanlikelylooklikeathinline.Ifyoumovedthepaperdownalittle,itwouldnolongerlooklikealine.Youwouldseemoreofthepaper,becauseyoureyeswouldbelookingdownatthepageinsteadofdirectlyattheedgeofthepaper. glLoadIdentity(); //ResetTheModelviewMatrix glTranslatef(0.0f,-0.6f,zoom); //ZoomAndRaiseCameraAboveTheFloor(Up0.6Units) Nextwesetthecolormask.Somethingnewtothistutorial!The4valuesforcolormaskrepresentred,green,blueandalpha.BydefaultallthevaluesaresettoGL_TRUE. IftheredvalueofglColorMask({red},{green},{blue},{alpha})wassettoGL_TRUE,andalloftheothervalueswere0(GL_FALSE),theonlycolorthatwouldshowuponthescreenisred.Ifthevalueforredwas0(GL_FALSE),buttheothervalueswereallGL_TRUE,everycolorexceptredwouldbedrawntothescreen. Wedon'twantanythingdrawntothescreenatthemoment,withallofthevaluessetto0(GL_FALSE),colorswillnotbedrawntothescreen. glColorMask(0,0,0,0); //SetColorMask Nowevenmorefunstuff...Settingupthestencilbufferandstenciltesting! Westartoffbyenablingstenciltesting.Oncestenciltestinghasbeenenabled,weareabletomodifythestencilbuffer. It'sveryhardtoexplainthecommandsbelowsopleasebearwithme,andifyouhaveabetterexplanation,pleaseletmeknow.Inthecodebelowwesetupatest.ThelineglStencilFunc(GL_ALWAYS,1,1)tellsOpenGLwhattypeoftestwewanttodooneachpixelwhenanobjectisdrawntothescreen. GL_ALWAYSjusttellsOpenGLthetestwillalwayspass.Thesecondparameter(1)isareferencevaluethatwewilltestinthethirdlineofcode,andthethirdparameterisamask.ThemaskisavaluethatisANDedwiththereferencevalueandstoredinthestencilbufferwhenthetestisdone.Areferencevalueof1ANDedwithamaskvalueof1is1.SoifthetestgoeswellandwetellOpenGLto,itwillplaceaoneinthestencilbuffer(reference&mask=1). Quicknote:Stenciltestingisaperpixeltestdoneeachtimeanobjectisdrawntothescreen.ThereferencevalueANDedwiththemaskvalueistestedagainstthecurrentstencilvalueANDedwiththemaskvalue. Thethirdlineofcodetestsforthreedifferentconditionsbasedonthestencilfunctionwedecidedtouse.ThefirsttwoparametersareGL_KEEP,andthethirdisGL_REPLACE. ThefirstparametertellsOpenGLwhattodoifthetestfails.BecausethefirstparameterisGL_KEEP,ifthetestfails(whichitcan'tbecausewehavethefuntionsettoGL_ALWAYS),wewouldleavethestencilvaluesetatwhateveritcurrentlyis. ThesecondparametertellsOpenGLwhatdodoifthestenciltestpasses,butthedepthtestfails.Inthecodebelow,weeventuallydisabledepthtestingsothisparametercanbeignored. Thethirdparameteristheimportantone.IttellsOpenGLwhattodoifthetestpasses!InourcodewetellOpenGLtoreplace(GL_REPLACE)thevalueinthestencilbuffer.ThevalueweputintothestencilbufferisourreferencevalueANDedwithourmaskvaluewhichis1. Aftersettingupthetypeoftestingwewanttodo,wedisabledepthtestingandjumptothecodethatdrawsourfloor. InsimpleenglishIwilltrytosumupeverythingthatthecodedoesupuntilnow... WetellOpenGLnottodrawanycolorstothescreen.Thismeansthatwhenwedrawthefloor,itwontshowuponthescreen.BUT...eachspotonthescreenwheretheobject(ourfloor)shouldbeifwecouldseeitwillbetestedbasedonthetypeofstenciltestingwedecidetodo.Thestencilbufferstartsoutfullof0's(empty).Wewanttosetthestencilvalueto1whereverourobjectwouldhavebeendrawnifwecouldseeit.SowetellOpenGLwedon'tcareabouttesting.Ifapixelshouldhavebeendrawntothescreen,wewantthatspotmarkedwitha1.GL_ALWAYSdoesexactlythat.Ourreferenceandmaskvaluesof1makesurethatthevalueplacedintothestencilbufferisindeedgoingtobe1!Asweinvisiblydraw,ourstenciloperationcheckseachpixellocation,andreplacesthe0witha1. glEnable(GL_STENCIL_TEST); //EnableStencilBufferFor"marking"TheFloor glStencilFunc(GL_ALWAYS,1,1); //AlwaysPasses,1BitPlane,1AsMask glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE); //WeSetTheStencilBufferTo1WhereWeDrawAnyPolygon //KeepIfTestFails,KeepIfTestPassesButBufferTestFails //ReplaceIfTestPasses glDisable(GL_DEPTH_TEST); //DisableDepthTesting DrawFloor(); //DrawTheFloor(DrawsToTheStencilBuffer) //WeOnlyWantToMarkItInTheStencilBuffer Sonowwehaveaninvisiblestencilmaskofthefloor.Aslongasstenciltestingisenabled,theonlyplacespixelswillshowupareplaceswherethestencilbufferhasavalueof1.Allofthepixelsonthescreenwheretheinvisiblefloorwasdrawnwillhaveastencilvalueof1.Meaningaslongasstenciltestingisenabled,theonlypixelsthatwewillseearethepixelsthatwedrawinthesamespotourinvisiblefloorwasdefinedinthestencilbuffer.Thetrickbehindcreatingareallookingreflectionthatreflectsinthefloorandnowhereelse! Sonowthatweknowtheballreflectionwillonlybedrawnwherethefloorshouldbe,it'stimetodrawthereflection!Weenabledepthtesting,andsetthecolormaskbacktoallones(meaningallthecolorswillbedrawntothescreen). InsteadofusingGL_ALWAYSforourstencilfunctionwearegoingtouseGL_EQUAL.We'llleavethereferenceandmaskvaluesat1.ForthestenciloperationwewillsetalltheparameterstoGL_KEEP.Inenglish,anyobjectwedrawthistimearoundwillactuallyappearonthescreen(becausethecolormaskissettotrueforeachcolor).AslongasstenciltestingisenabledpixelswillONLYbedrawnifthestencilbufferhasavalueof1(referencevalueANDedwiththemask,whichis1EQUALS(GL_EQUAL)thestencilbuffervalueANDedwiththemask,whichisalso1).Ifthestencilvalueisnot1wherethecurrentpixelisbeingdrawnitwillnotshowup!GL_KEEPjusttellsOpenGLnottomodifyanyvaluesinthestencilbufferifthetestpassesORfails! glEnable(GL_DEPTH_TEST); //EnableDepthTesting glColorMask(1,1,1,1); //SetColorMasktoTRUE,TRUE,TRUE,TRUE glStencilFunc(GL_EQUAL,1,1); //WeDrawOnlyWhereTheStencilIs1 //(I.E.WhereTheFloorWasDrawn) glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP); //Don'tChangeTheStencilBuffer Nowweenablethemirroredclippingplane.Thisplaneisdefinedbyeqr,andonlyallowsobjecttobedrawnfromthecenterofthescreen(wheretheflooris)downtothebottomofthescreen(anynegativevalueonthey-axis).Thatwaythereflectedballthatwedrawcan'tcomeupthroughthecenterofthefloor.Thatwouldlookprettybadifitdid.Ifyoudon'tunderstandwhatImean,removethefirstlinebelowfromthesourcecode,andmovetherealball(nonreflected)throughthefloor.Ifclippingisnotenabled,youwillseethereflectedballpopoutofthefloorastherealballgoesintothefloor. Afterweenableclippingplane0(usuallyyoucanhavefrom0-5clippingplanes),wedefinetheplanebytellingittousetheparametersstoredineqr. Wepushthematrix(whichbasicallysavesthepositionofeverythingonthescreen)anduseglScalef(1.0f,-1.0f,1.0f)tofliptheobjectupsidedown(creatingareallookingreflection).SettingtheyvalueofglScalef({x},{y},{z})toanegativevalueforcesOpenGLtorenderoppositeonthey-axis.It'salmostlikeflippingtheentirescreenupsidedown.Whenpositionanobjectatapositivevalueonthey-axis,itwillappearatthebottomofthescreeninsteadofatthetop.Whenyourotateanobjecttowardsyourself,itwillrotateawayfromyou.Everythingwillbemirroredonthey-axisuntilyoupopthematrixorsettheyvaluebackto1.0finsteadof-1.0fusingglScalef({x},{y},{z}). glEnable(GL_CLIP_PLANE0); //EnableClipPlaneForRemovingArtifacts //(WhenTheObjectCrossesTheFloor) glClipPlane(GL_CLIP_PLANE0,eqr); //EquationForReflectedObjects glPushMatrix(); //PushTheMatrixOntoTheStack glScalef(1.0f,-1.0f,1.0f); //MirrorYAxis ThefirstlinebelowpositionsourlighttothelocationspecifiedbyLightPos.Thelightshouldshineonthebottomrightofthereflectedballcreatingaveryreallookinglightsource.Thepositionofthelightisalsomirrored.Ontherealball(ballabovethefloor)thelightispositionedatthetoprightofyourscreen,andshinesonthetoprightoftherealball.Whendrawingthereflectedball,thelightispositionedatthebottomrightofyourscreen. Wethenmoveupordownonthey-axistothevaluespecifiedbyheight.Translationsaremirrored,soifthevalueofheightis5.0f,thepositionwetranslatetowillbemirrored(-5.0f).Positioningthereflectedimageunderthefloor,insteadofabovethefloor! Afterpositionourreflectedball,werotatetheballonboththexaxisandyaxis,basedonthevaluesofxrotandyrot.Keepinmindthatanyrotationsonthexaxiswillalsobemirrored.Soiftherealball(ballabovethefloor)isrollingtowardsyouonthex-axis,itwillberollingawayfromyouinthereflection. AfterpositioningthereflectedballanddoingourrotationswedrawtheballbycallingDrawObject(),andpopthematrix(restoringthingstohowtheywerebeforewedrewtheball).Poppingthematrixallcancelsmirroringonthey-axis. Wethendisableourclippingplane(plane0)sothatwearenotstuckdrawingonlytothebottomhalfofthescreen,andlast,wedisablestenciltestingsothatwecandrawtootherspotsonthescreenotherthanwherethefloorshouldbe. Notethatwedrawthereflectedballbeforewedrawthefloor.I'llexplainwhylateron. glLightfv(GL_LIGHT0,GL_POSITION,LightPos); //SetUpLight0 glTranslatef(0.0f,height,0.0f); //PositionTheObject glRotatef(xrot,1.0f,0.0f,0.0f); //RotateLocalCoordinateSystemOnXAxis glRotatef(yrot,0.0f,1.0f,0.0f); //RotateLocalCoordinateSystemOnYAxis DrawObject(); //DrawTheSphere(Reflection) glPopMatrix(); //PopTheMatrixOffTheStack glDisable(GL_CLIP_PLANE0); //DisableClipPlaneForDrawingTheFloor glDisable(GL_STENCIL_TEST); //WeDon'tNeedTheStencilBufferAnyMore(Disable) Westartoffthissectionofcodebypositioningourlight.They-axisisnolongerbeingmirroredsodrawingthelightthistimearoundwillpositionitatthetopofthescreeninsteadofthebottomrightofthescreen. Weenableblending,disablelighting,andsetthealphavalueto80%usingthecommandglColor4f(1.0f,1.0f,1.0f,0.8f).TheblendingmodeissetupusingglBlendFunc(),andthesemitransparentfloorisdrawnovertopofthereflectedball. Ifwedrewthefloorfirstandthenthereflectedball,theeffectwouldn'tlookverygood.Bydrawingtheballandthenthefloor,youcanseeasmallamountofcoloringfromthefloormixedintothecoloringoftheball.IfIwaslookingintoaBLUEmirror,Iwouldexpectthereflectiontolookalittleblue.Byrenderingtheballfirst,thereflectedimagelookslikeit'stintedthecolorofthefloor. glLightfv(GL_LIGHT0,GL_POSITION,LightPos); //SetUpLight0Position glEnable(GL_BLEND); //EnableBlending(OtherwiseTheReflectedObjectWontShow) glDisable(GL_LIGHTING); //SinceWeUseBlending,WeDisableLighting glColor4f(1.0f,1.0f,1.0f,0.8f); //SetColorToWhiteWith80%Alpha glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); //BlendingBasedOnSourceAlphaAnd1MinusDestAlpha DrawFloor(); //DrawTheFloorToTheScreen Nowwedrawthe'real'ball(theonethatfloatsabovethefloor).Wedisabledlightingwhenwedrewthefloor,butnowit'stimetodrawanotherballsowewillturnlightingbackon. Wedon'tneedblendinganymoresowedisableblending.Ifwedidn'tdisableblending,thecolorsfromthefloorwouldmixwiththecolorsofour'real'ballwhenitwasfloatingovertopofthefloor.Wedon'twantthe'real'balltolooklikethereflectionsowedisableblending. Wearenotgoingtocliptheactualball.Iftherealballgoesthroughthefloor,weshouldseeitcomeoutthebottom.Ifwewereusingclippingtheballwouldn'tshowupafteritwentthroughthefloor.Ifyoudidn'twanttoseetheballcomethroughthefloor,youwouldsetupaclippingequationthatsettheYvalueto+1.0f,thenwhentheballwentthroughthefloor,youwouldn'tseeit(youwouldonlyseetheballwhenitwasdrawnonatapositivevalueonthey-axis.Forthisdemo,there'snoreasonweshouldn'tseeitcomethroughthefloor. Wethentranslateupordownonthey-axistothepositionspecifiedbyheight.Onlythistimethey-axisisnotmirrored,sotheballtravelstheoppositedirectionthatthereflectedimagetravels.Ifwemovethe'real'balldownthereflectedballwillmoveup.Ifwemovethe'real'ballup,thereflectedballwillmovedown. Werotatethe'real'ball,andagain,becausethey-axisisnotmirrored,theballwillspintheoppositedirectionofthereflectedball.Ifthereflectedballisrollingtowardsyouthe'real'ballwillberollingawayfromyou.Thiscreatestheillusionofarealreflection. Afterpositioningandrotatingtheball,wedrawthe'real'ballbycallingDrawObject(). glEnable(GL_LIGHTING); //EnableLighting glDisable(GL_BLEND); //DisableBlending glTranslatef(0.0f,height,0.0f); //PositionTheBallAtProperHeight glRotatef(xrot,1.0f,0.0f,0.0f); //RotateOnTheXAxis glRotatef(yrot,0.0f,1.0f,0.0f); //RotateOnTheYAxis DrawObject(); //DrawTheBall Thefollowingcoderotatestheballonthexandyaxis.Byincreasingxrotbyxrotspeedwerotatetheballonthex-axis.Byincreasingyrotbyyrotspeedwespintheballonthey-axis.Ifxrotspeedisaveryhighvalueinthepositiveornegativedirectiontheballwillspinquickerthanifxrotspeedwasalowvalue,closerto0.0f.Samegoesforyrotspeed.Thehigherthevalue,thefastertheballspinsonthey-axis. BeforewereturnTRUE,wedoaglFlush().ThistellsOpenGLtorendereverythingleftintheGLpipelinebeforecontinuing,andcanhelppreventflickeringonslowervideocards. xrot+=xrotspeed; //UpdateXRotationAngleByxrotspeed yrot+=yrotspeed; //UpdateYRotationAngleByyrotspeed glFlush(); //FlushTheGLPipeline returnTRUE; //EverythingWentOK } Thefollowingcodewillwatchforkeypresses.Thefirst4lineschecktoseeifyouarepressingoneofthe4arrowkeys.Ifyouare,theballisspunright,left,downorup. Thenext2lineschecktoseeifyouarepressingthe'A'or'Z'keys.Pressing'A'willzoomyouinclosertotheballandpressing'Z'willzoomyouawayfromtheball. Pressing'PAGEUP'willincreasethevalueofheightmovingtheballup,andpressing'PAGEDOWN'willdecreasethevalueofheightmovingtheballdown(closertothefloor). voidProcessKeyboard() //ProcessKeyboardResults { if(keys[VK_RIGHT]) yrotspeed+=0.08f; //RightArrowPressed(Increaseyrotspeed) if(keys[VK_LEFT]) yrotspeed-=0.08f; //LeftArrowPressed(Decreaseyrotspeed) if(keys[VK_DOWN]) xrotspeed+=0.08f; //DownArrowPressed(Increasexrotspeed) if(keys[VK_UP]) xrotspeed-=0.08f; //UpArrowPressed(Decreasexrotspeed) if(keys['A']) zoom+=0.05f; //'A'KeyPressed...ZoomIn if(keys['Z']) zoom-=0.05f; //'Z'KeyPressed...ZoomOut if(keys[VK_PRIOR]) height+=0.03f; //PageUpKeyPressedMoveBallUp if(keys[VK_NEXT]) height-=0.03f; //PageDownKeyPressedMoveBallDown } TheKillGLWindow()codehasn'tchanged,soI'llskipoverit. GLvoidKillGLWindow(GLvoid) //ProperlyKillTheWindow Youcanskimthroughthefollowingcode.EventhoughonlyonelineofcodehaschangedinCreateGLWindow(),Ihaveincludedallofthecodesoit'seasiertofollowthroughthetutorial. BOOLCreateGLWindow(char*title,intwidth,intheight,intbits,boolfullscreenflag) { GLuint PixelFormat; //HoldsTheResultsAfterSearchingForAMatch WNDCLASS wc; //WindowsClassStructure DWORD dwExStyle; //WindowExtendedStyle DWORD dwStyle; //WindowStyle fullscreen=fullscreenflag; //SetTheGlobalFullscreenFlag hInstance =GetModuleHandle(NULL); //GrabAnInstanceForOurWindow wc.style =CS_HREDRAW|CS_VREDRAW|CS_OWNDC; //RedrawOnSize,AndOwnDCForWindow wc.lpfnWndProc =(WNDPROC)WndProc; //WndProcHandlesMessages wc.cbClsExtra =0; //NoExtraWindowData wc.cbWndExtra =0; //NoExtraWindowData wc.hInstance =hInstance; //SetTheInstance wc.hIcon =LoadIcon(NULL,IDI_WINLOGO); //LoadTheDefaultIcon wc.hCursor =LoadCursor(NULL,IDC_ARROW); //LoadTheArrowPointer wc.hbrBackground =NULL; //NoBackgroundRequiredForGL wc.lpszMenuName =NULL; //WeDon'tWantAMenu wc.lpszClassName ="OpenGL"; //SetTheClassName if(!RegisterClass(&wc)) //AttemptToRegisterTheWindowClass { MessageBox(NULL,"FailedToRegisterTheWindowClass.","ERROR",MB_OK|MB_ICONEXCLAMATION); returnFALSE; //ReturnFALSE } if(fullscreen) //AttemptFullscreenMode? { DEVMODEdmScreenSettings; //DeviceMode memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); //MakesSureMemory'sCleared dmScreenSettings.dmSize=sizeof(dmScreenSettings); //SizeOfTheDevmodeStructure dmScreenSettings.dmPelsWidth =width; //SelectedScreenWidth dmScreenSettings.dmPelsHeight =height; //SelectedScreenHeight dmScreenSettings.dmBitsPerPel =bits; //SelectedBitsPerPixel dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT; //TryToSetSelectedModeAndGetResults.NOTE:CDS_FULLSCREENGetsRidOfStartBar if(ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL) { //IfTheModeFails,OfferTwoOptions.QuitOrUseWindowedMode if(MessageBox(NULL,"TheRequestedFullscreenModeIsNotSupportedBy\nYourVideoCard.UseWindowedModeInstead?","NeHeGL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES) { fullscreen=FALSE; //WindowedModeSelected.Fullscreen=FALSE } else { //PopUpAMessageBoxLettingUserKnowTheProgramIsClosing MessageBox(NULL,"ProgramWillNowClose.","ERROR",MB_OK|MB_ICONSTOP); returnFALSE; //ReturnFALSE } } } if(fullscreen) //AreWeStillInFullscreenMode? { dwExStyle=WS_EX_APPWINDOW; //WindowExtendedStyle dwStyle=WS_POPUP|WS_CLIPSIBLINGS|WS_CLIPCHILDREN; //WindowsStyle ShowCursor(FALSE); //HideMousePointer } else { dwExStyle=WS_EX_APPWINDOW|WS_EX_WINDOWEDGE; //WindowExtendedStyle dwStyle=WS_OVERLAPPEDWINDOW|WS_CLIPSIBLINGS|WS_CLIPCHILDREN;//WindowsStyle } //CreateTheWindow if(!(hWnd=CreateWindowEx( dwExStyle, //ExtendedStyleForTheWindow "OpenGL", //ClassName title, //WindowTitle dwStyle, //WindowStyle 0,0, //WindowPosition width,height, //SelectedWidthAndHeight NULL, //NoParentWindow NULL, //NoMenu hInstance, //Instance NULL))) //DontPassAnythingToWM_CREATE { KillGLWindow(); //ResetTheDisplay MessageBox(NULL,"WindowCreationError.","ERROR",MB_OK|MB_ICONEXCLAMATION); returnFALSE; //ReturnFALSE } static PIXELFORMATDESCRIPTORpfd= //pfdTellsWindowsHowWeWantThingsToBe { sizeof(PIXELFORMATDESCRIPTOR), //SizeOfThisPixelFormatDescriptor 1, //VersionNumber PFD_DRAW_TO_WINDOW| //FormatMustSupportWindow PFD_SUPPORT_OPENGL| //FormatMustSupportOpenGL PFD_DOUBLEBUFFER, //MustSupportDoubleBuffering PFD_TYPE_RGBA, //RequestAnRGBAFormat bits, //SelectOurColorDepth 0,0,0,0,0,0, //ColorBitsIgnored 0, //NoAlphaBuffer 0, //ShiftBitIgnored 0, //NoAccumulationBuffer 0,0,0,0, //AccumulationBitsIgnored 16, //16BitZ-Buffer(DepthBuffer) Theonlychangeinthissectionofcodeisthelinebelow.Itis*VERYIMPORTANT*youchangethevaluefrom0to1orsomeothernonzerovalue.Inalloftheprevioustutorialsthevalueofthelinebelowwas0.InordertouseStencilBufferingthisvalueHAStobegreaterthanorequalto1.Thisvalueisthenumberofbitsyouwanttouseforthestencilbuffer. 1, //UseStencilBuffer(*Important*) 0, //NoAuxiliaryBuffer PFD_MAIN_PLANE, //MainDrawingLayer 0, //Reserved 0,0,0 //LayerMasksIgnored }; if(!(hDC=GetDC(hWnd))) //DidWeGetADeviceContext? { KillGLWindow(); //ResetTheDisplay MessageBox(NULL,"Can'tCreateAGLDeviceContext.","ERROR",MB_OK|MB_ICONEXCLAMATION); returnFALSE; //ReturnFALSE } if(!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) //DidWindowsFindAMatchingPixelFormat? { KillGLWindow(); //ResetTheDisplay MessageBox(NULL,"Can'tFindASuitablePixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION); returnFALSE; //ReturnFALSE } if(!SetPixelFormat(hDC,PixelFormat,&pfd)) //AreWeAbleToSetThePixelFormat? { KillGLWindow(); //ResetTheDisplay MessageBox(NULL,"Can'tSetThePixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION); returnFALSE; //ReturnFALSE } if(!(hRC=wglCreateContext(hDC))) //AreWeAbleToGetARenderingContext? { KillGLWindow(); //ResetTheDisplay MessageBox(NULL,"Can'tCreateAGLRenderingContext.","ERROR",MB_OK|MB_ICONEXCLAMATION); returnFALSE; //ReturnFALSE } if(!wglMakeCurrent(hDC,hRC)) //TryToActivateTheRenderingContext { KillGLWindow(); //ResetTheDisplay MessageBox(NULL,"Can'tActivateTheGLRenderingContext.","ERROR",MB_OK|MB_ICONEXCLAMATION); returnFALSE; //ReturnFALSE } ShowWindow(hWnd,SW_SHOW); //ShowTheWindow SetForegroundWindow(hWnd); //SlightlyHigherPriority SetFocus(hWnd); //SetsKeyboardFocusToTheWindow ReSizeGLScene(width,height); //SetUpOurPerspectiveGLScreen if(!InitGL()) //InitializeOurNewlyCreatedGLWindow { KillGLWindow(); //ResetTheDisplay MessageBox(NULL,"InitializationFailed.","ERROR",MB_OK|MB_ICONEXCLAMATION); returnFALSE; //ReturnFALSE } returnTRUE; //Success } WndProc()hasnotchanged,sowewillskipoverit. LRESULTCALLBACKWndProc( HWND hWnd, //HandleForThisWindow UINT uMsg, //MessageForThisWindow WPARAM wParam, //AdditionalMessageInformation LPARAM lParam) //AdditionalMessageInformation Nothingnewhere.TypicalstarttoWinMain(). intWINAPIWinMain( HINSTANCE hInstance, //Instance HINSTANCE hPrevInstance, //PreviousInstance LPSTR lpCmdLine, //CommandLineParameters int nCmdShow) //WindowShowState { MSG msg; //WindowsMessageStructure BOOL done=FALSE; //BoolVariableToExitLoop //AskTheUserWhichScreenModeTheyPrefer if(MessageBox(NULL,"WouldYouLikeToRunInFullscreenMode?","StartFullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO) { fullscreen=FALSE; //WindowedMode } Theonlyrealbigchangeinthissectionofthecodeisthenewwindowtitletoleteveryoneknowthetutorialisaboutreflectionsusingthestencilbuffer.Alsonoticethatwepasstheresx,resyandresbppvariablestoourwindowcreationprocedureinsteadoftheusual640,480and16. //CreateOurOpenGLWindow if(!CreateGLWindow("BanuOctavian&NeHe'sStencil&ReflectionTutorial",resx,resy,resbpp,fullscreen)) { return0; //QuitIfWindowWasNotCreated } while(!done) //LoopThatRunsWhiledone=FALSE { if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) //IsThereAMessageWaiting? { if(msg.message==WM_QUIT) //HaveWeReceivedAQuitMessage? { done=TRUE; //IfSodone=TRUE } else //IfNot,DealWithWindowMessages { TranslateMessage(&msg); //TranslateTheMessage DispatchMessage(&msg); //DispatchTheMessage } } else //IfThereAreNoMessages { //DrawTheScene.WatchForESCKeyAndQuitMessagesFromDrawGLScene() if(active) //ProgramActive? { if(keys[VK_ESCAPE]) //WasEscapePressed? { done=TRUE; //ESCSignalledAQuit } else //NotTimeToQuit,UpdateScreen { DrawGLScene(); //DrawTheScene SwapBuffers(hDC); //SwapBuffers(DoubleBuffering) InsteadofcheckingforkeypressesinWinMain(),wejumptoourkeyboardhandlingroutinecalledProcessKeyboard().NoticetheProcessKeyboard()routineisonlycallediftheprogramisactive! ProcessKeyboard(); //ProcessedKeyboardPresses } } } } //Shutdown KillGLWindow(); //KillTheWindow return(msg.wParam); //ExitTheProgram } Ireallyhopeyou'veenjoyedthistutorial.Iknowitcouldusealittlemorework.ItwasoneofthemoredifficulttutorialsthatIhavewritten.It'seasyformetounderstandwhateverythingisdoing,andwhatcommandsIneedtousetocreatecooleffects,butwhenyousitdownandactuallytrytoexplainthingskeepinginmindthatmostpeoplehaveneverevenheardofthestencilbuffer,it'stough!Ifyounoticeanythingthatcouldbemadeclearerorifyoufindanymistakesinthetutorialpleaseletmeknow.Asalways,Iwantthistutorialtobethebestitcanpossiblybe,yourfeedbackisgreatlyappreciated. BanuOctavian(Choko) JeffMolofee(NeHe) *DOWNLOADVisualC++CodeForThisLesson. *DOWNLOADBorlandC++Builder6CodeForThisLesson.(ConversionbyChristianKindahl)*DOWNLOADCodeWarrior5.3CodeForThisLesson.(ConversionbyScottLupton)*DOWNLOADDelphiCodeForThisLesson.(ConversionbyMichalTucek)*DOWNLOADDevC++CodeForThisLesson.(ConversionbyDan)*DOWNLOADEuphoriaCodeForThisLesson.(ConversionbyEvanMarshall)*DOWNLOADLCCWin32CodeForThisLesson.(ConversionbyRobertWishlaw)*DOWNLOADLinuxCodeForThisLesson.(ConversionbyGrayFox)*DOWNLOADLWJGLCodeForThisLesson.(ConversionbyMarkBernard)*DOWNLOADMacOSX/CocoaCodeForThisLesson.(ConversionbyBryanBlackburn)*DOWNLOADVisualStudio.NETCodeForThisLesson.(ConversionbyGrantJames)   ©1997-2014Gamedev.Allrightsreserved. NeHe™andNeHeProductions™aretrademarksofGameDev.net,LLC OpenGL®isaregisteredtrademarkofSiliconGraphicsInc.



請為這篇文章評分?