WebGL学习系列-第一个程序

Posted 那个天真的人

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WebGL学习系列-第一个程序相关的知识,希望对你有一定的参考价值。

前言

本篇学习第一个WebGL程序——画一个点,通过此程序来理解WebGL程序的结构,这是所有后续知识的开端。

画一个点

先看一下效果图:

为了画这么一个点,在WebGL可不太简单,它会涉及到WebGL上下文以及着色器的概念,不要着急,咱们慢慢来理解。

WebGL上下文

学过Canvas的同学应该都知道,想要在浏览器中使用Canvas画图,需要先取得一个上下文,就像创建一个场景一样,有了场景才可以绘制。在WebGL中,想要进行绘制,一样需要先获取一个上下文,而且跟Canvas 2D开发上下文的获取非常的类似。

1、首先,要有一个canvas dom元素

<canvas id="webgl" width="400" height="400">
    您的浏览器不支持canvas,建议使用Chrome浏览器
</canvas>

2、获取上一步得到的 canvas dom元素,然后获取WebGL上下文

var canvas = document.getElementById('webgl');
// 获取WebGL上下文
function createWebGLContext(canvas)
    var names = ["experimental-webgl", "webgl" , "webkit-3d", "moz-webgl"];
    var webglContext = null;
    for (var i = 0; i < names.length; i++) 
        try 
            webglContext = canvas.getContext(names[i]);
            if(webglContext)
                break;
            
         catch(e) 

    
    return webglContext;

考虑到兼容性,所以我们尝试多种获取WebGL上下文的方式,只要任何一种获取成功即可返回WebGL上下文。

背景清除

学习WebGL开发的时候,有个非常重要的概念就是清除重绘。打个比方,我们想要在界面上画两个矩形,先画第一个,然后隔5秒后再画第二个,最终界面上显示2个矩形。那么正确的做法是先绘制第一个矩形,然后使用一个5秒的定时器,5秒后清空之前绘制的所有内容,然后绘制两个矩形。说白了,就是你所看到的东西是一次性绘制出来的,而不能先绘制一点,隔一会再绘制一点,每一次绘制都要进行整个画面的重绘。而在重绘的时候,一般我们都会设置一个重绘的默认背景颜色,设置一次即可。

// 这里指定默认背景色为黑色,不透明
context.clearColor(0.0, 0.0, 0.0, 1.0);

设置完默认背景色之后,在每一次绘制之前,我们手动清除绘制区域,然后重绘整个画面。

// 清空
context.clear(context.COLOR_BUFFER_BIT);

注意到这里我们使用 context.COLOR_BUFFER_BIT,其实,我们每一次绘制的时候,可能要画非常多的图形,不是说我们画一个图形就立马在浏览器上显示的,那样很可能出现闪屏,所以我们绘制其实是绘制在称为颜色缓冲区的一块内存空间来的,等全部绘制好之后,再一次性刷新到浏览器展现,这样才不会闪屏。除了COLOR_BUFFER_BIT(颜色缓冲区),以后我们还会接触context.DEPTH_BUFFER_BIT(深度缓冲区),后续篇章再解释。

绘制

有了上下文,知道了绘制的方法,接下来我们就要绘制了,本篇我们要绘制一个点,为了绘制一个点,我们要有绘制点的api,要知道点的位置,大小和颜色。

我们使用 context.drawArrays(context.POINTS, 0, 1); 来绘制一个点,drawArrays可以用于绘制点、线段、扇形等各类形状,参数简要介绍如下:

绘制基本图形:
drawArrays(mode , first  , count)   

mode:  绘制的形状,用于决定点是怎么连接成图形的
first: 顶点开始索引
count: 使用多少个顶点进行绘制

此api能够绘制的基本图形如下所示:

有了绘制的api还不够,我们发现没有地方指定点的位置、大小、颜色之类的属性,这对于新学习的朋友一定感到很迷惑,到现在还没有看到可以设置点属性的地方,这也是WebGL比较麻烦,但也是非常精华的一部分,这一块实际上是由着色器来完成的。

先来看一张图:

在WebGL中,有两类着色器,分别为顶点着色器和片元着色器,它们的作用上图已经表述得很清楚了,着色器其实就是一段代码,然后会被底层不断的调用,从而获取绘制所需要的点的一些信息。为了使用着色器,需要创建一个program对象,program对象内部再绑定顶点着色器和片元着色器对象,而着色器对象又是需要我们创建的,创建后指定代码片段字符串即可,最后在context中,使用这个program即可,drawArrays接口内部会自行跟program中的着色器通信对接。

