Este artigo é uma continuação de artigos anteriores da WebGL. Se você não os leu, sugiro que comece por lá e então, volte para este artigo.
Uma pergunta comum é “como eu desenho texto na WebGL”. A primeira coisa a se perguntar é, qual é o seu propósito ao desenhar o texto. Você está em um navegador, o navegador exibe texto. Portanto, sua primeira resposta deve ser usar HTML para exibir texto.
Vamos primeiro fazer o exemplo mais fácil: você só quer desenhar algum texto sobre sua WebGL. Podemos chamar isso de uma sobreposição de texto. Basicamente, este é um texto que permanece na mesma posição.
A maneira simples é fazer um elemento HTML ou elementos e usar o CSS para que eles se sobreponham.
Por exemplo: primeiro, crie um container e coloque uma tela e algum HTML para ser sobreposto dentro do container.
<div class="container">
<canvas id="canvas" width="400" height="300"></canvas>
<div id="overlay">
<div>Time: <span id="time"></span></div>
<div>Angle: <span id="angle"></span></div>
</div>
</div>
Em seguida, configure o CSS para que a tela e o HTML se sobrepõem
.container {
position: relative;
}
#overlay {
position: absolute;
left: 10px;
top: 10px;
}
Agora, procure esses elementos no tempo de inicialização e crie ou procure as áreas que deseja realizar a mudança.
// look up the elements we want to affect
var timeElement = document.querySelector("#time");
var angleElement = document.querySelector("#angle");
// Create text nodes to save some time for the browser
// and avoid allocations.
var timeNode = document.createTextNode("");
var angleNode = document.createTextNode("");
// Add those text nodes where they need to go
timeElement.appendChild(timeNode);
angleElement.appendChild(angleNode);
Finalmente atualize os nós ao renderizar
function drawScene(time) {
var now = time * 0.001; // convert to seconds
...
// convert rotation from radians to degrees
var angle = radToDeg(rotation[1]);
// only report 0 - 360
angle = angle % 360;
// set the nodes
angleNode.nodeValue = angle.toFixed(0); // no decimal place
timeNode.nodeValue = now.toFixed(2); // 2 decimal places
E aqui está esse exemplo
Observe como eu coloco espaços dentro dos divs especificamente para as partes que eu queria mudar. Estou fazendo o pressuposto aqui que é mais rápido do que apenas usar os divs sem extensões e fazer algo como
timeNode.nodeValue = "Time " + now.toFixed(2);
Também estou usando text nodes chamando node = document.createTextNode()
e depois node.nodeValue = someMsg
.
Eu também poderia usar someElement.innerHTML = someHTML
. Isso seria mais flexível porque você poderia
inserir string HTML arbitrárias, embora possa ser um pouco mais lento, já que o navegador precisa criar
e destruir os nós cada vez que você os configura. Bom, você decide o que é melhor.
O ponto importante para sair da técnica de sobreposição é que a WebGL é executada em um navegador. Lembrar de usar os recursos do navegador quando apropriado. Muitos programadores OpenGL costumam ter que renderizar cada parte de suas aplicações 100% do zero, mas já que a WebGL é executada em um navegador, ela já tem toneladas de recursos. Use-os. Isso tem muitos benefícios. Por exemplo, você pode usar estilos CSS para facilmente dá a essa sobreposição um estilo interessante.
Por exemplo, este é o mesmo exemplo, mas adicionado de algum estilo. O fundo é arredondado, as letras têm um brilho ao redor delas. Há uma borda vermelha. Você obtém tudo isso, essencialmente de graça, usando o HTML.
A próxima coisa mais comum a querer fazer é posicionar algum texto em relação a algo que você está renderizando. Nós também podemos fazer isso em HTML.
Neste caso, voltaremos a criar um container com o canvas e outro container para o nosso HTML em movimento
<div class="container">
<canvas id="canvas" width="400" height="300"></canvas>
<div id="divcontainer"></div>
</div>
E vamos configurar o CSS
.container {
position: relative;
overflow: none;
}
#divcontainer {
position: absolute;
left: 0px;
top: 0px;
width: 400px;
height: 300px;
z-index: 10;
overflow: hidden;
}
.floating-div {
position: absolute;
}
A parte position: absolute;
faz com que o #divcontainer
seja posicionado em termos absolutos, relativo
ao primeiro nó pai com outro estilo position: relative
ou position: absolute
. Nesse caso
esse é o recipiente que tanto o canvas como o #divcontainer
estão dentro.
O left: 0px; topo: 0px
faz com que o #divcontainer
se alinhe com tudo. O z-index: 10
o faz
flutuae sobre o canvas. E o overflow: hidden
faz com que seus filhos sejam cortados.
Finalmente, .floating-div
será usado para a div posicionável que criamos.
Então, agora precisamos procurar o divcontainer, criar um div e anexá-lo.
// look up the divcontainer
var divContainerElement = document.querySelector("#divcontainer");
// make the div
var div = document.createElement("div");
// assign it a CSS class
div.className = "floating-div";
// make a text node for its content
var textNode = document.createTextNode("");
div.appendChild(textNode);
// add it to the divcontainer
divContainerElement.appendChild(div);
Agora podemos posicionar a div através da configuração do seu estilo.
div.style.left = Math.floor(x) + "px";
div.style.top = Math.floor(y) + "px";
textNode.nodeValue = now.toFixed(2);
Aqui está um exemplo em que nos limitamos ao div.
Então, o próximo passo que queremos é, colocá-lo em relação a algo na cena 3D. Como fazemos isso? Nós fazemos exatamente como pedimos ao GPU para fazê-lo quando nós falavamos sobre projeção em perspectiva 3D.
Com esse exemplo aprendemos a usar matrizes, como multiplicá-las, e como aplicar uma matriz de projeção para convertê-los em um clipspace. Passamos tudo para o nosso shader e ele multiplica vértices no espaço local e os converte para clipspace. Nós também podemos fazer todas os cálculos em JavaScript. Então, podemos multiplicar o clipspace (-1 a +1) em pixels e então, usar para posicionar a div.
gl.drawArrays(...);
// We just got through computing a matrix to draw our
// F in 3D.
// choose a point in the local space of the 'F'.
// X Y Z W
var point = [100, 0, 0, 1]; // this is the front top right corner
// compute a clipspace position
// using the matrix we computed for the F
var clipspace = m4.transformVector(matrix, point);
// divide X and Y by W just like the GPU does.
clipspace[0] /= clipspace[3];
clipspace[1] /= clipspace[3];
// convert from clipspace to pixels
var pixelX = (clipspace[0] * 0.5 + 0.5) * gl.canvas.width;
var pixelY = (clipspace[1] * -0.5 + 0.5) * gl.canvas.height;
// position the div
div.style.left = Math.floor(pixelX) + "px";
div.style.top = Math.floor(pixelY) + "px";
textNode.nodeValue = now.toFixed(2);
E voila, o canto superior esquerdo da nossa div está perfeitamente alinhado com o canto superior direito do F.
Claro, se você quiser mais texto, faça mais divs.
Você pode ver a fonte desse último exemplo para ver os detalhes. Um ponto importante é que estou apenas adivinhando que criando, adicionando e removendo elementos HTML do DOM é lento, então o exemplo acima os cria e os mantém por aí. Ele esconde os inutilizados em vez de removê-los do DOM. Você teria que analisar com mais detalhes para saber se isso é mais rápido. Esse foi apenas o método que eu escolhi.
Esperemos que esteja claro como usar o HTML para inserir texto. Em seguida, vamos aprender sobre o uso do Canvas 2D para texto.