This is the fourth day of my participation in the Novembermore Challenge.The final text challenge in 2021

WebGL Lesson 32: Simple 2D LightingCopy the code

Helpful hints

This article is the 32nd in the WebGL Curriculum column, and it is highly recommended to start with the previous one. Because we spent a lot of time talking about vectors and matrix operations. These basics will influence your thinking.

The code of this lesson can be directly jumped to:Lesson 32 Code

primers

Try to cover some of the basics in 2D, because these concepts are common to BOTH 2D and 3D. For example, the concept of lighting.

Adding a little light to a rendered image adds texture to the effect. That’s one of the main problems in modern graphics, is to make light as realistic as possible.

Of course, we’re just going to use the general simplified algorithm here.

Here, for example, it’s like a searchlight that lights up wherever it hits, with a soft divergence effect:

This article will tell you how to achieve the above effect.

To review, how does WebGL display images

    1. Upload the image to GL
    1. Set the uv of the vertex
    1. Sample in fragment_shader

The second point I want to focus on here is the setting of uv at the vertex.

Again, let’s talk about what our vertex data looks like:

        this.data = [
            // The first triangle
            -1, -1.0.0.// Lower left corner dot
            1, -1.1.0.// Lower right corner dot
            1.1.1.1.// Dot in the upper right corner
            // The second triangle
            1.1.1.1.// Dot in the upper right corner
            -1.1.0.1.// top left point
            -1, -1.0.0.// Lower left corner dot
        ];

Copy the code

We can see it’s made up of two triangles, six points. Each vertex contains four pieces of data, respectively

  • x
  • y
  • u
  • v

For example, the first lower-left dot:

            -1, -1.0.0.// Lower left corner dot
Copy the code

Negative 1, negative 1 means that this point is the bottom left corner of the screen. (0,0) means that this point should be anchored to the lower left corner of the picture.

Then write out the coordinates and UV of these points in turn, which is the above code.

Somebody asked why six dots, not four dots. Aren’t some of these points duplicated?

That’s a good question, because that’s the way we’re going to draw it, you have to draw it like this, and then you have to draw it like this, triangle by triangle.

Of course, there are other ways to draw triangles, and we’re going to use this one for the sake of illustration, and it doesn’t hurt.

Pull the image and pass it to GL

This was covered in the previous article on texturing, see lesson 26 for details.

The process of drawing triangles

This process is very important because it helps us understand the texture and lighting.

First, the first triangle, the first three lines of the data code above, is the first three points.

So when you draw it, if you determine the position, you can determine that this triangle is the lower right half of the triangle

If the positions of the three points are fixed, the colors of the three points can be sampled in the picture according to the UV information of the three points. If the anchoring is ok, the final display of the three points is as follows:

The question is, of course, the UV of the three points can sample the correct color in the image, so where does the color in the middle of the three points come from?

The answer is interpolation:

Fragment_shader interpolates the region in between, calculates the UV, and then samples.

This is important because it is useful when calculating light.

How about light?

Let’s start with a very simple algorithm:

Set a UNIFORM variable, UNIFORM VEC2 u_lightPos

This variable represents the position of the light source, and our algorithm is very simple,


l i g h t = 1 d i s t a n c e ( p o s . u _ l i g h t P o s ) light = 1 – distance(pos, u\_lightPos)

In other words, the closer you are to this light source, the brighter it gets, and the farther you go, the darker it gets, and beyond 1, it’s just dark.

Implemented in shader

The first question is, should the above code be written to vertex_shader or fragment_shader?

To do an experiment, let’s write it in vertex_shader:

vertex_shader:

    <script id="vertex_shader" type="myshader">
        // Vertex Shader
        precision mediump int;
        precision mediump float;
        
        uniform mat3 u_all;
        uniform vec2 u_lightPos;         // The position of the light source

        attribute vec2 a_PointVertex; // Vertex coordinates
        attribute vec2 a_PointUV; / / vertex UV

        varying vec2 uv;
        varying float light;

        void main() {
          vec3 coord = u_all * vec3(a_PointVertex, 1.0);
          gl_Position = vec4(coord.x, coord.y, 0.0.1.0);
          uv = a_PointUV;
          uv.y = 1.0 - uv.y;
          light = 1.0 - distance(coord.xy, u_lightPos);
        }
    </script>

