WebGL第九课:搞一搞 vertex shader(3) 拉伸和平移

824 阅读3分钟
本文标题:WebGL第九课:搞一搞 vertex shader(3) 拉伸和平移

首先给出拉伸平移的定义,没有定义,我们就没法得出正确的逻辑:

  • 拉伸: 每一个点的x坐标或者y坐标乘以一个相同的数。这个数我们称之为拉伸系数
讲解:  
要点1: 注意是每一个点,并不是某些点,或者某几个点。  
要点2:拉伸有两个方向,`x方向``y方向`。
    对于`x方向`来说,每一个点的`x坐标`必须乘以同一个数,才能叫拉伸。
    `y方向`的拉伸同理。`x和y方向`,两个方向本身可以是`独立的`。也就是说我们会有两个`拉伸系数`。
要点3:系数可以是正的也可以是负的。
  • 平移: 每一个点的x坐标或者y坐标一个相同的数。这个数我们称之为平移系数
讲解:  
要点1: 注意是每一个点,并不是某些点,或者某几个点。  
要点2:平移也是两个方向,`x方向``y方向`,对于`x方向`来说,每一个点的`x坐标`必须加上同一个数,才能叫平移。
  `y方向`的平移同理。`x和y方向`,两个方向本身可以是`独立的`。也就是说我们也是有两个`平移系数`。
要点3:系数可以是正的也可以是负的。

根据和上面对于拉伸平移的定义,我们更新我们整体的代码如下:

<!doctype html>
<html>

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

</head>

<body>
    <p>
        <b>scale value:</b>
        <input id="scalex" type="range" min="-1" max="1" value="0" step="0.1" oninput="updatefunc()" />
        <b id="scalevaluex">0</b>
        <input id="scaley" type="range" min="-1" max="1" value="0" 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>
        <input id="offsety" type="range" min="-1" max="1" value="0" step="0.1" oninput="updatefunc()" />
        <b id="offsetvaluey">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;

        attribute vec2 a_PointVertex;
        
        void main() {
          gl_Position = vec4(a_PointVertex, 0.0, 1.0);
          gl_Position.x *= u_x_scale;
          gl_Position.x += u_x_offset;
          gl_Position.y *= u_y_scale;
          gl_Position.y += 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.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 = -5; idx <= 5; idx++) {
            for (var idy = -5; idy <= 5; idy++) {
                pointCount++;
                pointData.push(idx / 5);
                pointData.push(idy / 5);
            }
        }
        //
        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 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 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");

        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));

            scaleValueDomX.innerText = scaleDomX.value;
            offsetValueDomX.innerText = offsetDomX.value;
            scaleValueDomY.innerText = scaleDomY.value;
            offsetValueDomY.innerText = offsetDomY.value;

            gl.clearColor(0, 0, 0, 0);
            gl.clear(gl.COLOR_BUFFER_BIT);
            gl.drawArrays(gl.POINTS, 0, pointCount);
        }
    </script>
    </script>
</body>

</html>

在浏览器里直接打开这个页面,效果如下:

9-1.png
大家可以拖动四个slider,来看看都有什么效果。 大概的逻辑就是:


我们一共用了四个uniform变量来分别接收  
x方向的拉伸值:`u_x_scale`  
y方向的拉伸值:`u_y_scale`  
x方向的平移值:`u_x_offset`  
y方向的平移值:`u_y_offset`    
并且在`html`里用了四个`slider`来控制这四个变量。  

以上东西都没有涉及到新知识点,这里我们还是将重点放在 vertex shader 里, 如下:

    <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;

        attribute vec2 a_PointVertex;
        
        void main() {
          gl_Position = vec4(a_PointVertex, 0.0, 1.0);
          gl_Position.x *= u_x_scale;
          gl_Position.x += u_x_offset;
          gl_Position.y *= u_y_scale;
          gl_Position.y += u_y_offset;
          
          gl_PointSize = 3.0;
        }
    </script>

在上面的代码里,我们发现一个东西,对于两个方向的拉伸和平移来说,我们都把
计算拉伸的代码放在前面,计算平移的代码放在后面
这是随意这么写的吗,还是说只能这么写,拉伸难道只能放在平移前面???

关于拉伸和平移的先后顺序

事实上,这取决于你的业务需求,一般的业务需求,和人们的一般感受,我们先拉伸再平移,是合乎正常人的想象的。
如果你先平移再拉伸的话,会有出其不意的效果发生,不信你可以自己将上面代码中的顺序变一下。自己做做实验。

对于百分之九十九点九九九的场景里,先拉伸再平移,相信我,就这么办!!!



正文结束,下面是答疑
小能能说:这次课还挺简单的,期待下次课