📜 ⬆️ ⬇️

Own WebGL engine. Article number 1. Canvas

Through a series of articles I will try to disassemble the engine on the webgl.

The basic requirement is minimum data entry. After all, roughly speaking, the engine is a model created to simplify the task. The material is designed for beginners, for those who read the basics of webgl and wants to try to start working. Those like me.

The first. Task description on fingers

It is necessary to create classes of objects (primitives), which from a set of points. At the same time, the primitives should be independent of each other. Each primitive can be moved, rotated around the center or around an arbitrary point.
It is necessary to create a mechanism for describing these objects.
And finally, you need to create something like a map on which you can install our objects and on which you can move freely.
')
The second. Mechanism of the outline

The mechanism of painting, just a canvas and paint for the artist. What is the necessary minimum that the user of our engine must finally bring in so that he has a canvas ready? In my opinion, this is just a link to the DOM canvas. And then you can set the color and size.

var scene = new Scene("webglID"); scene.setBackgroundColor([0.1,0.5,0.6,0.2]); scene.setViewPort(300, 300); 


What could be easier if we consider that we only need the first line?
"WebglID" is the id of the canvas element in which the drawing will occur.
The implementation of this mechanism is also not a big deal, as long as there is no drawing.

 function Scene(canvasID) { this.backgroundColor = {red:1.0, green:1.0, blue:1.0, alpha:1.0}; this.canvas = document.getElementById(canvasID); this.getContext(); } Scene.prototype = { setViewPort: function(width,height){ this.gl.viewportWidth = width; this.gl.viewportHeight = height; }, setBackgroundColor: function(colorVec){ if (colorVec){ if (colorVec.length > 0) { this.backgroundColor.red = colorVec[0]; } if (colorVec.length > 1) { this.backgroundColor.green = colorVec[1]; } if (colorVec.length > 2) { this.backgroundColor.blue = colorVec[2]; } if (colorVec.alpha > 3) { this.backgroundColor.red = colorVec[3]; } } }, getContext:function(){ var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]; this.gl = null; for (var ii = 0; ii < names.length; ++ii) { try { this.gl = this.canvas.getContext(names[ii]); } catch(e) {} if (this.gl) { break; } } } } 


