WebGL 绘制一个点
通过WebGL可以渲染出来各种各样酷炫的3D效果,但是考虑到WebGL复杂性,为了大家降低初学难度,下面的代码仅仅在Canvas画布上绘制了一个点,对WebGL的整个绘图渲染流程进行了完整的演示,麻雀虽小,五脏俱全。
学习建议
本节课你不需要刻意去记忆具体知识,对于暂时不理解的小细节可以跳过,先有个整体印象即可,可以把全部代码复制到编辑器中,根据课程对相关知识模块的介绍,尝试更改几个参数,体验一下渲染效果。
在后面的学习中,都可以在本节课源码的基础上进行增删代码即可,初学WebGL没必要每一行代码都要自己去敲出来。
本课程风格和大多数课程风格不同,注意一定要结合案例代码学习,在案例代码的基础上调试体验总结,就像做化学实验一样,不要仅仅阅读文字。
关键词
- 超文本语言HTML
- 脚本语言Javascript
- Canvas画布
- 着色器语言GLSL ES
- WebGL API
关于HTML/CSS/JavaScript不再进行过多的介绍,默认你有一定的前端基础,下面结合WebGL绘制点的代码,对Canvas画布、着色器语言GLSL ES和WebGL API相关知识进行简单介绍。
WebGL绘制一个点(.html文件完整源码)
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title>使用WebGL绘制一个点</title>
6 </head>
7 <body>
8 <!--canvas标签创建一个宽高均为500像素,背景为蓝色的矩形画布-->
9 <canvas id="webgl" width="500" height="500" style="background-color: blue"></canvas>
10
11
12 <script>
13 //通过getElementById()方法获取canvas画布
14 var canvas=document.getElementById('webgl');
15 //通过方法getContext()获取WebGL上下文
16 var gl=canvas.getContext('webgl');
17
18 //顶点着色器源码
19 var vertexShaderSource = '' +
20 'void main(){' +
21 //给内置变量gl_PointSize赋值像素大小
22 ' gl_PointSize=20.0;' +
23 //顶点位置,位于坐标原点
24 ' gl_Position =vec4(0.0,0.0,0.0,1.0);' +
25 '}';
26
27 //片元着色器源码
28 var fragShaderSource = '' +
29 'void main(){' +
30 //定义片元颜色
31 ' gl_FragColor = vec4(1.0,0.0,0.0,1.0);' +
32 '}';
33
34 //初始化着色器
35 var program = initShader(gl,vertexShaderSource,fragShaderSource);
36 //开始绘制,显示器显示结果
37 gl.drawArrays(gl.POINTS,0,1);
38
39 //声明初始化着色器函数
40 function initShader(gl,vertexShaderSource,fragmentShaderSource){
41 //创建顶点着色器对象
42 var vertexShader = gl.createShader(gl.VERTEX_SHADER);
43 //创建片元着色器对象
44 var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
45 //引入顶点、片元着色器源代码
46 gl.shaderSource(vertexShader,vertexShaderSource);
47 gl.shaderSource(fragmentShader,fragmentShaderSource);
48 //编译顶点、片元着色器
49 gl.compileShader(vertexShader);
50 gl.compileShader(fragmentShader);
51
52 //创建程序对象program
53 var program = gl.createProgram();
54 //附着顶点着色器和片元着色器到program
55 gl.attachShader(program,vertexShader);
56 gl.attachShader(program,fragmentShader);
57 //链接program
58 gl.linkProgram(program);
59 //使用program
60 gl.useProgram(program);
61 //返回程序program对象
62 return program;
63 }
64 </script>
65 </body>
66 </html>
创建Canvas画布
创建一个Canvas
画布,用于显示WebGL的渲染结果,canvas元素和div等元素一样是HTML的一个元素,只是Canvas
画布具有2D和3D绘图功能。
<!--canvas标签创建一个宽高均为500像素,背景为蓝色的矩形画布-->
<canvas id="webgl" width="500" height="500" style="background-color: blue"></canvas>
通过JavaScript获取上面创建的Canvas元素返回一个Canvas对象。
//通过getElementById()方法获取canvas画布对象
var canvas= document.getElementById('webgl')
canvas对象也可以不通过<canvas>
标签创建,然后id方式获取,也可以通过DOM直接创建
//通过getElementById()方法获取canvas画布对象
var canvas = document.createElement('canvas');
Canvas对象方法.getContext()
HTML的Canvas元素提供了2D和3D绘图两种功能,平时程序员之间交流所说的Canvas一词就是指Canvas的2D绘图功能,通过Canvas元素实现的3D绘图功能,也就是所谓的WebGL,或者说WebGL依赖于Canvas元素实现。
执行canvas.getContext('2d')
返回对象具有一系列绘制二维图形的方法,比如绘制直线、圆弧等API。关于canvas 2D绘图相关内容,本课程不介绍,可以百度关键词Canvas,
//通过方法getContext()获取Canvas 2D绘图上下文
var gl=canvas.getContext('2d');
c.moveTo(0,0);//直线起点坐标
c.lineTo(50,50);//直线第2个点坐标
c.lineTo(0,100);//直线第3个点坐标
c.stroke();//把点连成直线绘制出来
执行canvas.getContext('webgl');
返回对象具有一系列绘制渲染三维场景的方法,也就是WebGL API。
//通过方法getContext()获取WebGL上下文
var gl=canvas.getContext('webgl');
...
//调用WebGL API绘制渲染方法drawArrays
gl.drawArrays(gl.POINTS,0,1);
...
着色器语言GLSL ES
下面代码中的两个字符串vertexShaderSource
和fragShaderSource
是WebGL的着色器代码,着色器代码通过着色器语言GLSL ES编写,对于前端工程来说学习WebGL,还需要学习一门新的语言着色器语言GLSL ES。关于着色器语言的学习,可以跟着课程一边写案例,一边去学习,这样更容易理解。
着色器语言用于计算机图形编程,运行在GPU中,平时所说的大多数语言编写的程序都是运行在CPU中。 与OpenGL API相配合的是着色器语言GLSL,与OpenGL ES API、WebGL API相互配合的是着色器语言GLSL ES。OpenGL标准应用的是客户端 OpenGL ES应用的是移动端,WebGL标准应用的是浏览器平台。
顶点着色器和片元着色器经过WebGL编译处理后,会在GPU的顶点着色器单元和片元着色器单元上执行。
顶点着色器定义了顶点的渲染位置和点的渲染像素大小
//顶点着色器源码
var vertexShaderSource = '' +
'void main(){' +
//给内置变量gl_PointSize赋值像素大小
' gl_PointSize=20.0;' +
//顶点位置,位于坐标原点
' gl_Position =vec4(0.0,0.0,0.0,1.0);' +
'}';
片元着色器定义了点的渲染结果像素的颜色值
//片元着色器源码
var fragShaderSource = '' +
'void main(){' +
//定义片元颜色
' gl_FragColor = vec4(1.0,0.0,0.0,1.0);' +
'}';
gl_PointSize、gl_Position、gl_FragColor都是内置变量,也就是说不需要声明,这一点与多数编程语言不同,这主要是由GPU的特殊性决定,感兴趣的话 可以研究显卡的硬件结构,渲染管线等概念。
通过程序可以看出来顶点着色器源码vertexShaderSource
、片元着色器源码fragShaderSource
,都是只有一个主函数main,也就是入口函数。
给内置变量gl_Position
赋值vec4(0.0,0.0,0.0,1.0)
,也就是设置顶点位置坐标,vec4代表的是一种数据类型, 在这里可以理解为vec4()是一个可以构造出vec4类型数据的构造函数,前三个参数表示顶点坐标值xyz。
给内置变量gl_FragColor
赋值vec4(1.0,0.0,0.0,1.0)
,也就是设置会在屏幕上显示的像素的颜色,vec4()构造函数 前三个参数,表示颜色RGB值,最后一个参数是透明度A。在WebGL着色器中颜色值使用[0,1]区间表示。
你可以通过改变WebGL着色器代码内置变量gl_PointSize
、gl_Position
、gl_FragColor
测试WebGL渲染效果的变化。
gl_PointSize=20.0
改为gl_PointSize=10.0
,观察屏幕点的大小变化gl_Position =vec4(0.5,0.5,0.0,1.0)
改为gl_Position =vec4(0.5,0.5,0.0,1.0)
,观察屏幕点的位置变化
gl_FragColor=vec4(0.0,0.0,1.0,1.0)
更改为gl_FragColor = vec4(0.0,0.0,1.0,1.0)
,观察屏幕点的颜色变化
WebGL API
一句话来描述,WebGL API指的就是gl=canvas.getContext('webgl')
返回对象gl
的一系列绘制渲染方法,通过WebGL API可以把一个三维场景绘制渲染出来。比如上面代码中gl.createShader()
、gl.shaderSource()
、gl.drawArrays()
等方法就是WebGl API。
WebGL API多数与GPU硬件相关,控制相关图形处理单元,比如说通过gl.createShader()
、gl.shaderSource()
等方法可以对着色器代码进行编译,然后在GPU的着色器单元上执行;比如说drawArrays
不执行,GPU渲染管线的顶点、片元着色器是不会把顶点坐标转化为显示器上的像素显示出来。
如果你有 数字电路的知识,可编程芯片不仅仅只有GPU,针对不同的应用情形,都有特定的可编程芯片,图形处理用到的是可编程GPU,也就是说可以运行程序。处理声音是声卡,处理图像是显卡,自然而然它们 都会以CPU为核心,接受CPU的调度。以上描述在有些地方可能不太严谨,大家也不必记忆,主要目的是让大家有基本的认识,可以更好的编写程序。GPU相比CPU最大的特点是并行计算,不过WebGL API都 进行了封装,如果你想学习并行计算可以关注CUDA或OpenCL。GPU硬件(渲染管线)、显卡驱动、操作系统、浏览器、WebGL API是逐层抽象的。每一层都会为上一层提供一个接口,这里可以看出WebGL API是 首先通过浏览器的的解析,才能够经过一系列层驱动GPU工作,生成像素缓存,显示器会按照一定的频率扫描像素缓存,最终显示出来。
初始化着色器函数initShader()
关于着色器函数initShader()
中封装的WebGL API代码,案例代码中都进行了注释,可以先简单阅读一下,具体的细节可以先不去学习,当成一个黑箱处理,你只需要知道通过initShader()
函数可以完成着色器代码的编译,然后在GPU上执行。
初始化着色器函数initShader()
,主要是把顶点着色器源代码vertexShaderSource
, 片元着色器源代码fragShaderSource
,进行编译处理,然后顶点着色器代码在GPU的顶点着色器单元执行,片元着色器代码在GPU的片元着色器单元执行。
//声明初始化着色器函数
function initShader(gl,vertexShaderSource,fragmentShaderSource){
//创建顶点着色器对象
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
//创建片元着色器对象
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
//引入顶点、片元着色器源代码
gl.shaderSource(vertexShader,vertexShaderSource);
gl.shaderSource(fragmentShader,fragmentShaderSource);
//编译顶点、片元着色器
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
//创建程序对象program
var program = gl.createProgram();
//附着顶点着色器和片元着色器到program
gl.attachShader(program,vertexShader);
gl.attachShader(program,fragmentShader);
//链接program
gl.linkProgram(program);
//使用program
gl.useProgram(program);
//返回程序program对象
return program;
}
绘制方法gl.drawArrays()
gl.drawArrays()
方法的作用就是通知GPU执行着色器代码,然后根据着色器代码在Canvas画布上进行渲染绘制。
着色器代码放在script标签中
WebGL着色器代码在JavaScript中以字符串的形式存在,编写代码比较麻烦,为了编写方便,可以把着色器代码写在script或者其他HTML元素标签中,然后通过元素.innerText
属性获得元素中的字符串,也就是着色器的代码。
在原来WebGL绘制一个点的代码基础上进行更改
<!-- 顶点着色器源码 -->
<script id="vertexShader" type="x-shader/x-vertex">
void main() {
//给内置变量gl_PointSize赋值像素大小
gl_PointSize=20.0;
//顶点位置,位于坐标原点
gl_Position =vec4(0.0,0.0,0.0,1.0);
}
</script>
<!-- 片元着色器源码 -->
<script id="fragmentShader" type="x-shader/x-fragment">
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
</script>
//顶点着色器源码
var vertexShaderSource = document.getElementById('vertexShader').innerText;
//片元着色器源码
var fragShaderSource = document.getElementById('fragmentShader').innerText;
//初始化着色器
var program = initShader(gl,vertexShaderSource,fragShaderSource);