Muchos objetos en tres.js tienen una propiedad necesaria para superar, y rara vez se escriben en los documentos (pero no hay muchos documentos en tres.js, y muchos problemas aún tienen que confiar en problemas en Github). No saben cómo escribir esto en varios tutoriales en línea, porque para programas introductorios simples, esta propiedad no se puede utilizar.
Entonces, ¿para qué se usa este atributo? En pocas palabras, le digo al renderizador que debería actualizar el caché en este cuadro. Aunque es muy simple de usar como un bit por bandera, porque necesita saber por qué necesita actualizar el caché y qué cachés actualizar, todavía es necesario comprenderlo cuidadosamente.
Por qué necesitaUpdateEn primer lugar, echemos un vistazo a por qué se necesita caché. La existencia de caché generalmente es reducir el número de tiempos de transmisión de datos, reduciendo así el tiempo dedicado a la transmisión de datos. Aquí, también es cierto que al final no es fácil para un objeto (malla) en la pantalla. Necesita ser transferido al campo de batalla tres veces.
El primero es leer todos los datos de vértices y datos de textura del disco local a la memoria a través del programa.
Luego, después de que el programa haya realizado el procesamiento apropiado en la memoria, debe transferir los datos de vértices y los datos de textura de los objetos que deben dibujarse a la pantalla a la memoria de video.
Finalmente, al representar cada cuadro, los datos del vértice y los datos de textura en la memoria de video se enjuagan en la GPU para el ensamblaje y el dibujo.
Según el modelo de transmisión de datos similar a la pirámide, el primer paso es obviamente el más lento. Si se transmite a través de la red en un entorno como WebGL, será aún más lento. El segundo es el tiempo desde la memoria hasta la memoria de video, que será una simple prueba de datos más adelante.
Luego está la frecuencia de uso de estos tres pasos. Para escenarios pequeños, el primer paso es una vez, es decir, cada vez que se inicializa el programa, todos los datos de un escenario se cargarán en la memoria. Para escenarios grandes, se puede hacer algo de carga asincrónica, pero actualmente no es el problema que estamos considerando. La frecuencia del segundo paso debería ser lo más importante para hablar este tiempo. Primero, escriba un programa simple para probar el consumo causado al hacer este paso de transmisión.
var lienvas = document.createElement ('Canvas');
var _gl = canvas.getContext ('experimental-webgl');
vértices var = [];
para (var i = 0; i <1000*3; i ++) {
vértices.push (i * math.random ());
}
var buffer = _gl.createBuffer ();
console.profile ('buffer_test');
bindBuffer ();
console.profileend ('buffer_test');
función bindBuffer () {
para (var i = 0; i <1000; i ++) {
_gl.bindbuffer (_gl.array_buffer, buffer);
_gl.bufferdata (_gl.array_buffer, new Float32Array (Vertices), _gl.static_draw);
}
}
Explicemos brevemente este programa primero. Vértices es una matriz que guarda los vértices. Aquí, 1,000 vértices se generan aleatoriamente. Debido a que cada vértice tiene tres coordenadas X, Y y Z, se necesita una matriz de 3000 de tamaño. El comando _gl.createBuffer abre un caché para almacenar datos de vértice en la memoria de video, y luego usa _gl.bufferData para transferir los datos de vértice generados de la memoria a la memoria del video. Aquí suponemos que hay 1000 objetos con 1000 vértices en una escena, cada vértice es 3 32 bits 4 bytes de datos flotantes. Calcule los datos de casi 1000 x 1000 x 12 = 11m. El perfil toma alrededor de 15 ms. Aquí podemos ver cómo 15 ms es solo un poco de tiempo. Sin embargo, para un programa en tiempo real, si desea garantizar una velocidad de cuadro de 30 fps, el tiempo requerido para cada cuadro debe controlarse a unos 30 ms. ¿Cómo puede tomar la mitad del tiempo hacer la transmisión de datos? Debe saber que la cabeza grande debe ser las operaciones de dibujo en la GPU y varios procesos en la CPU, y debe ser tacaño con cada paso de la operación en todo el proceso de renderizado.
Por lo tanto, se debe minimizar el número de transmisiones en este paso. De hecho, se puede usar para transferir todos los datos del vértice y los datos de textura de la memoria a la memoria de video cuando se carga. Esto es lo que hace tres.js ahora. Los datos de vértice del objeto que debe dibujarse se transfiere a la memoria de video por primera vez y almacenan en caché el búfer a la geometría .__ WebGlverTexBuffer. Después de eso, cada vez que dibuje, juzgará la propiedad de la geometría de Vértices. Si no necesita actualizar, use el caché actual directamente. Si ve que VerticesNeedUpate es verdadero, los datos de vértices en la geometría se transferirán a la geometría .__ WebGlvertExbuffer. En general, no necesitamos este paso para los objetos estáticos. Sin embargo, si encontramos objetos que cambian con frecuencia, como el uso de vértices como sistemas de partículas, y malla que usa animaciones de esqueleto, estos objetos cambiarán sus vértices en cada cuadro, por lo que cada cuadro debe establecer su propiedad Vértices.
De hecho, en los programas de WebGL, se cambiarán más posiciones de vértice en el sombreador de vértice para completar los efectos de partículas y la animación de esqueleto. Aunque es más fácil expandirse si se coloca en el lado de la CPU para calcular, debido a las limitaciones de la potencia informática de JavaScript, se colocarán más de estas operaciones computacionales en el lado de la GPU. En este caso, no hay necesidad de retransmitir los datos del vértice, por lo que el caso anterior no se usa mucho en el programa real, y se trata más de actualizar la textura y el caché de material.
El caso anterior describe principalmente un escenario en el que se transmiten los datos del vértice. Además de los datos del vértice, también hay una gran cabeza que es la textura. Una textura de formato R8G8B8A8 de tamaño 1024*1024 debe ocupar hasta 4 m de tamaño de memoria, así que mire el siguiente ejemplo.
var lienvas = document.createElement ('Canvas');
var _gl = canvas.getContext ('experimental-webgl');
var texture = _gl.createTexture ();
var img = nueva imagen;
img.onload = function () {
console.profile ('prueba de textura');
BindTexture ();
console.profileend ('prueba de textura');
}
img.src = 'test_tex.jpg';
función bindTexture () {
_gl.bindTexture (_gl.Texture_2d, texture);
_gl.texImage2d (_gl.texture_2d, 0, _gl.rgba, _gl.rgba, _gl.unsigned_byte, img);
}
No hay necesidad de repetir el pervertido 1000 veces aquí. Se necesitan 30 ms para transmitir la textura de 10241024 a la vez, y una imagen 256256 es de casi 2 m. Por lo tanto, en tres.js, la textura solo debe transmitirse una vez al principio. Después de eso, si la propiedad Texture.NeedSupdate no se establece manualmente en verdadero, la textura que se ha transferido a la memoria de video se utilizará directamente.
Qué cachés necesitan ser actualizadosLo anterior describe por qué tres.js necesita agregar dicho atributo de necesidad de Update a través de dos casos. A continuación, enumere varios escenarios para saber en qué circunstancias necesita actualizar manualmente estos cachés.
Carga asíncrona de texturasEste es un pequeño pozo, porque la imagen frontal se carga asincrónicamente. Si escribe Texture.NeedSupdate = True directamente después de crear IMG, el renderizador tres.js usará _gl.texImage2d para transferir datos de textura vacía a la memoria de video en este cuadro, y luego configure este indicador en falso. Luego, cuando se carga la imagen, los datos de la memoria de video no se actualizarán. Por lo tanto, debe esperar a que se cargue toda la imagen en el evento de Onload antes de escribir Texture.NeedSupdate = True
Textura de videoLa mayoría de las texturas son como el caso de arriba para cargar y transmitir imágenes directamente, pero no para texturas de video, porque el video es una transmisión de imágenes, y la imagen que se muestra en cada cuadro es diferente, por lo que debe configurar las necesidades de necesidad en verdadero para cada cuadro para actualizar los datos de textura en la tarjeta gráfica.
Usar el búfer renderEl búfer de renderizado es un objeto relativamente especial. En general, el programa se enmarcará directamente a la pantalla después de que se dibuje toda la escena. Sin embargo, si hay más procesamiento posterior o XXX basado en la pantalla (como el hecho ambiente basado en pantalla), la escena debe dibujarse primero en un búfer de renderizado. Este búfer es en realidad una textura, pero es generado por el dibujo anterior, no cargado desde el disco. Hay un objeto de textura especial webglrenderTarget en tres.js para inicializar y guardar RenderBuffer. Esta textura también debe establecerse en verdadero en cada cuadro.
Las necesidades de material de materialEl material se describe en tres.js a tres. Material. De hecho, el material no tiene muchos datos para transmitir, pero ¿por qué necesita hacer un DelEdPdate? Aquí hablaré sobre el sombreador. Shader se traduce como un sombreador, que proporciona la posibilidad de programar vértices y píxeles en GPU. Hay un término sombreado en la pintura para representar el método de pintura ligero y oscuro. El sombreado en GPU es similar. El programa calcula la luz y la oscuridad de la luz para expresar el material de un objeto. Ok, dado que Shader es un programa que se ejecuta en la GPU, como todos los programas, es necesario realizar una operación de compilación y vinculación. En WebGL, el programa Shader se compila en tiempo de ejecución, lo que, por supuesto, lleva tiempo, por lo que es mejor ser compilado y ejecutado hasta el final del programa. Entonces, cuando el material se inicializa en tres.js, el programa sombreador se compila y vincula y el objeto del programa obtenido después del enlace de compilación se almacena en caché. En general, un material ya no necesita recompilar todo el sombreador. Para ajustar el material, solo necesita modificar los parámetros uniformes del sombreador. Sin embargo, si reemplaza todo el material, como reemplazar el sombreador Pong original con un sombreador Lambert, debe configurar material. Sin embargo, esta situación es rara, y la más común es la que se menciona a continuación.
Agregar y quitar lucesEsto debería ser más común en la escena. Tal vez muchas personas que acaban de comenzar a usar tres.js caerán en este pozo. Después de agregar una luz a la escena dinámicamente, encuentran que la luz no funciona. Sin embargo, cuando se usa el sombreador construido en tres.js, por ejemplo, Phong, Lambert, mirando el código fuente en el renderizador, encontrará que tres.js usa #define en el código de sombreador incorporado para establecer la cantidad de luces en la escena. El valor de este #define se obtiene por sombreador de empalme de cadena cada vez que se actualiza el material. El código es el siguiente.
"#define max_dir_lights" + parámetros.maxdirlights,
"#define max_point_lights" + parámetros.maxpointlights,
"#define max_spot_lights" + parámetros.maxspotlights,
"#define max_hemi_lights" + parámetros.maxhemilights,
De hecho, este método de escritura puede reducir efectivamente el uso de registros de GPU. Si solo hay una luz, solo puede declarar la variable uniforme requerida para una luz. Sin embargo, cuando cambia el número de luces, especialmente cuando se agrega, debe volver a coser y compilar y vincular el sombreador. En este momento, también debe establecer el material.
Cambiar texturaEl cambio de textura aquí no significa actualizar los datos de la textura, sino que el material original usó la textura, pero no se usó más adelante, o el material original no usó la textura, y luego la agregó. Si el material no se actualiza manualmente, el efecto final será diferente de lo que piensa. La razón de este problema es similar a la iluminación mencionada anteriormente, y también se debe a que se agregó una macro al sombreador para determinar si se usó la textura.
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": "",
Por lo tanto, cada vez el mapa de tiempo, envmap o mapa de luz cambia el valor real, debe actualizar el material
Cambios en otros datos de vérticeDe hecho, el cambio de textura anterior creará un problema. Es principalmente porque no hay textura durante la inicialización. Sin embargo, en este entorno agregado dinámicamente, no es suficiente establecer material. También necesita establecer geometry.uvsneedSupdate en verdadero. ¿Por qué hay tal problema? Se debe a la optimización del programa por tres.js. Al inicializar la geometría y el material por primera vez en Renderer, si se juzga que no hay textura, aunque hay cada datos de Vértice UV en los datos en la memoria, tres.js no copiará estos datos en la memoria de video. La intención original debe ser guardar un valioso espacio de memoria de video. Sin embargo, después de agregar textura, la geometría no transferirá de manera inteligente estos datos UV para su uso de textura. Debemos establecer manualmente UVSNeedSupdate para informarle que es hora de actualizar UV, esta pregunta realmente me hizo hacer trampa durante mucho tiempo al principio.
Para los atributos necesarios de varios tipos de datos de vértice, puede ver este problema
https://github.com/mrdoob/three.js/wiki/updates
por finLa optimización de tres.js está haciendo un buen trabajo, pero trae varias dificultades que pueden ser tocadas por varias optimizaciones. La mejor manera de hacerlo es mirar el código fuente o ir a Github para mencionar los problemas