(The getContext method is taken from the article . (Just wrote before - this.gl = this.canvas.getContext ("webgl");)

Canvas is created, it remains to purchase brushes and paints

Before you add the ability to draw in our engine, you need to decide:
  1. We will draw by vertices or indices.
  2. What figures will our primitives consist of?



For the engine, I chose to draw by index, as it seems to me, it is obvious. A type of figure - TRIANGLES. It is less obvious here, but I will try to explain.

We will use one buffer for all objects and, accordingly, all vertices and indices. Further, if it is possible to use several buffers, the type of the figure will be located in the primitive object itself. (If you already have such an opportunity - please write in the comments.) At the same time, the objects should be independent from each other, so we still have a choice among - LINES, TRIANGLES and POINTS. I chose TRIANGLES as a figure filling in from the inside.

The drawing process itself will consist of two stages - we add the object (s) to the scene and draw the whole scene. In fact, in the future it will be 3 stages - the detection of objects that we need to draw, adding to the scene, and of drawing the whole scene.

 var vertex = [ -50,50,50, 50,50,50, 50,-50,50, -50,-50,50 ]; var indices = [0,1,3,1,2,3]; var obj = new botuObject(vertex,indices); scene.AddObject(obj); scene.draw(); 


botuObject is our first primitive. Not the most elegant, well, what is. It simply contains the vertices and indices that are passed to it. By and large, all primitives will contain vertices and indices, only other primitives will calculate these vertices themselves, during initialization. About them will be written in detail in subsequent articles.

Primitive implementation:

 function botuObject(vertex,indices){ this.vertex = vertex; this.indices = indices; this.vertex.size = 3; } 


Canvas final version:
 /*.  WebGl. canvasID - id  canvas  html */ function Scene(canvasID) { /*    */ this.backgroundColor = {red:1.0, green:1.0, blue:1.0, alpha:1.0}; /* */ this.canvas = document.getElementById(canvasID); /* webgl Context*/ this.getContext(); /*   */ this.indicBuffer = null; /* */ this.vecVertex = []; /* */ this.vecIndices = []; this.initProgram("vertexShader", "fragmentShader"); } Scene.prototype = { /* ,     */ clear: function(){ this.indicBuffer = null; this.vecVertex = []; this.vecIndices = []; }, /* WebGl Context,    */ getContext:function(){ var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]; this.gl = null; for (var ii = 0; ii < names.length; ++ii) { try { this.gl = this.canvas.getContext(names[ii]); } catch(e) {} if (this.gl) { break; } } }, /*   .        vertex,   2  ,         2-  */ initBuffers: function (vertex, indices) { /*  */ this.vertexBuffer = this.gl.createBuffer(); /* */ this.vertexBuffer.size = vertex.size; /*  */ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer); /*   - botuPosition*/ this.program.botuPositionAttr = this.gl.getAttribLocation(this.program, "botuPosition"); /* ,        - this.program.botuPositionAttr*/ this.gl.enableVertexAttribArray(this.program.botuPositionAttr); /*  ,    Float32,    */ this.gl.bufferData(this.gl.ARRAY_BUFFER,new Float32Array(vertex), this.gl.STATIC_DRAW); /*    this.program.botuPositionAttr,   - this.vertexBuffer.size   */ this.gl.vertexAttribPointer(this.program.botuPositionAttr,this.vertexBuffer.size,this.gl.FLOAT,false,0,0); /*   */ if(indices) { /*  */ this.indicBuffer = this.gl.createBuffer(); /* -   - */ this.indicBuffer.numberOfItems = indices.length; /*  */ this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.indicBuffer); /*  ,     Uint16,    indices*/ this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), this.gl.STATIC_DRAW); } }, /*  , vxShaderDom - id  script      , frShaderDom - id  script       */ initProgram: function (vxShaderDom, frShaderDom) { /* */ var vxShader = document.getElementById(vxShaderDom).textContent; /* */ var frShader = document.getElementById(frShaderDom).textContent; /*    this.gl  :   vxShader    - frShader*/ this.program = createProgram(this.gl,vxShader, frShader); /*     */ this.gl.useProgram(this.program); /*     - vxs    - frs     context*/ function createProgram(context, vxs, frs) { /* */ var prg = context.createProgram(); /*  */ var VertexShader = createShader(context, context.VERTEX_SHADER, vxs); /*  */ var FragmentShader = createShader(context, context.FRAGMENT_SHADER, frs); /*  */ context.attachShader(prg,VertexShader); /*  */ context.attachShader(prg,FragmentShader); /*    - context*/ context.linkProgram(prg); /*  */ if (!context.getProgramParameter(prg, context.LINK_STATUS)) { /*  */ alert(context.getProgramInfoLog(prg)); } /*  */ return prg; } /*    context   type,    - shader*/ function createShader(context,type,shader) { /*    - type*/ var sh = context.createShader(type); /*  - shader*/ context.shaderSource(sh, shader); /*  */ context.compileShader(sh); /*   */ if (!context.getShaderParameter(sh, context.COMPILE_STATUS)) { /*   */ alert(context.getShaderInfoLog(sh)); } //   return sh; } }, /*  - width   - height */ setViewPort: function(width,height){ this.gl.viewportWidth = width; this.gl.viewportHeight = height; }, /*     colorVec*/ setBackgroundColor: function(colorVec){ if (colorVec){ if (colorVec.length > 0) { /* */ this.backgroundColor.red = colorVec[0]; } if (colorVec.length > 1) { /*- */ this.backgroundColor.green = colorVec[1]; } if (colorVec.length > 2) { /*- */ this.backgroundColor.blue = colorVec[2]; } } }, /*  botuObj  */ AddObject: function(botuObj){ /*   */ this.vecVertex.size = botuObj.vertex.size; /*-    */ var next = Math.max(this.vecVertex.length / this.vecVertex.size,0); /*  */ this.vecVertex = this.vecVertex.concat(botuObj.vertex); /*  ,              */ this.vecIndices = this.vecIndices.concat(botuObj.indices.map(function(i){return i + next})); /* */ this.vecVertex.size = botuObj.vertex.size; }, /**/ draw: function () { /*  */ this.gl.viewport(0, 0, this.gl.viewportWidth, this.gl.viewportHeight); /*  */ this.gl.clearColor(this.backgroundColor.red,this.backgroundColor.green,this.backgroundColor.blue,this.backgroundColor.alpha); /*- */ this.gl.clear(this.gl.COLOR_BUFFER_BIT); /*         this.vecVertex    this.vecIndices*/ this.initBuffers(this.vecVertex, this.vecIndices); /*    - DEPTH_TEST*/ this.gl.enable(this.gl.DEPTH_TEST); /*   -  - TRIANGLES, -  - this.indicBuffer.numberOfItems*/ this.gl.drawElements(this.gl.TRIANGLES, this.indicBuffer.numberOfItems, this.gl.UNSIGNED_SHORT, 0); } } 


Reading someone else's code is the most ungrateful thing, except when it is a master code. This code does not pretend to this, so in brief about all the methods:



So, the first obvious “defect” of the engine is shaders. I have them installed as follows:

  <script type="x-shader" id="vertexShader"> attribute vec3 botuPosition; varying vec4 colorPos; void main(){ colorPos = abs(vec4(botuPosition.x, botuPosition.y, botuPosition.z,200.0) / 200.0); gl_Position = vec4(botuPosition,200); } </script> <script type="x-shader" id="fragmentShader"> precision highp float; varying vec4 colorPos; void main(){ gl_FragColor = colorPos; } </script> 


This is a temporary option. When adding texturing, the shaders will need to be corrected a little.

What's next


In the next article - the description of the "samopisny" matrix And also the first “normal” primitive is a cube.

Source: https://habr.com/ru/post/227201/


All Articles