Comments: Three. many objects in js have a needsUpdate attribute, which is rarely written in the document (but three. there are not many js documents, and many problems have to be solved by the issues on github. This is not written in various online tutorials, because for simple entry-level programs, the three of this attribute is not used. many objects in js have a needsUpdate attribute, which is rarely written in the document (but three. there are not many js documents, and many problems have to be solved by the issues on github. This is not written in various online tutorials, because for simple entry-level programs, this attribute is unavailable.
So what is this attribute used for? To put it bluntly, I told renderer that I should update the cache. Although it is very easy to use as a flag, however, it is necessary to know why to update the cache and what cache to update.
Why is needsUpdate required?
First of all, let's take a look at why cache is needed. The cache usually exists to reduce the number of data transfers and reduce the time consumed by the program in data transmission. Here, it is also a general object (Mesh) it is not easy to display the screen successfully at the end, and it needs to be transferred to three battlefields.
First, the program reads all vertex data and texture data from the local disk to the memory.
After the program makes proper processing in the memory, it needs to transmit the vertex data and texture data of the objects to be drawn to the Display memory.
At last, the vertex data and texture data in the video memory are flushed into the GPU for assembly and painting during each frame rendering.
According to the pyramid data transmission model, the first step is obviously the slowest. If it is transmitted over the network in an environment like WebGL, it will be even slower, the second is the time from memory transfer to video memory, which will be followed by a simple data test.
Next, the usage frequency of these three steps. For small scenarios, the first step is one-time, it means that all data in a scenario is loaded into the memory every time the program is initialized. For large scenarios, some asynchronous loading may be performed, however, we are not considering this issue for the moment. The frequency of the second step should be the most important. First, write a simple program to test the consumption caused by this step of transmission.
The Code is as follows:
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 ');
Function bindBuffer (){
For (var I = 0; I <1000; I ++ ){
_ Gl. bindBuffer (_ gl. ARRAY_BUFFER, buffer );
_ Gl. bufferData (_ gl. ARRAY_BUFFER, new Float32Array (vertices), _ gl. STATIC_DRAW );
}
}
In this program, vertices is an array that stores vertices. 1000 vertices are randomly generated because each vertex has three coordinates: x, y, and z, therefore, an array of 3000 sizes, _ gl. the createBuffer command opens a cache for storing vertex data in the video memory, and then uses _ gl. bufferData transfers a copy of the generated vertex data from the memory to the video memory. Assume that an object with 1000 1000 vertices in a scenario, and each vertex is 3 float data with 32-bit and 4-byte data, computing is about 1000x1000x12 = 11 m data, profile is about to consume 15 ms of time, here may look at 15 ms is so time, but for a real-time program, to ensure the frame rate of 30 FPS, the time required for each frame should be controlled at around 30 ms. It takes only half of the time for data transmission, you need to know that the big data should be the drawing operations in the GPU and various processing operations in the CPU. You should be stingy with every step of the rendering process.
Therefore, we should try our best to reduce the transmission times of this step. In fact, we can transfer all the vertex data and texture data from the memory to the video memory when loading. This is now three. in js, the first time the vertex data of the object to be drawn (Geometry) is transmitted to the video memory, and the buffer is cached to geometry. _ webglVertexBuffer: The verticesNeedUpdate attribute of Geometry will be determined each time it is drawn. If no update is required, the current cache will be used directly. If verticesNeedUpate is true, the vertex data in Geometry is transmitted to geometry again. in _ webglVertexBuffer, we generally do not need to perform this operation on static objects. However, if a vertex changes frequently, such as a particle system using a vertex, in addition, the Mesh with the skeleton animation is used. Each frame of these objects changes its vertex, so each frame needs to verticesNeedUp Setting the date attribute to true tells renderer that I need to re-transmit data!
In fact, in the WebGL program, more will change the vertex position in vertex shader to complete particle effects and bone animation, although it is easier to expand if it is placed on the cpu side for computing, however, due to the limited computing capability of javascript, more operations with large computing volumes will be placed on the gpu side. In this case, you do not need to re-transmit the vertex data, so the above case is not practical in the actual program, and more will be updated to the texture and material cache.
The above case mainly describes a scenario for transmitting vertex data. In addition to vertex data, there is also a big part of texture, the memory occupied by a 1024*1024 R8G8B8A8 texture is as high as 4 MB.
The Code is as follows:
Var canvas = document. createElement ('canvas ');
Var _ gl = canvas. getContext ('experimental-webgl ');
Var texture = _ gl. createTexture ();
Var img = new Image;
Img. onload = function (){
Console. profile ('texture test ');
BindTexture ();
Console. profileEnd ('texture test ');
}
Img. src = 'test_tex.jpg ';
Function bindTexture (){
_ Gl. bindTexture (_ gl. TEXTURE_2D, texture );
_ Gl. texImage2D (_ gl. TEXTURE_2D, 0, _ gl. RGBA, _ gl. RGBA, _ gl. UNSIGNED_BYTE, img );
}
Here, we don't need to persist for 1000 times. It takes 30 ms to transmit 10241024 of the texture at a time, and 256256 of the texture at a time is about 2 ms, so three. in js, we try to transmit the texture only once at the beginning, and then if texture. if the needsUpdate attribute is not manually set to true, the texture that has been transmitted to the video memory will be directly used.
Cache to be updated
The preceding two cases describe why three. js needs to add such a needsUpdate attribute. Next, we will list several scenarios to know under which conditions the cache needs to be updated manually.
Asynchronous texture Loading
This is a small pitfall, because the front-end images are asynchronously loaded. If you have created an img, write texture directly. if needsUpdate = true, three. in js renderer, _ gl is used in this frame. texImage2D transfers empty texture data to the video memory, and then sets this flag to false. After that, the display data will not be updated when the image is loaded, therefore, texture must be written after loading the entire image in the onload event. needsUpdate = true
Video Texture
Most textures are directly loaded and transmitted once like the above case, but not for video textures, because the video is an image stream, the images to be displayed for each frame are different. Therefore, you must set needsUpdate to true for each frame to update the texture data in the video card.
Use render buffer
The render buffer is a special object. Generally, after a program is drawn in the entire scenario, it is flushed directly to the screen, however, if post processing or screen based xxx (for example, screen based ambient occlusion) is added, the scenario needs to be first drawn to a render buffer, which is actually a texture, it is only generated by drawing in the previous step, rather than being loaded from the disk. Three. js has a special texture object WebGLRenderTarget to initialize and save the renderbuffer. For this texture, you also need to set needsUpdate to true at each frame.
NeedsUpdate of Material
Material in three. in js, THREE is used. as described by Material, there is actually no data to be transmitted in the Material, but why is it necessary to create a needsUpdate? Here we also need to talk about the shader. The shader is a coloring tool, it provides the possibility to program the processing of vertices and pixels in the gpu. There is a shading term in the painting to represent the brightness and shade of the painting. The shading in the GPU is similar, the program calculates the light and shade of the light to express the material of the object. OK. Since the shader is a program running on the GPU, a compilation link operation is required like all programs, in WebGL, the shader program is compiled at runtime, which of course takes time. Therefore, it is best to run the shader program after one compilation. Therefore, during material initialization in three. js, the shader program is compiled and the program object obtained after the compilation link is cached. Generally, a material does not need to re-compile the entire shader. To adjust the material, you only need to modify the shader's uniform parameter. However, if the entire material is replaced, for example, replacing the original phong shader with a lambert shader, you need to set material. needsUpdate to true to re-compile it. However, this situation is rare. More often, it is a situation mentioned below.
Add and delete lights
This should be quite common in the scenario. three may have been used at the beginning. js users will all fall into this trap. After dynamically adding a light to the scene, they will find that the light does not work, but three is used. in the case of js built-in shader, such as phong and lambert, the source code in renderer will find three. js uses # define in the built-in shader code to set the number of lights in the scenario. The value of this # define is obtained by concatenating the shader string each time the material is updated. The Code is as follows:
The Code is as follows:
"# Define MAX_DIR_LIGHTS" + parameters. maxDirLights,
"# Define MAX_POINT_LIGHTS" + parameters. maxPointLights,
"# Define MAX_SPOT_LIGHTS" + parameters. maxSpotLights,
"# Define MAX_HEMI_LIGHTS" + parameters. maxHemiLights,
Indeed, this method can effectively reduce the use of gpu registers. If there is only one light, you can declare only the uniform variable required by one light, but the number of lights changes each time, in particular, you need to reassemble and compile the shader link when adding the material. At this time, you also need to reassemble the material of all materials. set needsUpdate to true;
Change texture
The texture change here is not about updating the texture data, but about using the texture of the original material, or not using the texture of the original material, if you do not manually update the material, the final result will be different from what you think. The cause of this problem is similar to that of adding the light above, this is also because a macro is added to the shader to determine whether a texture is used,
The Code is as follows:
Parameters. map? "# Define USE_MAP ":"",
Parameters. envMap? "# Define USE_ENVMAP ":"",
Parameters. lightMap? "# Define USE_LIGHTMAP ":"",
Parameters. bumpMap? "# Define USE_BUMPMAP ":"",
Parameters. normalMap? "# Define USE_NORMALMAP ":"",
Parameters. specularMap? "# Define USE_SPECULARMAP ":"",
Therefore, each time map, envMap, or lightMap, changes the true value, the material must be updated.
Changes to other vertex data
In fact, the above texture changes will also produce a problem, mainly because there is no texture during initialization, but then dynamically added to this environment, simply put the material. it is not enough to set needsUpdate to true. You also need to set geometry. uvsNeedsUpdate is set to true. Why is this problem still caused by three. js program optimization. When initializing geometry and material for the first time in renderer, if it is determined that there is no texture, although the data in the memory contains uv data for each vertex, three. javascript still does not copy the data to the video memory. The original intention should be to save some valuable video memory space, however, after adding a texture, geometry does not intelligently re-transmit the uv data for texture use. We must manually set uvsNeedsUpdate to inform it of uv update, it was a very long time before I began to solve this problem.
For the needUpdate attribute of several vertex data, see this issue.
Https://github.com/mrdoob/three.js/wiki/Updates
Last
Three. js optimization is good, but it brings about various pitfalls that may be trampled under various optimizations. The best way to do this is to check the source code, or go to github to mention issues.