Copy the code

fragment_shader:

    <script id="fragment_shader" type="myshader">
        // Fragment shader
        precision mediump int;
        precision mediump float;

        uniform sampler2D u_funny_cat; // What is the cat
        varying vec2 uv;
        varying float light;

        void main() {
            vec4 sample_color = texture2D(u_funny_cat, uv);
            gl_FragColor = vec4(sample_color.xyz * light, 1.0);
        }
    </script>

Copy the code

Result a run, as shown in figure:

All black. Why is that?

If we do not set uniform variables, this variable will default to 0, that is, all dimensions will be 0.

That is, the default light source position is (0,0)(0,0)(0,0).

So the coordinates of the six points in my data are all 2\ SQRT 22 away from (0,0)(0,0)(0,0)

Light =(1−2)<0light =(1 -\ SQRT 2)<0light=(1−2)<0.

(1−2)(1- SQRT 2)(1−2)(1−2)

For each triangle, light is calculated and passed to fragment_shader with varying. Fragment_shader receives the same three light values, so no matter how it interpolates, the middle portion will be (1−2)(1-\ SQRT 2)(1−2).

In other words, this procedure should be evaluated in fragment_shader.

We pass the position of the vertices to fragment_shader, which is then calculated using the formula:

    <script id="vertex_shader" type="myshader">
        // Vertex Shader
        precision mediump int;
        precision mediump float;
        
        uniform mat3 u_all;
        attribute vec2 a_PointVertex; // Vertex coordinates
        attribute vec2 a_PointUV; // Vertex coordinates

        varying vec2 uv;
        varying vec2 pos;

        void main() {
          vec3 coord = u_all * vec3(a_PointVertex, 1.0);
          gl_Position = vec4(coord.x, coord.y, 0.0.1.0);
          uv = a_PointUV;
          uv.y = 1.0 - uv.y;
          pos = coord.xy;
        }
    </script>
    <script id="fragment_shader" type="myshader">
        // Fragment shader
        precision mediump int;
        precision mediump float;

        uniform sampler2D u_funny_cat; // What is the cat
        uniform vec2 u_lightPos;         // The position of the light source

        varying vec2 uv;
        varying vec2 pos;

        void main() {
            vec4 sample_color = texture2D(u_funny_cat, uv);
            float light = 1.0 - distance(pos, u_lightPos);
            gl_FragColor = vec4(sample_color.xyz * light, 1.0);
        }
    </script>

Copy the code

The result is as follows:

Make some atmosphere

A white light hit the past, no meaning, we full atmosphere:

What about this one? It’s easy.

We’re going to sample the picture, the G component and the B component, and we’re going to set them all to 0. And then just keep the R component.

The code is as follows:


        void main() {
            vec4 sample_color = texture2D(u_funny_cat, uv);
            float light = 1.0 - distance(pos, u_lightPos);
            sample_color.x  = sample_color.x * light;
            sample_color.y  = 0.0;
            sample_color.z  = 0.0;
            
            gl_FragColor = vec4(sample_color.xyz, 1.0);
        }

Copy the code

move

Again, write a timer in the console to move the position of the light source to make the result more interesting:

// In the browser directly type:
    setInterval(() = > {
        let x = (Math.random() - 0.5) * 2;
        let y = (Math.random() - 0.5) * 2;
        gl.uniform2f(u_lightPos, x, y);
        gl_draw();
    }, 50);
Copy the code

The effect is as follows:

With the end of the text, here is the q&ACopy the code

Small n can say: the above interpolation there, really need to think about it.

  • This process needs to be mastered, and naturally many questions will be answered.