这学期选修了计算机图形学这门课,最近刚开始接触WebGL,目前只学到二维的部分,尝试绘制一只平面的小鸡。

效果图

首先上效果图:

下面直接进入代码部分:

drawCircle.html

<!DOCTYPE html>
<html><head lang="en"><meta charset="UTF-8"><title>DrawCircle</title><script id="vertex-shader" type="x-shader/x-vertex">attribute vec4 vPosition;uniform mat4 u_transMat;void main(){gl_Position = u_transMat * vPosition;}</script><script id="fragment-shader" type="x-shader/x-fragment">precision mediump float;uniform vec4 u_FragColor;void main(){gl_FragColor = u_FragColor;}</script><script type="text/javascript" src="../Common/webgl-utils.js"></script><script type="text/javascript" src="../Common/initShaders.js"></script><script type="text/javascript" src="drawCircle.js"></script></head><body><canvas id="gl-canvas" width="512" height="512">Oops ... your browser doesn't support the HTML5 canvas element</canvas></body>
</html>

在html文件中,声明了两个着色器。
顶点着色器内,设置了attribute变量vPosition用于记录顶点的属性,uniform变量u_transMat为变换矩阵,用于对绘制出的图案进行旋转平移操作。
片元着色器内,首先设置精度为中低精度,uniform变量u_FragColor用于记录片元颜色。
body中,声明一个canvas画布,所有的绘制都在画布上进行。
需要注意的是,gl_Position的值是用变换矩阵 * 顶点坐标向量得到的,变换矩阵和顶点坐标向量的位置不能交换,因为我们最终需要得到的gl_Position是一个四阶行向量。

drawCircle.js

js代码部分比较长,让我分解为多个部分。
通过对小鸡的分析,不难得知,组成该图形的图形为三角形、圆形、线段。

1. 我们首先来介绍绘制圆的函数getCircleVertex。

// 画圆
// 半径r 面数m 度数c
function getCircleVertex(r, m, c) {var arr = [];var addAng = c / m;var angle = 0;for (var i = 0; i < m; i++) {arr.push(Math.sin(Math.PI / 180 * angle) * r, Math.cos(Math.PI / 180 * angle) * r, 0, 1.0);arr.push(0.0, 0.0, 0.0, 1.0);angle = angle + addAng;arr.push(Math.sin(Math.PI / 180 * angle) * r, Math.cos(Math.PI / 180 * angle) * r, 0, 1.0);}return arr;
}

由于WebGL没有提供圆这个基本图形,于是我们把圆细分,通过绘制多个三角形来达到圆的效果。该函数它有三个参数:r表示圆的半径,m表示绘制该圆需要的三角形面数(即个数,数字越大,圆的效果越好),c表示需要绘制的度数(c取360时为整圆,取180时为半圆,以此类推),函数返回值为存储了构成圆的每个三角形的三个顶点的数组(有点绕)。

观察for循环内部,我们使用JavaScript内置的Math.sin()和Math.cos()来计算三角形(可看做扇形)圆心角的正余弦,但这两个方法必须接收弧度值,所以要先把角度转换为弧度值。
由于最后我们使用gl.TRIANGLES来绘制三角形,因此每一次for循环就将三角形的三个角的坐标加入到arr数组中。

