webgl的api地址如下:
www.khronos.org/registry/we… www.khronos.org/registry/we…
着色器语言在网页中的几种写法
1.将着色器语言写在一个单独的文件中,如vtrtex.bns文件。通过获取文件获取文本内容。
着色器语言规范中没有官方扩展名。 OpenGL无法处理从文件加载着色器;您只需将着色器代码作为字符串传递,因此没有特定的文件格式。 但是,Khronos的参考GLSL编译器/验证器glslang使用以下扩展名来确定该文件用于的着色器类型:.vert-顶点着色器 .tesc-镶嵌控件着色器 .tese-镶嵌评估着色器 .geom-几何着色器 .frag-片段着色器 .comp-计算着色器
2.将着色器语言写成字符串赋值给js的变量。
// 顶点着色器程序
var VSHADER_SOURCE =
'void main() {\n' +
' gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + // Set the vertex coordinates of the point
' gl_PointSize = 10.0;\n' + // Set the point size
'}\n';
3.通过script标签插入html,然后通过id获取标签内部的文本。
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 v3Position; //顶点坐标
attribute vec3 av3Color; //顶点颜色属性
varying vec3 vv3Color; //顶点着色器和片段着色器通讯的变量,注意这里顶点和片段着色器定义的变量名字必须相同
void main(void){
vv3Color = av3Color;
gl_Position = vec4(v3Position, 1.0);
}
</script>
WebGL程序的大致流程
我们常说把大象装进冰箱需要三步,那么写一个WebGL程序应该也只需要三步:
- 1、把数据放入缓冲区
- 2、把缓冲区的数据给着色器
- 3、着色器把数据给GPU。
下面是梳理的一个WebGL程序的大致流程图:
demo1
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script>
var webgl = null;
function Init(){
var canvas = document.getElementById('myCanvas');
webgl = canvas.getContext("webgl");//类似创建绘制的引擎或者初始化
//设定WebGL的渲染区域(视见区)
webgl.viewport(0,0,canvas.clientWidth,canvas.clientHeight)
//方法用于设置清空颜色缓冲时的颜色值
webgl.clearColor(0.0,0.0,0.0,1.0);
//方法使用预设值来清空缓冲
// gl.COLOR_BUFFER_BIT //颜色缓冲区
// gl.DEPTH_BUFFER_BIT //深度缓冲区
// gl.STENCIL_BUFFER_BIT //模板缓冲区
webgl.clear(webgl.COLOR_BUFFER_BIT);
// 必须强调
// 1. glClearColor只起到Set的作用,并不Clear任何!不要混淆~
// 2. glClearColor 的作用是,指定刷新颜色缓冲区时所用的颜色。
// 所以,完成一个刷新过程是要 glClearColor(COLOR) 与 glClear(GL_COLOR_BUFFER_BIT) 配合使用。glClearColor(0.0, 0.0, 1.0, 1.0);//蓝色
// glClear(GL_COLOR_BUFFER_BIT);
// 清除颜色缓冲区的作用是,防止缓冲区中原有的颜色信息影响本次绘图(注意!即使认为可以直接覆盖原值,也是有可能会影响的),当绘图区域为整个窗口时,就是通常看到的,颜色缓冲区的清除值就是窗口的背景颜色。所以,这两条清除指令并不是必须的:比如对于静态画面只需要设置一次,比如不需要背景色/背景色为白色。
// glClear 比手动涂抹一个背景画布效率高且省力,所以通常使用这种方式。
}
//、、帧缓存区(不只一张) 二维的图片 颜色缓冲区,深度缓冲区,模板缓冲区
</script>
</head>
<body onload='Init()'>
<canvas id="myCanvas" style="border:1px solid red;" width="600" height="450"></canvas>
</body>
</html>
demo2
WebGL是要与显卡直接交互的,首先是要将shader代码编译:
function initShaders(webgl, vertexShaderId, framentShaderId, bindVariableData) {
//生成shaderobject
var vertexShaderObject = webgl.createShader(webgl.VERTEX_SHADER);
var fragmentShaderObject = webgl.createShader(webgl.FRAGMENT_SHADER);
//编译shader
compileShader(webgl, shaderSourceFromScript(vertexShaderId), shaderSourceFromScript(framentShaderId), vertexShaderObject, fragmentShaderObject);
//链接shader
//创建一个程序对象
programObject = webgl.createProgram();
//将着色器变量关联到一个属性索引,该操作必须在链接之前进行
if(!bindVariableData instanceof Array) {
alert("输入参数有错");
return;
}
for(var i = 0; i < bindVariableData.length; i++) {
//将通用顶点索引绑定到属性变量
webgl.bindAttribLocation(programObject, bindVariableData[i][0], bindVariableData[i][1]);
}
programObject = linkShader(webgl, programObject, vertexShaderObject, fragmentShaderObject);
return programObject;
}
//编译shader
//shaderVectexCode shaderFramentCode glsl的字符串
function compileShader(webgl, shaderVectexCode, shaderFramentCode, vertexShaderObj, fragmentShaderObj) {
//将shader代码装载到shader Object中
webgl.shaderSource(vertexShaderObj, shaderVectexCode);
webgl.shaderSource(fragmentShaderObj, shaderFramentCode);
//编译shader代码
webgl.compileShader(vertexShaderObj);
webgl.compileShader(fragmentShaderObj);
//检查是否编译成功
if(!webgl.getShaderParameter(vertexShaderObj, webgl.COMPILE_STATUS)) {
alert("error:vertexShaderObject");
return;
}
if(!webgl.getShaderParameter(fragmentShaderObj, webgl.COMPILE_STATUS)) {
alert("error:framentShaderObject");
return;
}
}
编译两段代码,使其形成能在显卡执行的二进制代码,这两段代码相当于库文件,然后创建一个程序,相当于一个exe,将上面两个shader代码链接起来。再执行链接操作形成一个可以能在显卡上执行的程序。通过programObject句柄关联。
句柄(handle)是C++程序设计中经常提及的一个术语。它并不是一种具体的、固定不变的数据类型或实体,而是代表了程序设计中的一个广义的概念。句柄一般是指获取另一个对象的方法——一个广义的指针,它的具体形式可能是一个整数、一个对象或就是一个真实的指针,而它的目的就是建立起与被访问对象之间的唯一的联系。
运行一个程序需要输入输出,输入是GLSL vertex shader 代码里的vec3 v3Position 和 vec3 vv3Color
webgl.bindAttribLocation(programObject,v3PositionIndex,"v3Position")
将v3PositionIndex 与 v3Position 绑定,v3PositionIndex 为输入入口 现在要将顶点数据传到显卡上去。先在显卡上创建一个缓冲区存储数据,申明缓冲区存储类型,然后将ArrayData数据传到缓冲区上。
triangleBuffer = webgl.createBuffer();//在显卡上创建一个缓冲区
webgl.bindBuffer(webgl.ARRAY_BUFFER,triangleBuffer);//申明缓存区的存储类型为ARRAY_BUFFER
webgl.bufferData(webgl.ARRAY_BUFFER,new Float32Array(ArrayData),webgl.STATIC_DRAW);//给缓存区赋值
接下来就要在显卡运行上面创建的程序了,使用缓冲区(bufferData()),启用v3PositionIndex,告诉显卡程序怎么样使用显卡缓冲区里面的数据(vertexAttribPointer()),然后绘制。
webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);
webgl.enableVertexAttribArray(v3PositionIndex);
webgl.vertexAttribPointer(v3PositionIndex, 3, webgl.FLOAT, false, 0, 0);
webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleColorBuffer);
webgl.enableVertexAttribArray(v3ColorIndex);
webgl.vertexAttribPointer(v3ColorIndex, 3, webgl.FLOAT, false, 0, 0);
//绘制数据
webgl.drawArrays(webgl.TRIANGLES, 0, 3);
完整代码
<html>
<head>
<!--
1、从canvas元素中获取webgl context
2、利用GLSL ES语言,编写顶点着色器和片元着色器,并成对应的着色器程序
3、准备好你想要绘制的图像的顶点数据,并写入缓冲区
4、把着色器中的变量与载有顶点数据的缓冲区对应起来
5、最后执行着色器程序,并在canvas上绘制出图形
-->
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 v3Position; //顶点坐标
attribute vec3 av3Color; //顶点颜色属性
varying vec3 vv3Color; //顶点着色器和片段着色器通讯的变量,注意这里顶点和片段着色器定义的变量名字必须相同
void main(void){
vv3Color = av3Color;
gl_Position = vec4(v3Position, 1.0);
}
</script>
<script id="shader-fs" type="x-shader/x-fragment">
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
varying vec3 vv3Color;
void main(void){
gl_FragColor = vec4(vv3Color, 1.0);
}
</script>
<script>
var webgl = null;
var vertexShaderObject = null;
var fragmentShaderObject = null;
var programObject = null;
var triangleBuffer = null;
var triangleColorBuffer = null;
var v3PositionIndex = 0;
var v3ColorIndex = 1;
var r = 1.0;
var g = 1.0;
var b = 1.0;
var isDesc = false;
//初始化代码
function init() {
//初始化webgl渲染区域
webgl = initCanvas("webglCanvas");
//初始化shader程序
var bind1 = [v3PositionIndex, "v3Position"];
var bind2 = [v3ColorIndex, "av3Color"]; //av3Color变量名
var bindData = new Array();
bindData.push(bind1);
bindData.push(bind2);
//初始化Shader程序,返回链接好的程序对象
var programObject = initShaders(webgl, "shader-vs", "shader-fs", bindData);
//将定义好的WebGLProgram 对象添加到当前的渲染状态中。
webgl.useProgram(programObject);
//变色
window.setInterval("changeColor()", 10);
//初始化顶点数据
triangleBuffer = initVextexData(webgl);
}
//初始化Shader程序,返回链接好的程序对象
function initShaders(webgl, vertexShaderId, framentShaderId, bindVariableData) {
//生成shaderobject
var vertexShaderObject = webgl.createShader(webgl.VERTEX_SHADER);
var fragmentShaderObject = webgl.createShader(webgl.FRAGMENT_SHADER);
//编译shader
compileShader(webgl, shaderSourceFromScript(vertexShaderId), shaderSourceFromScript(framentShaderId),vertexShaderObject, fragmentShaderObject);
//链接shader
//创建一个程序对象
programObject = webgl.createProgram();
//将着色器变量关联到一个属性索引,该操作必须在链接之前进行
if (!bindVariableData instanceof Array) {
alert("输入参数有错");
return;
}
for (var i = 0; i < bindVariableData.length; i++) {
//将通用顶点索引绑定到属性变量
//console.log(programObject, bindVariableData[i][0], bindVariableData[i][1])
webgl.bindAttribLocation(programObject, bindVariableData[i][0], bindVariableData[i][1]);
}
programObject = linkShader(webgl, programObject, vertexShaderObject, fragmentShaderObject);
return programObject;
}
//编译shader
//shaderVectexCode shaderFramentCode glsl的字符串
function compileShader(webgl, shaderVectexCode, shaderFramentCode, vertexShaderObj, fragmentShaderObj) {
//将shader代码装载到shader Object中
webgl.shaderSource(vertexShaderObj, shaderVectexCode);
webgl.shaderSource(fragmentShaderObj, shaderFramentCode);
//编译shader代码
webgl.compileShader(vertexShaderObj);
webgl.compileShader(fragmentShaderObj);
//检查是否编译成功
if (!webgl.getShaderParameter(vertexShaderObj, webgl.COMPILE_STATUS)) {
alert("error:vertexShaderObject");
return;
}
if (!webgl.getShaderParameter(fragmentShaderObj, webgl.COMPILE_STATUS)) {
alert("error:framentShaderObject");
return;
}
}
//顶点数据
function initVextexData(webgl) {
//顶点坐标
var jsArrayData = [
0.0, 1.0, 0.0,
-1.0, 0.0, -1.0,
1.0, 0.0, 0.0
]
//创建一个webgl能够访问的缓冲
var triangleBuffer = webgl.createBuffer();
//绑定buffer
webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);
//将js数据拷贝到buffer上
webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(jsArrayData), webgl.STATIC_DRAW);
//颜色数据
var jsArrayColor = [
1.0, 0.0, 0.0, //上顶点
0.0, 1.0, 0.0, //左顶点
0.0, 0.0, 1.0 //右顶点
];
//创建颜色缓冲,将颜色数据拷贝进颜色缓冲
triangleColorBuffer = webgl.createBuffer();
webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleColorBuffer);
webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(jsArrayColor), webgl.STATIC_DRAW);
return triangleBuffer;
}
//渲染场景
function renderScene() {
//开始绘制
//清空屏幕
webgl.clearColor(0.0, 0.0, 0.0, 1.0);
webgl.clear(webgl.COLOR_BUFFER_BIT);
//webgl中顶点数组数据可能N个,我们这里需要告诉webgl我们用哪一个,
//使用缓冲区
//绑定一个顶点数组数据
webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);
//用以说明 enableVertexArray() 是如何激活顶点属性,并将顶点数据传输到shader函数的。
//启动关联索引上的数据 //可以打开属性数组列表中指定索引处的通用顶点属性数组
webgl.enableVertexAttribArray(v3PositionIndex);
//指定关联索引上的数据元素或者元素数据的正确信息 变量间隔
webgl.vertexAttribPointer(v3PositionIndex, 3, webgl.FLOAT, false, 0, 0);
//绑定颜色buffer
webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleColorBuffer);
webgl.enableVertexAttribArray(v3ColorIndex);
webgl.vertexAttribPointer(v3ColorIndex, 3, webgl.FLOAT, false, 0, 0);
//https://blog.csdn.net/ithanmang/article/details/89520623
//绘制数据
webgl.drawArrays(webgl.TRIANGLES, 0, 3);
}
//不断改变rgb值,实现变色的动画
function changeColor() { //isDesc默认fasle
if (isDesc && r <= 0.1){
isDesc = false;
}
if (!isDesc && r >= 1.0){
isDesc = true;
}
!isDesc ? r += 0.01 : r -= 0.01;
!isDesc ? g += 0.01 : g -= 0.01;
!isDesc ? b += 0.01 : b -= 0.01;
var jsArrayColor = [//数据 ——》显卡
r, 0.0, 0.0, //上顶点
0.0, g, 0.0, //左顶点
0.0, 0.0, b //右顶点
];
//内存 显卡
triangleColorBuffer = webgl.createBuffer(); //一个用于储存顶点数据或着色数据的WebGLBuffer对象
webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleColorBuffer);//将给定的WebGLBuffer绑定到目标 存储的类型,顶点类型
//赋值 //webgl.STATIC_DRAW 静态还是动态,是不是经常改
webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(jsArrayColor), webgl.STATIC_DRAW);
renderScene();
}
//初始化canvas
function initCanvas(canvasId) {
//获取绘制上下文
var myCanvasObject = document.getElementById(canvasId);
var context = null;
try {
context = myCanvasObject.getContext("experimental-webgl");
//设置视口
context.viewport(0, 0, myCanvasObject.width, myCanvasObject.height);
} catch (ex) {
alert(ex.toString());
}
if (!context) {
alert("我靠, 你的浏览器不支持WebGL,换个浏览器吧");
return null;
}
return context;
}
//解析Shader代码
function shaderSourceFromScript(scriptID) {
var shaderScript = document.getElementById(scriptID);
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;
}
//链接Shader程序
function linkShader(webgl, programObj, vertexShaderObj, fragmentShaderObj) {
//一个程序对象只能并且必须附带一个顶点着色器和片段着色器
webgl.attachShader(programObj, vertexShaderObj);
webgl.attachShader(programObj, fragmentShaderObj);
//将着色器变量关联到一个属性索引
// webgl.bindAttribLocation(programObj, v3PositionIndex, "v3Position");
webgl.linkProgram(programObj);
//检查是否链接成功
if (!webgl.getProgramParameter(programObj, webgl.LINK_STATUS)) {
alert("error:ProgramObject");
return;
}
return programObj;
}
</script>
</head>
<body onload="init()">
<canvas id="webglCanvas" style="border:1px solid red" width="600" height="600"></canvas>
</body>
</html>