WebGL2 Drawing Without Data

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

In GLSL ES 3.0 there is a special variable, gl_VertexID available in vertex shaders. Effectively it counts vertices. Let's use it to draw calculate vertex ... English Deutsch 日本語 한국어 PortuguêsBrasileiro 简体中文 TableofContents WebGL2Fundamentals.org Fix,Fork,Contribute WebGL2DrawingWithoutData Thisarticleassumesyou'vereadmanyoftheotherarticles startingwiththefundamentals. Ifyouhavenotreadthempleasestarttherefirst. InthearticleonthesmallestWebGLprograms wecoveredsomeexamplesofdrawingwithverylittlecode. Inthisarticlewillgooverdrawingwithnodata. Traditionally,WebGLappsputgeometrydatainbuffers. Theythenuseattributestopullvertexdatafromthosebuffers intoshadersandconvertthemtoclipspace. Thewordtraditionallyisimportant.It'sonlyatradition todoitthisway.Itisinnowayarequirement.WebGLdoesn't carehowwedoit,itonlycaresthatourvertexshaders assignclipspacecoordinatestogl_Position. InGLSLES3.0thereisaspecialvariable,gl_VertexID availableinvertexshaders.Effectivelyitcountsvertices. Let'suseittodrawcalculatevertexpositionswithnodata. Wellcomputethepointsofacirclebasedonthatvariable. #version300es uniformintnumVerts; #definePIradians(180.0) voidmain(){ floatu=float(gl_VertexID)/float(numVerts);//goesfrom0to1 floatangle=u*PI*2.0;//goesfrom0to2PI floatradius=0.8; vec2pos=vec2(cos(angle),sin(angle))*radius; gl_Position=vec4(pos,0,1); gl_PointSize=5.0; } Thecodeaboveshouldbeprettystraightforward. gl_VertexIDisgoingtocountfrom0tohowever manyverticesweasktodraw.We'llpassthatsamenumber inasnumVerts. Basedonthatwegeneratepositionsforacircle. Ifwestoppedtherethecirclewouldbeanellipse becauseclipspaceisnormalized(goesfrom-1to1) acrossanddownthecanvas.Ifwepassintheresolution wecantakeintoaccountthat-1to1acrossmightnot representthesamespaceas-1to1downthecanvas. #version300es uniformintnumVerts; +uniformvec2resolution; #definePIradians(180.0) voidmain(){ floatu=float(gl_VertexID)/float(numVerts);//goesfrom0to1 floatangle=u*PI*2.0;//goesfrom0to2PI floatradius=0.8; vec2pos=vec2(cos(angle),sin(angle))*radius; +floataspect=resolution.y/resolution.x; +vec2scale=vec2(aspect,1); +gl_Position=vec4(pos*scale,0,1); gl_PointSize=5.0; } Andourfragmentshadercanjustdrawasolidcolor #version300es precisionhighpfloat; outvec4outColor; voidmain(){ outColor=vec4(1,0,0,1); } InourJavaScriptatinittimewe'llcompiletheshaderandlookuptheuniforms, //setupGLSLprogram constprogram=webglUtils.createProgramFromSources(gl,[vs,fs]); constnumVertsLoc=gl.getUniformLocation(program,'numVerts'); constresolutionLoc=gl.getUniformLocation(program,'resolution'); Andtorenderwe'llusetheprogram, settheresolutionandnumVertsuniforms,anddrawthepoints. gl.useProgram(program); constnumVerts=20; //telltheshaderthenumberofverts gl.uniform1i(numVertsLoc,numVerts); //telltheshadertheresolution gl.uniform2f(resolutionLoc,gl.canvas.width,gl.canvas.height); constoffset=0; gl.drawArrays(gl.POINTS,offset,numVerts); Andwegetacircleofpoints. clickheretoopeninaseparatewindow Isthistechniqueuseful?Wellwithsomecreativecode wecouldmakeastarfieldorasimpleraineffectwith almostnodataandasingledrawcall. Let'sdotherainjusttoseeitwork.Firstwe'll changethevertexshaderto #version300es uniformintnumVerts; uniformfloattime; voidmain(){ floatu=float(gl_VertexID)/float(numVerts);//goesfrom0to1 floatx=u*2.0-1.0;//-1to1 floaty=fract(time+u)*-2.0+1.0;//1.0->-1.0 gl_Position=vec4(x,y,0,1); gl_PointSize=5.0; } Forthissituationwedon'tneedtheresolution. We'veaddedatimeuniformwhichwillbethetime insecondssincethepageloaded. For'x'we'rejustgoingtogofrom-1to1 For'y'weusetime+ubutfractreturns onlythefractionalportionsoavaluefrom0.0to1.0. Byexpandingthatto1.0to-1.0wegetaythatrepeats overtimebutonethatisoffsetdifferentlyforeach point. Let'schangethecolortoblueinthefragmentshader. precisionhighpfloat; outvec4outColor; voidmain(){ -outColor=vec4(1,0,0,1); +outColor=vec4(0,0,1,1); } TheninJavaScriptweneedtolookupthetimeuniform //setupGLSLprogram constprogram=webglUtils.createProgramFromSources(gl,[vs,fs]); constnumVertsLoc=gl.getUniformLocation(program,'numVerts'); -constresolutionLoc=gl.getUniformLocation(program,'resolution'); +consttimeLoc=gl.getUniformLocation(program,'time'); Andweneedtoconverttocodetoanimate bycreatingarenderloopandsettingthetimeuniform. +functionrender(time){ +time*=0.001;//converttoseconds +webglUtils.resizeCanvasToDisplaySize(gl.canvas); +gl.viewport(0,0,gl.canvas.width,gl.canvas.height); gl.useProgram(program); constnumVerts=20; //telltheshaderthenumberofverts gl.uniform1i(numVertsLoc,numVerts); +//telltheshaderthetime +gl.uniform1f(timeLoc,time); constoffset=0; gl.drawArrays(gl.POINTS,offset,numVerts); +requestAnimationFrame(render); +} +requestAnimationFrame(render); clickheretoopeninaseparatewindow ThisgivesusPOINTSgoingdownthescreenbuttheyareall inorder.Weneedtoaddsomerandomness.Thereisno randomnumbergeneratorinGLSL.Insteadwecanusea functionthatgeneratessomethingthatappearsrandom enough. Here'sone //hashfunctionfromhttps://www.shadertoy.com/view/4djSRW //givenavaluebetween0and1 //returnsavaluebetween0and1that*appears*kindofrandom floathash(floatp){ vec2p2=fract(vec2(p*5.3983,p*5.4427)); p2+=dot(p2.yx,p2.xy+vec2(21.5351,14.3137)); returnfract(p2.x*p2.y*95.4337); } andwecanusethatlikethis voidmain(){ floatu=float(gl_VertexID)/float(numVerts);//goesfrom0to1 -floatx=u*2.0-1.0;//-1to1 +floatx=hash(u)*2.0-1.0;//randomposition floaty=fract(time+u)*-2.0+1.0;//1.0->-1.0 gl_Position=vec4(x,y,0,1); gl_PointSize=5.0; } Wepasshashourprevious0to1valueanditgivesus backapseudorandom0to1value. Let'salsomakethepointssmaller gl_Position=vec4(x,y,0,1); -gl_PointSize=5.0; +gl_PointSize=2.0; Andbumpupthenumberofpointswe'redrawing -constnumVerts=20; +constnumVerts=400; Andwiththatweget clickheretoopeninaseparatewindow Ifyoulookreallycloselyyoucanseetherainisrepeating. Lookforsomeclumpofpointsandwatchastheyfalloff thebottomandpopbackonthetop. Iftherewasmoregoingoninthebackgroundlikeif thischeapraineffectwashappeningovera3Dgame it'spossiblenoonewouldevernoticeit'srepeating. Wecanfixtherepetitionbyaddinginalittlemorerandomness. voidmain(){ floatu=float(gl_VertexID)/float(numVerts);//goesfrom0to1 +floatoff=floor(time+u)/1000.0;//changesoncepersecondpervertex -floatx=hash(u)*2.0-1.0;//randomposition +floatx=hash(u+off)*2.0-1.0;//randomposition floaty=fract(time+u)*-2.0+1.0;//1.0->-1.0 gl_Position=vec4(x,y,0,1); gl_PointSize=2.0; } Inthecodeaboveweaddedoff.Sincewe'recallingfloor thevalueoffloor(time+u)willeffectivelygiveus asecondtimerthatonlychangesoncepersecondforeachvertex. Thisoffsetisinsyncwiththecodemovingthepointdownthescreen soatthesameinstancethepointjumpsbacktothetop ofthescreensomesmallamountisaddedtothevalue hashisbeingpassedwhichmeansthisparticularpoint willgetanewrandomnumberandthereforeanewrandomhorizontalposition. Theresultisaraineffectthatdoesn'tappeartorepeat clickheretoopeninaseparatewindow Canwedomorethangl.POINTS?Ofcourse! Let'smakecircles.Todothisweneedtrianglesaround acenterlikeslicesofpie.Wecanthinkofeachtriangle as2pointsaroundtheedgeofthepiefollowedby1pointinthecenter. Wethenrepeatforeachsliceofthepie. Sofirstwewantsomekindofcounterthatchangesonceperpieslice intsliceId=gl_VertexID/3; Thenweneedacountaroundtheedgeofthecirclethatgoes 0,1,?,1,2,?,2,3,?,... The?valuedoesn'treallymatterbecauselookingatthe diagramabovethe3rdvalueisalwaysinthecenter(0,0) sowecanjustmultiplyby0regardlessofvalue. Togetthepatternabovethiswouldwork inttriVertexId=gl_VertexID%3; intedge=triVertexId+sliceId; Forpointsontheedgevspointsinthecenterweneed thispattern.2ontheedgethen1inthecenter,repeat. 1,1,0,1,1,0,1,1,0,... Wecangetthatpatternwith floatradius=step(1.5,float(triVertexId)); step(a,b)is0ifacodegoeshereforcodeblocks commentspoweredbyDisqus



請為這篇文章評分?