学习WebGL之绘制三角形

本系列所有文章目录

本文将介绍如何使用WebGL绘制一个三角形,下面是运行截图,可以前往我的博客查看代码演示。

想要使用WebGL绘制一个三角形,我们需要先了解一些基本概念。

WebGL如何绘制几何体?

从三角形到四边形,再到正方体,或是更为复杂的3D模型,都可以称作几何体,它们都是由点组成的。三个点是三角形,四个点就是四边形。我们通常能想到关于点的描述无非就是位置,我们可以用一个数组来表示点的位置。比如x,y坐标位于(0,1), (-1,-1)(1,-1)的三个点就可以构成一个三角形。如果切换到3D世界,加上z轴的坐标,就是(0,1,0), (-1,-1,0)(1,-1,0)。我们把这些数据按照顺序都放到一个数组里,就变成了(0,1,0,-1,-1,0,1,-1,0),这就是WebGL需要的数据。不过只有这些是不够的,我们还要告诉WebGL,每个点的数据由3个数字构成,这样WebGL就知道每隔3个数取一次点的位置数据了。最后我们还需要告诉WebGL要怎样使用这些点,是填充一个三角形,还是画一个没有填充的三角形,甚至是只画3个点。一个顶点所能包含的信息远远不止位置,这将在后续的文章中详述。

在WebGL的世界里,通常称这些点为顶点。

坐标系

默认情况下,屏幕坐标如下图所示。x轴从左往右,y轴从下往上,z轴从里向外。

Shader概念

Shader是WebGL中很重要的概念,它主要分为Vertex Shader和Fragment Shader。Vertex Shader主要处理顶点数据,它将我们传递进去的顶点数据处理后告诉GPU,GPU就知道需要在哪绘制几何体了。Fragment Shader主要处理像素数据,每个像素在呈现前都要经过Fragment Shader的处理。下面是本文使用的两个简单的Shader。

<script type="x-shader/vertex-shader" id="shader-vs">
            attribute vec4 position;
            void main() {
                gl_Position = position;
            }
</script>
<script type="x-shader/fragment-shader" id="shader-fs">
            void main() {
                gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
            }
</script>

上面是Vertex Shader,attribute vec4 position;是我们传递进去的顶点位置,attribute表示这是一个顶点属性,vec4表示这是一个4维向量,由4个float组成,刚好对应x,y,z,ww是什么?在进行矩阵变换时要保持矩阵尺寸的匹配,所以需要用4个float表示位置,这将在后续文章中详细介绍。gl_Position是Shader内置的变量,赋给它的值最后会被交给GPU。

下面是Fragment Shader,只做了一件事,给像素赋予了白色vec4(1.0, 1.0, 1.0, 1.0);,rgba都是1,这里rgba的取值范围是0到1。gl_FragColor也是内置变量,它的值最终会赋给对应的像素。

这两个script节点写在了html中,可以通过getElementById获取到他们的文本。

使用Shader

使用Shader的方式比较繁琐,需要编译链接,你可以把Shader当做运行在GPU上的小程序,编译链接后才能在GPU上运行。下面是编译链接的代码。

function makeProgram() {
  program = gl.createProgram();

  vertexShaderNode = document.getElementById("shader-vs");
  vertexShader = makeShader(vertexShaderNode.textContent, gl.VERTEX_SHADER);
  fragmentShaderNode = document.getElementById("shader-fs");
  fragmentShader = makeShader(fragmentShaderNode.textContent, gl.FRAGMENT_SHADER);
  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);
  gl.linkProgram(program);

  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    console.log('Unable to initialize the shader program: ' + gl.getProgramInfoLog(program));
  }
  return program;
}

function makeShader(shaderSrc, shaderType) {
  shader = gl.createShader(shaderType);
  gl.shaderSource(shader, shaderSrc);
  gl.compileShader(shader);
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    console.log('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
    gl.deleteShader(shader);
    return null;
  }
  return shader;
}

makeShader负责编译单个Shader,makeProgram负责编译链接Vertex Shader和Fragment Shader。最后生成小程序program,它是连接CPU和GPU的桥梁,我会在渲染时使用它。

准备三角形的顶点数据

