WebGL2 Fog
文章推薦指數: 80 %
In between 0 and 1 you get a percentage of both colors. You could implement mix yourself like ... On a low powered GPU you might just use gl_FragCoord.z . English Deutsch 日本語 한국어 PortuguêsBrasileiro 简体中文 TableofContents WebGL2Fundamentals.org Fix,Fork,Contribute WebGL2Fog ThisarticleispartofaseriesofarticlesaboutWebGL. Thefirstarticlestartswiththefundamentals. FoginWebGLisinterestingtomebecauseofhowfakeitseemswhenIthinkabouthowitworks.Basicallywhatyoudoisusesomekindofdepthordistancefromthecameracalculationinyourshaderstomakethecolormoreorlessthefogcolor. Inotherwordsyoustartwithabasicequationlikethis outColor=mix(originalColor,fogColor,fogAmount); WherefogAmountisavaluefrom0to1.Themixfunctionmixesthefirst2values.WhenfogAmountis0mixreturnsoriginalColor.ThenfogAmountis1mixreturnsfogColor.Inbetween0and1yougetapercentageofbothcolors.Youcouldimplementmixyourselflikethis outColor=originalColor+(fogColor-originalColor)*fogAmount; Let'smakeashaderthatdoesthis.We'lluseatexturedcubefromthearticleontextures. Let'saddthemixingtothefragmentshader #version300es precisionhighpfloat; //Passedinfromthevertexshader. invec2v_texcoord; //Thetexture. uniformsampler2Du_texture; +uniformvec4u_fogColor; +uniformfloatu_fogAmount; outvec4outColor; voidmain(){ +vec4color=texture(u_texture,v_texcoord); +outColor=mix(color,u_fogColor,u_fogAmount); } Thenatinittimeweneedtolookupthenewuniformlocations varfogColorLocation=gl.getUniformLocation(program,"u_fogColor"); varfogAmountLocation=gl.getUniformLocation(program,"u_fogAmount"); andatrendertimesetthem varfogColor=[0.8,0.9,1,1]; varsettings={ fogAmount:.5, }; ... functiondrawScene(time){ ... //ClearthecanvasANDthedepthbuffer. //Cleartothefogcolor gl.clearColor(...fogColor); gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT); ... //setthefogcolorandamount gl.uniform4fv(fogColorLocation,fogColor); gl.uniform1f(fogAmountLocation,settings.fogAmount); ... } Andhereyou'llseeifyoudragtheslideryoucanchangebetweenthetextureandthefogcolor clickheretoopeninaseparatewindow Sonowallwereallyneedtodoisinsteadofpassinginthefogamountwecomputeitbasedonthesomethinglikethedepthfromthecamera. Recallfromthearticleoncamerasthatafterweapplytheviewmatrixallpositionsarerelativetothecamera.Thecameralooksdownthe-zaxissoifwejustlookatthezpositionaftermultiplyingbytheworldandviewmatriceswe'llhaveavaluethatrepresentshowfarawaysomethingisfromthezplaneofthecamera. Let'schangethevertexshadertopassthatdatatothefragmentshadersowecanuseitcomputeafogamount.Todothatlet'ssplitu_matrixinto2parts.AprojectionmatrixandaworldViewmatrix. #version300es invec4a_position; invec2a_texcoord; -uniformmat4u_matrix; +uniformmat4u_worldView; +uniformmat4u_projection; outvec2v_texcoord; +outfloatv_fogDepth; voidmain(){ //Multiplythepositionbythematrix. -gl_Position=u_matrix*a_position; +gl_Position=u_projection*u_worldView*a_position; //Passthetexcoordtothefragmentshader. v_texcoord=a_texcoord; +//Passjustthenegatedzpositionrelativetothecamera. +//thecameraislookinginthe-zdirectionsonormallystuff +//infrontofthecamerahasanegativeZposition +//butbynegatinghewegetapositivedepth. +v_fogDepth=-(u_worldView*a_position).z; } Nowinthefragmentshaderwewantittoworkthatifthedepthislessthansomevalue,don'tmixanyfog(fogAmount=0).Ifthedepthisgreaterthansomevaluethen100%fog(fogAmount=1).Betweenthose2valuesmixthecolors. WecouldwritecodetodothatbutGLSLhasafunction,smoothstepthatdoesjustthat.Yougiveittheminvalue,themaxvalue,andthevaluetotest.Ifthetestvalueislessthanorequaltotheminvalueitreturns0.Ifthetestvalueisgreaterthanorequaltothemaxvalueitreturns1.Iftestisbetweenthose2valuesitreturnssomethingbetween0and1inproportiontowherethetestvalueisbetweenminandmax. So,itshouldbeprettyeasytousethatinourfragmentshadertocomputeafogamount #version300es precisionhighpfloat; //Passedinfromthevertexshader. invec2v_texcoord; infloatv_fogDepth; //Thetexture. uniformsampler2Du_texture; uniformvec4u_fogColor; -uniformfloatu_fogAmount; +uniformfloatu_fogNear; +uniformfloatu_fogFar; outvec4outColor; voidmain(){ vec4color=texture(u_texture,v_texcoord); +floatfogAmount=smoothstep(u_fogNear,u_fogFar,v_fogDepth); -outColor=mix(color,u_fogColor,u_fogAmount); +outColor=mix(color,u_fogColor,fogAmount); } andofcourseweneedtolookupalltheseuniformsatinittime //lookupuniforms +varprojectionLocation=gl.getUniformLocation(program,"u_projection"); +varworldViewLocation=gl.getUniformLocation(program,"u_worldView"); vartextureLocation=gl.getUniformLocation(program,"u_texture"); varfogColorLocation=gl.getUniformLocation(program,"u_fogColor"); +varfogNearLocation=gl.getUniformLocation(program,"u_fogNear"); +varfogFarLocation=gl.getUniformLocation(program,"u_fogFar"); andsetthematrendertime varfogColor=[0.8,0.9,1,1]; varsettings={ -fogAmount:.5, +fogNear:1.1, +fogFar:2.0, }; //Drawthescene. functiondrawScene(time){ ... //Computetheprojectionmatrix varaspect=gl.canvas.clientWidth/gl.canvas.clientHeight; varprojectionMatrix= m4.perspective(fieldOfViewRadians,aspect,1,2000); varcameraPosition=[0,0,2]; varup=[0,1,0]; vartarget=[0,0,0]; //Computethecamera'smatrixusinglookat. varcameraMatrix=m4.lookAt(cameraPosition,target,up); //Makeaviewmatrixfromthecameramatrix. varviewMatrix=m4.inverse(cameraMatrix); -varviewProjectionMatrix=m4.multiply(projectionMatrix,viewMatrix); - -varmatrix=m4.xRotate(viewProjectionMatrix,modelXRotationRadians); -matrix=m4.yRotate(matrix,modelYRotationRadians); +varworldViewMatrix=m4.xRotate(viewMatrix,modelXRotationRadians); +worldViewMatrix=m4.yRotate(worldViewMatrix,modelYRotationRadians); //Setthematrices. -gl.uniformMatrix4fv(matrixLocation,false,matrix); +gl.uniformMatrix4fv(projectionLocation,false,projectionMatrix); +gl.uniformMatrix4fv(worldViewLocation,false,worldViewMatrix); //Telltheshadertousetextureunit0foru_texture gl.uniform1i(textureLocation,0); //setthefogcolorandnear,farsettings gl.uniform4fv(fogColorLocation,fogColor); +gl.uniform1f(fogNearLocation,settings.fogNear); +gl.uniform1f(fogFarLocation,settings.fogFar); -gl.uniform1f(fogAmountLocation,settings.fogAmount); Whilewe'reatitletsdraw40cubesintothedistancetomakeiteasiertoseethefog. varsettings={ fogNear:1.1, fogFar:2.0, +xOff:1.1, +zOff:1.4, }; ... constnumCubes=40; for(leti=0;i<=numCubes;++i){ varworldViewMatrix=m4.translate(viewMatrix,-2+i*settings.xOff,0,-i*settings.zOff); worldViewMatrix=m4.xRotate(worldViewMatrix,modelXRotationRadians+i*0.1); worldViewMatrix=m4.yRotate(worldViewMatrix,modelYRotationRadians+i*0.1); gl.uniformMatrix4fv(worldViewLocation,false,worldViewMatrix); //Drawthegeometry. gl.drawArrays(gl.TRIANGLES,0,6*6); } Andnowwegetdepthbasedfog clickheretoopeninaseparatewindow Note:Wedidn'taddanycodetomakesurefogNearislessthenorequaltofogFarwhicharearguablyinvalidsettingssobesuretosetbothappropriately. AsImentionedaboveit'sfeelslikeatricktome.Itworksbecausethefogcolorwe'refadingtomatchesthebackgroundcolor.Changethebackgroundcolorandtheillusiondisappears. -gl.clearColor(...fogColor); +gl.clearColor(1,0,0,1);//red getsus sojustremembertoyouneedtosetthebackgroundcolortomatchthefogcolor. Usingthedepthworksandit'scheapbutthere'saproblem.Let'ssayyouhaveacircleofobjectsaroundthecamera.We'recomputingafogamountbasedonthedistancefromthecamera'szplane.ThatmeansasyouturnthecameraobjectswillappeartocomeintoandoutofthefogslightlyastheirviewspaceZvaluegetscloserto0 Youcanseetheprobleminthisexample clickheretoopeninaseparatewindow Abovethereisaringof8cubesdirectlyaroundthecamera.Thecamerainspinninginplace.ThatmeansthecubesarealwaysthesamedistancefromthecamerabutadifferentdistancefromtheZplaneandsoourfogamountcalculationresultsinthecubesneartheedgecomingoutofthefog. Thefixistoinsteadcomputethedistancefromthecamerawhichwillbethesameforallcubes Todothiswejustneedtopassthevertexpositioninviewspacefromthevertexshadertothefragmentshader #version300es invec4a_position; invec2a_texcoord; uniformmat4u_worldView; uniformmat4u_projection; outvec2v_texcoord; -outfloatv_fogDepth; +outvec3v_position; voidmain(){ //Multiplythepositionbythematrix. gl_Position=u_projection*u_worldView*a_position; //Passthetexcoordtothefragmentshader. v_texcoord=a_texcoord; -//Passjustthenegatedzpositionrelativetothecamera. -//thecameraislookinginthe-zdirectionsonormallystuff -//infrontofthecamerahasanegativeZposition -//butbynegatinghewegetapositivedepth. -v_fogDepth=-(u_worldView*a_position).z; +//Passtheviewpositiontothefragmentshader +v_position=(u_worldView*a_position).xyz; } andtheninthefragmentshaderwecanusethepositiontocomputethedistance #version300es precisionhighpfloat; //Passedinfromthevertexshader. invec2v_texcoord; -infloatv_fogDepth; +invec3v_position; //Thetexture. uniformsampler2Du_texture; uniformvec4u_fogColor; uniformfloatu_fogNear; uniformfloatu_fogFar; outvec4outColor; voidmain(){ vec4color=texture(u_texture,v_texcoord); -floatfogAmount=smoothstep(u_fogNear,u_fogFar,v_fogDepth); +floatfogDistance=length(v_position); +floatfogAmount=smoothstep(u_fogNear,u_fogFar,fogDistance); outColor=mix(color,u_fogColor,fogAmount); } Andnowthecubesnolongercomeoutofthefogasthecameraturns clickheretoopeninaseparatewindow Sofarallofourfoghasusedalinearcalculation.Inotherwordsthefogcolorgetsappliedlinearlybetweennearandfar.Likemanythingsintherealworldfogapparentlyworksexponentially.Itgetsthickerwiththesquareofthedistancefromtheviewer.Acommonequationforexponentialfogis #defineLOG21.442695 fogAmount=1.-exp2(-fogDensity*fogDensity*fogDistance*fogDistance*LOG2)); fogAmount=clamp(fogAmount,0.,1.); Tousethiswe'dchangethefragmentshadertosomethinglike #version300es precisionhighpfloat; //Passedinfromthevertexshader. invec2v_texcoord; invec3v_position; //Thetexture. uniformsampler2Du_texture; uniformvec4u_fogColor; -uniformfloatu_fogNear; -uniformfloatu_fogFar; +uniformfloatu_fogDensity; outvec4outColor; voidmain(){ vec4color=texture(u_texture,v_texcoord); #defineLOG21.442695 floatfogDistance=length(v_position); -floatfogAmount=smoothstep(u_fogNear,u_fogFar,fogDistance); +floatfogAmount=1.-exp2(-u_fogDensity*u_fogDensity*fogDistance*fogDistance*LOG2); fogAmount=clamp(fogAmount,0.,1.); outColor=mix(color,u_fogColor,fogAmount); } Andwegetdistanceexp2densitybasedfog clickheretoopeninaseparatewindow Onethingtonoticeaboutdensitybasedfogistherearenonearandfarsettings.Itmightbemorerealisticbutitalsomightnotfityouraestheticneeds.Whichoneyoupreferisanartisticdecision. Therearemanyotherwaystocomputefog.OnalowpoweredGPUyoumightjustusegl_FragCoord.z.gl_FragCoordisaglobalvariablethatWebGLsets.Thexandycomponentsarethecoordinateofthepixelbeingdrawn.Thezcoordinateisthedepthofthatpixelfrom0to1.Whilenotdirectlytranslatableintodistanceyoucanstillgetsomethingthatlookslikefogbypickingsomevaluesbetween0and1fornearandfar.NothinghastobepassedfromthevertexshadertothefragmentshaderandnodistancecalculationsareneededsothisisonewaytomakeacheapfogeffectonalowpoweredGPU. clickheretoopeninaseparatewindow English Deutsch 日本語 한국어 PortuguêsBrasileiro 简体中文 Fundamentals HowtouseWebGL2 Fundamentals HowItWorks ShadersandGLSL WebGL2StateDiagram WebGL2vsWebGL1 What'snewinWebGL2 MovingfromWebGL1toWebGL2 DifferencesfromWebGLFundamentals.orgtoWebGL2Fundamentals.org ImageProcessing ImageProcessing ImageProcessingContinued 2Dtranslation,rotation,scale,matrixmath 2DTranslation 2DRotation 2DScale 2DMatrices 3D Orthographic3D 3DPerspective 3D-Cameras 3D-MatrixNaming Lighting DirectionalLighting PointLighting SpotLighting StructureandOrganization LessCode,MoreFun DrawingMultipleThings SceneGraphs Geometry 3DGeometry-Lathe Loading.objfiles Loading.objw.mtlfiles Textures Textures DataTextures Using2orMoreTextures CrossOriginImages PerspectiveCorrectTextureMapping PlanarandPerspectiveProjectionMapping RenderingToATexture RendertoTexture Shadows Shadows Techniques 2D 2D-DrawImage 2D-MatrixStack Sprites 3D Cubemaps Environmentmaps Skyboxes Skinning Fog Picking(clickingonstuff) Text Text-HTML Text-Canvas2D Text-UsingaTexture Text-UsingaGlyphTexture GPGPU GPGPU Tips SmallestPrograms DrawingWithoutData Shadertoy PullingVertices Optimization IndexedVertices(gl.drawElements) InstancedDrawing Misc SetupAndInstallation Boilerplate ResizingtheCanvas Animation Points,Lines,andTriangles MultipleViews,MultipleCanvases VisualizingtheCamera WebGL2andAlpha 2Dvs3Dlibraries Anti-Patterns WebGL2MatricesvsMathMatrices PrecisionIssues Takingascreenshot PreventtheCanvasBeingCleared GetKeyboardInputFromaCanvas UseWebGL2asBackgroundinHTML CrossPlatformIssues QuestionsandAnswers Reference Attributes TextureUnits Framebuffers readPixels References HelperAPIDocs TWGL,AtinyWebGLhelperlibrary github Issue/Bug?Createanissueongithub. Use
codegoeshere
forcodeblocks
commentspoweredbyDisqus
延伸文章資訊
- 1(gl_FragCoord.z / gl_FragCoord.w) for quick depth calculation ...
You can refer to chapter 2.13 “Coordinate Transformations” of OpenGL specs, and to the fact, that...
- 2View Space From gl_FragCoord - Game Development Stack ...
Space & Sound? Defying Physics? 1 · Why are normal maps in tangent space but not in normal space?...
- 3WebGL2 Fog
In between 0 and 1 you get a percentage of both colors. You could implement mix yourself like ......
- 4Deceit in Depth
If you do not write one, then OpenGL will happily use gl_FragCoord.z as the ... is always written...
- 5Getting the real fragment depth in GLSL - Game Development ...
Alternatively, you can get it recover it from gl_FragCoord... In a fragment shader, gl_FragCoord ...