WebGL入门---交互绘制点

上一节只是实现了一个静态点的绘制,但是真正的 WebGL 应用总是需要通过网页和用户进行交互,进而改变画面的。所以接下来,我们要实现一个简单交互程序:在鼠标点击过的位置绘制一个点,而且这个点的颜色是随机的。
这要求我们有能力通过 JavaScript 往着色器程序中传入顶点位置和颜色数据,从而改变点的位置和颜色。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>从一个点开始</title><meta charset="UTF-8">
</head>
<body>
    <canvas id="canvas"></canvas>
    <script type="shader-source" id="vertexShader">
          // 设置浮点数精度为中等精度
        precision mediump float;
        // 接收点在canvas坐标系上的坐标
        attribute vec2 a_Position;
        //接受canvas的宽高尺寸
        attribute vec2 a_Screen_Size;
        void main(){
            // start 将屏幕坐标系转化为裁剪坐标系
            //上面这句代码用来将浏览器窗口坐标转换成裁剪坐标,之后通过透视除法,除以 w 值(此处为 1 )转变成设备坐标(NDC坐标系)。
            //这个算法首先将(x,y) 转化到【0, 1】区间,再将 【0, 1】之间的值乘以 2 转化到 【0, 2】区间,之后再减去 1 ,转化到 【-1, 1】之        
            //间的值,即 NDC 坐标
            vec2 position = (a_Position/a_Screen_Size)*2.0 - 1.0;
            position = position*vec2(1.0,-1.0);
            // 声明顶点的位置
            gl_Position = vec4(position, 0.0, 1.0);
            // 声明待绘制点的大小
            gl_PointSize = 10.0;
        }
    </script>
    <script type="shader-source" id="fragmentShader">
        // 设置浮点数精度为中等精度
        precision mediump float;
        //接收javascript 传过来的颜色值
        uniform vec4 u_Color;
        void main(){
            vec4 color = u_Color / vec4(255, 255, 255, 1);
            gl_FragColor = color; 
            
        }
    </script>

    <script>
         var random = Math.random;
        function randomColor() {
            return {
                r: random() * 255,
                g: random() * 255,
                b: random() * 255,
                a: random() * 1
            };
        }
        function $$(str) {
          if (!str) return null;
          if (str.startsWith('#')) {
            return document.querySelector(str);
          }
          let result = document.querySelectorAll(str);
          if (result.length == 1) {
            return result[0];
          }
          return result;
        }
        function getCanvas(id) {
          return $$(id);
        }
        function getContext(canvas) {
          return canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
        }
        /**
         *
         *
         * @param {*} gl,webgl绘图环境
         * @param {*} type,着色器类型
         * @param {*} scriptId,着色器源码scrpit标签id
         * @returns 返回着色器对象
         */
         function createShaderFromScript(gl, type, scriptId) {
             
             let sourceScript = $$('#' + scriptId);
             if (!sourceScript) {
               return null;
             }
             return createShader(gl, type, sourceScript.innerHTML);
           }
        function createShader(gl, type, source) {
          let shader = gl.createShader(type);
          gl.shaderSource(shader, source);
          gl.compileShader(shader);
          //检测是否编译正常。
          let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
          if (success) {
            return shader;
          }
          console.error(gl.getShaderInfoLog(shader));
          gl.deleteShader(shader);
        }
        function createProgram(gl, vertexShader, fragmentShader) {
          let program = gl.createProgram();
          vertexShader && gl.attachShader(program, vertexShader);
          fragmentShader && gl.attachShader(program, fragmentShader);
          gl.linkProgram(program);
          let result = gl.getProgramParameter(program, gl.LINK_STATUS);
          if(result){
              return program
          }
          let errorLog = gl.getProgramInfoLog(program);
          gl.deleteProgram(program);
          throw errorLog;
        }
    </script>
    <script>
                // var canvas = document.querySelector('#canvas');
        var canvas = getCanvas('#canvas');
        //获取webgl绘图环境
        var gl = getContext(canvas);
         //创建顶点着色器
         var vertexShader = createShaderFromScript(gl, gl.VERTEX_SHADER,'vertexShader');
        //创建片元着色器
        var fragmentShader = createShaderFromScript(gl, gl.FRAGMENT_SHADER,'fragmentShader');
        var program = createProgram(gl ,vertexShader, fragmentShader);
        // 使用刚创建好的着色器程序。
        gl.useProgram(program);
        //找到顶点着色器中的变量a_Position
        var a_Position = gl.getAttribLocation(program, 'a_Position');
        //找到顶点着色器中的变量a_Screen_Size
        var a_Screen_Size = gl.getAttribLocation(program, 'a_Screen_Size');
        //找到片元着色器中的变量u_Color
        var u_Color = gl.getUniformLocation(program, 'u_Color');
        //为顶点着色器中的 a_Screen_Size 传递 canvas 的宽高信息
        gl.vertexAttrib2f(a_Screen_Size, canvas.width, canvas.height);
        //存储点击位置的数组。
        var points = [];
        canvas.addEventListener('click', e => {
            var x = e.pageX;
            var y = e.pageY;
            var color = randomColor();
            points.push({ x: x, y: y, color: color })
            gl.clearColor(0, 0, 0, 1.0);
            //用上一步设置的清空画布颜色清空画布。
                gl.clear(gl.COLOR_BUFFER_BIT);
            for (let i = 0; i < points.length; i++) {
                var color = points[i].color;
                //为片元着色器中的 u_Color 传递随机颜色
                gl.uniform4f(u_Color, color.r, color.g, color.b, color.a);
                //为顶点着色器中的 a_Position 传递顶点坐标。
                gl.vertexAttrib2f(a_Position, points[i].x, points[i].y);
                //绘制点
                gl.drawArrays(gl.POINTS, 0, 1);
            }
        })
        // 设置清屏颜色
        gl.clearColor(0, 0, 0, 1.0);
        // 用上一步设置的清空画布颜色清空画布。
        gl.clear(gl.COLOR_BUFFER_BIT);
    </script>
</body>
</html>
posted @ 2022-02-25 15:09  自在一方  阅读(176)  评论(0编辑  收藏  举报