OpenGL ES 着色器语言 开篇 通过一个例子体验如何画一个三维物体 WebGL 是很底层的 API,它只能用来画点、线和三角形,那么我们如何来画正方形呢? 其实大家看到的那些精美的3D 模型,其实都是一个个非常小的三角形组成的。 比如这个冰箱就是由3 万多个三角形组成。为什么选择三角形呢?这是因为任何多边形都可以最终分解为多个三角形,也就是说三角形是多边形的基本单位,并且三角形一定在一个平面上。 可以使用两个三角形组合来表示一个正方形,立方体有6 个面,也就需要 12 个三角形,每个三角形需要 3 个顶点,那么最终我们就需要 36 个顶点! 但是立方体比较特殊,它其实只有8 个顶点,一个顶点被三个面共用。那么有什么方法让我们只用定义 8 个顶点呢?OpenGL 还可以通过我们定义的顶点索引来渲染三角形,比如我们发送 8 个顶点和一个顶点索引数组到 GPU,然后 OpenGL 就可以使用索引数组的顺序来渲染三角形了。 比如索引数组 [1,2,3,3,2,0] 并且我们是画三角形的话,这就表示使用顶点数组下标为 1、2 和 3 的顶点来渲染一个三角形,然后用 3、2 和 0 下标渲染另一个三角形。 canvas.width = canvas.height = 300document.body.appendChild(canvas)const gl = canvas.getContext('webgl') gl.viewport(0, 0, gl.canvas.width, gl.canvas.height) const program =createProgramFromSource(gl, ` attribute vec4 aPos; attribute vec4 aColor; varying vec4 vColor; void main() { gl_Position = aPos; vColor = aColor; } `, ` precision mediump float; varying vec4 vColor; void main() { gl_FragColor = vColor; } `) const points =newFloat32Array([ -0.5,0.5,-0.5, 0.5,0.5,-0.5, 0.5,-0.5,-0.5, -0.5,-0.5,-0.5, 0.5,0.5,0.5, -0.5,0.5,0.5, -0.5,-0.5,0.5, 0.5,-0.5,0.5])const colors =newFloat32Array([ 1,0,0, 0,1,0, 0,0,1, 1,0,1, 0,0,0, 0,0,0, 0,0,0, 0,0,0])const indices =newUint8Array([ 0, 1, 2, 0, 2, 3, // 前 1, 4, 2, 4, 7, 2, // 右 4, 5, 6, 4, 6, 7, // 后 5, 3, 6, 5, 0, 3, // 左 0, 5, 4, 0, 4, 1, // 上 7, 6, 3, 7, 3, 2 // 下]) const [posLoc, posBuffer] =createAttrBuffer(gl, program, 'aPos', points)const [colorLoc, colorBuffer] =createAttrBuffer(gl, program, 'aColor', colors)const indexBuffer = gl.createBuffer() gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW) gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer) gl.vertexAttribPointer(posLoc, 3, gl.FLOAT, false, 0, 0) gl.enableVertexAttribArray(posLoc) gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer) gl.vertexAttribPointer(colorLoc, 3, gl.FLOAT, false, 0, 0) gl.enableVertexAttribArray(colorLoc) gl.enable(gl.DEPTH_TEST) gl.clearColor(0, 1, 1, 1) gl.clear(gl.COLOR_BUFFER_BIT| gl.DEPTH_BUFFER_BIT) gl.drawElements( gl.TRIANGLES, // 要渲染的图元类型 indices.length, // 要渲染的元素数量 gl.UNSIGNED_BYTE, // 元素数组缓冲区中的值的类型 0// 元素数组缓冲区中的偏移量, 字节单位) functioncreateShader(gl, type, source) { const shader =gl.createShader(type) gl.shaderSource(shader, source) gl.compileShader(shader) return shader;} functioncreateProgramFromSource(gl, vertex, fragment) { const vertexShader =createShader(gl, gl.VERTEX_SHADER,vertex) const fragmentShader =createShader(gl, gl.FRAGMENT_SHADER, fragment) const program =gl.createProgram() gl.attachShader(program, vertexShader) gl.attachShader(program, fragmentShader) gl.linkProgram(program) gl.useProgram(program) return program} functioncreateAttrBuffer(gl, program, attr, data) { const location =gl.getAttribLocation(program, attr) const buffer =gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer) gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW) return [location, buffer]} 上面代码画了一个边长是 1 的立方体,立方体的正中心就在坐标轴原点。 我们除了定义每个顶点的坐标,还定义了每个顶点的颜色,靠近屏幕外的4 个顶点设置成彩色,后 4 个顶点设置成黑色。 然后使用 Uint8Array 定义了顶点索引(如果又索引值大于 256 就应该使用 Uint16Array)。 颜色数据和坐标一样,创建一个缓存然后,告诉WebGL 如何获取获取。但是顶点索引数据有一点点不同,它的绑定点不是 gl.ARRAY_BUFFER 而是 gl.ELEMENT_ARRAY_BUFFER 它是用于元素索引的 Buffer。 这里还开启了深度测试,这样后画的三角形就不会覆盖先画的,而是根据它们的Z 值判断。另外清理的时候不用调用两次 clear 函数,而是使用 | 运算符,gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT。 最后一步将 drawArrays 换成 drawElements,表示我们用索引来渲染图形。 存储限定字varying 存储限定字其实一共有三个 attribute、uniform 和 varying。上面已经介绍了前两个,它们都是从外部JS 获取数据。 varying 是顶点着色器向片元着色器传送数据。上面例子中我们将 aColor 赋值给 vColor,然后在片元着色器中就可以使用 vColor 了。 叫 varying 也是有原因的,我们可以先来看看上面代码最终渲染成什么样子。 我们设置前面4 个顶点颜色分别是红、绿、蓝和粉色,怎么渲染出来的是一种渐变色? 前面将过,片段着色器执行的次数一般比顶点着色器执行次数多得多。这是因为在片元着色器之前会执行光栅化,会将图元离散化,变成一个个像素,然后每个像素都会执行片元着色器,来确定这个像素的颜色。 varying 变量从顶点着色器向片元着色器传递时会被 OpenGL 插值,也就是我们定义了三角形 3 个顶点的颜色,三角形内部的像素都是根据这 3 个顶点颜色插值出来的。比如一个线段一个端点是红色,另一个是绿色,那么这个线段中间就是 50% 的红色和 50% 的绿色。 旋转和透视 我们渲染的是一个立方体,为什么显示出来确实一个正方形? 因为这个立方体的正面正对着我们,我们就只能看见它的正面,如果我们将这个立方体稍微旋转一下,就可以看出来这个是立方体了。 现实生活中,我们看物体会有近大远小的效果,也就是有透视效果。在3D 图形中也应该也有类似的效果,现在我们渲染的这个立方体是没有透视效果的,也就是前面那个面会和后面那个面一样大。 |
版块:
web3.0前端学院
分类:
1. 本站所有资源来源于用户上传和网络,仅作为演示数据,如有侵权请邮件联系站长!
2. 盗版,破解有损他人权益和违法作为,请各位站长支持正版!
2. 盗版,破解有损他人权益和违法作为,请各位站长支持正版!