手摸手带你用webgl原生绘制一个矩形

1,009 阅读3分钟

webgl和threejs

WebGL是一种3D绘图标准,这种绘图技术标准允许把JavaScript和OpenGL ES 2.0结合在一起,通过增加OpenGL ES 2.0的一个JavaScript绑定,WebGL可以为HTML5 Canvas提供硬件3D加速渲染,这样Web开发人员就可以借助系统显卡来在浏览器里更流畅地展示3D场景和模型了,还能创建复杂的导航和数据视觉化。

Three.js是一款开源的主流3D绘图JS引擎(名字Three就是3D的含义), threejs对webgl进行了封装 , 让我们更容易的创建3D应用 . 框架内置了很多现成工具 , 比如模型加载器 , 支持obj , fbx , 等通用模型格式 . 还提供了鼠标控制如缩放 , 放大等等 .

使用threejs虽然能快速创建3D应用 , 但是对理解底层的webgl代码没有了解 . 更别谈webgl的爸爸OpenGL了 .

下面我将用webgl绘制一个矩形

最终效果

image.png

创建一个canvas

<canvas id="box" width=300 height=300></canvas>

// 获取canvas的dom对象
const canvas = document.getElementById('box')

// 获取绘制webgl的上下文, 之后我们就可以使用webgl这个对象来创建3d应用
const webgl = canvas.getContext('webgl')
准备要绘制的矩形的坐标
//  v1--------v0
//  |         |
//  |         | 
//  |         |
//  v2--------v3
// 改正方形由4个点组成
let jsArrayData = new Float32Array([
  // x   y    z         
  -0.5, +0.5, 0.0,  // v1
  +0.5, +0.5, 0.0,  // v0
  +0.5, -0.5, 0.0,  // v3
  -0.5, -0.5, 0.0,  // v2
])

// 通过绘制两个三角形来组成一个正方形, 下面的数组里的数字表示jsArrayData里的索引,
// 如: 0表示-0.5, 2表示0.0
// 0, 1, 2表示用这三个点绘制一个三角形    
let indexDatas = [      
    0, 1, 2,      
    0, 2, 3    
]

将正方形的顶点和三角形的顶点索引绑定到webgl中

// 创建顶点缓冲区
triangleBuffer = webgl.createBuffer()
// 指明缓冲区的类型
// webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer)
// // 分配内存
// webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(jsArrayData), webgl.STATIC_DRAW)
webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer)
// 分配内存
webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(jsArrayData), webgl.STATIC_DRAW)

// 创建索引缓冲区
indexBuffer = webgl.createBuffer()
// 指明缓冲区的类型
webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, indexBuffer)
// 分配内存
webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexDatas), webgl.STATIC_DRAW)
// 使用缓冲区, 指定绘制所用的顶点数据, 从该缓冲区中获取
webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, indexBuffer)

将顶点数据传到shader中

// 将v3PositionIndex变量绑定到顶点shader里的v3Position    
webgl.bindAttribLocation(programObject, v3PositionIndex, 'v3Position')
    
// 启用v3PositionIndex
webgl.enableVertexAttribArray(v3PositionIndex)    
//  3表示每个顶点有三个数据(x, y, z)
// 4 * 3 表示所有顶点在内存中占用的字节数, 4表示一个顶点占用的字节数, 3表示一个顶点有3个数据
// 0 表示取数据的时候从第0个元素开始    
webgl.vertexAttribPointer(v3PositionIndex, 3, webgl.FLOAT, false, 4 * 3, 0)

绑定顶点着色器和片元着色器

// 创建shader
vertexShaderObject = webgl.createShader(webgl.VERTEX_SHADER)
fragmentShaderObject = webgl.createShader(webgl.FRAGMENT_SHADER)

webgl.shaderSource(vertexShaderObject, `
  attribute vec3 v3Position;
  void main(void)
  {
    gl_Position = vec4(v3Position, 1.0);
  }
`)

webgl.shaderSource(fragmentShaderObject, `
  precision  lowp  float;
  void main(void)
  {
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
  }
`)

// 编译shader
webgl.compileShader(vertexShaderObject)
webgl.compileShader(fragmentShaderObject) 

绑定着色器和顶点数据到webgl

// 创建一段空程序    
programObject = webgl.createProgram()    
// 绑定顶点着色器和片元着色器    
webgl.attachShader(programObject, vertexShaderObject)    
webgl.attachShader(programObject, fragmentShaderObject)    
// 将v3PositionIndex变量绑定到顶点shader里的v3Position    
webgl.bindAttribLocation(programObject, v3PositionIndex, 'v3Position')    
// 执行连接, 此段程序不再是空    
webgl.linkProgram(programObject)    
// 使用这段程序    
webgl.useProgram(programObject)

执行绘制

