2024-05-14

WebGL Canvas

创建 canvas 刷底色

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      body {
        overflow: hidden; /* 防止不同浏览器长宽大小导致出现滚动条 */
      }
      /* 不应该使用 css 控制 canvas 大小,会使内部元素显示出现问题
        #canvas {
            width: 100%;
            height: 100vh;
        }
      */
    </style>
  </head>
  <body>
    <canvas id="canvas"></canvas>
    <script>
      const canvas = document.getElementById('canvas')
      // 使用 window.innerHeight/Width 设置 canvas 大小
      canvas.height = window.innerHeight
      canvas.width = window.innerWidth
      const gl = canvas.getContext('webgl') // 创建 gl 上下文
      gl.clearColor(0.5, 0.5, 0.5, 1.0) // 指定将要用来清空绘图区的颜色
      gl.clear(gl.COLOR_BUFFER_BIT) // 使用之前指定的颜色,清空绘图区
    </script>
  </body>
</html>

webgl 中的颜色配置

const canvas = document.getElementById('canvas')
const gl = canvas.getContext('webgl')
gl.clearColor(r, g, b, a); // clearColor 中 r、g、b 的定义域是[0,1] css 里的是 [0, 255]
// css 转 canvas
function css2gl(cssStr) {
    const reg = RegExp(/\((.*)\)/)
    const rgbaStr = reg.exec(cssStr)[1]
    const rgb = rgbaStr.split(',').map(m => parseInt(m))
    const r = rgb[0] / 255
    const g = rgb[1] / 255
    const b = rgb[2] / 255
    const a = rgb[3] || 1
    return {
        r,
        g,
        b,
        a,
    }
}
const color = css2gl('rgb(255,0,125)')
gl.clearColor(color.r, color.g, color.b, color.a)
// 还可以使用 three.js 的 Color 对象进行转换
// 使用 import 需要加上 <script type="module">
import { Color } from "https://unpkg.com/three/build/three.module.js";
const color = new Color(1, 0, 0);
// 建立色相偏移动画
!(function ani() {
    color.offsetHSL(0.005, 0, 0); 
    // 将提供的 h、s 和 l 值添加到当前颜色的 h、s 和 l 值上
    // 色调(Hue)、饱和度(Saturation)、亮度(Lightness)
    gl.clearColor(color.r, color.g, color.b, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);
    requestAnimationFrame(ani); // 循环渲染接口,调用频率跟显示器刷新率一致,运行在主线程,主线程繁忙会受到影响
})();

坐标系

canvas 2d 画布和 webgl 画布使用的坐标系都是二维直角坐标系

2d
  • canvas 2d 坐标系的原点在左上角
  • canvas 2d 坐标系的y 轴方向是朝下的
  • canvas 2d 坐标系的坐标基底有两个分量,分别是一个像素的宽和一个像素的高,即1个单位的宽便是1个像素的宽,1个单位的高便是一个像素的高
webgl
  • webgl坐标系的坐标原点在画布中心
  • webgl坐标系的y 轴方向是朝上的
  • webgl坐标基底中的两个分量分别是半个canvas的宽和canvas的高,即1个单位的宽便是半个个canvas的宽,1个单位的高便是半个canvas的高

webgl 最简单的图形

2d
//canvas画布
const canvas=document.getElementById('canvas');
//二维画笔
const ctx=canvas.getContext('2d');
//设置画笔的颜色
ctx.fillStyle = 'green'
//用画笔画一个矩形
ctx.fillRect(20, 10, 150, 100)
webgl

由于浏览器 2d 是用 js 线程渲染,而 3d 是用 GUI 线程渲染的,使用 GLSL ES 语言,写法有所不同

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      body {
        overflow: hidden;
      }
    </style>
  </head>
  <body>
    <!-- 不推荐使用 <canvas id="canvas"></canvas> vscode 无法直接判断类型 -->
    <script id="vertexShader" type="x-shader/x-vertex">
      //顶点着色器
      // vec4()  是一个4维矢量对象
      // 将 vec4() 赋值给顶点点位gl_Position 的时候,其中的前三个参数是x、y、z,第4个参数默认1.0
      void main() {
          gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
          gl_PointSize = 100.0;
      }
    </script>
    <script id="fragmentShader" type="x-shader/x-fragment">
      //片元着色器
      // 将vec4() 赋值给片元颜色gl_FragColor 的时候,其中的参数是r,g,b,a
      void main() {
          gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
      }
    </script>
    <script>
      // 直接使用 createElement,vscode 可以判断类型
      const canvas = document.createElement('canvas')
      document.querySelector('body').appendChild(canvas)
      // 设置大小
      canvas.height = window.innerHeight
      canvas.width = window.innerWidth
      // 获取上下文
      const gl = canvas.getContext('webgl')
      // 获取着色器
      const vsSource = document.getElementById('vertexShader').innerText
      const fsSource = document.getElementById('fragmentShader').innerText
      initShaders(gl, vsSource, fsSource)
      // 设置背景
      gl.clearColor(0, 0, 0, 1)
      gl.clear(gl.COLOR_BUFFER_BIT)
      // 绘制方块
      console.log(gl.POINTS) // 0
      // https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/drawArrays
      gl.drawArrays(gl.POINTS, 0, 1)
      function initShaders(gl, vsSource, fsSource) {
        //创建程序对象
        const program = gl.createProgram()
        //建立着色对象
        const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource)
        const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource)
        //把顶点着色对象装进程序对象中
        gl.attachShader(program, vertexShader)
        //把片元着色对象装进程序对象中
        gl.attachShader(program, fragmentShader)
        //连接webgl上下文对象和程序对象
        gl.linkProgram(program)
        //启动程序对象
        gl.useProgram(program)
        //将程序对象挂到上下文对象上
        gl.program = program
        return true
      }

      function loadShader(gl, type, source) {
        //根据着色类型,建立着色器对象
        const shader = gl.createShader(type)
        //将着色器源文件传入着色器对象中
        gl.shaderSource(shader, source)
        //编译着色器对象
        gl.compileShader(shader)
        //返回着色器对象
        return shader
      }
    </script>
  </body>
</html>
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容