WebGL03

WebGL绘制动态点

之前的例子我们都是在顶点着色器中定义静态的顶点坐标数据。

动态传递顶点坐标

要动态绘制点,需要建立动态获取顶点坐标的途径,能够通过js与GLSL着色器代码之间进行交互。所以我们需要通过变量来传递动态的值。

const vertexCode=`
    // 定义一个a_Position类型为vec4的attribute变量
    attribute vec4 a_Position;
    void main(){
      // 将变量赋值给顶点坐标
      gl_Position=a_Position;
      // 顶点渲染像素大小
      gl_PointSize=24.0;
    }

在GLSL着色器定义变量,变量名书写格式对应类型开头第一个字母小写加上下划线最后跟上你定义开头字母大写语义化变量名。

attribute变量

GLSL中有三种类型变量。

  • attribute —— 在顶点着色器中使用
  • varying —— 在顶点、片元着色器中定义,用于从顶点着色向片元着色器中传递数据。
  • uniform —— 可理解为全局变量,在顶点着色器和片元着色器中都能访问。 对于上面在GLSL代码中定义的变量,js都可以获取到其对应的引用并且设置对应的值。
    如何获取对应变量,以及动态值的传递。
  • gl.getAttribLocation 参数(program,name)=> 获取到对应顶点着色器中对应类型定义变量名引用下标数字
  • gl.vertexAttrib[1234]f[v] 参数(index,v0,v1)

示例

// 获取 a_Position 的变量地址
const a_Position = gl.getAttribLocation(program, 'a_Position')
// 对 a_Position 变量赋值
gl.vertexAttrib2f(a_Position, -.9, .7)
var canvas = document.getElementById("canvas");

canvas.width = canvas.height = 600;

const gl = canvas.getContext("webgl");
const vertexCode = `
  attribute vec4 a_Position;
  void main () {
    // 顶点坐标
    gl_Position = a_Position;
    // 顶点渲染像素大小
    gl_PointSize = 10.0;
  }
`;

const fragmentCode = ` void main () {
    // 顶点颜色——蓝色 (R, G, Bule, A)
    gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
  }
`;

//着色器代码的初始化
// 绘制实现

// var canvas = document.getElementById("redFn").addEventListener("click",()=>{
//   gl.clearColor(1.0,0.0,0.0,1.0)
//   gl.clear(gl.COLOR_BUFFER_BIT)
// });

function createShader({ glslCode, type }) {
  // 创建对应类型着色器
  const shader = gl.createShader(type);
  // 传入对应着色器代码
  gl.shaderSource(shader, glslCode);
  // 编译着色器
  gl.compileShader(shader);
  return shader;
}

// 创建顶点着色器
const vertexShader = createShader({
  glslCode: vertexCode,
  type: gl.VERTEX_SHADER
});
// 创建片元着色器
const fragmentShader = createShader({
  glslCode: fragmentCode,
  type: gl.FRAGMENT_SHADER
});

// 创建着色器程序
const program = gl.createProgram();

// 分别添加两个着色器 gl.attachShader
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);

// 链接两个着色器
gl.linkProgram(program);
gl.useProgram(program);

// 动态修改顶点位置
const a_Position=gl.getAttribLocation(program,"a_Position")
gl.vertexAttrib2f(a_Position,-0.9,0.9)

gl.drawArrays(gl.POINTS, 0, 1);

根据鼠标在画布点击位置绘制点

我们需要根据获取到鼠标位置转换成WebGL的坐标值,由于WebGL坐标系原点在中心。
鼠标位置和坐标的转换

  • x轴坐标 x=(offsetX-1/2width)/(1/2width)
  • y轴坐标 y=(1/2height-offsetY)/(1/2height)

示例

var canvas = document.getElementById("canvas");

canvas.width = canvas.height = 600;

const gl = canvas.getContext("webgl", { preserveDrawingBuffer: true });
const vertexCode = `
  attribute vec4 a_Position;
  void main () {
    // 顶点坐标
    gl_Position = a_Position;
    // 顶点渲染像素大小
    gl_PointSize = 10.0;
  }
`;

const fragmentCode = ` void main () {
    // 顶点颜色——蓝色 (R, G, Bule, A)
    gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
  }
`;

//着色器代码的初始化
// 绘制实现

document.getElementById("redFn").addEventListener("click", () => {
  gl.clear(gl.COLOR_BUFFER_BIT);
});

function createShader({ glslCode, type }) {
  // 创建对应类型着色器
  const shader = gl.createShader(type);
  // 传入对应着色器代码
  gl.shaderSource(shader, glslCode);
  // 编译着色器
  gl.compileShader(shader);
  return shader;
}

// 创建顶点着色器
const vertexShader = createShader({
  glslCode: vertexCode,
  type: gl.VERTEX_SHADER
});
// 创建片元着色器
const fragmentShader = createShader({
  glslCode: fragmentCode,
  type: gl.FRAGMENT_SHADER
});

// 创建着色器程序
const program = gl.createProgram();

// 分别添加两个着色器 gl.attachShader
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);

// 链接两个着色器
gl.linkProgram(program);
gl.useProgram(program);

// 动态修改顶点位置
const a_Position = gl.getAttribLocation(program, "a_Position");
gl.vertexAttrib2f(a_Position, -0.9, 0.9);

gl.clearColor(0.0, 0.0, 0.0, 0.5);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, 1);

canvas.addEventListener("click", (e) => {
  const halfWidth = canvas.width / 2;
  const halfHeight = canvas.height / 2;
  const x = (e.offsetX - halfWidth) / halfWidth;
  const y = (halfHeight - e.offsetY) / halfHeight;
  // gl.clear(gl.COLOR_BUFFER_BIT);
  gl.vertexAttrib2f(a_Position, x, y);
  gl.drawArrays(gl.POINTS, 0, 1);
});

默认我们在绘制点之后,颜色缓冲区就会被默认设置成透明度为0的透明背景,所以我们希望背景色保持不变,需要在每次绘制前调用gl.clear方法。
目前我们绘制点在都不会被保留,如果我们想让每次的绘制点都保留下来。

缓存颜色缓冲区

其实WebGL的绘制是在颜色缓冲区进行的,绘制结束后颜色缓冲区结果显示到屏幕上,默认情况下颜色缓冲区就会被重置,所以内容会丢失。

preserveDrawingBuffer 在获取绘图上下文时传入该参数,我们就可以缓存每次绘制操作。

canvas = document.querySelector('#ice-2_5')
// getContext 中配置 preserveDrawingBuffer 为 true
gl = canvas.getContext('WebGL', { preserveDrawingBuffer: true })

写在最后

未完待续…
希望大家好好生活,健康快乐。