adodo0829 / blog

搭建知识体系
29 stars 4 forks source link

wegbl01-绘制流程 #103

Open adodo0829 opened 2 years ago

adodo0829 commented 2 years ago

webgl 绘制流程

渲染管线, 就是绘制流程, 流程包含哪些步骤

基础概念

示例

1.获取 WebGL 上下文

<canvas id="canvas"></canvas>

<script>
  const canvas = document.querySelector("#canvas");
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  //二维画笔
  // const gl=canvas.getContext('2d');
  //三维画笔
  const gl = canvas.getContext("webgl");
  //声明颜色 rgba
  gl.clearColor(1, 1, 0, 1);
  //刷底色
  gl.clear(gl.COLOR_BUFFER_BIT);

  //css颜色
  const rgbaCss = "rgba(255,100,0,1)";
  //正则
  const reg = RegExp(/\((.*)\)/);
  //捕捉数据
  const rgbaStr = reg.exec(rgbaCss)[1];
  //加工数据
  const rgba = rgbaStr.split(",").map((n) => parseInt(n));
  const r = rgba[0] / 255;
  const g = rgba[1] / 255;
  const b = rgba[2] / 255;
  const a = rgba[3];
  //声明颜色 rgba
  gl.clearColor(r, g, b, a);
  //刷底色
  gl.clear(gl.COLOR_BUFFER_BIT);
  //设置视口的大小
  gl.viewport(0, 0, canvas.width, canvas.height);
</script>

2.初始化着色器

// 顶点着色器
const vertexShader = `
// 类型是 vec3,即三元组浮点数向量(3-component floating point vector)
attribute vec3 aPos;
void main(){
    gl_Position = vec4(aPos, 1);
}
`;

// 片段着色器
const fragmentShader = `
void main() {
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
`;

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 createProgram(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);
  return program;
}

function loadShader(gl, type, source) {
  //根据着色类型,建立着色器对象
  const shader = gl.createShader(type);
  //将着色器源文件传入着色器对象中
  gl.shaderSource(shader, source);
  //编译着色器对象
  gl.compileShader(shader);
  //返回着色器对象
  return shader;
}

3.设置缓冲区

WebGL 中,缓冲区的作用至关重要,需要绘制的几何体的所有信息都是通过缓冲区来传递给着色器程序的,比如几何体的坐标、颜色等等。 在下面的代码中,我们使用了顶点缓冲区对象(Vertex Buffer Object,VBO),该缓冲区就是用来存放几何体的顶点信息。

function setupBuffer() {
  // 对于一个点来说,它仅包含一个顶点,一个顶点包含 x、y、z 三个坐标值,因此我们定义的 vertex 数组包含三个元素:0、0、0。
  let vertex = [0, 0, 0];

  // 创建缓冲区对象
  let vertexBuffer = gl.createBuffer();

  // WebGL中,使用一个缓冲区前需要对其进行绑定操作,一旦绑定后,后续的缓冲区操作都会在当前绑定的缓冲区上进行,直到我们取消绑定或者绑定了其他的缓冲区对象
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);

  // bufferData方法传递数据时使用了Float32Array这个typed array,WebGL不支持直接使用JavaScript的原始数组类型。
  // 使用typed array可以以二进制方式来操作数据,同时也指定了数据的类型。
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertex), gl.STATIC_DRAW);

  let aVertexPosition = gl.getAttribLocation(glProgram, "aPos");
  // 将aPos属性指向当前绑定的顶点缓冲区对象,这样aPos和vertexBuffer对象就关联了
  gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(aVertexPosition);
}
void vertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset);

// index:属性的索引,通过 getAttribLocation 可以获得。你可以理解成属性的地址。
// size:每个顶点包含多少个数值,最简单的情况是三个,即x、y、z三维坐标。
// type:指定缓冲区的数据类型。
// nomalized:和数据转换有关系,一般用false。
// stride:说明数据存储的方式,单位是字节。0表示一个属性的数据是连续存放的,非连续存放的情况我们后面会讲到。
// offset:该属性在每个顶点数据中偏移值,单位是字节并且是type的整数倍。这个值是配合stride一同使用的,0表示从头开始读取

4.绘制

调用 drawArrays 将点绘制到屏幕上

function draw() {
  // drawArrays方法将会使用处于启用状态的包含坐标信息的属性,即本例中的aPos,我们看到它在之前已经被启用并且指向包含顶点信息的VBO
  gl.drawArrays(gl.POINTS, 0, 1);
}

// void drawArrays(GLenum mode, GLint first, GLsizei count)
// mode:绘制模式,gl.POINTS 常量表示点。
// first:第一个元素所在位置,即偏移量,32bit 整型。
// count:绘制的元素个数,即顶点的个数,32bit 整型。

小结

vertex buffer objects(coordinates(坐标数组)) <=(point to)= { vertex shader(attributes) + fragment shader(fragment buffer) } =(link)=> program

// 修改点大小
// 顶点着色器中内置的gl_PointSize变量可以用来修改点尺寸,单位为像素
gl_PointSize = 20.0;

// 修改点颜色
gl_FragColor = vec4(178.0 / 255.0, 105.0 / 255.0, 9.0 / 255.0, 1.0);

// 修改点位置
// 点坐标是在vertex变量中定义的
let vertex = [0.5, 0.5, 0];
// 修改x和y坐标为0.5, 原点为中心, 范围[-1, 1]