WebGL的顶点数据需要放到它生成的容器中才能被它使用。所以我们需要使用glcreateBuffer方法来生成需要的顶点数据容器,然后将顶点数据放到容器中。这里使用Float32Array是因为WebGL对数据大小很敏感,我们渲染时需要明确告诉它一个顶点数据有多大,所以这里使用32位的浮点数据明确的转换triangle数组。gl.STATIC_DRAW表示我们不会修改这块缓存,GPU会针对性的做一些优化。

function makeBuffer() {
  var triangle = [
    0.0, 1.0, 0.0, 
    -1.0, -1.0, 0.0,
    1.0, -1.0, 0.0,
  ];
  buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangle), gl.STATIC_DRAW);
  return buffer;
}

绘制三角形

gl.viewport(0,0,canvas.width,canvas.height);
gl.clearColor(1.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, triangleBuffer);
positionLoc = gl.getAttribLocation(program, 'position');
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 4 * 3, 0);

gl.drawArrays(gl.TRIANGLES, 0, 3);

绘制分为下面几步:

  • 设置viewport大小,viewport表示绘制的区域,你也可以设置0,0,100,100试试看绘制区域发生了什么样的变化。
  • 使用我们生成的programgl.useProgram(program);,这表示下面的绘制操作都会使用我们的Shader。
  • gl.bindBuffer(gl.ARRAY_BUFFER, triangleBuffer);,绑定三角形的顶点数据Buffer,表示我们要绘制这个三角形。
  • 下面三行正是我刚开始说的告诉WebGL我们的顶点数据格式是什么样的。首先获取Shader中position属性在Shader中的位置,然后使用这个位置激活该属性,最后告诉WebGLposition属性有3个gl.FLOAT大小,每个顶点数据的大小也是3个gl.FLOAT = 3 * 4 个字节,position属性在每个顶点数据中的偏移是0。读者可以参照上面这句话理解gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 4 * 3, 0);
  • gl.drawArrays(gl.TRIANGLES, 0, 3);绘制三角形,gl.TRIANGLES表示我想绘制填充的三角形,0表示从第0个顶点开始绘制,3表示绘制3个顶点。WebGL会使用这3个顶点绘制出一个填充颜色的三角形。

把它们放到一起

setupGLEnv中配置programbuffer

function setupGLEnv(canvasID) {
  canvas = document.getElementById(canvasID);
  gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
  if (!gl) {
    alert('天呐!您的浏览器竟然不支持WebGL,快去更新浏览器吧。');
  }
  program = makeProgram();
  triangleBuffer = makeBuffer();
}

render中调用绘制代码。

function render(deltaTime, elapesdTime) {
  gl.viewport(0,0,canvas.width,canvas.height);
  gl.clearColor(1.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);

  gl.useProgram(program);
  gl.bindBuffer(gl.ARRAY_BUFFER, triangleBuffer);
  positionLoc = gl.getAttribLocation(program, 'position');
  gl.enableVertexAttribArray(positionLoc);
  gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 4 * 3, 0);

  gl.drawArrays(gl.TRIANGLES, 0, 3);
}

大功告成。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容

  • 你好,三角形 图形渲染管线(Pipeline) 3D坐标转为2D坐标的处理过程是由OpenGL的图形渲染管线(Pi...
    IceMJ阅读 7,341评论 2 13
  • 学习代码地址OpenGL(ES)学习一:准备OpenGL(ES)学习二:绘制一个三角形 就像学习编程的hello ...
    FindCrt阅读 725评论 0 1
  • 目录结构: 第一步,明确要干嘛 第二步,怎么去画(纯理论) 第三步,怎么去画(实战) 第四步,练练手 第一步,明确...
    半纸渊阅读 7,981评论 18 57
  • 梦里 约上心爱的男生 去爷爷的藕塘,采莲蓬与荷花 瞧着爷爷的身影在塘里熟练地穿行 我们坐在田埂上甩着脚丫,相视而笑...
    芬妮80阅读 207评论 0 0
  • 一个刷新,你换掉了个性签名。 -翻到笔记本上有一句:去追随那些能使你安宁的东西。我想来想去,发现不过只是想要见一见...
    苏小北阿北阅读 493评论 4 11