再来看一张顶点着色器的示意图:

再对应到代码中就很容易理解了,片元着色器的使用流程也是一样的。

本示例中我们的顶点着色器代码片段如下:

// 顶点着色器代码(决定顶点在哪里、大小、颜色)
var VSHADER_SOURCE = 
  'void main() \\n' +
  '  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\\n' + // 设置顶点的位置
  '  gl_PointSize = 10.0;\\n' +                    // 设置顶点的大小
  '\\n';

注意,着色器的代码片段是以字符串形式存在 的,而且像其他语言一样有个main函数,顶点着色器有几个内置变量,目前我们使用了gl_Position 和 gl_PointSize ,分别表示顶点的位置和大小,而且我们在这里固化了变量的值,以后再来研究怎么实现动态变量,现在你要知道,当我们执行drawArrays的时候,底层会执行顶点着色器的代码,获取点的位置和大小。

片元着色器的代码如下:

var FSHADER_SOURCE =
      'void main() \\n' +
      '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\\n' + // 设置顶点的颜色
      '\\n';`````````

在片元着色器中,我们使用了内部属性 gl_FragColor,用于表示像素的颜色值,此代码片段在真正执行时,对于每一个像素,都会被调用一次,所以称之为片元着色器也挺符合的。

小结

经过上面的分析,我们发现在WebGL中绘制一个点确实不太容易,但是当理清了各个涉及到的知识点之后,画其他东西原理都差不多的,最主要还是要理解着色器的概念,这只是第一篇,往后随着不断学习,不断加深对着色器的理解。

源码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>第一个WebGL程序-画一个点</title>
  </head>
  <body onload="main()">
    <canvas id="webgl" width="400" height="400">
        您的浏览器不支持canvas,建议使用Chrome浏览器
    </canvas>
    <script>    
    // 主程序入口
    function main()
        var canvas = document.getElementById('webgl');
        var context = createWebGLContext(canvas);
        var program = createProgram(context , VSHADER_SOURCE ,FSHADER_SOURCE);
        context.program = program;
        context.useProgram(program);

        // 每一次重绘时的背景色
        context.clearColor(0.0, 0.0, 0.0, 1.0);
        // 清除 <canvas>
        context.clear(context.COLOR_BUFFER_BIT);
        // 画一个点
        context.drawArrays(context.POINTS, 0, 1);
    

    // 获取WebGL上下文
    function createWebGLContext(canvas)
        var names = ["experimental-webgl", "webgl" , "webkit-3d", "mozwebgl"];
        var webglContext = null;
        for (var i = 0; i < names.length; i++) 
            try 
                webglContext = canvas.getContext(names[i]);
                if(webglContext)
                    break;
                
             catch(e) 

        
        return webglContext;
    
    // 创建一个program(相当于着色器的上下文)
    function createProgram(context, vshader, fshader)
        var vertexShader = loadShader(context, context.VERTEX_SHADER,vshader);
        var fragmentShader = loadShader(context,context.FRAGMENT_SHADER,fshader);
        var program = context.createProgram();
        context.attachShader(program, vertexShader);
        context.attachShader(program, fragmentShader);

        context.linkProgram(program);
        return program;
       
    function loadShader(context, type, source)
        var shader = context.createShader(type);
        context.shaderSource(shader, source);
        context.compileShader(shader);
        return shader;
    

    // 顶点着色器代码(决定顶在哪里,大小)
    var VSHADER_SOURCE = 
      'void main() \\n' +
      '  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\\n' + // 设置顶点的位置
      '  gl_PointSize = 10.0;\\n' +                    // 设置顶点的大小
      '\\n';

    // 片元着色器代码(给像素上色)
    var FSHADER_SOURCE =
      'void main() \\n' +
      '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\\n' + // 设置顶点的颜色
      '\\n';
    </script>
  </body>
</html>

源码下载

点击下载源码(画一个点)

以上是关于WebGL学习系列-第一个程序的主要内容,如果未能解决你的问题,请参考以下文章

WebGL学习系列-基础矩阵变换

WebGL学习系列-基础矩阵变换

WebGL学习系列-基本图形变换

WebGL学习系列-WebGL简介

第1236期探索基于 WebGL 的动画与交互

WebGPU学习系列目录