一、主代码
<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);
//设置屏幕背景色RGBA
gl.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)//保证角度范围不超过360
currentAngle -= 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方式打开指定URL
req.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, //摄像机位置x
cy, //摄像机位置y
cz, //摄像机位置z
tx, //摄像机目标点x
ty, //摄像机目标点y
tz, //摄像机目标点z
upx, //摄像机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面的left
right, //near面的right
bottom, //near面的bottom
top, //near面的top
near, //near面距离
far //far面距离
)
{
frustumM(this.mProjMatrix, 0, left, right, bottom, top, near, far);
}
//设置正交投影参数
this.setProjectOrtho=function
(
left, //near面的left
right, //near面的right
bottom, //near面的bottom
top, //near面的top
near, //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; //初始化着色器程序id
this.drawSelf=function(ms)//绘制物体的方法
{
gl.useProgram(this.program);//指定使用某套着色器程序
//获取总变换矩阵引用id
var 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;//给此片元颜色值
}