原生webgl学习(六) WebGL写简单的汉字(一)
本专栏所有文章示例代码均可在我的gitee码云上获取,读者可自行下载:https://gitee.com/babyogl/learnWebGL;本文demo代码在chapter-03下的draw-word-01.html,读者可以自行下载查看;
如果读者还对本专栏的文章内容和编程方式还不是很了解,建议先对前面的文章进行学习:博客专栏:webgl基础学习。前面的几节课,笔者为大家演示了WebGL如何绘制三角形和矩形,也说过绘制这些图案当然不是无聊绘制着玩的,这节课我们就利用绘制多个三角形和矩形,然后来组成我们的一些简单的汉字。下面我们先来看一张图
这个“王”字(ps:会不会令你想起隔壁老王?哈哈) ,总共有四笔,我们可以用四个矩形来组成它,分别用16个顶点,以第一横为例,要画两个三角形,画图时顶点的顺序组合为:(v0,v1, v2),(v2,v1,v3),其实不只这种顺序组合,读者可以自行探索其他的组合。其他的笔画也按照这个顺序来,最终经过绘制后,就会有一个王字清晰地呈现在我们面前,像前面的文章一样,给其加一个交互菜单栏工具,用于控制其位置的平移、旋转缩放。不同的是,这一次的旋转,我们利用更加有逼格的形式实现。
着色器
来看一下着色器:
<script id="v-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
uniform vec2 u_resolution;//分辨率
uniform vec2 u_translate;//平移
uniform vec2 u_rotate;//旋转
uniform vec2 u_scale;//缩放
varying vec4 v_color;
void main()
{
vec2 sPosition = a_position * u_scale;
vec2 rotatePosition = vec2(
sPosition.x * u_rotate.y + sPosition.y * u_rotate.x,
sPosition.y * u_rotate.y - sPosition.x * u_rotate.x);
vec2 position = rotatePosition + u_translate;
vec2 zeroToOne = position / u_resolution;
vec2 zeroToTwo = zeroToOne * 2.0;
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);//(1,-1)将坐标原点设置为左上角
v_color = vec4(gl_Position.xyz * 0.5, 0.6);
}
</script>
<script id="f-shader" type="x-shader/x-vertex">
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_color;
void main()
{
gl_FragColor = v_color.rbga;
}
</script>
初始化数据
初始化相关的数据,包括平移、旋转、缩放、顶点位置、以及菜单栏相关命令初始化:
let translate = [100, 150];
let rotate = [0, 1];
let scale = [1, 1];
//王
let wang = [
//第一横
0, 0,
150, 0,
0, 30,
0, 30,
150, 0,
150, 30,
//第二横
0, 80,
150, 80,
0, 110,
0, 110,
150, 80,
150, 110,
//第三横
0, 160,
150, 160,
0, 190,
0, 190,
150, 160,
150, 190,
//竖
60, 30,
60, 160,
90, 30,
90, 30,
60, 160,
90, 160
];
let guiField = {
'平移X': translate[0],
'平移Y': translate[1],
'缩放X': scale[0],
'缩放Y': scale[1]
};
建立菜单栏和建立绘制函数
function initGui(gl, program) {
let gui = new dat.GUI();
gui.add(guiField, '平移X', 0, gl.canvas.width-200).onChange(function(e) {
translate[0] = e;
drawScene(gl, program);
});
gui.add(guiField, '平移Y', 0, gl.canvas.height-200).onChange(function(e) {
translate[1] = e;
drawScene(gl, program);
});
gui.add(guiField, '缩放X', -4, 4).onChange(function (e) {
scale[0] = e;
drawScene(gl, program)
});
gui.add(guiField, '缩放Y', -4, 4).onChange(function (e) {
scale[1] = e;
drawScene(gl, program)
});
let ui = document.getElementById('ui-translate');
ui.appendChild(gui.domElement);
}
//获取着色器中的attribute变量的位置
function getAttribute(gl, program, name) {
return gl.getAttribLocation(program, name);
}
//获取着色器中的uniform变量的位置
function getUniform(gl, program, name) {
return gl.getUniformLocation(program, name)
}
function drawScene(gl, program) {
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
//告诉WebGL如何从剪辑空间转换为像素
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
//清除canvas
gl.clear(gl.COLOR_BUFFER_BIT);
//应用着色器程序
gl.useProgram(program);
//开启顶点属性
gl.enableVertexAttribArray(program.pLocation);
//绑定缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER, program.positionBuffer);
setGeometry(gl, wang);
//获取缓冲区数据
gl.vertexAttribPointer(program.pLocation, 2, gl.FLOAT, false, 0, 0);
//设置分辨率
gl.uniform2f(program.rLocation, gl.canvas.width, gl.canvas.height);
//旋转
gl.uniform2fv(program.roLocation, rotate);
//设置平移
gl.uniform2fv(program.tLocation, translate);
//设置缩放
gl.uniform2fv(program.sLocation, scale);
gl.drawArrays(gl.TRIANGLES, 0, wang.length / 2);
}
主函数
function main() {
let canvas = document.getElementById('translate');
let gl = canvas.getContext('webgl', {antialias: true, depth: false})
if (!gl) {
alert("您的浏览器不支持WebGL!")
}
//获取顶点和片段着色器文本
let vShaderSource = document.getElementById('v-shader').text;
let fShaderSource = document.getElementById('f-shader').text;
//创建着色器程序
let program = initShader(gl, vShaderSource, fShaderSource);
program.pLocation = getAttribute(gl, program, 'a_position');
program.rLocation = getUniform(gl, program, 'u_resolution');
program.tLocation = getUniform(gl, program, 'u_translate');
program.roLocation = getUniform(gl, program, 'u_rotate');
program.sLocation = getUniform(gl, program, 'u_scale');
//创建缓冲区
program.positionBuffer = gl.createBuffer();
//绑定缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER, program.positionBuffer);
drawScene(gl, program);
initGui(gl, program);
//利用jQuery绘制圆控制图形的旋转
$("#ui-rotate").gmanUnitCircle({
width: 200,
height: 200,
value: 0,
slide: function(e,u) {
rotate[0] = u.x;
rotate[1] = u.y;
drawScene(gl, program);
}
});
}
当然,除了“王”字,笔者也将构成“华”的顶点数组post出来,笔者可以自行尝试:
let hua = [
45, 60,
75, 80,
0, 140,
35, 120,
65, 120,
35, 240,
35, 240,
65, 120,
65, 240,
125, 80,
155, 80,
125, 240,
125, 240,
155, 80,
155, 240,
200, 80,
220, 100,
155, 140,
135, 210,
205, 210,
135, 240,
135, 240,
205, 210,
205, 240,
0, 250,
210, 250,
0, 280,
0, 280,
210, 250,
210, 280,
85, 180,
115, 180,
85, 400,
85, 400,
115, 180,
115, 400
];
程序实现效果
这种单个字的效果看起来可能不是很炫酷,下一节笔者将带领大家一起绘制多个字,并控制它们的平移,旋转,缩放。