WebGL2简单实例
一、主代码
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>Triangle</title> <!--标题--><script type="text/javascript" src="js/GLUtil.js"></script><script type="text/javascript" src="js/Matrix.js"></script><script type="text/javascript" src="js/MatrixState.js"></script><script type="text/javascript" src="js/Triangle.js"></script><script type="text/javascript" src="js/LoadShaderUtil.js"></script><script>'use strict';//GLES上下文var gl;//变换矩阵管理类对象var ms=new MatrixState();//要绘制的3D物体var ooTri;//着色器程序列表,集中管理var shaderProgArray=new Array();var currentAngle;var incAngle;var canvas;//初始化的方法function start(){
// //获取3D Canvas
// var canvas = document.getElementById('bncanvas');
// //获取GL上下文
// gl = canvas.getContext('webgl2', { antialias: true });gl = initWebGLCanvas("bncanvas");if (!gl) //若获取GL上下文失败{alert("创建GLES上下文失败,不支持webGL2.0!");//显示错误提示信息return;}//设置视口gl.viewport(0, 0, canvas.width, canvas.height);//设置屏幕背景色RGBAgl.clearColor(0.0,0.0,0.0,1.0);//初始化变换矩阵ms.setInitStack();//设置摄像机
// ms.setCamera(0,0,17,0,0,0,0,1,0);//立方体摄像机位置ms.setCamera(0,0,-5,0,0,0,0,1,0);//三角形摄像机位置//设置投影参数ms.setProjectFrustum(-1.5,1.5,-1,1,1,100);gl.enable(gl.DEPTH_TEST);//开启深度检测//加载着色器程序loadShaderFile("shader/vtrtex.bns",0);loadShaderFile("shader/fragment.bns",0);if(shaderProgArray[0])//如果着色器已加载完毕{ooTri=new Triangle(gl,shaderProgArray[0]);//创建三角形绘制对象}else{setTimeout(function(){ooTri=new Triangle(gl,shaderProgArray[0]);},90);//休息90ms后再执行}//初始化旋转角度currentAngle = 0;//初始化角度步进值incAngle = 0.4;//定时绘制画面setInterval("drawFrame();",16.6);}//绘制一帧画面的方法function drawFrame(){ if(!ooTri){console.log("加载未完成!");//提示信息return;}//清除着色缓冲与深度缓冲gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); //保护现场ms.pushMatrix(); //执行旋转,即按哪个轴旋转ms.rotate(currentAngle,0,1,0);//绘制物体ooTri.drawSelf(ms);//恢复现场ms.popMatrix();//修改旋转角度currentAngle += incAngle;if (currentAngle > 360)//保证角度范围不超过360currentAngle -= 360; } </script>
</head><body onload="start();"><canvas height="800" width="1200" id="bncanvas">若看到这个文字,说明浏览器不支持WebGL!</canvas>
</body>
</html>
1、引入工具函数
此代码显示一个三角形,在网页首先引入本地几个文 件:
Triangles.js 三角形绘制 对象
GLUtil.js : GL工具函数
LoadShaderUtil.js 着色器装载函数
Matrix.js 矩阵基础函数
MatrixState.js: 矩阵状态
2、 启动函数 start()
代码启动函数为start(),具体流程为:
1、获取初始化的WebGL句柄
2、检测获取WebGL句柄的结果,若失败返回
3、设置视口
4、设置屏幕背景
5、初始化变换矩阵
6、设置摄像机
7、开启深度检测
8、加载着色器程序
9、定时绘制画面
3、渲染函数drawFrame()
绘制一 帧画面的方法drawFrame()流程如下:
1、检测绘制对象是否加载,若未加载则返回
2、清除着色缓冲与深度缓冲
3、保护现场
4、执行旋转
5、绘制物体
6、恢复现场
7、修改旋转角度
二、GL工具函数( GLUtil)
//初始化WebGL Canvas的方法
function initWebGLCanvas(canvasName) {canvas = document.getElementById(canvasName);//获取Canvas对象var context = canvas.getContext('webgl2', { antialias: true });//获取GL上下文return context;//返回GL上下文对象
}
//加载单个着色器的方法
function loadSingleShader(ctx, shaderScript)
{if (shaderScript.type == "vertex")//若为顶点着色器{var shaderType = ctx.VERTEX_SHADER;//顶点着色器类型}else if (shaderScript.type == "fragment")//若为片元着色器{var shaderType = ctx.FRAGMENT_SHADER;//片元着色器类型}else {//否则打印错误信息console.log("*** Error: shader script of undefined type '"+shaderScript.type+"'");return null;}//根据类型创建着色器程序var shader = ctx.createShader(shaderType);//加载着色器脚本ctx.shaderSource(shader, shaderScript.text);//编译着色器ctx.compileShader(shader);//检查编译状态var compiled = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS);if (!compiled && !ctx.isContextLost()) {//若编译出错var error = ctx.getShaderInfoLog(shader);//获取错误信息console.log("*** Error compiling shader :"+error);//打印错误信息ctx.deleteShader(shader);//删除着色器程序return null;//返回空}return shader;//返回着色器程序
}//加载链接顶点、片元着色器的方法
function loadShaderSerial(gl, vshader, fshader)
{//加载顶点着色器var vertexShader = loadSingleShader(gl, vshader);//加载片元着色器var fragmentShader = loadSingleShader(gl, fshader);//创建着色器程序var program = gl.createProgram();//将顶点着色器和片元着色器挂接到着色器程序gl.attachShader (program, vertexShader);//将顶点着色器添加到着色器程序中gl.attachShader (program, fragmentShader);//将片元着色器添加到着色器程序中//链接着色器程序gl.linkProgram(program);//检查链接是否成功var linked = gl.getProgramParameter(program, gl.LINK_STATUS);if (!linked && !gl.isContextLost())//若链接不成功{//获取并在控制台打印错误信息var error = gl.getProgramInfoLog (program);//获取错误信息console.log("Error in program linking:"+error);//打印错误信息gl.deleteProgram(program);//删除着色器程序gl.deleteProgram(fragmentShader);//删除片元着色器gl.deleteProgram(vertexShader);//删除顶点着色器return null;//返回空}gl.useProgram(program);//gl.enable(gl.DEPTH_TEST);//打开深度检测return program;//返回着色器程序
}
此工具函数 包含三个函数,分别是:
initWebGLCanvas 初始化WebGL Canvas的方法
loadSingleShader 加载单个着色器的方法
loadShaderSerial 加载链接顶点、片元着色器的方法
三、色器装载函数( LoadShaderUtil)
function shaderObject(typeIn,textIn)//声明shaderObject类
{this.type=typeIn;//初始化type成员变量this.text=textIn;//初始化text成员变量
}
var shaderStrArray=["a","a"];//存储着色器
var shaderNumberCount=0; //数组索引值
var shaderTypeName=["vertex","fragment"];//着色器名称数组
function processLoadShader(req,index)//处理着色器脚本内容的回调函数
{if (req.readyState == 4) //若状态为4{var shaderStr = req.responseText;//获取响应文本shaderStrArray[shaderNumberCount]=new shaderObject(shaderTypeName[shaderNumberCount],shaderStr);//顶点着色器脚本内容shaderNumberCount++;if(shaderNumberCount>1)//如果着色器内容不为空{shaderProgArray[index]=loadShaderSerial(gl,shaderStrArray[0], shaderStrArray[1]);//加载着色器}}
}
//加载着色器的方法
function loadShaderFile(url,index)//从服务器加载着色器脚本的函数
{var req = new XMLHttpRequest();//创建XMLHttpRequest对象req.onreadystatechange = function () //设置响应回调函数{ processLoadShader(req,index) };//调用processLoadShader处理响应req.open("GET", url, true);//用GET方式打开指定URLreq.responseType = "text";//设置响应类型req.send(null);//发送HTTP请求
}
shaderObject 声明shaderObject类
processLoadShader 处理着色器脚本内容的回调函数
loadShaderFile 从服务器加载着色器脚本的函数
四、 矩阵状态(MatrixState)
function MatrixState()
{this.mProjMatrix = new Array(16);//投影矩阵this.mVMatrix = new Array(16);//摄像机矩阵this.currMatrix=new Array(16);//基本变换矩阵this.mStack=new Array(100);//矩阵栈//初始化矩阵的方法this.setInitStack=function(){this.currMatrix=new Array(16);//创建用于存储矩阵元素的数组setIdentityM(this.currMatrix,0);//将元素填充为单位阵的元素值}//保护变换矩阵,当前矩阵入栈this.pushMatrix=function(){this.mStack.push(this.currMatrix.slice(0));}//恢复变换矩阵,当前矩阵出栈this.popMatrix=function(){this.currMatrix=this.mStack.pop();}//执行平移变换this.translate=function(x,y,z)//设置沿xyz轴移动{translateM(this.currMatrix, 0, x, y, z);//将平移变换记录进矩阵}//执行旋转变换this.rotate=function(angle,x,y,z)//设置绕xyz轴移动{rotateM(this.currMatrix,0,angle,x,y,z);//将旋转变换记录进矩阵}//执行缩放变换this.scale=function(x,y,z)//设置绕xyz轴移动{scaleM(this.currMatrix,0,x,y,z)//将缩放变换记录进矩阵}//设置摄像机this.setCamera=function(cx, //摄像机位置xcy, //摄像机位置ycz, //摄像机位置ztx, //摄像机目标点xty, //摄像机目标点ytz, //摄像机目标点zupx, //摄像机UP向量X分量upy, //摄像机UP向量Y分量upz //摄像机UP向量Z分量){setLookAtM(this.mVMatrix,0,cx,cy,cz,tx,ty,tz,upx,upy,upz);}//设置透视投影参数this.setProjectFrustum=function(left, //near面的leftright, //near面的rightbottom, //near面的bottomtop, //near面的topnear, //near面距离far //far面距离){frustumM(this.mProjMatrix, 0, left, right, bottom, top, near, far);}//设置正交投影参数this.setProjectOrtho=function(left, //near面的leftright, //near面的rightbottom, //near面的bottomtop, //near面的topnear, //near面与视点的距离far //far面与视点的距离){orthoM(this.mProjMatrix, 0, left, right, bottom, top, near, far);}//获取具体物体的总变换矩阵this.getFinalMatrix=function(){var mMVPMatrix=new Array(16);multiplyMM(mMVPMatrix, 0, this.mVMatrix, 0, this.currMatrix, 0);multiplyMM(mMVPMatrix, 0, this.mProjMatrix, 0, mMVPMatrix, 0);return mMVPMatrix;}//获取具体物体的变换矩阵this.getMMatrix=function(){return this.currMatrix;}
}
此矩阵状态类,定义了四个矩阵:
mProjMatrix = new Array(16);//投影矩阵
mVMatrix = new Array(16);//摄像机矩阵
currMatrix=new Array(16);//基本变换矩阵
mStack=new Array(100);//矩阵栈
成员函数如下所示:
setInitStack 初始化矩阵的方法
pushMatrix 保护变换矩阵,当前矩阵入栈
popMatrix 恢复变换矩阵,当前矩阵出栈
translate 执行平移变换
rotate 执行旋转变换
scale 执行缩放变换
setCamera 设置摄像机
setProjectFrustum 设置透视投影参数
setProjectOrtho 设置正交投影参数
getFinalMatrix 获取具体物体的总变换矩阵
getMMatrix 获取具体物体的变换矩阵
五 、三角形绘制 对象(Triangles)
function Triangle( //声明绘制用物体对象所属类gl, //GL上下文programIn //着色器程序id
){//this.vertexData=vertexDataIn; //初始化顶点坐标数据this.vertexData= [//三角形顶点3.0,0.0,0.0,0.0,0.0,0.0,0.0,3.0,0.0//立方体顶点// 3.0,3.0,3.0,// 3.0,-3.0,3.0,// -3.0,3.0,3.0,// -3.0,3.0,3.0,// 3.0,-3.0,3.0,// -3.0,-3.0,3.0,//// 3.0,3.0,3.0,// 3.0,-3.0,-3.0,// 3.0,3.0,-3.0,//// 3.0,3.0,3.0,// 3.0,-3.0,3.0,// 3.0,-3.0,-3.0,//// -3.0,3.0,3.0,// -3.0,-3.0,-3.0,// -3.0,3.0,-3.0,//// -3.0,3.0,3.0,// -3.0,-3.0,3.0,// -3.0,-3.0,-3.0,//// 3.0,3.0,-3.0,// 3.0,-3.0,-3.0,// -3.0,3.0,-3.0,//// -3.0,3.0,-3.0,// 3.0,-3.0,-3.0,// -3.0,-3.0,-3.0];this.vcount=this.vertexData.length/3;//得到顶点数量this.vertexBuffer=gl.createBuffer(); //创建顶点坐标数据缓冲gl.bindBuffer(gl.ARRAY_BUFFER,this.vertexBuffer); //绑定顶点坐标数据缓冲//将顶点坐标数据送入缓冲gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(this.vertexData),gl.STATIC_DRAW);this.colorsData=[//三角形顶点颜色1.0,1.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,1.0,0.0,1.0//立方体顶点颜色// 1.0,0.0,1.0,1.0, //初始化顶点颜色数据// 1.0,0.0,0.0,1.0,// 1.0,0.0,0.0,1.0,// 1.0,1.0,1.0,1.0, //初始化顶点颜色数据// 0.0,0.0,1.0,1.0,// 0.0,1.0,0.0,1.0,//// 1.0,1.0,1.0,1.0, //初始化顶点颜色数据// 0.0,0.0,1.0,1.0,// 0.0,1.0,0.0,1.0,//// 1.0,1.0,1.0,1.0, //初始化顶点颜色数据// 0.0,0.0,1.0,1.0,// 0.0,1.0,0.0,1.0,//// 1.0,1.0,1.0,1.0, //初始化顶点颜色数据// 0.0,0.0,1.0,1.0,// 0.0,1.0,0.0,1.0,//// 1.0,1.0,1.0,1.0, //初始化顶点颜色数据// 0.0,0.0,1.0,1.0,// 0.0,1.0,0.0,1.0,//// 1.0,1.0,1.0,1.0, //初始化顶点颜色数据// 0.0,0.0,1.0,1.0,// 0.0,1.0,0.0,1.0,//// 1.0,1.0,1.0,1.0, //初始化顶点颜色数据// 0.0,0.0,1.0,1.0,// 0.0,1.0,0.0,1.0];this.colorBuffer=gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER,this.colorBuffer); //绑定颜色数据缓冲//将颜色数据送入缓冲gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(this.colorsData),gl.STATIC_DRAW);this.program=programIn; //初始化着色器程序idthis.drawSelf=function(ms)//绘制物体的方法{gl.useProgram(this.program);//指定使用某套着色器程序//获取总变换矩阵引用idvar uMVPMatrixHandle=gl.getUniformLocation(this.program, "uMVPMatrix");//将总变换矩阵送入渲染管线gl.uniformMatrix4fv(uMVPMatrixHandle,false,new Float32Array(ms.getFinalMatrix()));gl.enableVertexAttribArray(gl.getAttribLocation(this.program, "aPosition"));//启用顶点坐标数据数组gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); //绑定顶点坐标数据缓冲//给管线指定顶点坐标数据gl.vertexAttribPointer(gl.getAttribLocation(this.program,"aPosition"),3,gl.FLOAT,false,0, 0);gl.enableVertexAttribArray(gl.getAttribLocation(this.program, "aColor"));//启用颜色坐标数据数组gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); //绑定颜色坐标数据缓冲//给管线指定颜色坐标数据gl.vertexAttribPointer(gl.getAttribLocation(this.program,"aColor"),4,gl.FLOAT,false,0, 0);gl.drawArrays(gl.TRIANGLES, 0, this.vcount); //用顶点法绘制物体}
}
绘制 对象 具体流程 如下:
缓冲 区初始化:
1、定义顶点数据,送数据至顶点坐标数据缓冲
2、定义颜色数据、送数据至颜色数据缓冲
3、初始化着色器程序
绘制 方法drawSelf(ms)
1、指定使用某套着色器程序
2、获取总变换矩阵,将总变换矩阵送入渲染管线
3、启用顶点坐标数据数组,绑定顶点坐标数据缓冲,给管线指定顶点坐标数据
4、启用颜色坐标数据数组,绑定颜色坐标数据缓冲,给管线指定颜色坐标数据
5、用顶点法绘制物体
六、 着色器
顶点着色器
#version 300 es
uniform mat4 uMVPMatrix; //总变换矩阵
layout (location = 0) in vec3 aPosition; //顶点位置
layout (location = 1) in vec4 aColor; //顶点颜色
out vec4 vColor; //用于传递给片元着色器的变量void main()
{gl_Position = uMVPMatrix * vec4(aPosition,1); //根据总变换矩阵计算此次绘制此顶点位置vColor = aColor;//将接收的颜色传递给片元着色器
}
片段着色器
#version 300 es
precision mediump float;
in vec4 vColor; //接收从顶点着色器过来的参数
out vec4 fragColor;//输出到的片元颜色
void main()
{fragColor = vColor;//给此片元颜色值
}