WebGL的标准来自于OpenGLES2.0,而OpenGLES2.0来自于纯可编程管线技术,有别与以前OpenGL的固定管线技术,可编程管线技术更加灵活,效率更高,但是对开发人员的要求更高。比如:以前光照只需要调几个函数就行,现在则需要写比较底层的东西,如怎么反射,怎么折射等等。本节通过绘制一个三角形来介绍下WebGL的基本开发流程。
废话不多说先上代码:
<html>
<head>
<title>webgl-lesson2</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<script id="shader-vs" > //顶点着色器vertexShader,处理顶点
attribute vec3 v3Position;
void main(void)
{
gl_Position = vec4(v3Position,1.0);
}
</script>
<script id="shader-fs" >//片段着色器fragmentShader,填充颜色
void main(void)
{
gl_FragColor = vec4(1.0,1.0,1.0,1.0);
}//两段shader代码都在显卡在执行
</script>
<script type="text/javascript">
function getShaderSource(scritptID)
{
var shaderScript = document.getElementById(scritptID);
if(shaderScript == null) return "";
var sourceCode = "";
var child = shaderScript.firstChild;
while(child)
{
if(child.nodeType == child.TEXT_NODE) sourceCode+=child.textContent;
child = child.nextSibling;
}
return sourceCode;
}
var webgl = null;
var vertexShaderObject = null;
var fragmentShaderObject = null;
var programObject = null;
var triangleBuffer = null;
var v3PositionIndex = 0;
function Init()
{
var canvas = document.getElementById("myCanvas");
webgl = canvas.getContext("webgl");
webgl.viewport(0,0,canvas.clientWidth,canvas.clientHeight);
vertexShaderObject = webgl.createShader(webgl.VERTEX_SHADER);//创建一个空的vertexShader对象,里面什么也没有
fragmentShaderObject = webgl.createShader(webgl.FRAGMENT_SHADER);//同理创建一个空的fragmentShader对象
webgl.shaderSource(vertexShaderObject,getShaderSource("shader-vs"));//相当于给vertexShader赋值,使其指向shader-vs里面的代码字符串
webgl.shaderSource(fragmentShaderObject,getShaderSource("shader-fs"));//相当于给fragmentShader赋值,使其指向shader-fs里面的代码字<span style="white-space:pre"></span>符串
//编译两段代码,使其形成能在显卡执行的二进制代码
webgl.compileShader(vertexShaderObject);
webgl.compileShader(fragmentShaderObject);
//检测编译错误
if(!webgl.getShaderParameter(vertexShaderObject,webgl.COMPILE_STATUS))
{
alert("error:vertexShaderObject");
return;
}
if(!webgl.getShaderParameter(fragmentShaderObject,webgl.COMPILE_STATUS))
{
alert("error:fragmentShaderObject");
return;
}
programObject = webgl.createProgram();//创建一个程序,相当于创建一个空的exe文件,前面的shader相当于两个库文件
//将exe文件与库文件关联在一起
webgl.attachShader(programObject,vertexShaderObject);
webgl.attachShader(programObject,fragmentShaderObject);
webgl.linkProgram(programObject);//链接,形成一个能显卡上执行在可执行程序,通过programObject句柄关联
//检测链接错误
if(!webgl.getProgramParameter(programObject,webgl.LINK_STATUS))
{
alert("error:programObject");
return;
}
webgl.useProgram(programObject);//使用刚刚创建好的可执行程序,使用一个程序需要输入输出
webgl.bindAttribLocation(programObject,v3PositionIndex,"v3Position");//一个程序都有输入输出,输入就是shader-vs里面的v3Position变量<span style="white-space:pre"></span> ,这里通过v3PositionIndex与v3Position绑定,v3PositionIndex相<span style="white-space:pre"></span> 当于一个输入入口
var jsArrayData =
[
0.0,1.0,0.0, //上顶点
-1.0,-1.0,0.0,//左顶点
1.0,-1.0,0.0 //右顶点
];
triangleBuffer = webgl.createBuffer();//在显卡上创建一个缓冲区
webgl.bindBuffer(webgl.ARRAY_BUFFER,triangleBuffer);//申明缓存区的存储类型为ARRAY_BUFFER
webgl.bufferData(webgl.ARRAY_BUFFER,new Float32Array(jsArrayData),webgl.STATIC_DRAW);//给缓存区赋值
webgl.clearColor(0.0,0.0,0.0,1.0);
webgl.clear(webgl.COLOR_BUFFER_BIT);
webgl.bindBuffer(webgl.ARRAY_BUFFER,triangleBuffer);//使用缓冲区
webgl.enableVertexAttribArray(v3PositionIndex);//启用v3PositionIndex
webgl.vertexAttribPointer(v3PositionIndex,
3,
webgl.FLOAT,
false,
0,
0);//给v3PositionIndex传值
webgl.drawArrays(webgl.TRIANGLES,
0,
3);//绘制
}
</script>
</head>
<body onload="Init()">
<canvas id="myCanvas" style="border:1px solid red;" width="600" height="600"></canvas>
</body>
</html>
执行结果如下:
WebGL是高效的,原因是它能与显卡直接交互,通过使用GLSL语言(OpenGL Shading Language)。 它是是开发人员用来在OpenGL中着色编程编写的短小的自定义程序,他们是在图形卡的GPU (Graphic Processor Unit图形处理单元)上执行的,代替了固定的渲染管线的一部分。比如:视图转换、投影转换等。GLSL(GL Shading Language)的着色器代码分成2个部分:Vertex Shader(顶点着色器)和Fragment Shader(片断着色器),有时还会有Geometry Shader(几何着色器)。负责运行顶点着色的是顶点着色器。它可以得到当前OpenGL 中的状态,GLSL内置变量进行传递。
本例中的GLSL为:
顶点着色器Vertex Shader代码:
attribute vec3 v3Position;片段着色器 Fragment Shader代码:
void main(void)
{
gl_Position = vec4(v3Position,1.0);
}
void main(void)现在不需要了解得多深,只需要简单了解 顶点着色器 代码是处理顶点的,gl_Position是顶点的位置,片段着色器代码是填充颜色
{
gl_FragColor = vec4(1.0,1.0,1.0,1.0);
}
下面讲述下WebGL的基本流程:
WebGL是要与显卡直接交互的,首先是要将shader代码编译:
vertexShaderObject = webgl.createShader(webgl.VERTEX_SHADER);这两段代码相当于库文件,然后创建一个程序,相当于一个exe,将上面两个shader代码链接起来。再执行链接操作
fragmentShaderObject = webgl.createShader(webgl.FRAGMENT_SHADER);
webgl.shaderSource(vertexShaderObject,getShaderSource("shader-vs"));
webgl.shaderSource(fragmentShaderObject,getShaderSource("shader-fs"));
//编译两段代码,使其形成能在显卡执行的二进制代码
webgl.compileShader(vertexShaderObject);
webgl.compileShader(fragmentShaderObject);
programObject = webgl.createProgram();//创建一个程序,相当于创建一个空的exe文件,前面的shader相当于两个库文件形成一个可以能在显卡上执行的程序。通过programObject句柄关联
//将exe文件与库文件关联在一起
webgl.attachShader(programObject,vertexShaderObject);
webgl.attachShader(programObject,fragmentShaderObject);
webgl.linkProgram(programObject);//链接,形成一个能显卡上执行在可执行程序,通过programObject句柄关联
运行一个程序需要输入输出,输入是GLSL vertex shader 代码里的vec3 v3Position,
webgl.bindAttribLocation(programObject,v3PositionIndex,"v3Position");将v3PositionIndex 与 v3Position 绑定,v3PositionIndex 为输入入口
现在要将顶点数据jsArrayData传到显卡上去。先在显卡上创建一个缓冲区存储数据,申明缓冲区存储类型,然后将jsArrayData数据传到缓冲区上。
triangleBuffer = webgl.createBuffer();//在显卡上创建一个缓冲区
webgl.bindBuffer(webgl.ARRAY_BUFFER,triangleBuffer);//申明缓存区的存储类型为ARRAY_BUFFER
webgl.bufferData(webgl.ARRAY_BUFFER,new Float32Array(jsArrayData),webgl.STATIC_DRAW);//给缓存区赋值
接下来就要在显卡运行上面创建的程序了,使用缓冲区(bufferData()),启用v3PositionIndex,告诉显卡程序怎么样使用显卡缓冲区里面的数据(vertexAttribPointer()),然后绘制
webgl.bindBuffer(webgl.ARRAY_BUFFER,triangleBuffer);//使用缓冲区
webgl.enableVertexAttribArray(v3PositionIndex);//启用v3PositionIndex
webgl.vertexAttribPointer(v3PositionIndex,
3,
webgl.FLOAT,
false,
0,
0);//给v3PositionIndex传值
webgl.drawArrays(webgl.TRIANGLES,
0,
3);//绘制
为什么webgl.bindBuffer()使用两次,看了官方文档,有一句:glBindBuffer
lets youcreate oruse a named buffer object,glBindBuffer
能创建或使用一个命名的缓冲区。具体的用法下节在讨论
本节完,下节将讨论更加细节的东西。