WebGL入门---编写第一个 WebGL 程序
WebGL 应用包含两个要素:JavaScript程序和着色器程序。本节我们通过绘制一个点来演示这个过程,麻雀虽小,但五脏俱全。使用 WebGL 绘制一个点虽然简单,但是它仍需要 JavaScript 程序和着色器程序共同完成。
我们的目标是绘制一个在屏幕中心,大小为 10,颜色是红色的点。
1、准备着色器源码
我们从着色器程序开始入手,先用GLSL编写顶点着色器和片元着色器。
- 顶点着色器
顶点着色器的主要任务是告诉 GPU 在裁剪坐标系的原点(也就是屏幕中心)画一个大小为 10 的点。
void main(){
//声明顶点位置
gl_Position = vec4(0.0, 0.0, 0.0, 1.0); // 分号不能少,要不然编译有问题
//声明待绘制的点的大小。
gl_PointSize = 10.0; // 分号不能少,要不然编译有问题
}
- 片元着色器
顶点着色器中的数据经过图元装配和光栅化之后,来到了片元着色器,在本例中,片元着色器的任务是通知 GPU 将光栅化后的像素渲染成红色,所以片元着色器要对内置变量 gl_FragColor (代表像素要填充的颜色)进行赋值。
void main(){
//设置像素的填充颜色为红色。
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 分号不能少,要不然编译有问题
}
2、准备 HTML 文件
目前的html页面代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>从一个点开始</title><meta charset="UTF-8">
</head>
<body>
<canvas id="canvas"></canvas>
<script type="shader-source" id="vertexShader">
void main(){
// 声明顶点的位置
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
// 声明待绘制点的大小
gl_PointSize = 10.0;
}
</script>
<script type="shader-source" id="fragmentShader">
void main(){
//设置像素的填充颜色为红色。
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
</script>
</body>
</html>
3、JavaScript 程序
获取canvas对象下的WEBGL环境
var canvas = document.querySelector('#canvas');
//在某些浏览器中,我们还需要做下兼容处理,加上实验前缀。
var gl = canvas.getContext('webgl') || canvas.getContext("experimental-webgl");
创建顶点着色器对象:
// 获取顶点着色器源码
var vertexShaderSource = document.querySelector('#vertexShader').innerHTML;
// 创建顶点着色器对象
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
// 将源码分配给顶点着色器对象
gl.shaderSource(vertexShader, vertexShaderSource);
// 编译顶点着色器程序
gl.compileShader(vertexShader);
接下来,创建片元着色器,该过程和顶点着色器的创建过程类似,区别在于着色器源码和着色器类型。
// 获取片元着色器源码
var fragmentShaderSource = document.querySelector('#fragmentShader').innerHTML;
// 创建片元着色器程序
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
// 将源码分配给片元着色器对象
gl.shaderSource(fragmentShader, fragmentShaderSource);
// 编译片元着色器
gl.compileShader(fragmentShader);
着色器对象创建完毕,接下来我们开始创建着色器程序
//创建着色器程序
var program = gl.createProgram();
//将顶点着色器挂载在着色器程序上。
gl.attachShader(program, vertexShader);
//将片元着色器挂载在着色器程序上。
gl.attachShader(program, fragmentShader);
//链接着色器程序
gl.linkProgram(program);
// 使用刚创建好的着色器程序。
gl.useProgram(program);
准备工作做好了,接下来开始绘制:
//设置清空画布颜色为黑色。
gl.clearColor(0.0, 0.0, 0.0, 1.0);
//用上一步设置的清空画布颜色清空画布。
gl.clear(gl.COLOR_BUFFER_BIT);
//绘制点。
gl.drawArrays(gl.POINTS, 0, 1);
完整的html代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>从一个点开始</title><meta charset="UTF-8">
</head>
<body>
<canvas id="canvas"></canvas>
<script type="shader-source" id="vertexShader">
void main(){
// 声明顶点的位置
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
// 声明待绘制点的大小
gl_PointSize = 10.0;
}
</script>
<script type="shader-source" id="fragmentShader">
void main(){
//设置像素的填充颜色为红色。
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
</script>
<script>
// 获取canvas元素
var canvas = document.querySelector('#canvas');
// 在某些浏览器中,我们还需要做下兼容处理,加上实验前缀。
var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
// 获取顶点着色器源码
var vertexShaderSource = document.querySelector('#vertexShader').innerHTML
// 创建顶点着色器对象
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
// 将源码分配给顶点着色器对象
gl.shaderSource(vertexShader, vertexShaderSource);
// 编译顶点着色器程序
gl.compileShader(vertexShader)
// 获取偏远着色器源码
var fragmentShaderSource = document.querySelector('#fragmentShader').innerHTML;
// 创建片元着色器程序
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
// 将源码分配给片元着色器对象
gl.shaderSource(fragmentShader,fragmentShaderSource);
// 编译片元着色器
gl.compileShader(fragmentShader)
// 创建着色器程序
var program = gl.createProgram();
// 将顶点着色器挂载在着色器程序上
gl.attachShader(program, vertexShader);
// 将片元着色器挂在在着色器程序上
gl.attachShader(program, fragmentShader);
// 链接着色器程序
gl.linkProgram(program)
// 有时候一个 WebGL 应用包含多个 program,所以在使用某个 program 绘制之前,我们要先启用它。
// 使用刚创建好的着色器程序。
gl.useProgram(program);
// 设置清空画布颜色为黑色。
gl.clearColor(0.0,0.0,0.0,1.0);
// 用上一步设置的清空画布颜色清空画布
gl.clear(gl.COLOR_BUFFER_BIT)
// 绘制点
gl.drawArrays(gl.POINTS, 0, 1)
</script>
</body>
</html>
重复代码的封装
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>从一个点开始</title><meta charset="UTF-8">
</head>
<body>
<canvas id="canvas"></canvas>
<script type="shader-source" id="vertexShader">
void main(){
// 声明顶点的位置
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
// 声明待绘制点的大小
gl_PointSize = 10.0;
}
</script>
<script type="shader-source" id="fragmentShader">
void main(){
//设置像素的填充颜色为红色。
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
</script>
<script>
function $$(str) {
if (!str) return null;
if (str.startsWith('#')) {
return document.querySelector(str);
}
let result = document.querySelectorAll(str);
if (result.length == 1) {
return result[0];
}
return result;
}
function getCanvas(id) {
return $$(id);
}
function getContext(canvas) {
return canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
}
/**
*
*
* @param {*} gl,webgl绘图环境
* @param {*} type,着色器类型
* @param {*} scriptId,着色器源码scrpit标签id
* @returns 返回着色器对象
*/
function createShaderFromScript(gl, type, scriptId) {
let sourceScript = $$('#' + scriptId);
if (!sourceScript) {
return null;
}
return createShader(gl, type, sourceScript.innerHTML);
}
function createShader(gl, type, source) {
let shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
//检测是否编译正常。
let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.error(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
function createProgram(gl, vertexShader, fragmentShader) {
let program = gl.createProgram();
vertexShader && gl.attachShader(program, vertexShader);
fragmentShader && gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
let result = gl.getProgramParameter(program, gl.LINK_STATUS);
if(result){
return program
}
let errorLog = gl.getProgramInfoLog(program);
gl.deleteProgram(program);
throw errorLog;
}
</script>
<script>
//获取canvas
var canvas = getCanvas(id);
//获取webgl绘图环境
var gl = getWebGLContext(canvas);
//创建顶点着色器
var vertexShader = createShaderFromScript(gl, gl.VERTEX_SHADER,'vertexShader');
//创建片元着色器
var fragmentShader = createShaderFromScript(gl, gl.FRAGMENT_SHADER,'fragmentShader');
//创建着色器程序
var program = createProgram(gl ,vertexShader, fragmentShader);
//告诉 WebGL 运行哪个着色器程序
gl.useProgram(program);
//设置清空画布颜色为黑色。
gl.clearColor(0.0, 0.0, 0.0, 1.0);
//用上一步设置的清空画布颜色清空画布。
gl.clear(gl.COLOR_BUFFER_BIT);
//绘制点
gl.drawArrays(gl.POINTS, 0, 1);
</script>
</body>
</html>