Muitos objetos em três.js têm uma propriedade de necessidade de update e raramente são escritos nos documentos (mas não há muitos documentos em três.js, e muitos problemas ainda precisam confiar em questões no GitHub). Eles não sabem como escrever isso em vários tutoriais on -line, porque, para programas introdutórios simples, essa propriedade não pode ser usada.
Então, para que serve esse atributo? Em poucas palavras, digo ao renderizador que devo atualizar o cache nesse quadro. Embora seja muito simples de usar como um bit de sinalizador, porque você precisa saber por que precisa atualizar o cache e quais caches atualizar, ainda é necessário entendê -lo com cuidado.
Por que precisa de updatePrimeiro de tudo, vamos dar uma olhada no motivo pelo qual o cache é necessário. A existência de cache geralmente é reduzir o número de tempos de transmissão de dados, reduzindo assim o tempo gasto na transmissão de dados. Aqui, também é verdade que não é fácil para um objeto (malha) ser exibido com sucesso na tela no final. Ele precisa ser transferido para o campo de batalha três vezes.
O primeiro é ler todos os dados e dados de textura do vértice do disco local na memória através do programa.
Depois que o programa fizer o processamento apropriado na memória, ele precisa transferir os dados e dados de textura dos vértex dos objetos que precisam ser atraídos para a tela para a memória de vídeo.
Finalmente, ao renderizar cada quadro, os dados e dados de textura do vértice na memória de vídeo são liberados na GPU para montagem e desenho.
De acordo com o modelo de transmissão de dados do tipo pirâmide, a primeira etapa é obviamente a mais lenta. Se for transmitido pela rede em um ambiente como o WebGL, será ainda mais lento. A segunda é a hora da memória à memória de vídeo, que será um teste de dados simples posteriormente.
Depois, há a frequência de uso dessas três etapas. Para pequenos cenários, o primeiro passo é único, ou seja, toda vez que o programa é inicializado, todos os dados de um cenário serão carregados na memória. Para cenários grandes, alguns carregamentos assíncronos podem ser feitos, mas atualmente não é o problema que estamos considerando. A frequência do segundo passo deve ser a coisa mais importante para falar sobre esse tempo. Primeiro, escreva um programa simples para testar o consumo causado pela etapa de transmissão.
var canvas = document.createElement ('canvas');
var _GL = Canvas.getContext ('Experimental-Webgl');
var vertices = [];
for (var i = 0; i <1000*3; i ++) {
vertices.push (i * math.random ());
}
var buffer = _gl.createbuffer ();
console.profile ('buffer_test');
bindBuffer ();
console.profileend ('buffer_test');
função bindBuffer () {
for (var i = 0; i <1000; i ++) {
_gl.bindbuffer (_gl.array_buffer, buffer);
_gl.bufferdata (_gl.array_buffer, new float32Array (vértices), _gl.static_draw);
}
}
Vamos explicar brevemente este programa primeiro. Os vértices são uma matriz que salva vértices. Aqui, 1.000 vértices são gerados aleatoriamente. Como cada vértice possui três coordenadas x, y e z, é necessária uma matriz de 3000 tamanho. O comando _gl.createbuffer abre um cache para armazenar dados de vértice na memória de vídeo e, em seguida, usa _gl.bufferdata para transferir os dados de vértices gerados da memória para a memória de vídeo. Aqui, assumimos que existem 1000 objetos com 1000 vértices em uma cena, cada vértice é de 3 bytes de 32 bits de dados float. Calcule os dados de quase 1000 x 1000 x 12 = 11m. O perfil leva cerca de 15ms. Aqui podemos ver como 15ms é apenas um pouco de tempo. No entanto, para um programa em tempo real, se você deseja garantir uma taxa de quadros de 30fps, o tempo necessário para cada quadro deve ser controlado a cerca de 30ms. Como pode levar metade do tempo para apenas fazer transmissão de dados? Você deve saber que a cabeça grande deve ser as operações de desenho na GPU e vários processos na CPU, e você deve ser mesquinho a cada etapa da operação em todo o processo de renderização.
Portanto, o número de transmissões nesta etapa deve ser minimizado. De fato, ele pode ser usado para transferir todos os dados e dados de textura do vértice da memória para a memória de vídeo quando forem carregados. Isso é o que três.js faz agora. Os dados do vértice do objeto que precisam ser desenhados são transferidos para a memória de vídeo pela primeira vez e cache o buffer da geometria .__ webglvertexBuffer. Depois disso, toda vez que você desenha, você julgará a propriedade da geometria dos vertisesneedupdate. Se você não precisar atualizar, use o cache atual diretamente. Se você vir que o VerticesNeedUpate é verdadeiro, os dados do vértice na geometria serão transferidos para a geometria .__ webglvertexBuffer. Geralmente, não precisamos desta etapa para objetos estáticos. No entanto, se encontrarmos objetos que mudam com frequência, como o uso de vértices como sistemas de partículas e malha que usa animações de esqueletos, esses objetos alterarão seus vértices em cada quadro; portanto, cada quadro precisa definir sua propriedade VERTICESNEEDUPDATE como True para dizer ao renderizador que eu preciso retransmitir os dados!
De fato, nos programas WebGL, mais posições de vértice serão alteradas no shader do vértice para completar os efeitos das partículas e a animação do esqueleto. Embora seja mais fácil expandir se for colocado no lado da CPU para calcular, devido às limitações do poder de computação do JavaScript, mais dessas operações computacionais serão colocadas no lado da GPU. Nesse caso, não há necessidade de retransmitir os dados do vértice; portanto, o caso acima não é usado muito no programa real e trata -se mais de atualizar o cache de textura e material.
O caso acima descreve principalmente um cenário em que os dados do vértice são transmitidos. Além dos dados do vértice, também há uma cabeça grande que é a textura. Uma textura do formato R8G8B8B8A8 de tamanho 1024*1024 deve ocupar até o tamanho da memória de até 4M, então observe o exemplo a seguir.
var canvas = document.createElement ('canvas');
var _GL = Canvas.getContext ('Experimental-Webgl');
var textura = _gl.createTexture ();
var iMg = nova imagem;
img.onload = function () {
console.profile ('Teste de textura');
bindTexture ();
console.profileend ('Teste de textura');
}
img.src = 'test_tex.jpg';
função bindTexture () {
_gl.bindTexture (_gl.texture_2d, textura);
_gl.TexIMAGE2D (_GL.TEXTURE_2D, 0, _GL.RGBA, _GL.RGBA, _GL.Unsigned_Byte, IMG);
}
Não há necessidade de repetir as 1000 vezes pervertidas aqui. São necessários 30ms para transmitir a textura de 10241024 por vez, e uma imagem 256256 é de quase 2ms. Portanto, em três.js, a textura deve ser transmitida apenas uma vez no início. Depois disso, se a propriedade Texture.NeedSupdate não estiver definida manualmente como true, a textura que foi transferida para a memória de vídeo será usada diretamente.
O que os caches precisam ser atualizadosO acima descreve por que o Three.js precisa adicionar esse atributo de necessidade de update através de dois casos. Em seguida, liste vários cenários para saber em que circunstâncias você precisa atualizar manualmente esses caches.
Carregamento assíncrono de texturasEste é um pequeno poço, porque a imagem front-end é carregada de forma assíncrona. Se você gravar texture.needsupdate = true diretamente após a criação do IMG, o renderizador do três.js usará _gl.teximage2d para transferir dados de textura vazia para a memória de vídeo nesse quadro e, em seguida, definirá esse sinalizador como false. Então, quando a imagem é carregada, os dados de memória de vídeo não serão atualizados. Portanto, você deve esperar que toda a imagem seja carregada no evento OnLoad antes de escrever textura.needsupdate = true
Textura de vídeoA maioria das texturas é como o caso acima para carregar e transmitir imagens diretamente, mas não para texturas de vídeo, porque o vídeo é um fluxo de imagens e a imagem a ser exibida em cada quadro é diferente; portanto, você precisa definir o NecessupDate como true para cada quadro para atualizar os dados da textura na placa gráfica.
Use Render BufferO buffer de renderização é um objeto relativamente especial. Geralmente, o programa será liberado diretamente para a tela após a cena inteira ser desenhada. No entanto, se houver mais xxx baseado em tela ou em tela (como ocorrência ambiental baseada em tela), a cena precisará ser desenhada primeiro em um buffer de renderização. Esse buffer é na verdade uma textura, mas é gerada pelo desenho anterior, não carregado do disco. Existe um objeto de textura especial webglrenderStarget em três.js para inicializar e salvar renderbuffer. Esta textura também precisa ser definida como true em cada quadro.
Necessidades do materialO material é descrito em três.js a três.Material. De fato, o material não possui muitos dados a serem transmitidos, mas por que você precisa fazer um necessidade? Aqui vou falar sobre o shader. O shader é traduzido como um shader, que fornece a possibilidade de programar vértices e pixels na GPU. Há um termo sombreado na pintura para representar o método claro e sombrio de pintura. O sombreamento da GPU é semelhante. A luz e a escuridão da luz são calculadas pelo programa para expressar o material de um objeto. OK, como o Shader é um programa em execução na GPU, como todos os programas, é necessário executar uma operação de compilação e vinculação. No WebGL, o programa Shader é compilado no tempo de execução, o que, é claro, leva tempo, por isso é melhor ser compilado e executar até o final do programa. Portanto, quando o material é inicializado em três.js, o programa Shader é compilado e vinculado e o objeto do programa obtido após o link de compilação ser armazenado em cache. Geralmente, um material não precisa mais recompilar o shader inteiro. Para ajustar o material, você só precisa modificar os parâmetros uniformes do shader. No entanto, se você substituir o material inteiro, como substituir o shader original de pong por um shader de Lambert, precisará definir material.needsupdate como fiel ao recompilar. No entanto, essa situação é rara e a mais comum é a mencionada abaixo.
Adicione e remova as luzesIsso deve ser mais comum na cena. Talvez muitas pessoas que acabaram de começar a usar três.js caem neste poço. Depois de adicionar uma luz à cena dinamicamente, eles acham que a luz não funciona. No entanto, ao usar o shader construído em três.js, por exemplo, Phong, Lambert, olhando para o código-fonte no renderizador, você descobrirá que o Three.js usa #define no código do shader embutido para definir o número de luzes na cena. O valor deste #Define é obtido pelo shader String Splicing sempre que o material é atualizado. O código é o seguinte.
"#Define max_dir_lights" + parameters.maxdirlights,
"#Define max_point_lights" + parameters.maxpointlights,
"#Define max_spot_lights" + parameters.maxspotlights,
"#Define max_hemi_lights" + parameters.maxhemilights,
De fato, este método de redação pode efetivamente reduzir o uso de registros de GPU. Se houver apenas uma luz, você poderá declarar apenas a variável uniforme necessária para uma luz. No entanto, quando o número de luzes muda, especialmente ao adicionar, você precisa re-posse, compilar e vincular o shader. Neste momento, você também precisa definir o material.NeedSupdate de todos os materiais como verdadeiros;
Alterar texturaA mudança de textura aqui não significa atualizar os dados da textura, mas que o material original usou a textura, mas não foi usado posteriormente, ou o material original não usou a textura e depois a adicionou. Se o material não for atualizado manualmente, o efeito final será diferente do que você pensa. A razão para esse problema é semelhante à iluminação mencionada acima, e é também porque uma macro foi adicionada ao shader para determinar se a textura foi usada.
parâmetros.map? "#define use_map": "",
parâmetros.envmap? "#define use_envmap": "",
parâmetros.lightmap? "#define use_lightmap": "",
parâmetros.bumpmap? "#define use_bumpmap": "",
parâmetros.normalmap? "#define use_normalmap": "",
parâmetros.specularmap? "#define use_specularmap": "",
Portanto, sempre que o mapa do tempo, EnvMap ou LightMap altera o valor verdadeiro, você precisa atualizar o material
Alterações em outros dados de vérticesDe fato, a mudança de textura acima criará um problema. É principalmente porque não há textura durante a inicialização. No entanto, nesse ambiente adicionado dinamicamente, não é suficiente definir material.needsupdate como true. Ele também precisa definir geometria.UVSNeedSupdate como true. Por que existe um problema? É devido à otimização do programa por três.js. Ao inicializar a geometria e o material pela primeira vez no renderizador, se for julgado que não houver textura, embora existam dados UV de vértice nos dados na memória, três.js não copiarão esses dados na memória de vídeo. A intenção original deve ser salvar algum espaço valioso de memória de vídeo. No entanto, após a adição de textura, a geometria não transferirá de forma inteligente esses dados UV para uso da textura. Devemos definir manualmente o UVSNeedSupdate para informá -lo de que é hora de atualizar o UV, essa pergunta realmente me fez trapacear por um longo tempo no começo.
Para os atributos necessitados de vários tipos de dados de vértices, você pode ver esse problema
https://github.com/mrdoob/three.js/wiki/Updates
afinalA otimização do Three.js está fazendo um bom trabalho, mas traz várias armadilhas que podem ser tocadas por várias otimizações. A melhor maneira de fazer isso é olhar para o código -fonte ou ir ao github para mencionar problemas