2. 接着进入js主体代码部分

 window.onload = function init () {var canvas = document.getElementById( "gl-canvas" );var gl = WebGLUtils.setupWebGL( canvas );if ( !gl ) { alert( "WebGL isn't available" ); }// 设置窗口大小gl.viewport( 0, 0, canvas.width, canvas.height );gl.clearColor( 0.0, 0.0, 0.0, 1.0 );// 初始化着色器var program = initShaders( gl, "vertex-shader", "fragment-shader" );gl.useProgram( program );// 获取vPosition变量的存储位置var vPosition = gl.getAttribLocation(program, "vPosition");if (vPosition < 0) {console.log('Failed to get the storage location of vPosition');return;}// 获取u_transMat变量的存储位置var u_transMat = gl.getUniformLocation(program, "u_transMat");if (u_transMat < 0) {console.log('Failed to get the storage location of u_transMat');return;}// 获取u_FragColor变量的存储位置var u_FragColor = gl.getUniformLocation(program, 'u_FragColor');if (!u_FragColor) {console.log('Failed to get the storage location of u_FragColor');return;}var colors = [[1.0, 0.843, 0.0, 1.0], //金黄色[1.0, 0.647, 0.0, 1.0], //橙色[0.824, 0.412, 0.118, 1.0], //巧克力色[0.0, 0.0, 0.0, 1.0] //黑色];

以上完成了基本的初始化操作。我们首先获取到canvas画布,然后得到绘图上下文gl,设置清空画布的颜色为黑色,并初始化着色器。获取我们之前设置在着色器内attribute和uniform变量的存储位置,这里为了提高代码的稳健性,我们对获取到的变量进行了非空校验。最后定义了一个color数组,用于存储绘制中需要用到的四种颜色。

3. 下面,步入真正的绘制阶段。

注意:需要按照一定的顺序来进行绘制,因为当绘制部分有重叠时,后绘制的部分会遮挡先绘制的部分。
所有绘制点的坐标需要在纸上提前设计、演算好,这里只做对绘制过程的大致解释。

* 首先绘制鸡的嘴巴(三角形):
    // 画鸡嘴(三角形)vertices = [-0.392, -0.04, 0.0, 1.0,-0.42, -0.08, 0.0, 1.0,-0.36, -0.08, 0.0, 1.0];mat4 = new Float32Array([1.0, 0.0, 0.0, 0.0,0.0, 1.0, 0.0, 0.0,0.0, 0.0, 1.0, 0.0,0.0, 0.0, 0.0, 1.0]);// 创建缓存var buffer = gl.createBuffer(); // 为顶点创建的缓存gl.bindBuffer(gl.ARRAY_BUFFER, buffer); // 绑定缓冲区gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);gl.vertexAttribPointer(vPosition, 4, gl.FLOAT, false, 0, 0);gl.enableVertexAttribArray(vPosition);gl.uniform4f(u_FragColor, colors[1][0], colors[1][1], colors[1][2], colors[1][3]);gl.uniformMatrix4fv(u_transMat, false, mat4);gl.clear(gl.COLOR_BUFFER_BIT);gl.drawArrays(gl.TRIANGLES, 0, 3);

鸡的嘴巴是一个三角形,我们直接将计算出的三角形的三个顶点赋值给vertices数组,此处不需要对三角形进行平移或旋转操作,因此直接将旋转矩阵的值给为单位矩阵即可。由于是第一次绘制,我们首先需要为顶点创建出缓冲区,然后就是常规的操作了,将缓冲区和顶点绑定,向缓冲区内写入已创建好的顶点数据vertices,将点的位置传递到attribute变量中,激活变量,向uniform变量写入数据(分别写入了片元颜色和变换矩阵的数据)。最后,在第一次绘制前,我们需要清空画布,然后再开始绘制。至此,鸡嘴就完成了。

* 下面进入鸡头(圆形)的绘制:
// 画鸡头(圆)var ms = 180; // 组成圆的划分三角形个数var vertices = getCircleVertex(0.1, ms, 360);var Tx = -0.3;var Ty = 0.0;var Tz = 0.0;var mat4 = new Float32Array([1.0, 0.0, 0.0, 0.0,0.0, 1.0, 0.0, 0.0,0.0, 0.0, 1.0, 0.0,Tx, Ty, Tz, 1.0]);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // 向缓冲区写入顶点数据gl.vertexAttribPointer(vPosition, 4, gl.FLOAT, false, 0, 0);gl.uniform4f(u_FragColor, colors[0][0], colors[0][1], colors[0][2], colors[0][3]);gl.uniformMatrix4fv(u_transMat, false, mat4);gl.drawArrays(gl.TRIANGLES, 0, ms*3);

定义一个ms变量,用于表示划分圆所使用的划分三角形个数。利用我们已经写好的getCircleVertex函数,传入相应参数,此处头的半径给为0.1,就可以得到所有的划分三角形的顶点数组了。利用这些三角形数据,作出的圆的圆心是位于原点处的,我们还需要为变换矩阵进行赋值,以使圆平移到正确的位置。mat4矩阵是一个平移矩阵,Tx、Ty、Tz分别表示需要在x、y、z轴上平移的距离,由于我们是作的二维图形,Tz直接赋为0就好,Tx、Ty根据自己的设计进行赋值,此处我将它们分别赋值为-0.3, 0.0,负数表示向负方向平移。
然后的操作就和第一步绘制鸡嘴巴的类似了,只不过这次不用再清空画布,否则第一次绘制的图像会消失,只需要将相应的数据再次绑定到缓冲区,赋值给相应的变量即可,最后,19行这里调用画图函数时要注意,将第三个参数更改为ms*3,因为我们一共绘制了ms数量个三角形。

* 其余部分的绘制:

剩下的绘制部分就大同小异了,不再一一赘述。只对某些部分进行一定的解释:

  1. 对于需要同时进行旋转和平移操作的图形,要在草稿纸上计算好旋转矩阵和平移矩阵的积,将这个结果赋值给变换矩阵。
  2. 在绘制鸡的身体和尾巴时,它们都不是整圆,在给getCircleVertex函数传入参数时,第三个参数要相应修改为扇形的圆心角的角度值。
  3. 鸡的左腿和右腿是用线段来绘制的,由于单条线段的宽度过小(此处我在网上搜索过一些改变线的宽度的例子,但我实际使用后反应无效,无奈只能想出了以下办法),我在代码中同时传入了两条邻近线段的顶点值,并使用gl.LINE_LOOP的绘制方式,让这两条线段绘制的位置贴近,视觉上达到加粗的效果。
// 画鸡内侧翅膀(0.1半圆)vertices = getCircleVertex(0.1, ms, 180);mat4 = new Float32Array([0.866, -0.5, 0.0, 0.0,0.5, 0.866, 0.0, 0.0,0.0, 0.0, 1.0, 0.0,-0.05, 0.02, 0.0, 1.0]);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);gl.vertexAttribPointer(vPosition, 4, gl.FLOAT, false, 0, 0);gl.uniform4f(u_FragColor, colors[1][0], colors[1][1], colors[1][2], colors[1][3]);gl.uniformMatrix4fv(u_transMat, false, mat4);gl.drawArrays(gl.TRIANGLES, 0, ms*3);// 画鸡身体(半圆)vertices = getCircleVertex(0.2, ms, 180);mat4 = new Float32Array([0.0, -1.0, 0.0, 0.0,1.0, 0.0, 0.0, 0.0,0.0, 0.0, 1.0, 0.0,0.0, 0.0, 0.0, 1.0]);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);gl.vertexAttribPointer(vPosition, 4, gl.FLOAT, false, 0, 0);gl.uniform4f(u_FragColor, colors[0][0], colors[0][1], colors[0][2], colors[0][3]);gl.uniformMatrix4fv(u_transMat, false, mat4);gl.drawArrays(gl.TRIANGLES, 0, ms*3);// 画鸡尾巴(60°扇形)vertices = getCircleVertex(0.08, ms, 60);mat4 = new Float32Array([0.5, -0.866, 0.0, 0.0,0.866, 0.5, 0.0, 0.0,0.0, 0.0, 1.0, 0.0,0.2, 0.0, 0.0, 1.0]);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);gl.vertexAttribPointer(vPosition, 4, gl.FLOAT, false, 0, 0);gl.uniformMatrix4fv(u_transMat, false, mat4);gl.drawArrays(gl.TRIANGLES, 0, ms*3);// 画鸡外侧翅膀(0.15半圆)vertices = getCircleVertex(0.15, ms, 180);mat4 = new Float32Array([0.707, -0.707, 0.0, 0.0,0.707, 0.707, 0.0, 0.0,0.0, 0.0, 1.0, 0.0,0.0, 0.0, 0.0, 1.0]);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);gl.vertexAttribPointer(vPosition, 4, gl.FLOAT, false, 0, 0);gl.uniform4f(u_FragColor, colors[1][0], colors[1][1], colors[1][2], colors[1][3]);gl.uniformMatrix4fv(u_transMat, false, mat4);gl.drawArrays(gl.TRIANGLES, 0, ms*3);// 画鸡左腿(直线)vertices = [-0.05, -0.19, 0.0, 1.0,-0.05, -0.25, 0.0, 1.0,-0.045, -0.195, 0.0, 1.0,-0.045, -0.25, 0.0, 1.0];mat4 = new Float32Array([1.0, 0.0, 0.0, 0.0,0.0, 1.0, 0.0, 0.0,0.0, 0.0, 1.0, 0.0,0.0, 0.0, 0.0, 1.0]);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);gl.vertexAttribPointer(vPosition, 4, gl.FLOAT, false, 0, 0);gl.uniform4f(u_FragColor, colors[2][0], colors[2][1], colors[2][2], colors[2][3]);gl.uniformMatrix4fv(u_transMat, false, mat4);gl.drawArrays(gl.LINE_LOOP, 0, 4);// 画鸡右腿(直线)vertices = [0.05, -0.19, 0.0, 1.0,0.05, -0.25, 0.0, 1.0,0.045, -0.195, 0.0, 1.0,0.045, -0.25, 0.0, 1.0];gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);gl.vertexAttribPointer(vPosition, 4, gl.FLOAT, false, 0, 0);gl.uniformMatrix4fv(u_transMat, false, mat4);gl.drawArrays(gl.LINE_LOOP, 0, 4);// 画鸡左脚(三角形)vertices = [-0.045, -0.25, 0.0, 1.0,-0.04, -0.28, 0.0, 1.0,-0.09, -0.28, 0.0, 1.0];gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);gl.vertexAttribPointer(vPosition, 4, gl.FLOAT, false, 0, 0);gl.uniformMatrix4fv(u_transMat, false, mat4);gl.drawArrays(gl.TRIANGLES, 0, 3);// 画鸡右脚(三角形)vertices = [0.05, -0.25, 0.0, 1.0,0.0, -0.28, 0.0, 1.0,0.055, -0.28, 0.0, 1.0];gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);gl.vertexAttribPointer(vPosition, 4, gl.FLOAT, false, 0, 0);gl.uniformMatrix4fv(u_transMat, false, mat4);gl.drawArrays(gl.TRIANGLES, 0, 3);// 画鸡眼睛(圆)var vertices = getCircleVertex(0.01, ms, 360);var Tx = -0.35;var Ty = -0.015;var Tz = 0.0;var mat4 = new Float32Array([1.0, 0.0, 0.0, 0.0,0.0, 1.0, 0.0, 0.0,0.0, 0.0, 1.0, 0.0,Tx, Ty, Tz, 1.0]);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // 向缓冲区写入顶点数据gl.vertexAttribPointer(vPosition, 4, gl.FLOAT, false, 0, 0);gl.uniform4f(u_FragColor, colors[3][0], colors[3][1], colors[3][2], colors[3][3]);gl.uniformMatrix4fv(u_transMat, false, mat4);gl.drawArrays(gl.TRIANGLES, 0, ms*3);};

至此,一个小鸡就完成了。

使用WebGL绘制一只平面小鸡相关推荐

  1. cad沿线插入块 lisp_CAD制图软件中如何快速绘制推拉窗平面简图

    CAD建筑软件中为了方便绘图提供了强大的建筑门窗绘制功能.但是当电脑中没有CAD建筑软件的时候,怎么才能快速绘制出门窗或者简图呢?下面给大家分享一下CAD入门学习教程之在浩辰CAD中绘制推拉窗平面简图 ...

  2. 【视觉高级篇】20 # 如何用WebGL绘制3D物体?

    说明 [跟月影学可视化]学习笔记. 如何用 WebGL 绘制三维立方体 我们知道立方体有8个顶点,6个面,在 WebGL 中,需要用 12 个三角形来绘制它.把每个面的顶点分开,需要 24 个顶点. ...

  3. WebGL绘制立方体-每个面一种颜色

    WebGL绘制立方体-每个面一种颜色 本文是WebGL电子书的1.8节内容 思路很简单,在线框模式的立方体源码基础上直接进行更改,添加varying变量,引入顶点数据颜色,立方体6个面,每个面可以分为 ...

  4. WebGL绘制带箭头贴图的线

    示例 在讲述本文内容之前,我希望读者先具备以下知识点: 了解WebGL的基本知识,懂得调用自定义的Shader程序: 基本的数学基础和空间几何知识: 明白GPU的渲染管线流程: 因为,本文内容主要讲述 ...

  5. 利用javascript和WebGL绘制地球 【翻译】

    利用javascript和WebGL绘制地球 [翻译] 原翻译:利用javascript和WebGL绘制地球 [翻译] 在我们所有已知的HTML5API中,WebGL可能是最有意思的一个,利用这个AP ...

  6. 成功解决利用matplotlib.pyplot进行绘图的时候整个画布中的绘制曲线只显示一部分

    成功解决利用matplotlib.pyplot进行绘图的时候整个画布中的绘制曲线只显示一部分 目录 解决问题 解决思路 解决方法 解决问题 利用matplotlib.pyplot进行绘图的时候整个画布 ...

  7. 公鸡5元每只,母鸡3元每只,小鸡3只1元,100元买一百只鸡多少种办法?

    通过for循环实现百钱买百鸡的问题: 公鸡5元每只,母鸡3元每只,小鸡3只1元,100元买一百只鸡多少种办法? var count=0;             for (var i = 0; i&l ...

  8. Java练习习题,百钱买百鸡问题,用100文钱买鸡,公鸡5文钱一只,母鸡3文钱一只,小鸡3只1文钱

    需求说明: 用100文钱买鸡,公鸡5文钱一只,母鸡3文钱一只,小鸡3只1文钱,要求公鸡.母鸡.小鸡都必须要有,刚好用完100文钱,公鸡.母鸡.小鸡的数量之和也是100. public class te ...

  9. 要求用 100 元买 100 只鸡,其中公鸡五元一只,母鸡三元一只,小鸡 1 元三只,规定每种至少买一 只

    要求用 100 元买 100 只鸡,其中公鸡五元一只,母鸡三元一只,小鸡 1 元三只,规定每种至少买一 只 public static void main(String[] args) {for(in ...

最新文章

  1. C#WinForm制作异形窗体/控件
  2. 织梦后台添加友链,前台不显示|修改友情链接的显示行数
  3. 图像处理之基础---仿射变换
  4. eclipse在server中tomcat server找不到的问题
  5. Redux学习(一)——Redux的使用过程
  6. 计算机机器人方向,计算机考研想学习智能机器人方向都有那几个学校呢..._考研_帮考网...
  7. 【python】【openCV】分水岭算法
  8. 浅谈缓冲区溢出之栈溢出上
  9. IDEA快捷键整理(最详细的)
  10. 玩转SSRS第五篇---客户端报表
  11. 用DELPHI为ASP开发文件上载组件
  12. MediaCoder参数设置教程
  13. 软件测试工程师工作必备模板五件套
  14. c语言开发 kdj,KDJ——随机指标之王
  15. 什么是漏极开路【转】
  16. 转载:软件工程师的视角看网络(网络那点事)
  17. MySQL 避坑宝典 -- 来自小米的开源工具
  18. 【CV】斯坦福CS231n DL-CV by李飞飞 team
  19. Android 大牛 国内、国外Android开发者博客
  20. Interop统计WORD字数

热门文章

  1. 使用GAPM_LIST_SET_CMD-GAPM_SET_WL设置白名单的经历
  2. Braintree-国外支付对接(一)
  3. :braintree_Laravel和Braintree:中间件和其他高级概念
  4. Android系统相册图片分享到自己应用内部
  5. Kinect开发教程四:用Kinect控制鼠标玩水果忍者PC版
  6. 多用户商城系统电子商务解决方案
  7. android 渠道索取,Android多渠道打包时获取当前渠道
  8. python synonyms 近义词
  9. golang,OpenGL,计算机图形学(一)
  10. spring的自定义标签