作者:心叶
时间:2018-05-11 10:30

写在前面

阅读这篇文章的人应该都知道Canvas绘制二维位图是通过getContext('2d')获取类似画笔的东西在其中绘制,而这里是绘制三维位图,通过getContext('webgl')来获取这只神奇的笔。

由于本系列更多的是以个人以后复习或查阅为主要目的,因此阅读起来如果有没有说清楚的地方,请留言。

下面,我们开始吧!

绘图过程

canvas 2D绘制过程

页面添加canvas元素 -> 获取2D画笔 -> 设置好画笔等 -> 清空屏幕(如果需要) -> 绘制(相应API)

canvas WebGL绘制过程

页面添加canvas元素 -> 获取3D画笔 -> 设置好着色器等 -> 清空屏幕(如果需要) -> 绘制(相应API)

可以看出来,主要是【设置好着色器等】这一步和之前的绘制方法不一样,因此,我们先来说明一下,什么是着色器?

着色器

绘制三维图形的时候(以绘制一个点为例),你需要告诉他点的位置、尺寸和颜色,当你设置好这些以后就类似2D设置好画笔了,就可以绘制了,而这三个的设置就是设置着色器的过程。

着色器种类

着色器分为二类:顶点着色器和片段着色器,前者控制位置和尺寸,后者控制颜色。

着色器目前本篇就说这些,由于还没有实践的东西,说太多理解起来有点累。

绘制一个简单的点

下面,我们通过绘制一个简单的点为例来按照上面的过程来绘制,感受一下。

页面添加canvas元素

首先在页面中添加canvas标签:

<canvas id='webgl' width='400' height='400'></canvas>

获取3D画笔

获取3D画笔也2D的一样,只不过参数有点改变:

var canvas = document.getElementById('webgl');
var gl = canvas.getContext('webgl');

设置好着色器等

着色器的设置会有点麻烦,其实着色器就是二段字符串,先定义好,然后使其生效。

下面是大致过程:

定义着色器字符串 -> 创建着色器对象并绑定好对应的着色器字符串 -> 创建着色器程序并添加前面创建的着色器对象 -> 把着色器程序链接成一个完整的程序并使用他

说明:上面的过程看起来有点昏,是因为3D的绘制比较复杂,有很多新概念的东西,需要慢慢去适应。

下面看看具体代码:

【定义着色器字符串】
顶点着色器有二个固定的属性gl_Position和gl_PointSize,分别表示位置和尺寸:

<!-- 顶点着色器 -->
<script type='x-shader/x-vertex' id='vs-shader'>
    void main(){
        gl_Position=vec4(0.0,0.0,0.0,1.0);
        gl_PointSize=100.0;
    }
</script>

片段着色器有一个固定的属性gl_FragColor表示颜色:

<!-- 片段着色器 -->
<script type='x-shader/x-fragment' id='fs-shader'>
    void main(){
        gl_FragColor=vec4(1.0,1.0,0.0,1.0);
    }
</script>

这样,二段字符串就准备好了,也就是二个着色器字符串好了(为了方便定义在标签中,后续可以看见,他们就是二个字符串)。

【创建着色器对象并绑定好对应的着色器字符串】
具体在代码中备注,看代码:

//初始化着色器(上面第一步的其实就是这样在用,这里直接写字符串也可以,只不过上面的写法更方便编辑)
var vs_source = document.getElementById('vs-shader').innerHTML,
    fs_source = document.getElementById('fs-shader').innerHTML;

// 创建顶点着色器对象
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
// 绑定资源
gl.shaderSource(vertexShader, vs_source);
// 编译着色器
gl.compileShader(vertexShader);

// 创建片段着色器对象
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
// 绑定资源
gl.shaderSource(fragmentShader, fs_source);
// 编译着色器
gl.compileShader(fragmentShader);

【创建着色器程序并添加前面创建的着色器对象】
到这里,二个着色器就准备好了,可是需要程序使用,还需要变成着色器程序,看代码:

 // 创建一个着色器程序
var glProgram = gl.createProgram();

// 把前面创建的二个着色器对象添加到着色器程序中
gl.attachShader(glProgram, vertexShader);
gl.attachShader(glProgram, fragmentShader);

【把着色器程序链接成一个完整的程序并使用他】
此时,着色器程序也好了,我们需要指定使用他:

// 把着色器程序链接成一个完整的程序
gl.linkProgram(glProgram);

// 使用这个完整的程序
gl.useProgram(glProgram);

这样,着色器就设置好了,当然,这是最简单的着色器,具体还有很多操作,后续会说明,先理解这些基本的吧。

清空屏幕(如果需要)

// 设置清空颜色
gl.clearColor(0.5, 0.5, 0.5, 1.0);
// 用设置的颜色清空屏幕(参数代表的是清除颜色缓冲)
gl.clear(gl.COLOR_BUFFER_BIT);

绘制(相应API)

最后一步绘制点:

gl.drawArrays(gl.POINTS, 0, 1);

drawArrays就类似2D中的绘图方法,第一个参数代表绘制一个点,第二个参数代表从第一个点开始,第三个参数代码绘制个数一个。

你可能好奇他从哪里知道点的位置的?是的,你开始不是定义了顶点着色器吗?就是他。

完整代码

由于是第一个例子,给出上面的完整例子代码:

<!DOCTYPE html>
<html>

<head>
    <!-- 顶点着色器 -->
    <script type='x-shader/x-vertex' id='shader-vs'>
        void main(){
            gl_Position=vec4(0.0,0.0,0.0,1.0);
            gl_PointSize=100.0;
        }
    </script>
    <!-- 片段着色器 -->
    <script type='x-shader/x-fragment' id='shader-fs'>
        void main(){
            gl_FragColor=vec4(1.0,1.0,0.0,1.0);
        }
    </script>
</head>

<body>
    <canvas id='webgl' width='400' height='400'></canvas>
</body>
<script>

    // 1.获取webgl
    var canvas = document.getElementById('webgl');
    var gl = canvas.getContext('webgl');

    // 2.清空屏幕
    gl.clearColor(0.5, 0.5, 0.5, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);

    // 3.初始化着色器
    var vs_source = document.getElementById('shader-vs').innerHTML,
        fs_source = document.getElementById('shader-fs').innerHTML;

    // 创建顶点着色器对象
    var vertexShader = gl.createShader(gl.VERTEX_SHADER);
    // 绑定资源
    gl.shaderSource(vertexShader, vs_source);
    // 编译着色器
    gl.compileShader(vertexShader);

    var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fragmentShader, fs_source);
    gl.compileShader(fragmentShader);

    // 创建一个着色器程序
    var glProgram = gl.createProgram();

    // 把前面创建的二个着色器对象添加到着色器程序中
    gl.attachShader(glProgram, vertexShader);
    gl.attachShader(glProgram, fragmentShader);

    // 把着色器程序链接成一个完整的程序
    gl.linkProgram(glProgram);

    // 使用这个完整的程序
    gl.useProgram(glProgram);

    // 4.绘制一个点
    gl.drawArrays(gl.POINTS, 0, 1);

</script>

</html>

总结

一个简单的例子开始的入门就结束了,一定会有很多没有没有说清楚的,可以在下面留言,经过这个,你需要大概知道webgl的整体思想,绘图过程,最好可以感受这里例子的代码就可以了。

03-05 16:26