从今天开始,正式进入webgl的世界,正式由一个写C++,C#,java的码农转变成写前端的码农。还是有那么一点点兴奋。
这是webgl的第一篇博客,主要通过webgl绘制一个三角形。
调用流程:
1 | 获取webgl上下文 |
2 | 获取着色器字符串 |
3 | 创建,加载,编译着色器 |
4 | 给着色器的变量赋值 |
5 | 绘制图形 |
效果图:
详细步骤:
步骤一:获取webgl上下文,并告诉webgl作用的空间大小,设置画布的默认值为黑色
// 获取WebGL上下文
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}
// 告诉WebGL怎样把提供的gl_Position裁剪空间坐标对应到画布像素坐标(屏幕空间)
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// 清空画布
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
步骤二:获取着色器字符串,这里主要分为两个阶段,第一个阶段,定义着色器,第二个阶段 获取着色器字符串
着色器需要定义定点着色器和片元着色器
定点着色器定义如下:主要实现对坐标点位置的设置
<script id="2d-vertex-shader" type="notjs">
// 一个属性值,将会从缓冲中获取数据
attribute vec4 a_position;
// 所有着色器都有一个main方法
void main() {
// gl_Position 是一个顶点着色器主要设置的变量
gl_Position = a_position;
}
</script>
片元着色器定义如下,主要设置渲染的颜色
<script id="2d-fragment-shader" type="notjs">
// 片断着色器没有默认精度,所以我们需要设置一个精度
// mediump是一个不错的默认值,代表“medium precision”(中等精度)
precision mediump float;
void main() {
// gl_FragColor是一个片断着色器主要设置的变量
gl_FragColor = vec4(1, 0, 0.5, 1); // 返回“瑞迪施紫色”
}
</script>
最后在js代码里面,获取着色器字符串
// 获取着色器字符串
var vertexShaderSource = document.getElementById("2d-vertex-shader").text;
var fragmentShaderSource = document.getElementById("2d-fragment-shader").text;
步骤三:创建程序,加载,编译,连接着色器
创建一个着色器的公共函数:
function createShader(gl, type, source) {
var shader = gl.createShader(type);// 创建着色器对象
gl.shaderSource(shader, source);// 提供数据源
gl.compileShader(shader);// 编译 -> 生成着色器
var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.log(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
创建定点着色器和片元着色器
// 创建,加载,编译着色器
var vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
把定点着色器和片元着色器link到着色程序,这里写成一个公共函数
// 然后我们将这两个着色器 link(链接)到一个 program(着色程序)
function createProgram(gl, vertexShader, fragmentShader) {
var program = gl.createProgram();//创建着色程序
gl.attachShader(program, vertexShader);// 附加顶点着色器
gl.attachShader(program, fragmentShader);// 附加片元着色器
gl.linkProgram(program);//链接到着色程序
var success = gl.getProgramParameter(program, gl.LINK_STATUS);//判断着色程序是否创建成功
if (success) {
return program;
}
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
在js代码中,链接到着色程序以及告诉webgl使用这个着色程序
// 链接连个着色器到着色程序
var program = createProgram(gl, vertexShader, fragmentShader);
// 告诉它用我们之前写好的着色程序(一个着色器对)
gl.useProgram(program);
第四步:给着色器的变量赋值,这里主要是给定点变量赋值
下面代码功能为:获取变量a_position,定义一个数组,有3个点,再把数据放置到webgl的缓冲区,通过函数vertexAttribPointer告诉程序如何从缓冲区获取数据。最后启用属性
// 从刚才创建的GLSL着色程序中找到a_position属性值所在的位置
var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
// 三个二维点坐标
var positions = new Float32Array(
[0, 0,
0, 0.5,
0.7, 0,]
);
// 属性值从缓冲中获取数据,所以我们创建一个缓冲
var positionBuffer = gl.createBuffer();
// 绑定位置信息缓冲(下面的绑定点就是ARRAY_BUFFER)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
// 告诉属性怎么从positionBuffer中读取数据 (ARRAY_BUFFER)
var size = 2; // 每次迭代运行提取两个单位数据
var type = gl.FLOAT; // 每个单位的数据类型是32位浮点型
var normalize = false; // 不需要归一化数据
var stride = positions.BYTES_PER_ELEMENT*2; // 0 = 移动单位数量 * 每个单位占用内存(sizeof(type))每次迭代运行运动多少内存到下一个数据开始点
var offset = 0; // 从缓冲起始位置开始读取
gl.vertexAttribPointer(
positionAttributeLocation, size, type, normalize, stride, offset);
// 启用对应属性
gl.enableVertexAttribArray(positionAttributeLocation);
第五步:绘制图像,这里绘制一个三角形,一共3个点。
// 绘制
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 3;
gl.drawArrays(primitiveType, offset, count);