WebGL04

我们上一个小节已经了解了通过attribute变量动态传递值实现动态点的绘制。

uniform变量

我们要动态修改颜色值的话,就需要动态给片元着色器中gl_FragColor动态赋值。

uniform类型变量可以在顶点着色器和片元着色器中被访问

uniform vec4 u_FragColor

我们需要给我们的内置变量gl_FragColor动态赋值一个vec4类型变量

const fragmentCode=`
  precision mediump float;
  uniform vec4 u_FragColor;
  
  void main () {
    gl_FragColor = u_FragColor;
  }
`

我们可以注意到上面的片段代码,我们使用precision mediump float声明,使用精度限定词来指定变量的范围和精度。不加的话片元着色器代码会报错。 接下来就是和attribute变量赋值一样,我们需要先找到变量的存储位置,在设置值即可。

  • gl.getUniformLocation 参数(program,name)
  • gl.uniform[1234]f[v] 参数(index,v0-v3)
// 获取 unfirom 变量存储位置
const u_FragColor = gl.getUniformLocation(program, 'u_FragColor')
// 通过 随机数 对 rgb 进行赋值,透明度固定为 0.8
gl.uniform4f(u_FragColor, Math.random(), Math.random(), Math.random(), .8)

示例

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 = ` 
    precision mediump float;
    uniform vec4 u_FragColor;
    void main () {
    // 顶点颜色——蓝色 (R, G, Bule, A)
    gl_FragColor = u_FragColor;
  }
`;

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

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);
const u_FragColor = gl.getUniformLocation(program, "u_FragColor");
gl.uniform4f(u_FragColor, Math.random(), Math.random(), Math.random(), 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.uniform4f(u_FragColor, Math.random(), Math.random(), Math.random(), 0.9);
  //动态设置颜色值
  gl.drawArrays(gl.POINTS, 0, 1);
});

varying变量

我们通过varying变量也可以实现绘制动态变化的颜色点,该变量会通过顶点着色器向片元着色器传输数据。换句话说,如果顶点着色器和片元着色器有类型和变量名完全相同的varying变量,那我们就可以将变化的数据从顶点着色器向片元着色器传递。

const vertexCode=`
attribute vec4 a_Position;
// 定义 a_Color 动态接受值
  attribute vec4 a_Color;
   // 定义 v_Color 接收、传递颜色值(跟片元着色器一致)
  varying vec4 v_Color;
    void main () {
    gl_Position = a_Position;
    gl_PointSize = 24.0;
    v_Color = a_Color;
  }
`

const fragmentCode=`
precision mediump float;
// 定义 v_Color,注意类型、变量名跟顶点着色器的一致,用于接收变量
varying vec4 v_Color;
void main () {
    gl_FragColor = v_Color;
  }
`

示例

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

canvas.width = canvas.height = 600;

const gl = canvas.getContext("webgl", { preserveDrawingBuffer: true });
const vertexCode = `
   attribute vec4 a_Position;
  // 定义 a_Color 动态接受值
  attribute vec4 a_Color;
  // 定义 v_Color 接收、传递颜色值(跟片元着色器一致)
  varying vec4 v_Color;

  void main () {
    gl_Position = a_Position;
    gl_PointSize = 10.0;
    v_Color = a_Color;
  }
`;

const fragmentCode = ` 
   precision mediump float;
  // 定义 v_Color,注意类型、变量名跟顶点着色器的一致,用于接收变量
  varying vec4 v_Color;

  void main () {
    gl_FragColor = v_Color;
  }
`;

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

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");
const  a_Color = gl.getAttribLocation(program, 'a_Color')
gl.vertexAttrib2f(a_Position, -0.9, 0.9);
gl.vertexAttrib4f(a_Color, Math.random(), Math.random(), Math.random(), 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.vertexAttrib4f(a_Color, Math.random(), Math.random(), Math.random(), 0.9);

  //动态设置颜色值
  gl.drawArrays(gl.POINTS, 0, 1);
});

uniform、varying之间的区别

  • uniform是全局变量 varying变量用于从顶点着色器向片元着色器传递值,之后有个内插过程会涉及。
  • gl.drawArrays调用一次绘制一个点,我们使用缓冲区对象的时候可以实现调用drawArrays一次绘制多个点。
  • varying变量是可变我们通过缓冲区不断给a_Color赋值的,对应的片元着色器会接收到当前从顶点着色器传递下来的a_Color值,因此可以在一次绘制中,绘制多个不同颜色。

写在最后

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