WebGL打开 3D 世界的大门(二):工作原理

看过上一节的内容,应该对webgl有了大概的认识,但还是有的模糊不清。这一节内容关键来讲解一下,buffer和attribute,webgl怎么使用buffer,怎么给对应的顶点赋值的。

attribute

在glsl语法中 attribute 用来传递逐顶点数据(per-vertex data),例如顶点的位置、颜色、法线、纹理坐标等。 这些数据在顶点着色器中处理,每个顶点都会有一个独立的 attribute值。片元着色器是逐片元(per-fragment)执行的,它的输入是顶点着色器输出的插值结果(如 varying 变量)。片元着色器无法直接访问逐顶点的 attribute 数据,因为片元着色器的执行与顶点没有直接对应关系。片元着色器可以通过变量 varying,访问到顶点着色器的attribute。完成片元着色器和顶点着色器的互动。

buffer

缓冲操作是在GPU上获取顶点和其他顶点数据的一种方式,gl.createBuffer创建一个缓冲;gl.bindBuffer是设置缓冲为当前使用缓冲; gl.bufferData将数据拷贝到缓冲。一旦数据存到缓冲中,需要启用缓冲数,并且告诉WebGL怎么从缓冲中提取数据传给顶点着色器的属性。

片元着色器插值

如果片元着色器使用变量的话,他会根据顶点的颜色进行插值,在片元之间进行平滑过渡。效果如下:

下面看源码,地址: gitee.com/feng-lianxi...

顶点着色器:

ini 复制代码
  // an attribute will receive data from a buffer
  attribute vec2 a_position;
  uniform vec2 u_resolution;
  // attribute vec4 a_position;
  // all shaders have a main function
  varying vec4 v_color;
  attribute vec4 a_color;
  void main() {
    // gl_Position is a special variable a vertex shader
    // is responsible for setting
    // 从像素坐标转换到 0.0 到 1.0
    vec2 zeroToOne = a_position - u_resolution / 2.0;
    // 再把 0->1 转换 0->2
    vec2 zeroToTwo = zeroToOne / u_resolution * 2.0;
    // 把 0->2 转换到 -1->+1 (裁剪空间)
    // vec2 clipSpace = zeroToTwo - 1.0;
    gl_Position = vec4(zeroToTwo, 0, 1);
    gl_PointSize = 10.0;
    v_color = a_color;
  } 

注意这里的变量 v_color,还有属性值a_color,我们将属性值赋值个变量。片元着色器是使用变量渲染的,不能和顶点属性对应。

片元着色器:

csharp 复制代码
  precision mediump float;
  // uniform vec4 u_color;
  varying vec4 v_color;
  void main() {
    // gl_FragColor is a special variable a fragment shader
    // is responsible for setting
    gl_FragColor = v_color; // return redish-purple
  }

获取渲染程序的属性值:

ini 复制代码
var colorLocation = gl.getAttribLocation(program, "a_color");

创建buffer,并且将buffer绑定到,ARRAR_BUFFER上,

ini 复制代码
  var colorBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
  

随机设置八个顶点的颜色值:

ini 复制代码
function setColors(gl) {
  // 生成两个随机颜色
  var r1 = Math.random();
  var b1 = Math.random();
  var g1 = Math.random();

  var r2 = Math.random();
  var b2 = Math.random();
  var g2 = Math.random();

  gl.bufferData(
      gl.ARRAY_BUFFER,
      new Float32Array(
        [ r1, b1, g1, 1,
          r1, b1, g1, 1,
          r1, b1, g1, 1,
          r2, b2, g2, 1,
          r2, b2, g2, 1,
          r2, b2, g2, 1]),
      gl.STATIC_DRAW);
}

启用该属性,并且告诉webgl引擎怎么来取这些值:

ini 复制代码
  gl.enableVertexAttribArray(colorLocation);
  gl.vertexAttribPointer(colorLocation, 4, gl.FLOAT, false, 0, 0);
  

我们将来写到3D的时候,还可以引入顶点法向量的概念。

总结

通过这两节的内容,再配合手动敲一下代码,相信会对使用glsl语言做平面图形不再陌生。下一节是重点内容,重点讲解着色器和GLSL语法。其实写GLSL程序就是写着色器,并且将来关于怎么写着色器我会专门写一系列文章。

相关推荐
有志1 分钟前
Vue 学习总结(Java 后端工程师视角)
前端
踩着两条虫3 分钟前
VTJ.PRO 在线应用开发平台的DSL生命周期
前端·低代码·ai编程
我是伪码农3 分钟前
JS 复习
开发语言·前端·javascript
小碗细面4 分钟前
Claude Code 很强,但为什么我越来越常打开 Codex App?
前端·chatgpt·ai编程
愿你如愿5 分钟前
React Fiber 的主要目标是什么
前端·react.js
漂移的电子9 分钟前
【echarts 细节】
前端·javascript·echarts
im_AMBER11 分钟前
万字长文:从零实现 Yjs + Hocuspocus 协同文档
前端·react.js·前端框架
kyriewen11 分钟前
事件流与事件委托:当点击按钮时,浏览器里发生了什么?
前端·javascript·面试
是真的小外套13 分钟前
第十一章:Flask入门之从零构建Python Web应用
前端·python·flask
AY呀16 分钟前
# 从手写 debounce 到企业级实现:我在面试中如何“降维打击”面试官
前端·面试