WebGL2 Drawing Without Data
文章推薦指數: 80 %
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)is0ifacodegoeshere
forcodeblocks
commentspoweredbyDisqus
延伸文章資訊
- 1Opengl 4.3 gl_VertexID not incrementing with glDrawArrays
I'm having difficulty in understanding why gl_VertexID is not properly incrementing for each new ...
- 2Using gl_VertexID as an array index? - OpenGL - Khronos ...
- 3Intro to GLSL - Cornell CS
- 4OpenGL Programming Guide: The Official Guide to Learning ...
... Attributes Example Vertex Shader Example 3.12 Instancing Example Drawing Code Example 3.13 gl...
- 5What exactly is gl_VertexID? - Graphics and GPU Programming
For this, I was expecting that gl_VertexID would be the index into the vertex buffer of the curre...