原生webgl學習(六) WebGL寫簡單的漢字
阿新 • • 發佈:2018-12-15
本專欄所有文章示例程式碼均可在我的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
];
程式實現效果
這種單個字的效果看起來可能不是很炫酷,下一節筆者將帶領大家一起繪製多個字,並控制它們的平移,旋轉,縮放。