WebGL入门---编写第一个 WebGL 程序

WebGL 应用包含两个要素:JavaScript程序和着色器程序。本节我们通过绘制一个点来演示这个过程,麻雀虽小,但五脏俱全。使用 WebGL 绘制一个点虽然简单,但是它仍需要 JavaScript 程序和着色器程序共同完成。
我们的目标是绘制一个在屏幕中心,大小为 10,颜色是红色的点。

1、准备着色器源码

我们从着色器程序开始入手,先用GLSL编写顶点着色器和片元着色器。

  • 顶点着色器

顶点着色器的主要任务是告诉 GPU 在裁剪坐标系的原点(也就是屏幕中心)画一个大小为 10 的点。

void main(){
    //声明顶点位置
    gl_Position = vec4(0.0, 0.0, 0.0, 1.0); // 分号不能少,要不然编译有问题
    //声明待绘制的点的大小。
    gl_PointSize = 10.0; // 分号不能少,要不然编译有问题
}
  • 片元着色器

顶点着色器中的数据经过图元装配和光栅化之后,来到了片元着色器,在本例中,片元着色器的任务是通知 GPU 将光栅化后的像素渲染成红色,所以片元着色器要对内置变量 gl_FragColor (代表像素要填充的颜色)进行赋值。

void main(){
    //设置像素的填充颜色为红色。
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);  // 分号不能少,要不然编译有问题
}

2、准备 HTML 文件

目前的html页面代码如下

<!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">
        void main(){
            // 声明顶点的位置
            gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
            // 声明待绘制点的大小
            gl_PointSize = 10.0;
        }
    </script>
    <script type="shader-source" id="fragmentShader">
        void main(){
            //设置像素的填充颜色为红色。
            gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
            
        }
    </script>
</body>
</html>

3、JavaScript 程序

获取canvas对象下的WEBGL环境

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

完整的html代码如下

<!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">
        void main(){
            // 声明顶点的位置
            gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
            // 声明待绘制点的大小
            gl_PointSize = 10.0;
        }
    </script>
    <script type="shader-source" id="fragmentShader">
        void main(){
            //设置像素的填充颜色为红色。
            gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
            
        }
    </script>
    <script>
        // 获取canvas元素
        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)
        // 有时候一个 WebGL 应用包含多个 program,所以在使用某个 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>
</body>
</html>

重复代码的封装

<!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">
        void main(){
            // 声明顶点的位置
            gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
            // 声明待绘制点的大小
            gl_PointSize = 10.0;
        }
    </script>
    <script type="shader-source" id="fragmentShader">
        void main(){
            //设置像素的填充颜色为红色。
            gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
            
        }
    </script>

    <script>
         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>
        //获取canvas
        var canvas = getCanvas(id);

        //获取webgl绘图环境
        var gl = getWebGLContext(canvas);

        //创建顶点着色器
        var vertexShader = createShaderFromScript(gl, gl.VERTEX_SHADER,'vertexShader');
        //创建片元着色器
        var fragmentShader = createShaderFromScript(gl, gl.FRAGMENT_SHADER,'fragmentShader');

        //创建着色器程序
        var program = createProgram(gl ,vertexShader, fragmentShader);
        //告诉 WebGL 运行哪个着色器程序
        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>
</body>
</html>
posted @ 2022-01-11 15:44  自在一方  阅读(314)  评论(0编辑  收藏  举报