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 (上)的主要内容,如果未能解决你的问题,请参考以下文章

在vue中使用unity3D实现webGL将要呈现的效果

WebGL 初探

WebGL了解一下?

webgl 纹理

Windows上WebGL的软件和硬件要求

Unity WebGL WebSocket 消息头