WebGL学习(一)初识

284 阅读3分钟

0. 序言

学习webgl或者什么opengl你要记住这只是帮助你实现绘制的工具,最主要的是学习计算机图形的知识,而不是死磕这个api该怎么用。

学习webgl纯属个人兴趣,其实很早的时候我就想学了,打开学习网站,刚看第一章就打脑壳。

现在我准备沉下心来认真学习,顺便也记录一下我的学习历程,希望能帮助你。

我觉得最重要的还是坚持,这里面虽然涉及了一些数学,但是并不是很难,可以说大学学了一点点都能懂。

最重要的还是要自己多思考为什么,很多教程文章书本上并没有特别讲得清楚,甚至是错的。这需要你自己多看几篇文章总结。

1. 上手

介绍就不多说了,直接看看简单的实例。这里是webgl1不是webgl2、webgpu

import { useEffect, useRef } from 'react'
// 我是为了插件可以高亮提示所以单独写的文件,也可以直接写字符串
// 需要配置一下webpack,来读取文件内容
// >5.0在rules里面直接设置type:'asset/source'
// <5.0 需要安装raw-loader
import vert from './index.vert'
import frag from './index.frag'

const WebGL = () => {
  const canvas = useRef<HTMLCanvasElement>(null)
  useEffect(() => {
    if(canvas.current) {
      // 获取webgl上下文
      const gl = canvas.current.getContext('webgl')

      if(gl) {
        // 清空颜色缓冲
        gl.clearColor(0, 0, 0, 1)
        gl.clear(gl.COLOR_BUFFER_BIT)
        
        // 1.创建顶点、片元着色器
        const vertexShader = gl.createShader(gl.VERTEX_SHADER)
        const fragShader = gl.createShader(gl.FRAGMENT_SHADER)

        if(vertexShader && fragShader) {
          // 2.装载着色器代码
          gl.shaderSource(vertexShader, vert)
          gl.shaderSource(fragShader, frag)
          
          // 3.编译着色器
          gl.compileShader(vertexShader)
          gl.compileShader(fragShader)
            
          // 4.创建WebGLProgram对象
          // 这个对象是用来和js互操作的
          const program = gl.createProgram()

          if(program) {
            // 5.添加着色器
            gl.attachShader(program, vertexShader)
            gl.attachShader(program, fragShader)
            
            // 6.链接程序
            gl.linkProgram(program)
            // 7.WebGLProgram对象添加到渲染状态中
            gl.useProgram(program)
            
            // 8.绘制
            gl.drawArrays(gl.POINTS, 0, 1)
          }
        }
      }
    }
  }, [])
  return (
      <canvas ref = { canvas } style = { {width: 600, height: 600, padding: 20, border: 'solid 2px #000', margin: 20} }></canvas>
  )
}

顶点着色器代码

void main() {
  // 点位置
  gl_Position = vec4(0.5, 0.0, 0.0, 1.0);
  // 点大小
  gl_PointSize = 30.0;
}

片段着色器代码

void main() {
  // 颜色
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

效果

图片.png

这里有几个问题

  1. 画出来的点应该是正方形,这里却是长方形 (canvas设置问题)
  2. 这个长方形边缘有点模糊 (canvas设置问题)
  3. 为什么(0.5, 0.0, 0.0, 1.0)会出现在右下角位置(坐标)
  4. 为什么初始代码这么复杂,仅仅是画了一个点(webgl原理)

2. canvas设置问题

刚才我直接给canvas样式设置的宽高,这样是不正确的。样式中的宽高指的是元素的宽高,canvas的宽高属性指的是canvas画的图像的宽高,所以之前的设置仅仅是拉伸了图像

// 改成这样
<canvas ref = { canvas } width = { 600 } height = { 600 } style = { {padding: 20, border: 'solid 2px #000', margin: 20} }></canvas> 

3. 坐标

熟悉css等二维绘画方式的都知道,默认情况下坐标的原点是在左上角,而这里的webgl是用来绘制更复杂的三维图形,三维图像在前后左右都有绘制需求,所以原点是在正中心。

4. webgl原理

其实也不是原理,从另外一个角度来说webgl就是一个大的状态机,通过切换不同program执行不同的上线文。

这有一个回答,为什么要设计成这样

大概意思就是

  1. gpu工作方式就是这样,数据总线的速度比硬件慢,为了提高速度提前设置了很多状态
  2. 利于编译程序做一些缓存之类的优化,能够对比之前的状态来节约更新资源。
  3. 历史原因,以前没有想到会有现在这些图形技术。
  4. 因为保存了状态,调用函数的时候可以少传参数