WebGL2 é quase 100% compatível com o WebGL1. Se você usar apenas os recursos da WebGL1, então há apenas 2 diferenças.
Você usa " webgl2 "
em vez de " webgl "
ao chamar getContext
var gl = someCanvas.getContext("webgl2");
Nota: não há “experimental-webgl2”. Os fornecedores do navegador se reuniram e decidiram não mais prefixar coisas porque os sites dependem do prefixo.
Muitas extensões são uma parte padrão da WebGL2 e, portanto, não estão disponíveis como extensões
Por exemplo, objeto Vertex Array OES_vertex_array_object
é uma
característica padrão da WebGL2. Então, por exemplo, na WebGL1 você faria isso
var ext = gl.getExtension("OES_vertex_array_object");
if (!ext) {
// Diga ao usuário que ele não têm a extensão necessária ou trabalhar em torno dela
} else {
var someVAO = ext.createVertexArrayOES();
}
Na WebGL2 você faria isso
var someVAO = gl.createVertexArray();
Porque ele só existe.
Caso contrário, todas as suas coisas WebGL1 devem funcionar.
Dito isso, para tirar proveito da maioria dos recursos da WebGL2 você precisará fazer algumas mudanças.
A maior mudança é que você deve atualizar seus shaders para GLSL 3,00 ES. Para fazer isso, a primeira linha de seus shaders precisa ser
#version 300 es
NOTA: ESTA TEM QUE SER A PRIMEIRA LINHA! Sem comentários, sem linhas em branco antes de permitir.
Em outras palavras, isso é ruim
// RUIM!!!! +---Há uma nova linha aqui!
// RUIM!!!! V
var vertexShaderSource = `
#version 300 es
..
`;
Isso também é ruim
<!-- RUIM!! V<- há uma nova linha aqui!
<script id="vs" type="notjs">
#version 300 es
...
</script>
Isso é bom
var vertexShaderSource = `#version 300 es
...
`;
Isso também é bom
<script id="vs" type="notjs">#version 300 es
...
</script>
Ou você poderia fazer com que suas funções de compilação de shader tirem as primeiras linhas em branco.
Há várias mudanças que você precisa para fazer o seus shaders de cima para adicionar a cadeia de versão anterior
attribute
-> in
No GLSL 100 você pode ter
attribute vec4 a_position;
attribute vec2 a_texcoord;
attribute vec3 a_normal;
Em GLSL 300 es se torna
in vec4 a_position;
in vec2 a_texcoord;
in vec3 a_normal;
varying
to in
/ out
No GLSL 100, você pode declarar uma variação tanto no vertex como nos fragmentos shaders como esse
varying vec2 v_texcoord;
varying vec3 v_normal;
Em GLSL 300 es no vertex shader as variações se tornam
out vec2 v_texcoord;
out vec3 v_normal;
E no fragmento shader eles se tornam
in vec2 v_texcoord;
in vec3 v_normal;
gl_FragColor
No GLSL 100, seu fragmento shader configuraria a variável
especial gl_FragColor
para definir a saída do shader.
gl_FragColor = vec4(1, 0, 0, 1); // red
Em GLSL 300 es, você declara sua própria variável de saída e depois configura-a.
out vec4 myOutputColor;
void main() {
myOutputColor = vec4(1, 0, 0, 1); // red
}
Nota: Você pode escolher qualquer nome que desejar, mas os nomes não podem começar
com gl_
, então você não pode simplesmente fazer out vec4 gl_FragColor
texture2D
-> texture
etc.No GLSL 100 você obtém uma cor de uma textura como essa
uniform sampler2D u_some2DTexture;
uniform samplerCube u_someCubeTexture;
...
vec4 color1 = texture2D(u_some2DTexture, ...);
vec4 color2 = textureCube(u_someCubeTexture, ...);
No GLSL 300es, as funções de textura sabem automaticamente o
que fazer com base no tipo de amostrador, então agora é apenas
texture
uniform sampler2D u_some2DTexture;
uniform samplerCube u_someCubeTexture;
...
vec4 color1 = texture(u_some2DTexture, ...);
vec4 color2 = texture(u_someCubeTexture, ...);
No WebGL1, muitos recursos eram extensões opcionais. No WebGL2, todos os itens seguintes são características padrão
gl_FragDepth
(EXT_frag_depth)nas texturas WebGL1 que não tinham um poder de 2 não podiam ter mips. No WebGL2, esse limite é removido. Non-power de 2 texturas funciona exatamente como a potência de 2 texturas.
No WebGL1 para verificar se há suporte para a renderização para uma textura
de ponto flutuante, primeiro você verificaria e ativaria a extensão OES_texture_float
,
então você criaria uma textura de ponto flutuante, ligaria-a a um framebuffer e chamaria
gl.checkFramebufferStatus
veja se ele retornou gl.FRAMEBUFFER_COMPLETE
.
No WebGL2 você precisa verificar e ativar EXT_color_buffer_float
ou então
gl.checkFramebufferStatus
nunca retornará gl.FRAMEBUFFER_COMPLETE
para
uma textura de ponto flutuante.
Observe que isso também é verdadeiro para os anexos framebuffer HALF_FLOAT
.
Se você tem curiosidade, esse foi um erro na especificação WebLG1. O que aconteceu foi o envio da WebGL1 e o
OES_texture_float
foi adicionado e apenas assumiu que a maneira correta de usá-lo para renderizar era criar uma textura, anexá-la um framebuffer e verificar seu status. Mais tarde, alguém apontou de acordo com a especificação que não era suficiente porque a especificação diz que as cores escritas em um fragmento shader são sempre restritos de 0 a 1.EXT_color_buffer_float
remove essa restrição de clampping, mas como o WebGL já havia sido enviado há um ano ou teria quebrado muitos sites para reforçar a restrição. Para o WebGL2 eles conseguiram corrigi-lo e agora você deve habilitar oEXT_color_buffer_float
para usar as texturas de ponto flutuante como anexos framebuffer.Observe que o AFAIK, a partir de março de 2017, poucos dispositivos móveis suportam renderização para texturas de ponto flutuante.
De todos os recursos acima de um recurso, eu pessoalmente acho que você sempre deve SEMPRE usar os objetos do vertex array. Tudo realmente depende do que você está tentando fazer, mas os objetos do vertex array, em particular, parecem um recurso básico que sempre deve ser usado.
Na WebGL1 sem objetos vertex array todos os dados sobre atributos foram para o estado WebGL global. Você pode imaginar isso assim
var glState = {
attributeState: {
ELEMENT_ARRAY_BUFFER: null,
attributes: [
{ enable: ?, size: ?, type: ?, normalize: ?, stride: ?, offset: ?, buffer: ?, },
{ enable: ?, size: ?, type: ?, normalize: ?, stride: ?, offset: ?, buffer: ?, },
{ enable: ?, size: ?, type: ?, normalize: ?, stride: ?, offset: ?, buffer: ?, },
{ enable: ?, size: ?, type: ?, normalize: ?, stride: ?, offset: ?, buffer: ?, },
{ enable: ?, size: ?, type: ?, normalize: ?, stride: ?, offset: ?, buffer: ?, },
{ enable: ?, size: ?, type: ?, normalize: ?, stride: ?, offset: ?, buffer: ?, },
{ enable: ?, size: ?, type: ?, normalize: ?, stride: ?, offset: ?, buffer: ?, },
{ enable: ?, size: ?, type: ?, normalize: ?, stride: ?, offset: ?, buffer: ?, },
],
},
}
Chamar funções como gl.vertexAttribPointer
, gl.enableVertexAttribArray
e
gl.bindBuffer (gl.ELEMENT_ARRAY_BUFFER, ??)
afetariam esse estado global.
Antes de cada coisa que você queria desenhar, você precisava configurar todos os atributos e, se você
estivesse desenhando dados indexados, você precisava configurar o ELEMENT_ARRAY_BUFFER
.
Com objeto Vertex Array, todo o attributeState
acima se torna um Vertex Array.
Em outras palavras
var someVAO = gl.createVertexArray();
Faz uma nova instância da coisa acima chamada attributeState
.
gl.bindVertexArray(someVAO);
É equivalente a
glState.attributeState = someVAO;
O que isto significa é que você deve configurar todos os seus atributos em tempo de inicialização agora.
// no tempo inicial
for each model / geometry / ...
var vao = gl.createVertexArray()
gl.bindVertexArray(vao);
for each attribute
gl.enableVertexAttribArray(...);
gl.bindBuffer(gl.ARRAY_BUFFER, bufferForAttribute);
gl.vertexAttribPointer(...);
if indexed geometry
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bindVertexArray(null);
Então no tempo de renderização para usar uma geometria particular, tudo o que você precisa fazer é
gl.bindVertexArray(vaoForGeometry);
No WebGL1, o objeto de loop de inicialização teria aparecido no tempo de renderização. Esta é uma grande velocidade!
Há algumas advertências:
Os locais de atributo dependem do programa.
Se você estiver usando a mesma geometria com vários programas, considere atribuir manualmente locais de atributo. No GLSL 300 es você pode fazer isso no shader
Por example:
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec2 a_texcoord;
layout(location = 2) in vec3 a_normal;
layout(location = 3) in vec4 a_color;
Define os locais dos 4 atributos.
Você ainda pode fazer o modo WebGL1 chamando
gl.bindAttribLocation
antes de chamar gl.linkProgram
.
Por example:
gl.bindAttribLocation(someProgram, 0, "a_position");
gl.bindAttribLocation(someProgram, 1, "a_texcoord");
gl.bindAttribLocation(someProgram, 2, "a_normal");
gl.bindAttribLocation(someProgram, 3, "a_color");
Isso significa que você pode forçá-los a serem compatíveis em vários programas de shader. Se um programa não precisa de todos os atributos, os atributos que eles precisam ainda serão atribuídos aos mesmos locais
Se você não fizer isso, você precisará de VAOs diferentes para diferentes programas de shader ao usar a mesma geometria OR, você precisará apenas fazer a WebGL1 e não usar VAOs e sempre configurar atributos no tempo de renderização, o que é lento.
NOTA: dos 2 métodos acima, eu me inclino para usar o
gl.bindAttribLocation
porque é fácil tê-lo em um lugar no meu código onde,
o método de usar layout layout(location = ?)
deve
estar em todos os shaders, então, no interesse de DRY gl.bindAttribLocation
parece melhor. Talvez, se eu estivesse usando um gerador de shader, não haveria diferença.
Sempre desvincular o VAO quando terminar
gl.bindVertexArray(null);
Isso só vem da minha própria experiência. Se você olhar acima, o
estado ELEMENT_ARRAY_BUFFER
faz parte de uma Vertex Array.
Então, encontrei esse problema. Eu criei uma geometria, então
criei um VAO para essa geometria e configurei os atributos
e ELEMENT_ARRAY_BUFFER
. Então criei mais
geometria. Quando essa geometria configurou seus índices, porque
eu ainda tinha o anterior VAO ligado configuração os índices
efectuados a ELEMENT_ARRAY_BUFFER
vinculativo para o anterior
VAO. Levei várias horas para depurar.
Então, minha sugestão nunca é deixar um link VAO se você terminar
com isso. Ou vincule imediatamente o próximo VAO que você vai
usar ou, se tiver terminado, vincule null
Essa é minha pequena lista pessoal de coisas a serem conhecidas ao mudar de WebGL1 para WebGL2. Há ainda mais coisas que você pode fazer no WebGL2 embora.