zoukankan      html  css  js  c++  java
  • WebGL学习笔记一

    WebGL是什么?

      WebGL 是一组基于 JavaScript 语言的图形规范,浏览器厂商按照这组规范进行实现,为 Web 开发者提供一套3D图形相关的 API

      这些 API 能够让 Web 开发者使用 JavaScript 语言直接和显卡(GPU)进行通信。

    WebGL 应用由 JavaScript 程序和着色器程序构成。

    WebGL的编程开发者需要针对 CPU 和 GPU 进行编程,CPU 部分是 JavaScript 程序,GPU 部分是着色器程序。

    GLSL——OpenGL Shading Language(OpenGL 着色语言),用来在 OpenGL 编写着色器程序的语言

     术语:

      图元:WebGL 能够绘制的基本图形元素,包含三种:线段三角形

      片元:即像素,像素着色阶段是在片元着色器中

      剪裁坐标系:是顶点着色器中的 gl_Position 内置变量接收到的坐标所在的坐标系

      设备坐标系(NDC 坐标系):是裁剪坐标系各个分量对 w 分量相除得到的坐标系,特点是 x、y、z 坐标分量的取值范围都在 【-1,1】之间,可以将它理解为边长为 2 的正方体,坐标系原点在正方体中心

    立体模型:

      由一个一个的组成,GPU 将这些点用三角形图元绘制成一个个的微小平面,这些平面之间互相连接就构成了立体模型

    GPU 渲染管线的处理过程:

    1.顶点着色器阶段,利用 GPU 的并行计算优势对顶点逐个进行坐标变换。
    2.图元装配阶段,将顶点按照图元类型组装成图形。
    3.光栅化阶段,光栅化阶段将图形用不包含颜色信息的像素填充。
    4.片元着色器阶段,该阶段为像素着色,并最终显示在屏幕上。

    顶点着色器

    void main(){
        //声明顶点位置
        gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
        //声明待绘制的点的大小。
        gl_PointSize = 10.0;
    }

    片元着色器——顶点着色器中的数据经过图元装配和光栅化之后,来到了片元着色器

    void main(){
        //设置像素的填充颜色为红色。
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); 
    }

     说明:

      gl_Position、gl_PointSize、gl_FragColor 是 GLSL 的内置属性

      gl_Position:

        顶点的裁剪坐标系坐标,包含 X, Y, Z,W 四个坐标分量

        顶点着色器接收到这个坐标之后,对它进行透视除法,即将各个分量同时除以 W,转换成 NDC 坐标

        NDC 坐标每个分量的取值范围都在【-1, 1】之间,GPU 获取这个属性值作为顶点的最终位置进行绘制

      gl_PointSize:

        绘制到屏幕的点的大小,只有在绘制图元是的时候才会生效

      gl_FragColor:

        片元(像素)颜色,包含 R, G, B, A 四个颜色分量,且每个分量的取值范围在【0,1】之间,GPU 获取这个值作为像素的最终颜色进行着色

        常规颜色的【0,255】取值范围,转换公式为: (R值/255,G值/255,B值/255,A值/1)

      vec4:

        包含四个浮点元素的容器类型,vec 是 vector(向量)。

        vec4 代表包含 4 个浮点数的向量。 延伸:vec2包含2个浮点数的容器

    eg:

      (1)在页面上画一个点

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
        <head>
            <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
            <title>WebGL</title>
        </head>
        <body style="background: #000">
            <canvas id="canvas"></canvas>
        </body>
        <!-- 顶点着色器源码 -->
        <script type="shader-source" id="vertexShader">
         void main(){
              //声明顶点位置
              gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
              //声明要绘制的点的大小。
              gl_PointSize = 5.0;
          }
        </script>
        <!-- 片元着色器源码 -->
        <script type="shader-source" id="fragmentShader">
         void main(){
             //设置像素颜色为红色
            gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); 
        }
        </script>
        <script type="text/javascript">
            var canvas = document.querySelector('#canvas');
            var gl = canvas.getContext('webgl') || canvas.getContext("experimental-webgl");
            // 获取顶点着色器源码
            var vertexShaderSource = document.querySelector('#vertexShader').innerHTML;
            // 创建顶点着色器对象
            var vertexShader = gl.createShader(gl.VERTEX_SHADER);
            // 将源码分配给顶点着色器对象
            gl.shaderSource(vertexShader, vertexShaderSource);
            // 编译顶点着色器程序
            gl.compileShader(vertexShader);
    
            // 获取片元着色器源码
            var fragmentShaderSource = document.querySelector('#fragmentShader').innerHTML;
            // 创建片元着色器程序
            var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
            // 将源码分配给片元着色器对象
            gl.shaderSource(fragmentShader, fragmentShaderSource);
            // 编译片元着色器
            gl.compileShader(fragmentShader);
    
            //创建着色器程序
            var program = gl.createProgram();
            //将顶点着色器挂载在着色器程序上。
            gl.attachShader(program, vertexShader); 
            //将片元着色器挂载在着色器程序上。
            gl.attachShader(program, fragmentShader);
            //链接着色器程序
            gl.linkProgram(program)
            // 使用刚创建好的着色器程序。
            gl.useProgram(program);
    
            //设置清空画布颜色为黑色。
            gl.clearColor(0.0, 0.0, 0.0, 1.0);
            //用上一步设置的清空画布颜色清空画布。
            gl.clear(gl.COLOR_BUFFER_BIT);
            //绘制点。
            gl.drawArrays(gl.POINTS, 0, 1);
        </script>
    </html>

      说明:

        gl.drawArrays(mode, first, count);

          mode,代表图元类型。

          first,代表从第几个点开始绘制即顶点的起始位置

          count,代表绘制的点的数量。

     运行效果

      (2)在鼠标点击过的位置绘制一个随机颜色的点

      增加为着色器中变量进行赋值部分

       绘制过程:

        声明一个数组变量 points,存储点击位置的坐标。

        绑定 canvas 的点击事件。

        触发点击操作时,把点击坐标添加到数组 points 中。

        遍历每个点执行 drawArrays(gl.Points, 0, 1) 绘制操作

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
        <head>
            <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
            <title>WebGL</title>
        </head>
        <body style="background: #000">
            <canvas id="canvas"></canvas>
        </body>
        <!-- 顶点着色器源码 -->
        <script type="shader-source" id="vertexShader">
            //设置浮点数精度为中等精度
            precision mediump float;
            //接收点在 canvas 坐标系上的坐标 (x, y)
            attribute vec2 a_Position;
            //接收 canvas 的宽高尺寸
            attribute vec2 a_Screen_Size;
             void main(){
                  vec2 position = (a_Position / a_Screen_Size) * 2.0 - 1.0;
                   position = position * vec2(1.0, -1.0);
                   gl_Position = vec4(position, 0, 1);
                   //end 将屏幕坐标系转化为裁剪坐标(裁剪坐标系)
                   //声明要绘制的点的大小。
                   gl_PointSize = 5.0;
              }
        </script>
    
        <!-- 片元着色器源码 -->
        <script type="shader-source" id="fragmentShader">
            //设置浮点数精度为中等精度
            precision mediump float;
            //接收 JavaScript 传过来的颜色值(RGBA)。
            uniform vec4 u_Color;
             void main(){
                 //设置像素颜色为红色
                vec4 color = u_Color / vec4(255, 255, 255, 1);
                   gl_FragColor = color; 
            }
        </script>
        <script type="text/javascript" src="helper.js"></script>
        <script type="text/javascript">
            var canvas = getCanvas('#canvas');
            resizeCanvas(canvas);
            var gl = getContext(canvas);
            //创建顶点着色器
            var vertexShader = createShaderFromScript(gl, gl.VERTEX_SHADER,'vertexShader');
            //创建片元着色器
            var fragmentShader = createShaderFromScript(gl, gl.FRAGMENT_SHADER,'fragmentShader');
            //创建着色器程序
            var program = createSimpleProgram(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 })
                   render(gl);
             })
             //绘制函数
            function render(gl){
                //清除屏幕
                gl.clear(gl.COLOR_BUFFER_BIT);
                for(let i = 0; i<points.length;i++){
                    let color = points[i].color;
                    //向片元着色器传递颜色信息
                    gl.uniform4f(u_Color, color.r, color.g, color.b, color.a);
                    //向顶点着色器传递坐标信息。
                    gl.vertexAttrib2f(a_Position, points[i].x, points[i].y);
                    //绘制点。
                    gl.drawArrays(gl.POINTS, 0, 1);
                }
            }
    
            //设置清空画布颜色为黑色。
            gl.clearColor(0.0, 0.0, 0.0, 1.0);
            //绘制
            render(gl);
    
        </script>
    </html>
    helper.js
    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 resizeCanvas(canvas, width, height) {
      if (canvas.width !== width) {
        canvas.width = width ? width : window.innerWidth;
      }
      if (canvas.height !== height) {
        canvas.height = height ? height : window.innerHeight;
      }
    }
    
    function getContext(canvas) {
      return canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
    }
    function createSimpleProgram(gl, vertexShader, fragmentShader) {
      if (!vertexShader || !fragmentShader) {
        console.warn('着色器不能为空');
        return;
      }
      let program = gl.createProgram();
      gl.attachShader(program, vertexShader);
      gl.attachShader(program, fragmentShader);
      gl.linkProgram(program);
      let success = gl.getProgramParameter(program, gl.LINK_STATUS);
      if (success) {
        return program;
      }
      console.error(gl.getProgramInfoLog(program));
      gl.deleteProgram(program);
    }
    
    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 createShaderFromScript(gl, type, scriptId) {
      let sourceScript = $$('#' + scriptId);
      if (!sourceScript) {
        return null;
      }
      return createShader(gl, type, sourceScript.innerHTML);
    }
    View Code

      说明:

        向着色器中传递数据

          getAttribLocation:找到着色器中的 attribute 变量地址

          getUniformLocation:找到着色器中的 uniform 变量地址

          vertexAttrib2f:给 attribute 变量传递两个浮点数

          uniform4f:给uniform变量传递四个浮点数

    相关文章

    WebGL学习笔记二

  • 相关阅读:
    route add提示: "SIOCADDRT: No such process
    linux下route命令--说的比较清楚!
    linux route命令的使用详解
    Linux命令学习手册-route命令
    workqueue --最清晰的讲解
    udhcpc命令
    Mutex, semaphore, spinlock的深度解析
    dev_alloc_skb(len+16) skb_reserve(skb,2) skb_put(skb,len)
    skb_reserve(skb,2)中的2的意义
    option和 usb-serial驱动基本区别
  • 原文地址:https://www.cnblogs.com/baby123/p/11309814.html
Copyright © 2011-2022 走看看