WebGL第八课:搞一搞 vertex shader(2)

864 阅读2分钟
本文标题:WebGL第八课:搞一搞 vertex shader(2)

上次课,我们一开始画了一个周期的sin函数,然后画了两个周期的sin函数。
不过,我们是直接改的 vertex shader 代码。
如果我们在网页里,能通过一个滑竿(slider)来拖动改变,然后反应到图形里就好了。

根据上面的需求,我们先在网页里加一个,slider input 控件,
整个代码改动如下:


<!doctype html>
<html>

<head>
    <style>
        canvas {
            border: 1px solid #000000;
        }
    </style>

</head>

<body>
    <input id="slider" type="range" min="0" max="100" value="50" step="1" οnchange="sliderfunc()" />

    <canvas id="point" style="width:300px; height:300px">
    </canvas>
    <script id="vertex_shader" type="myshader">
        // Vertex Shader
        precision mediump int;
        precision mediump float;
        
        attribute vec2 a_PointVertex;
        
        void main() {
          gl_Position = vec4(a_PointVertex, 0.0, 1.0);
          gl_Position.y = sin(gl_Position.x * 3.14);
          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.0, 0.0, 1.0);
        }
        
    </script>
    <script type="text/javascript">
        var pointCanvas = document.getElementById('point'); // 我们的纸
        var gl = pointCanvas.getContext('webgl', { preserveDrawingBuffer: true }); // 我们的笔
        var pointCount = 0;
        var pointData = [];
        for (var idx = -500; idx <= 500; idx++) {
            pointCount++;
            pointData.push(idx / 500);
            pointData.push(0);
        }
        //
        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);
        //
        // gl.drawArrays(gl.POINTS, 0, pointCount);
    </script>
    <script>
        var sliderDom = document.getElementById("slider");
        function sliderfunc() {
        }
        sliderDom.addEventListener("change", sliderfunc);
    </script>
    </script>
</body>

</html>

我们在前面,加了一个 slider input 控件,在最后将这个控件绑定了一个事件,也就是
这个控件的值改变的时候,我们可以在 sliderfunc 函数中做一些事情。

var sliderDom = document.getElementById("slider");
function sliderfunc() {
    // 应该在这里来改变图形的绘制
}
sliderDom.addEventListener("change", sliderfunc);
重点新概念:uniform 变量
  • uniform 变量是可以写在 vertex shaderfragment shader 中的变量
  • 随时都可以通过glapi,改变uniform变量的值

我们改动我们的 vertex shader, 如下:

    <script id="vertex_shader" type="myshader">
        // Vertex Shader
        precision mediump int;
        precision mediump float;
        uniform float u_x_offset;
        
        attribute vec2 a_PointVertex;
        
        void main() {
          gl_Position = vec4(a_PointVertex, 0.0, 1.0);
          gl_Position.y = sin(gl_Position.x * 3.14 * u_x_offset);
          gl_PointSize = 3.0;
        }
    </script>
  • 注意,声明了一个 float 类型的 uniform 变量 u_x_offset;
  • 注意,sin 的传入参数,变成了 gl_Position.x * 3.14 * u_x_offset
  • 也就是说,我们可以把 u_x_offset 当做一个调节因子.

光这些还没用,我们要在 sliderfunc 里写一些代码,来从外面改变 vertex shader 中的 u_x_offset变量。

最后的代码改动如下:

    <script>
        var u_x_offset_loc = gl.getUniformLocation(program, "u_x_offset");

        var sliderDom = document.getElementById("slider");
        function sliderfunc() {
            gl.uniform1f(u_x_offset_loc, parseFloat(sliderDom.value) / 10);
            gl.clearColor(0, 0, 0, 0);
            gl.clear(gl.COLOR_BUFFER_BIT);
            gl.drawArrays(gl.POINTS, 0, pointCount);
        }
        sliderDom.addEventListener("change", sliderfunc);
    </script>

详解:

  • gl.getUniformLocation , 用于得到 vertex shader 中的 uniform 变量的位置
  • gl.uniform1f 改变 vertex shader中的 uniform变量的值
  • gl.clear(gl.COLOR_BUFFER_BIT) 清空整个图像 ,否则会重复绘制,越来越乱
  • gl.drawArrays(gl.POINTS, 0, pointCount) 重新绘制

效果如下:

8-1.png

8-2.png

随着我们拖动滑竿,不同周期的sin图像会被绘制到页面上。




正文结束,下面是答疑
小鸭鸭说:既然可以在 js 代码改变 uniform 变量的值,那么我搞个定时器,是不是就是动画了?
  • 答:完全正确✔️!!!!!下课~