WebGL101 Hello WebGL (上)
Posted WebGL-China
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WebGL101 Hello WebGL (上)相关的知识,希望对你有一定的参考价值。
WebGL游戏教程我们社区推出的一档原创类专辑,为了那些想学习WebGL的程序员们精心准备。这个教程会从基本的Hello World开始,直到讲述高级的Web3D游戏编程技术,敬请期待。
类似于学习其他一种编程语言的Hello World, 我们也会编写一个Hello WebGL, 用于绘制一个简单的三角形. 是的, WebGL是一个图形图像库. 它提供了一套API供javascript调用, 绘制出令人惊奇的美轮美奂的世界.
如何绘制
首先, 我们需要一个Canvas元素(来源于html5), 作为调用WebGL API绘制图像的画布(想象真实的美术作业, 没错, Canvas就是那个画布).
<canvas id="gl_canvas" style="border:1px solid blue;" width='300px' height='200px'></canvas>
然后在html body onload的时候调用JavaScript函数(这里的函数名称是main()), 剩余的就都交给JavaScript了.
<body onload='main()'>
接下来我们就开始绘制三角形了. 示例程序包含下面几个步骤
获取Canvas元素, 并取得webgl的上下文环境
创建shader对象, 读取编译shader代码
创建program对象, 并将上面创建的shader对象与之关联
提供三角形顶点数据, 调用WebGL API绘制图像
绘制三角形完整的JavaScript代码如下:
var gl = null;
var vertShader = null;
var fragShader = null;
var program = null;
/* 程序入口函数, 由html页面 onload 调用 */
function main(){
/* 获取canvas元素以及GL Context */
var canvas = document.getElementById("gl_canvas");
gl = canvas.getContext("webgl");
/* 获取shader程序片段内容 */
var vs_src = document.getElementById("vs_src").value;
var fs_src = document.getElementById("fs_src").value;
/* 加载shader */
loadShader(vs_src, fs_src);
/* 创建program */
createProgram();
/* 使用着色器程序 */
gl.useProgram(program);
/* 初始化GL需要的顶点数据 */
init();
/* 绘制三角形 */
render();
}
function loadShader(vs_src, fs_src){
/* 创建shader对象 */
vertShader = gl.createShader(gl.VERTEX_SHADER);
fragShader = gl.createShader(gl.FRAGMENT_SHADER);
/* 读取shader程序片段 */
gl.shaderSource(vertShader, vs_src);
gl.shaderSource(fragShader, fs_src);
/* 编译shader */
gl.compileShader(vertShader);
gl.compileShader(fragShader);
if(!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)){
alert("vertext shader error");
return;
}
if(!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)){
alert("fragment shader error");
return;
}
}
/* 创建program对象, 并关联shader对象 */
function createProgram(){
/* 创建program */
program = gl.createProgram();
/* 关联顶点shader对象和片段shader对象 */
gl.attachShader(program, vertShader);
gl.attachShader(program, fragShader);
/* 链接program */
gl.linkProgram(program);
if(!gl.getProgramParameter(program, gl.LINK_STATUS)){
alert("error:program");
return;
}
}
function init(){
/* 三角形顶点数据, rgba */
var vertext_data = [
0.0, 1.0, 0.0, 1.0,
-1.0, -1.0, 0.0, 1.0,
1.0, -1.0, 0.0, 1.0];
/* 创建缓存对象(buff object)名称 */
var buff = gl.createBuffer();
/* 绑定buff, 需要指定buff类型, 上面提供的vertext_data是顶点数组, 所以这里我们使用了ARRAY_BUFFER类型
* 在这里, bindBuffer函数创建<缓存对象(buff object)>并与buff(缓存对象名称)关联
*/
gl.bindBuffer(gl.ARRAY_BUFFER, buff);
/* 给<缓存对象(buff object)>分配内存, 填充顶点数据vertext_data */
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertext_data), gl.STATIC_DRAW);
/* 关联vert shader中的变量(用索引来表示, 这里我们用0, 因为在vert shader中就一个变量)
* 和我们提供的vertext_data数据的对应关系
*/
gl.vertexAttribPointer(0, 4, gl.FLOAT, false, 0, 0);
/* 启用顶点属性数组中的第一个属性, GL把shader所有的属性都映射到了一个数组中,
* 称为顶点属性数组<VertexAttribArray>, 所有的属性都可以用索引来获取
*/
gl.enableVertexAttribArray(0);
}
function render(){
/* 设置颜色缓冲区被清除时使用的颜色, 用来设置背景色 */
gl.clearColor(0.0, 0.0, 0.0, 1.0);
/* 清除颜色缓存 */
gl.clear(gl.COLOR_BUFFER_BIT);
/* 绘制三角形 */
gl.drawArrays(gl.TRIANGLES, 0, 3);
}
代码片段
1. 获取Canvas元素, 并取得webgl的上下文环境.
在WebGL中, 我们要调用的API都被封装在所谓的上下文环境对象中. 看下面的代码
var canvas = document.getElementById("gl_canvas");
gl = canvas.getContext("webgl");
代码里面, gl变量被赋值为上下文对象. 之后我们会大量用到gl变量.
另外, canvas.getContext()可以传入参数"2d", 用来绘制2d图形.
2. 创建shader对象, 读取编译shader代码
shader是提交给GPU来执行的小段代码. 我们在loadShader() 函数里面分别创建了顶点和片段shader对象. 读取shader代码内容, 并编译shader.
3. 创建program对象, 并关联shader对象
program对象就是所谓的着色器(shader)程序. 如程序所示, 我们先调用gl.createProgram()创建了着色器程序, 随后将之前创建的着色器对象(vertShader和fragShader)关联到
着色器程序(program), 接下来链接着色器程序--处理所有的与program关联的着色器对象来生成一个完整的着色器程序, 然后就可以使用这个着色器程序了gl.useProgram(program)
4. 初始化GL绘制图形所需要的顶点数据, 我们把它放在了init()函数里面
顶点数据我们放在了一个数组里面, 4个元素为一组, 代表一个顶点的rgb.
要将顶点数据提交给GPU, 首先需要创建一个缓存对象(buff object)--通过gl.createBuffer()来创建.
接下来调用函数bindBuffer(target, buff)--作用见注释, 它的第一个参数target可以根据实际用途选择gl.ARRAY_BUFFER或者gl.ELEMENT_ARRAY_BUFFER
5. 向缓冲区对象中写入数据
gl.bufferData(target,data,usage)函数会申请内存,并写入数据--数据的来源就是传入的第二个参数data, 之前定义好的顶点数组vertext_data
第三个参数usage根据用途可供的选择有gl.STATIC_DRAW, gl.STREAM_DRAW, gl.DYNAMIC_DRAW
gl.STATIC_DRAW 只向缓冲区写入一次数据, 很多次绘制
gl.STREAM_DRAW 只向缓冲区写入一次数据, 最多使用几次
gl.DYNAMIC_DRAW 多次写入数据, 多次绘制
6. 将缓冲区对象分配给变量
gl.vertexAttribPointer(location, size, type, normalized, stride, offset)
location 指定的attribute存储的位置
size 顶点数据分量个数--我们的顶点数据包含rgba, 所以是4
type 顶点数据类型, 可以使gl.FLOAT, gl.INT等
normalized 传入的顶点数据是否需要归一化
stride 顶点数据数组中的元素之间的大小间隔, 例子中数据是紧密相邻的, 所以传0
offset attribute变量对应于缓冲区数据开始的位置
7. 启用与此attibute索引关联的顶点数组--gl.enableVertexAttribArray(0);更多参看注释
8. 绘制三角形
gl.drawArrays(mode, first, count)
我们只画一个三角形,所以mode我们用gl.TRIANGLES, 另外可选的值包含gl.POINTS等.
first指定第一个顶点的位置
cout指定绘制多少个顶点, 一个三角形就是3个顶点
Hello World的程序讲解就到这里,请别小看这个粗糙的简单的代码,这个框架构成一个复杂游戏的基本结构,就是初始化,每帧的更新和绘制,结束。在后期的讲座中,我们会逐渐丰富这个程序,带领大家深入浅出,最后成为WebGL专家。
附上html源码
<html>
<head>
<meta http-equiv="content-type" content="text/html" />
<meta charset="utf-8" />
<script type="text/javascript" ></script>
</head>
<title>hello world</title>
<body onload='main()'>
<canvas id="gl_canvas" style="border:1px solid blue;" width='300px' height='200px'></canvas>
<div>
<textarea id="vs_src">
attribute vec4 pos;
//in vec4 pos;
void main(){
gl_Position = pos;
}
</textarea>
<textarea id="fs_src">
void main(){
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
</textarea>
</div>
</body>
</html>
以上是关于WebGL101 Hello WebGL (上)的主要内容,如果未能解决你的问题,请参考以下文章