WebGL2 is a pretty significant upgrade from WebGL1. If you're coming from WebGL1 and you want to know how to adjust your code so you can take advantage of WebGL2 see this article.
Here's the short list in no particular order
I think this is fairly important even though it was optionally available on WebGL1 now that it's always available on WebGL2 I think you should probably always use them.
In WebGL1 if your shader needed to know the size of a texture you had to pass the size in uniform manually. In WebGL2 you can call
vec2 size = textureSize(sampler, lod)
To get the size of any lod of a texture
It's often convenient to store large arrays of data in a texture. In WebGL 1 you could do that but you could only address textures with texture coordinates (0.0 to 1.0). In WebGL2 you can look up values from a texture by pixel/texel coordinates directly making array access slightly easier
vec4 values = texelFetch(sampler, ivec2Position, lod);
WebGL1 had just a few texture formats. WebGL2 has TONS!
3D texture are just that. Textures that have 3 dimensions.
A texture array is very similar to a 3D texture except that each slice is considered a separate texture. All the slices have to be the same size but this is a great way to give a shader access to hundreds of textures even though it only has a relatively small number of texture units. You can select the slice in your shader
vec4 color = texture(someSampler2DArray, vec3(u, v, slice));
In WebGL1 textures that were not a power of 2 could not have mips. In WebGL2 that limit is removed. Non-power of 2 texture work exactly the same as power of 2 textures.
In WebGL1 a loop in a shader had to use a constant integer expression. WebGL2 removes that limit (in GLSL 300 es)
In WebGL1 if needed to get the inverse of a matrix you had to
pass it in as a uniform. In WebGL2 GLSL 300 es there's the built in
inverse function as well as
In WebGL1 there are various compressed texture formats that are hardware dependent. S3TC was basically desktop only. PVTC was iOS only. Etc..
In WebGL2 these formats are supposed to be supported everywhere
Uniform Buffer Objects let you specify a bunch of uniforms from a buffer. The advantages are
You can manipulate all the uniforms in the buffer outside of WebGL
In WebGL1 if you had 16 uniforms that would require
16 calls to
gl.uniformXXX. That is relatively slow.
In WebGL2 if you use
a Uniform Buffer Object you can set the values in
much much faster. When all the values are set
you upload them all with 1 call to
gl.bufferSubData and then tell the program
to use that buffer with
gl.bindBufferRange so only
You can have different sets of uniforms buffer objects
First some terms. A Uniform Block is a collection of uniforms defined in a shader. A Uniform Buffer Object is a buffer that contains the values a Uniform Block will use. You can create as many Uniform Buffer Objects as you want and bind one of them to a particular Uniform Block when you draw.
For example, you could have 4 uniform blocks defined in a shader.
A global matrix uniform block that contains matrices that are the same for all draw calls like the projection matrix, view matrix, etc.
A per model uniform block that contains matrices that are different per model for example the world matrix and normal matrix.
A material uniform block that contains the material settings like diffuse, ambient, specular, etc..
A lighting uniform block that contains the lighting data like light color, light position, etc..
Then at runtime you could create one global uniform buffer object, one model uniform buffer object per model, one light uniform buffer object per light and one uniform buffer object per material.
To draw any particular item assuming all the values are already up to date all you have to do is bind your desired 4 uniform buffer objects
gl.bindBufferRange(..., globalBlockIndx, globalMatrixUBO, ...); gl.bindBufferRange(..., modelBlockIndx, someModelMatrixUBO, ...); gl.bindBufferRange(..., materialBlockIndx, someMaterialSettingsUBO, ...); gl.bindBufferRange(..., lightBlockIndx, someLightSettingsUBO, ...);
In WebGL2 you can have integer based textures where as in WebGL1 all textures represented floating point values even if they weren't represented by floating point values.
You can also have integer attributes.
On top of that, GLSL 300 es allows you to do bit manipulations of integers in the shaders.
WebGL2 allows your vertex shader to write its results back to a buffer.
In WebGL1 all the texture parameters were per texture. In WebGL2 you can optionally use sampler objects. With samplers, all the filtering and repeat/clamping parameters that were part of a texture move to the sampler. This means a single texture can be sampled in different ways. Repeating or clamped. Filtered or not filtered.
Depth textures were optional in WebGL1 and a PITA to work around. Now they're standard. Commonly used for computing shadow maps
These are now standard. Common uses include computing normals in the shaders instead of passing them in
Now Standard, common uses are drawing lots of trees, bushes or grass quickly.
Being able to use 32bit ints for indices removes the size limit of indexed geometry
Letting you write your own custom values to the depth buffer / z-buffer.
Being able to take the min or max of 2 colors when blending
Being able to draw to multiple buffers at once from a shader. This is commonly used for various deferred rendering techniques.
In WebGL1 this was an optional feature. There was a count of how many textures you could access in a vertex shader and that count was allowed to be 0. Most devices supported them. In WebGL2 that count is required to be at least 16.
In WebGL1 the canvas itself could be anti-aliased with the GPU's built in multi-sample system but there was no support for user controlled multi-sampling. In WebGL2 you can now make multi-sampled renderbuffers.
Occlusion queries let you ask the GPU to check if it were to render something would any pixels actually get drawn.
Floating point textures are used for many special effects and calculations. In WebGL1 they were optional. In WebGL2 they just exist.