Introduction
The article was created to show the basic actions required to display 3d in a modern browser using WebGL technology. To achieve the goal, we consider the problem of building several lines in three-dimensional space.
Scheme of work:
- Get the WebGL context from the canvas.
- Load the shader program. Namely:
- create a program of shaders;
- we get the source code separately for the vertex and fragment shaders;
- compile shader codes;
- we join the program;
- activate the program.
- We install two matrices: model-view and projection.
- Place, fill, activate vertex data buffers.
- We draw.
1. WebGL context
WebGL context can be obtained from the canvas DOM element by calling the getContext method (“experimental-webgl”). It should be noted that the Khronos Group recommends (https://www.khronos.org/webgl/wiki/FAQ) to get the WebGL context using the following method:
')
var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]; gl = null; for (var ii = 0; ii < names.length; ++ii) { try { gl = canvas.getContext(names[ii]); } catch(e) {} if (gl) { break; } }
Upon successful receipt of the context, the gl object has methods whose names are very similar to the OpenGL ES functions. For example, the clear function (COLOR_BUFFER_BIT) for WebGL would be gl.clear (gl.COLOR_BUFFER_BIT), which is very convenient. But keep in mind that not all WebGL functions have the same syntax as OpenGL ES 2.0 functions.
2. Shaders
A shader program is an integral part of building images using WebGL. It is through it that the position and color of each vertex of our lines is given. Our task uses two shaders: vertex and fragment. When constructing lines in three-dimensional space, the vertex shader is responsible for the position of the vertices in space, based on the values ​​of the species matrix and the perspective projection matrix. The fragment shader is used to calculate the color of our lines.
Vertex shader
attribute vec3 aVertexPosition; attribute vec4 aVertexColor; uniform mat4 mvMatrix; uniform mat4 prMatrix; varying vec4 color; void main(void) { gl_Position = prMatrix * mvMatrix * vec4 ( aVertexPosition, 1.0 ); color = aVertexColor; }
Fragment Shader
#ifdef GL_ES precision highp float; #endif varying vec4 color; void main(void) { gl_FragColor = color; }
What is defined after “uniform” is common to all vertices. Here it is transformation matrices: specific and perspective. What is defined after the “attribute” is used in the calculation of each vertex. Here is the position of the vertex and its color. After “varying” we define a variable that will be transferred from the vertex to the fragment shader. The result of calculating the position is assigned to the variable gl_Position, the colors - gl_FragColor.
3. Model-specific matrix and perspective projection matrix
Both matrices have a size of 4x4 and are used to calculate the display of three-dimensional objects on a two-dimensional plane. Their difference is that the species matrix determines how objects will look to the observer, for example, when their position changes, and the projection matrix initially defines the projection method.
In our program, the values ​​of the projection matrix are set when the gluPerspective function is called at the initialization stage, later this matrix does not change its values. The gluPerspective function is not standard, we defined it ourselves. Her arguments are: fovy, aspect, zNear, zFar. fovy - vertical viewing angle area in degrees; aspect - the ratio of the width of the viewing area to the height; zNear - distance to the near clipping plane (all that is closer is not drawn); zFar - distance to the far clipping plane (all that is further is not drawn).
To set the values ​​of the model-specific matrix, you can use several approaches. For example, to create and use the function gluLookAt (camX, camY, camZ, tarX, tarY, tarZ, upX, upY, upZ) is an analogue of the function for OpenGL, which takes as its arguments the coordinates of the camera position, the coordinates of the camera target, and the up-vector of the camera. Another way is to create and use glTranslate, glRotate, glScale functions that shift, rotate, scale relative to the observer (camera). For the primary positioning of the camera, gluLookAt can be used, and for subsequent transformations glTranslate, glRotate, glScale can be used. Anyway, these functions only change the values ​​of the same model-specific matrix. For the convenience of calculating matrices, you can use the sylvester.js library, which we will do.
Now that we have found a way to change the values ​​of both matrices, consider their transfer to the shaders program. In our vertex shader for the model-specific matrix, we use the variable “mvMatrix”. To transfer the matrix values ​​to this variable, we first need to get its index in the program. To do this, use the function loc = gl.getUniformLocation (shaderProgram, name), which is standard. As it is easy to guess, the first argument is a variable pointing to the shader program, which is obtained in the second stage, and the argument “name” is the name of the variable to which we want to transfer the value, in our case name = “mvMatrix”. Now, after receiving the index, we use the function gl.uniformMatrix4fv (loc, false, new Float32Array (mat.flatten ())) to transfer the value of the mat. Similarly, we obtain an index and set the value for the projection matrix. It should be remembered that the view matrix in the shader program must be updated whenever its values ​​change, so that they take effect.
4. Data Buffers
Using buffers in WebGL is required. The position of each point and its color will be stored in two buffers. Consider a piece of code that does all the work for a buffer that stores the coordinates of the points between which we draw lines.
vPosBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vPosBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verticies), gl.DYNAMIC_DRAW); gl.vertexAttribPointer(vPosLoc, 3, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(vPosLoc);
Here verticies is an array of coordinates of points of lines. The coordinates are 6 pieces each, the first 3 of which are the x-, y-, z-coordinate of the beginning of the line, the next, respectively, end. vPosLoc is the index of the “aVertexPosition” attribute in the shader program. Because In our program, the indexes were explicitly set using gl.bindAttribLocation (shaderProgram, loc, shadersAttrib) at the stage of assembling a shader program, then you do not need to receive them again. If this were not, then you should get the index using the command "vPosLoc = getAttribLocation (shaderProgram," aVertexPosition ")". Similar actions are carried out with the second buffer, the data will differ (instead of verticies an array of colors) and the index in the shader program (instead of vPosLoc).
5. Draw
Cleaning the color buffer or, more simply, setting the background using standard commands.
gl.clearColor(0, 0, 0, 1); gl.clear(gl.COLOR_BUFFER_BIT);
Now do the drawing
gl.drawArrays(gl.LINES, drawArrayIndexStart, drawArrayLength);
The first argument to this function says that we’ll draw exactly the lines, the second is the index in the buffer from which to start drawing, drawArrayLength is the number of elements to draw. Because we buffer the vertex coordinates from the verticies array, then
drawArrayLength = verticies.length / 3;
If we have changed straight lines, then perform steps 4, 5 for redrawing. If we have changed the position of the camera, then perform step 3 and step 5.
Conclusion
The task of building straight lines is not taken from the ceiling. There is a program that solves a system of differential equations and builds the result in 3d using OpenGL. It was decided to transfer the program to php and display the result using WebGL. To solve the problem of mapping in the three-dimensional space of lines, modern engines from the list (http://ru.wikipedia.org/wiki/WebGL) were studied: WebGLU, GLGE, C3DL, Copperlicht, SpiderGL and SceneJS. For this, an interface was created that allows universalizing the communication of the main program with third-party engines. Results have been achieved with WebGLU, C3DL. In others, either there is no simple way to build a line, or it is not optimal. In one of them, the line function is documented, but on the project's forum they made it clear that they would not be able to use it, and they suggested that it be drawn with polygons.
Unfortunately, using C3DL has not yet been able to optimize the process, which has led to a low fps value. When working with WebGLU, an error was made, which also affected the fps value. This forced to write your own engine, which is now used. In no case do I want to blame third-party engines, they are designed for a wider range of tasks than just drawing lines.
A few words about browsers. Tested on Firefox 4 beta 8, Chrome 8 with –enable-webgl. On this task, Firefox showed fps above Chrome 1.5-2 times. When upgrading to Chrome beta 9, the figures did not change. The fps numbers have not changed even when upgrading Firefox beta 8 to beta 9, except that there were more incomprehensible errors in the console and the scene using WebGLU was incorrectly displayed.
Links to the working version
Bibliography