This is the 8th day of my participation in the August More Text Challenge. For details, see:August is more challenging

WebGL Lesson 22:2D Stretching, Rotation and TranslationCopy the code

primers

We’ve spent a lot of time talking about how matrices work here. What we are going to do in this class is to do the actual situation in 2D.

Write a js file that generates the model and go out separately

It’s too hard to read everything in one file. So we’re going to create a new models_point-js to store the code for the generation aspect of the point. The point suffix is added because everything generated in this file is drawn using gl.points. When drawing triangles in the back, create a new file models_tri.js to do the triangle stuff.

In the same directory as the page, create a new models_point-js file and fill in the following code:

function GetQuad_Point(xcenter, ycenter, length) { let xMin = xcenter - length / 2; let yMin = ycenter - length / 2; let step = length / 20; // let res = []; let xidx = 0; let yidx = 0; for (xidx = 0; xidx ! = 20; xidx++) { for (yidx = 0; yidx ! = 20; yidx++) { res.push( xMin + xidx * step, yMin + yidx * step, ); } } return res; }Copy the code

The above code is nothing but a bunch of dots.

I’ll give you the overall code first

<! doctype html> <html> <head> <style> canvas { border: 1px solid #000000; } </style> <script type="text/javascript" src="models_point.js"></script> </head> <body> <p> <b>scale value:</b> <input Id = "scalex" type = "range" min = "- 2" Max = "2" value = "1" step = "0.1" the oninput = "updatefunc ()" / > < b id = "scalevaluex" > 0 < / b > <input id="scaley" type="range" min="-2" Max ="2" value="1" step="0.1" oninput=" updateFunc ()" /> <b id="scalevaluey">0</b> </p> <p> <b>offset value:</b> <input id="offsetx" type="range" min="-1" max="1" value="0" Step ="0.1" oninput=" updateFunc ()" /> <b id=" offsetValuex ">0</b> Value = "0" step = "0.1" the oninput = "updatefunc ()" / > < b id = "offsetvaluey" > 0 < / b > < / p > < p > < b > rotate value: < / b > < input Id ="rotate" type="range" min="0" Max ="6.28" value="0" step="0.01" onInput ="updatefunc()" /> <b id="rotatevalue">0</b> </p> <canvas id="point" style="width:300px; height:300px"> </canvas> <script id="vertex_shader" type="myshader"> // Vertex Shader precision mediump int; precision mediump float; uniform float u_x_scale; uniform float u_x_offset; uniform float u_y_scale; uniform float u_y_offset; uniform float u_rotate; attribute vec2 a_PointVertex; Void main() {gl_Position = vec4(a_PointVertex, 0.0, 1.0); gl_Position.x *= u_x_scale; gl_Position.y *= u_y_scale; float rx = gl_Position.x * cos(u_rotate) - gl_Position.y * sin(u_rotate); float ry = gl_Position.x * sin(u_rotate) + gl_Position.y * cos(u_rotate); gl_Position.x = rx + u_x_offset; gl_Position.y = ry + u_y_offset; Gl_PointSize = 3.0; } </script> <script id="fragment_shader" type="myshader"> // Fragment shader precision mediump int; precision mediump float; Void main() {gl_FragColor = vec4(0, 0, 0, 1.0); } </script> <script type="text/javascript"> var pointCanvas = document.getElementById('point'); Var gl = pointCanvas. GetContext ('webgl', {preserveDrawingBuffer: true}); Var pointData = GetQuad_Point(0, 0, 1); var pointCount = pointData.length / 2; var pointArray = new Float32Array(pointData); var buffer_id; buffer_id = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer_id); gl.bufferData(gl.ARRAY_BUFFER, pointArray, gl.STATIC_DRAW); // var vertex_shader_code = document.getElementById('vertex_shader').textContent; console.log(vertex_shader_code); var vertex_shader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertex_shader, vertex_shader_code); gl.compileShader(vertex_shader); // var fragment_shader_code = document.getElementById('fragment_shader').textContent; var fragment_shader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragment_shader, fragment_shader_code); gl.compileShader(fragment_shader); // var program = gl.createProgram(); gl.attachShader(program, vertex_shader); gl.attachShader(program, fragment_shader); gl.linkProgram(program); gl.useProgram(program); // var a_PointVertex = gl.getAttribLocation(program, 'a_PointVertex'); gl.vertexAttribPointer(a_PointVertex, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(a_PointVertex); // </script> <script> var u_x_offset_loc = gl.getUniformLocation(program, "u_x_offset"); var u_x_scale_loc = gl.getUniformLocation(program, "u_x_scale"); var u_y_offset_loc = gl.getUniformLocation(program, "u_y_offset"); var u_y_scale_loc = gl.getUniformLocation(program, "u_y_scale"); var u_rotate_loc = gl.getUniformLocation(program, "u_rotate"); var scaleDomX = document.getElementById("scalex"); var scaleValueDomX = document.getElementById("scalevaluex"); var scaleDomY = document.getElementById("scaley"); var scaleValueDomY = document.getElementById("scalevaluey"); var offsetDomX = document.getElementById("offsetx"); var offsetValueDomX = document.getElementById("offsetvaluex"); var offsetDomY = document.getElementById("offsety"); var offsetValueDomY = document.getElementById("offsetvaluey"); var rotateDom = document.getElementById("rotate"); var rotateValueDom = document.getElementById("rotatevalue"); function updatefunc() { gl.uniform1f(u_x_scale_loc, parseFloat(scaleDomX.value)); gl.uniform1f(u_y_scale_loc, parseFloat(scaleDomY.value)); gl.uniform1f(u_x_offset_loc, parseFloat(offsetDomX.value)); gl.uniform1f(u_y_offset_loc, parseFloat(offsetDomY.value)); gl.uniform1f(u_rotate_loc, parseFloat(rotateDom.value)); scaleValueDomX.innerText = scaleDomX.value; offsetValueDomX.innerText = offsetDomX.value; scaleValueDomY.innerText = scaleDomY.value; offsetValueDomY.innerText = offsetDomY.value; rotateValueDom.innerText = rotateDom.value; gl.enable(gl.CULL_FACE); gl.enable(gl.DEPTH_TEST); gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.drawArrays(gl.POINTS, 0, pointCount); } updatefunc(); </script> </script> </body> </html>Copy the code

Notice the sentence above:

<script type="text/javascript" src="models_point.js"></script>

Refer to the models_point.js file we just wrote.

So now it works perfectly, stretching, rotating, translating.

The matrix transformation of Vertex_SHAder is carried out

We know that stretch, rotation, translation, these three operations can be rolled into a matrix.

That way, we don’t have to do all these uniform variables.

All you need is a uniform variable of type matrix.

        uniform float u_x_scale;
        uniform float u_x_offset;
        uniform float u_y_scale;
        uniform float u_y_offset;
        uniform float u_rotate;
Copy the code

You can replace all of these variables with a matrix variable, and you’re done

For example:

        uniform mat3 u_all;
Copy the code

Our uniform variable u_all up here, of type MAT3, which is matrix 3, which is a 3 by 3 matrix.

Why we need a 3 by 3 matrix, we’ve already explained it when we talked about displacement, so if you don’t understand, go ahead and look for it.

Let’s start with the following thought:

  1. Change the (x,y) of the point we pass in from two dimensions to three dimensions, and write the third dimension 1. –>(x,y,1)
  2. Will u_all * (x, y, 1)
  3. The results I get, I just take the first two

The vertex_shader code looks like this:

<script id="vertex_shader" type="myshader"> // Vertex Shader precision mediump int; precision mediump float; uniform mat3 u_all; attribute vec2 a_PointVertex; Void main() {vec3 coord = u_all * vec3(a_PointVertex, 1.0); Gl_Position = vec4(coord.x, coord.y, 0.0, 1.0); Gl_PointSize = 3.0; } </scriptCopy the code

Let’s see if it’s too comfortable, and it’s missing a bunch of stuff.

Matrix transformation of JS

The uniform passed in becomes a MAT3, and naturally, the external JS code should be modified accordingly.

Let’s say that the tensile coefficient is a b, the radians of rotation is alpha, and the displacement is a b.

So the final matrix:


[ a c o s ( Alpha. ) b s i n ( Alpha. ) A a s i n ( Alpha. ) b c o s ( Alpha. ) B 0 0 1 ] \ begin {bmatrix} a * cos (alpha) & – b * sin (alpha) & a \ \ a * sin (alpha) & b * cos (alpha) & b \ \ & 0 0 & 1 \ end {bmatrix}

Based on this matrix, define a function:

function genMat3ForGL(a, b, alpha, A, B) {
    let mat3 = [
        a * Math.cos(alpha), a * Math.sin(alpha), 0,
        -b * Math.sin(alpha), b * Math.cos(alpha), 0,
        A, B, 1,
    ];
    return new Float32Array(mat3);
}
Copy the code

Notice, when we constructed this matrix up here, we filled it in column by column, which is another example of thinking of the matrix as an array of vectors.

We previously passed in the uniform variable as follows:

    gl.uniform1f(u_x_scale_loc, parseFloat(scaleDomX.value));
    gl.uniform1f(u_y_scale_loc, parseFloat(scaleDomY.value));

    gl.uniform1f(u_x_offset_loc, parseFloat(offsetDomX.value));
    gl.uniform1f(u_y_offset_loc, parseFloat(offsetDomY.value));

    gl.uniform1f(u_rotate_loc, parseFloat(rotateDom.value));

Copy the code

Five variables need to be passed in, so after matrixization:

    gl.uniformMatrix3fv(u_all_loc, false, genMat3ForGL(scaleDomX.value,
        scaleDomY.value, rotateDom.value, offsetDomX.value, offsetDomY.value
    ));
Copy the code

All you have to do is use the newly defined function above, knead the variables into a matrix, and you’re done.

Similarly, in js code, we also need to preacquire the position of this mat3 uniform variable:

    var u_all_loc = gl.getUniformLocation(program, "u_all");
Copy the code

After the transformation of the overall code, you are not given, please make your own code to repair: the final running effect is as follows:

conclusion

In this lesson, I’m going to actually do the stretching, the rotation, the translation, the three operations, and I’m going to replace it with a matrix, and I’m going to actually do it in code.

As you can see, when you replace it with a matrix, it’s a lot less code, it’s a lot cleaner.




At the end of the text, questions are answeredCopy the code

Little Yaya said: There is one thing that is not derived in the text, that is, the whole matrix of stretch -> rotation -> translation.

  • A: Last time we talked about the composite matrix of stretching and rotation:


[ a c o s ( Alpha. ) b s i n ( Alpha. ) a s i n ( Alpha. ) b c o s ( Alpha. ) ] \ begin {bmatrix} a * cos (alpha) & – b * sin (alpha) \ \ a * sin (alpha) & b * cos (alpha) \ end {bmatrix}

Let’s do the same thing. Let’s first raise this matrix by one dimension:


[ a c o s ( Alpha. ) b s i n ( Alpha. ) 0 a s i n ( Alpha. ) b c o s ( Alpha. ) 0 0 0 1 ] \ begin {bmatrix} a * cos (alpha) & – b * sin (alpha) & 0 \ \ a * sin (alpha) & b * cos (alpha) & 0 \ \ & 0 0 & 1 \ end {bmatrix}

Why did it rise to this:

because

[a ∗ cos (alpha) – b ∗ sin (alpha) 0 a ∗ sin (alpha) ∗ b cos (alpha) 0001] \ begin {bmatrix} a * cos (alpha) & – b * sin (alpha) & 0 \ \ a * sin (alpha) & b * cos (alpha) & 0 \ \ 0 & 0 and 1 \ end {bmatrix} ⎣ ⎢ ⎡ a ∗ cos (alpha) a ∗ sin (alpha) 0 – b ∗ sin (alpha) ∗ b cos (alpha) 0001 ⎦ ⎥ ⎤ * (xy1) \ left (\ begin {array} {cc} \ \ \ \ y 1 x {array} \ \ end right) ⎝ The result of ⎛xy1⎠ ⎞ is exactly what we want.

This matrix is then left multiplied by the displacement matrix:

[10A01B001]\begin{bmatrix} 1&0&a \\ 0&1&b \\ 0&0&1 \end{bmatrix}⎣⎢, 100010AB1 nation ⎥⎤ * [a ∗ cos (alpha) – b ∗ sin (alpha) 0 a ∗ sin (alpha) ∗ b cos (alpha) 0001] \ begin {bmatrix} a * cos (alpha) & – b * sin (alpha) & 0 \ \ a * sin (alpha) & b * cos (alpha) & 0 \ \ 0 & 0 and 1 {bmatrix} \ end ⎣ ⎢ ⎡ a ∗ cos (alpha) a ∗ sin (alpha) 0 – b ∗ sin (alpha) ∗ b cos (alpha) 0001 ⎦ ⎥ ⎤

=


[ a c o s ( Alpha. ) b s i n ( Alpha. ) A a s i n ( Alpha. ) b c o s ( Alpha. ) B 0 0 1 ] \ begin {bmatrix} a * cos (alpha) & – b * sin (alpha) & a \ \ a * sin (alpha) & b * cos (alpha) & b \ \ & 0 0 & 1 \ end {bmatrix}