Webgl基础笔记(二)

201 阅读3分钟

1. varying变量

顶点着色器片元着色器传递数据

(1) 流程介绍

const canvas = document.querySelector('#canvas')

const gl = canvas.getContext('webgl')

// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
      attribute vec4 aPosition;
      varying vec4 vColor;

      void main() {
        vColor = aPosition;
        gl_Position = aPosition;
      }
    ` // 顶点着色器
const FRAGMENT_SHADER_SOURCE = `
      precision lowp float;
      varying vec4 vColor;

      void main() {
        gl_FragColor = vColor; // vec4(1.0, 0.0, 0.0, 1.0)
      }
    ` // 片元着色器

const program = initShader(
  gl,
  VERTEX_SHADER_SOURCE,
  FRAGMENT_SHADER_SOURCE
)

const aPosition = gl.getAttribLocation(program, 'aPosition')

// 创建三个点的数组
const points = new Float32Array([
  -0.5, -0.5,
  0.5, -0.5,
  0, 0.5,
])

// 创建缓冲区对象
const buffer = gl.createBuffer()

// 绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)

// 将数据写入缓冲区对象
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW)

// 获取数据字节数
const BYTES = points.BYTES_PER_ELEMENT

// 将数据写入缓冲区对象
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, BYTES * 2, 0)

// 启用 attribute 变量
gl.enableVertexAttribArray(aPosition)

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

2. 从顶点到图形的渲染流程

  • 配成几何图形: 将独立的顶点坐标装配成几何图形(gl.drawArrays)
  • 光栅化: 将装配好的图形转换为片元
    • 剔除: 对于不透明物体,背面对于观察者来说是不可见的。那么渲染过程中,就会将不可见的部分剔除,不参与绘制。节省渲染开销。
    • 裁剪: 在可视范围之外的事物是看不到的。可视范围之外的部分被裁剪掉,不参与绘制。
  • 绘制: 所有片元会进行片元着色器的处理,绘制到浏览器

3. 在VScode中用js写webgl显示代码提示

如果在document.getElementById的上一行加入

/** @type {HTMLCanvasElement} */

这样再看canvas的类型就显示出来了,也有代码补全功能。

另外如果在函数中想使用传入的参数,给这个参数加入代码提示,则在对应的函数前加上:

/** @param {!WebGLRenderingContext} gl */

这样在函数中使用gl的时候也会有正确的代码提示。

4. 给图形添加背景图

(1) 使用单个纹理

/** @type {HTMLCanvasElement} */
const canvas = document.querySelector('#canvas')

const gl = canvas.getContext('webgl')

// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
        // 只传递顶点数据
        attribute vec4 aPosition;

        attribute vec4 aTex;

        varying vec2 vTex;

        void main() {
          gl_Position = aPosition; // vec4(0.0,0.0,0.0,1.0)
          vTex = vec2(aTex.x, aTex.y);
        }
      `; // 顶点着色器

const FRAGMENT_SHADER_SOURCE = `
        precision lowp float;
        uniform sampler2D uSampler;
        varying vec2 vTex;

        void main() {
          gl_FragColor = texture2D(uSampler, vTex);
        }
      `; // 片元着色器

const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)

const aPosition = gl.getAttribLocation(program, 'aPosition');
const aTex = gl.getAttribLocation(program, 'aTex');
const uSampler = gl.getUniformLocation(program, 'uSampler');

const points = new Float32Array([
  -0.5,  0.5, 0.0, 1.0,
  -0.5, -0.5, 0.0, 0.0,
  0.5,  0.5, 1.0, 1.0,
  0.5, -0.5, 1.0, 0.0,
])

const buffer = gl.createBuffer();
const BYTES = points.BYTES_PER_ELEMENT;

gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);

gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, BYTES * 4, 0);

gl.enableVertexAttribArray(aPosition)

gl.vertexAttribPointer(aTex, 2, gl.FLOAT, false, BYTES * 4, BYTES * 2);

gl.enableVertexAttribArray(aTex)

const img = new Image();

img.onload = function () {
  // 创建纹理对象
  const texture = gl.createTexture();

  // 翻转 图片 Y轴
  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1)

  // 开启一个纹理单元
  gl.activeTexture(gl.TEXTURE0);

  // 绑定纹理对象
  gl.bindTexture(gl.TEXTURE_2D, texture);

  // 处理放大缩小的逻辑
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)

  // 横向 纵向 平铺的方式
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)

  // 配置纹理图像
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, img);

  gl.uniform1i(uSampler, 0);

  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}

img.src = ''

(2) 使用多重纹理

/** @type {HTMLCanvasElement} */
const canvas = document.querySelector('#canvas')

const gl = canvas.getContext('webgl')

// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
        // 只传递顶点数据
        attribute vec4 aPosition;

        attribute vec4 aTex;

        varying vec2 vTex;

        void main() {
          gl_Position = aPosition; // vec4(0.0,0.0,0.0,1.0)
          vTex = vec2(aTex.x, aTex.y);
        }
      `; // 顶点着色器

const FRAGMENT_SHADER_SOURCE = `
        precision lowp float;
        uniform sampler2D uSampler;
        uniform sampler2D uSampler1;
        varying vec2 vTex;

        void main() {
          vec4 c1 = texture2D(uSampler, vTex);
          vec4 c2 = texture2D(uSampler1, vTex);
          
          gl_FragColor = c1 * c2;
        }
      `; // 片元着色器

const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)

const aPosition = gl.getAttribLocation(program, 'aPosition');
const aTex = gl.getAttribLocation(program, 'aTex');
const uSampler = gl.getUniformLocation(program, 'uSampler');
const uSampler1 = gl.getUniformLocation(program, 'uSampler1');

const points = new Float32Array([
  -0.5,  0.5, 0.0, 1.0,
  -0.5, -0.5, 0.0, 0.0,
  0.5,  0.5, 1.0, 1.0,
  0.5, -0.5, 1.0, 0.0,
])

const buffer = gl.createBuffer();
const BYTES = points.BYTES_PER_ELEMENT;

gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);

gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, BYTES * 4, 0);

gl.enableVertexAttribArray(aPosition)

gl.vertexAttribPointer(aTex, 2, gl.FLOAT, false, BYTES * 4, BYTES * 2);

gl.enableVertexAttribArray(aTex)

function getImage(location, imgUrl, index) {
  return new Promise(resolve => {
    const img = new Image();

    img.onload = function () {
      // 创建纹理对象
      const texture = gl.createTexture();

      // 翻转 图片 Y轴
      gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1)

      // 开启一个纹理单元
      gl.activeTexture(gl[`TEXTURE${index}`]);

      // 绑定纹理对象
      gl.bindTexture(gl.TEXTURE_2D, texture);

      // 处理放大缩小的逻辑
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)

      // 横向 纵向 平铺的方式
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)

      // 配置纹理图像
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, img);

      gl.uniform1i(location, index);

      resolve()
    }

    img.src = imgUrl
  })
}

Promise.all([getImage(uSampler, imgUrl1, 0), getImage(uSampler1, imgUrl2, 1), ]).then(() => {
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
})

5. 纹理取样器

(1) 二维纹理

(2) 立方体纹理