// 设置重绘背景的颜色    
webgl.clearColor(0.0, 0.0, 0.0, 1.0)
// 清空背景    
webgl.clear(webgl.COLOR_BUFFER_BIT)    
// 两个面的索引总共有6个数据    
webgl.drawElements(webgl.TRIANGLES, 6, webgl.UNSIGNED_SHORT, 0)

总代码


<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>WebGL</title>
</head>

<body>

  <canvas id="box" style="border: 1px solid black;" width=300 height=300></canvas>

  <script>
    let vertexShaderObject = null
    let fragmentShaderObject = null
    let programObject = null
    let indexBuffer = null
    let v3PositionIndex = 0
    let uniformColor

    // 1 创建上下文
    const canvas = document.getElementById('box')
    const webgl = canvas.getContext('webgl')

    webgl.viewport(0, 0, canvas.clientWidth, canvas.clientHeight)

    // 2 创建shader
    vertexShaderObject = webgl.createShader(webgl.VERTEX_SHADER)
    fragmentShaderObject = webgl.createShader(webgl.FRAGMENT_SHADER)

    webgl.shaderSource(vertexShaderObject, `
      attribute vec3 v3Position;
      void main(void)
      {
        gl_Position = vec4(v3Position, 1.0);
      }
    `)

    webgl.shaderSource(fragmentShaderObject, `
      precision  lowp  float;
      void main(void)
      {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
      }
    `)

    // 3 编译shader
    webgl.compileShader(vertexShaderObject)
    webgl.compileShader(fragmentShaderObject)

    if (!webgl.getShaderParameter(vertexShaderObject, webgl.COMPILE_STATUS)) {
      console.log('err---vertexShaderObject', webgl.getShaderInfoLog(vertexShaderObject))
    }

    if (!webgl.getShaderParameter(fragmentShaderObject, webgl.COMPILE_STATUS)) {
      console.log('err---fragmentShaderObject', webgl.getShaderInfoLog(fragmentShaderObject))
    }


    // 创建一段空程序
    programObject = webgl.createProgram()

    // 绑定顶点着色器和片元着色器
    webgl.attachShader(programObject, vertexShaderObject)
    webgl.attachShader(programObject, fragmentShaderObject)

    // 将v3PositionIndex变量绑定到顶点shader里的v3Position
    webgl.bindAttribLocation(programObject, v3PositionIndex, 'v3Position')

    // 执行连接, 此段程序不再是空
    webgl.linkProgram(programObject)

    // 使用这段程序
    webgl.useProgram(programObject)


    //  v1--------v0
    //  |         |
    //  |         | 
    //  |         |
    //  v2--------v3

    let jsArrayData = new Float32Array([
      // x   y    z         
      -0.5, +0.5, 0.0,  // v1
      +0.5, +0.5, 0.0,  // v0
      +0.5, -0.5, 0.0,  // v3
      -0.5, -0.5, 0.0,  // v2
    ])

    let indexDatas = [
      0, 1, 2,
      0, 2, 3
    ]

    // 创建顶点缓冲区

    triangleBuffer = webgl.createBuffer()
    // 指明缓冲区的类型
    // webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer)
    // // 分配内存
    // webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(jsArrayData), webgl.STATIC_DRAW)
    webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer)
    // 分配内存
    webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(jsArrayData), webgl.STATIC_DRAW)



    // 创建索引缓冲区
    indexBuffer = webgl.createBuffer()
    // 指明缓冲区的类型
    webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, indexBuffer)
    // 分配内存
    webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexDatas), webgl.STATIC_DRAW)
    // 使用缓冲区, 指定绘制所用的顶点数据, 从该缓冲区中获取
    webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, indexBuffer)



    webgl.enableVertexAttribArray(v3PositionIndex)

    // 指定绘制顶点的数据
    // v3PositionIndex 绑定shader里的v3Position
    // 3 每个顶点有3个数据组成, 每取一个顶点都会往后偏移3个
    // 4*3  4是一个float占的字节数, 6是指每个顶点有6个数据, 表示每隔24个字节到下一个顶点
    // 0 从缓冲区的第0个开始往后取
    webgl.vertexAttribPointer(v3PositionIndex, 3, webgl.FLOAT, false, 4 * 3, 0)

    // 设置重绘背景的颜色
    webgl.clearColor(0.0, 0.0, 0.0, 1.0)
    // 执行绘制
    webgl.clear(webgl.COLOR_BUFFER_BIT)

    // 两个面的索引总共有6个数据
    webgl.drawElements(webgl.TRIANGLES, 6, webgl.UNSIGNED_SHORT, 0)

  </script>

</body>

</html>

分享我私藏的TS教程,从0到高阶全系列,点击链接,0元获取 www.yidengxuetang.com/pub-page/in…