北鸟南游的博客 北鸟南游的博客
首页
  • 前端文章

    • JavaScript
    • Nextjs
  • 界面

    • html
    • css
  • 计算机基础
  • 后端语言
  • linux
  • mysql
  • 工具类
  • 面试相关
  • 图形学入门
  • 入门算法
  • 极客专栏
  • 慕课专栏
  • 电影资源
  • 儿童动漫
  • 英文
关于我
归档
GitHub (opens new window)
首页
  • 前端文章

    • JavaScript
    • Nextjs
  • 界面

    • html
    • css
  • 计算机基础
  • 后端语言
  • linux
  • mysql
  • 工具类
  • 面试相关
  • 图形学入门
  • 入门算法
  • 极客专栏
  • 慕课专栏
  • 电影资源
  • 儿童动漫
  • 英文
关于我
归档
GitHub (opens new window)
  • 面试
  • 算法入门
  • 图形学入门
    • threeJs使用
    • webgl基础1-着色器、矩阵变换
    • webgl基础1
    • webgl基础2-颜色、纹理
    • webgl基础3-三维
      • 创建视图矩阵
        • 决定视图的参数
        • 可视范围
        • 辅助函数
        • 视图矩阵实例代码
      • 正射投影(平行投影)
        • 概念定义
        • 首先推导左右区间
        • 以此类推上下区间和远近区间公式
        • 正射投影矩阵
        • 完整示例代码
      • 透视投影
        • 概念定义
        • 推导透视矩阵公式
        • 透视投影实例代码
      • 三维场景中2个重要概念
        • 隐藏面消除
        • 深度冲突
      • 绘制立方体图形
        • 顶点法
        • 索引法
    • webgl基础4-灯光
  • 极客专栏
  • 慕课专栏
  • vue3+vite封装element组件库
  • 基于云开发模式开发微信小程序
  • 珠峰培训资料
  • theme
  • graphics
北鸟南游
2024-04-27
目录

webgl基础3-三维

# 创建视图矩阵

# 决定视图的参数

三维场景的名词定义:视点、目标点、上方向

image.png 为确定观察者状态,需要获取视点【观察者的位置】、目标点【被观察目标所在的点】两项信息。最后要把观察到的景象绘制到屏幕上,还需要知道上方向; 有这三个信息可以确定观察者看到的内容。

  • 视点:观察者所在的三维空间中位置,视点坐标用(eyeX, eyeY, eyeZ)表示
  • 目标点:被观察目标所在的位置,目标点坐标用(atX, atY, atZ)表示。目标点和视点之间的连线,是视线方向。
  • 上方向:要想最终确定在屏幕上显示的内容,还需要确定一个上方向;如果仅仅确定视点和观察点,观察者还可以以视线为轴进行旋转,这样导致看到的目标点形状就会发生变化;上方向用3个分量的矢量来表示(upX, upY, upZ).

image.png

# 可视范围

image.png 三维空间中的三维物体只有在可视范围内,WEBGL才会绘制它;

人类只能看到眼前的东西,水平视角大约200度左右。绘制可视范围外的对象没有意义。

# 辅助函数

  • 归一化函数 normalized:将数据调整到-1到1或者0到1之间
  • 叉集 cross : 获取两个平面的法向量
  • 点集 dot : 获取某点在x、y、z轴的投影长度
  • 向量差:获取目标点和视点的向量
// 归一化函数
function normalized(arr) {
  let sum = 0;

  for (let i = 0; i < arr.length; i++) {
    sum += arr[i] * arr[i]
  }

  const middle = Math.sqrt(sum);

  for (let i = 0; i < arr.length; i++) {
    arr[i] = arr[i] / middle;
  }
}

// 叉积函数 获取法向量
function cross(a,b) {
  return new Float32Array([
    a[1] * b[2] - a[2] * b[1],
    a[2] * b[0] - a[0] * b[2],
    a[0] * b[1] - a[1] * b[0],
  ])
}

// 点积函数 获取投影长度
function dot(a, b) {
  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
}

// 向量差
function minus(a, b) {
  return new Float32Array([
    a[0] - b[0],
    a[1] - b[1],
    a[2] - b[2],
  ])
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

通过使用以上函数,推导出视图矩阵

// 视图矩阵获取
function getViewMatrix(eyex, eyey, eyez, lookAtx, lookAty, lookAtz, upx, upy, upz) {

  // 视点
  const eye = new Float32Array([eyex, eyey, eyez])
  // 目标点
  const lookAt = new Float32Array([lookAtx, lookAty, lookAtz])
  // 上方向
  const up = new Float32Array([upx, upy, upz])

  // 确定z轴
  const z = minus(eye, lookAt);

  normalized(z);
  normalized(up);

  // 确定x轴
  const x = cross(z, up);

  normalized(x);
  // 确定y轴
  const y = cross(x, z);

  return new Float32Array([
    x[0],       y[0],       z[0],       0,
    x[1],       y[1],       z[1],       0,
    x[2],       y[2],       z[2],       0,
    -dot(x,eye),-dot(y,eye),-dot(z,eye),1
  ])
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 视图矩阵实例代码

代码通过改变视点位置坐标,形成视图动画效果。 jcode (opens new window)

# 正射投影(平行投影)

# 概念定义

左侧为透视投影,右侧为正射投影;

在透视投影下,产生的三维场景有近大远小的透视感,更符合真是场景。

正射投影的好处是用户可以方便比较场景中物体的大小,这是因为物体看上去大小与其所在的位置没有关系,在建筑平面图等测绘图中使用。

image.png

下图是正射投影,投射到xy平面的示例。

A点做正射投影映射,转化到A';

# 首先推导左右区间

用L表示left的值,用R表示right的值

image.png

# 以此类推上下区间和远近区间公式

image.png 根据矢量点和一个矩阵相乘,推导出x'、y'、z'、w'的值

image.png

x' = ax + by+ cz + d;
y' = ex + fy + gz + h;
z' = ix + jy + kz + l;
w' = mx + ny + oz + p;
1
2
3
4

将左右区间、上下区间、远近区间公式和上面图中的x'、y'、z'、w'进行等式替换;

// 只有 a= 2/(r-l) 且 d=-(r+l)/(r-l)、b=c=0 时等式成立
// 只有 f= 2/(t-b) 且 h=-(t+b)/(t-b)、e=g=0 时等式成立
// 只有 k= 2/(f-n) 且 l=-(f+n)/(f-n)、i=j=0 时等式成立
// m=n=o=0且p=1
1
2
3
4

将以上等式矩阵,转为列矩阵,就是下面的正射投影矩阵

# 正射投影矩阵

// 获取正射投影矩阵
function getOrtho(l, r, t, b, n, f) {
  return new Float32Array([
    2 / (r - l), 0,           0,           0,
    0,           2/(t-b),     0,           0,
    0,           0,           -2/(f-n),    0,
    -(r+l)/(r-l),-(t+b)/(t-b),-(f+n)/(f-n),1
  ])
}
1
2
3
4
5
6
7
8
9

# 完整示例代码

案例通过移动视点坐标,实现视图改变的动画效果。 jcode (opens new window)

# 透视投影

# 概念定义

下图左侧为透视投影;

下图右侧在WEBGL场景中放置3个大小同样的三角形,区别是在Z轴的位置不同;视点在(0, 0, 5)位置,可以产生出如下图左侧的效果。

image.png

透视投影符合近大远小的规律;

image.png

先将透视投影缩放到正射投影,然后通过缩放矩阵和正射投影矩阵相乘,得到透视投影矩阵

image.png

# 推导透视矩阵公式

# 获取透视比例关系

根据相似三角形定律,可以得出左边的比例关系,从而推导出 y' = yn/f 和 x'=xn/f

image.png

在做映射时,z坐标的长度f是固定不变,f为定长1,所以 y' = yn和x'=xn

# 第一步:推算出a和b的值

从而可以推导出缩放矩阵的第一步

image.png

# 第二步:获取参数c和d值

然后进行透视矩阵的第二步推导,c和d的值;

image.png 根据相似三角形关系,得到如下等式

image.png 利用齐次坐标的一个性质,所谓齐次坐标就是使用n+1维来表示n维坐标。这里需要用到齐次坐标的一个性质,如果(x,y,z,1)表示投影到w=1平面的点坐标,那么(nx,ny,nz,n)坐标投影到w=1平面的是同一个点(nx/n,nz/n,nz/n,n/n)=(x,y,z,1),这就是齐次坐标的尺度不变性。

利用齐次坐标特性,将上面的公式得到的结果同时乘以Ze

image.png

Mpersp为4x4矩阵,根据上面的公式我们能得出矩阵的第一、二、四行的值

image.png


image.png


image.png

# 第三步根据收缩矩阵和正射投影矩阵的乘积,得到透视投影矩阵

[
  n*2/(r-l) + 0*0 + 0*0 + 0*-(r+l)/(r-l), n*0 + 0*2/(t-b) + 0*0 + 0* -(t+b)/(t-b), n*0 + 0*0 + 0* -2/(f-n) + 0*-(f+n)/(f-n), n*0 + 0*0 + 0*0 +0*1, 
  0*2/(r-l) + n*0 + 0*0 + 0*-(r+l)/(r-l), 0*0 + n*2/(t-b) + 0*0 + 0* -(t+b)/(t-b), 0*0 + n*0 + 0* -2/(f-n) + 0*-(f+n)/(f-n), 0*0 + n*0 + 0*0 +0*1,
  0*2/(r-l) + 0*0 + (f+n)*0+ -1*-(r+l)/(r-l), 0*0+0*2/(t-b)+(f+n)*0+ -1*-(t+b)/(t-b), 0*0+0*0+(f+n)*-2/(f-n)+ -1 *-(f+n)/(f-n),0*0+0*0+(f+n)*0+-1*1,
  0*2/(r-l)+0*0+fn*0+0*-(r+l)/(r-l), 0*0+0*2/(t-b)+fn*0+0*-(t+b)/(t-b), 0*0 + 0*0 + fn* -2/(f-n) + 0*-(f+n)/(f-n), 0*0+0*0+fn*0 + 0*1, 
]
// 由于r+l=0 以及 t+b = 0
// 可以将矩阵简化为
[
  2n/(r-l),  0,          0,            0,
  0,         2n/(t-b),   0,            0,
  0,         0,          -(f+n)/(f-n), -1,
  0,         0,          (-2nf)/(f-n), 0
]
// js中矩阵是列主序的矩阵,所以等到如下图公式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

第一列*第一行 = 第一行的第一列

第1列*第2行 = 第1行的第2列

第1列*第3行 = 第1行的第3列

第1列*第4行 = 第1行的第4列

第2列*第1行 = 第2行的第1列

.......

收缩矩阵和正交投影矩阵相乘结果如下:

image.png

# 第四步通过角度将透视矩阵进行转换

根据视角α 和 宽高比 aspect 求出 top、bottom、left、right四个边界方向

t = n * tan(α /2)

b = -t

r = n * aspect * tan(α/2)

l = -r

计算之后结果

r-l = 2* n * aspect * tan(α/2)

t-b = 2n* tan(α/2)

r+l = 0;

t+b=0

将结果带入到透视投影矩阵中

image.png

# 透视投影实例代码

jcode (opens new window) 通过点击上下左右键盘,可以对视图的物体进行转动

# 三维场景中2个重要概念

# 隐藏面消除

正确处理物体的前后关系,是三维场景中重要的概念。前面的物体会阻挡后边物体的显示。在webgl中叫做隐藏面消除。

# 正常显示前后关系

 var verticesColors = new Float32Array([
   // 顶点坐标和颜色
   0.0, 1.0, -4.0 , 0.4, 1.0, 0.4, // 绿色三角形在后边
   -0.5, -1.0, -4.0 , 0.4, 1.0, 0.4,
   0.5, -1.0, -4.0 , 1.0, 0.4, 0.4,
   
   0.0, 1.0, -2.0 , 1.0, 1.0, 0.4, // 黄色的在中间
   -0.5, -1.0, -2.0 , 1.0, 1.0, 0.4,
   0.5, -1.0, -2.0 , 1.0, 0.4, 0.4,
   
   0.0, 1.0, -1.5 , 0.4, 0.4, 1.0, // 蓝色的前面
   -0.5, -1.0, -1.5 , 0.4, 0.4, 1.0,
   0.5, -1.0, -1.5 , 1.0, 0.4, 0.4,
 ]);
1
2
3
4
5
6
7
8
9
10
11
12
13
14

WEBGL中按照定义顶点坐标的顺序来绘制,蓝色的最后定义,所以可以绘制到最外层,同时Z轴坐标也是最靠前,能够正确显示物体的前后关系。

# 完整示例代码

jcode (opens new window)

# 非正常显示前后关系

接下来调整下顶点坐标的绘制顺序,将蓝色的放到第一个绘制。 image.png

jcode (opens new window) 蓝色显示在最后,绿色显示在最前面。这个和定义的Z轴坐标是不符合的。按照定义Z轴坐标的关系,绿色的三角形依然在最后。

# 使用隐藏消除面,解决前后关系

为了解决不符合Z轴前后关系的问题,webgl提供隐藏消除面的功能。

开启隐藏面消除功能,需要以下2步。

  • 开启隐藏面消除,gl.enable(gl.DEPTH_TEST)
  • 在绘制之前,清除深度缓冲区 gl.clear(gl.DEPTH_BUFFER_BIT);

gl.enable(cap)函数的使用,cap表示开启的功能

  • cap: gl.DEPTH_TEST 或 gl.BLEND 或 gl.POLYGON_OFFSET_FILL

gl.clear()方法清除深度缓冲区

image.png

jcode (opens new window)

# 深度冲突

隐藏面消除可以解决坐标位置前后关系,但是如果Z轴坐标相同的图形,还是会出现问题,使得图像出现斑斑驳驳。这种现象称为深度冲突。

image.png

webgl提供一个多边形偏移的功能,可以解决这个问题。机制是在Z轴上添加一个偏移量,偏移量的值由物体表面相对于观察者视线的角度来确定。

  1. 启用多边形偏移,gl.enable(gl.POLYGON_OFFSET_FILL);
  2. 在绘制之前指定偏移量的参数,gl.polygonOffset(1.0, 1.0);

image.png

image.png

const vm = getViewMatrix(eyex, eyey, eyez, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
const perspective = getPerspective(35, ctx.width / ctx.height, 100, 1);
// 创建隐藏面消除
gl.clearColor(0, 0, 0, 1);
gl.enable(gl.DEPTH_TEST);

// 清除视图缓存区
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

gl.uniformMatrix4fv(mat, false, mixMatrix(vm, perspective));
gl.enable(gl.POLYGON_OFFSET_FILL);
gl.drawArrays(gl.TRIANGLES, 0, 3 * 1);
gl.polygonOffset(1.0, 1.0);
gl.drawArrays(gl.TRIANGLES, 0, 3 * 1);
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 绘制立方体图形

# 顶点法

image.png

绘制立方体需要定义8个顶点;

image.png

# 创建基础模板

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    canvas{
      margin: 50px auto 0;
      display: block;
      background: yellow;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="400" height="400">
    此浏览器不支持canvas
  </canvas>
</body>
</html>
<script>
	function initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) {
	  const vertexShader = gl.createShader(gl.VERTEX_SHADER);
	  const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);

	  gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE) // 指定顶点着色器的源码
	  gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE) // 指定片元着色器的源码

	  // 编译着色器
	  gl.compileShader(vertexShader)
	  gl.compileShader(fragmentShader)

	  // 创建一个程序对象
	  const program = gl.createProgram();

	  gl.attachShader(program, vertexShader)
	  gl.attachShader(program, fragmentShader)

	  gl.linkProgram(program)

	  gl.useProgram(program)

	  return program;
	}
	
	// 视图矩阵获取
	function getViewMatrix(eyex, eyey, eyez, lookAtx, lookAty, lookAtz, upx, upy, upz) {

	  // 视点
	  const eye = new Float32Array([eyex, eyey, eyez])
	  // 目标点
	  const lookAt = new Float32Array([lookAtx, lookAty, lookAtz])
	  // 上方向
	  const up = new Float32Array([upx, upy, upz])

	  // 确定z轴
	  const z = minus(eye, lookAt);

	  normalized(z);
	  normalized(up);

	  // 确定x轴
	  const x = cross(z, up);

	  normalized(x);
	  // 确定y轴
	  const y = cross(x, z);

	  return new Float32Array([
		x[0],       y[0],       z[0],       0,
		x[1],       y[1],       z[1],       0,
		x[2],       y[2],       z[2],       0,
		-dot(x,eye),-dot(y,eye),-dot(z,eye),1
	  ])
	}
	
	// 矩阵复合函数
	function mixMatrix(A, B) {
	  const result = new Float32Array(16);

	  for (let i = 0; i < 4; i++) {
		result[i] = A[i] * B[0] + A[i + 4] * B[1] + A[i + 8] * B[2] + A[i + 12] * B[3]
		result[i + 4] = A[i] * B[4] + A[i + 4] * B[5] + A[i + 8] * B[6] + A[i + 12] * B[7]
		result[i + 8] = A[i] * B[8] + A[i + 4] * B[9] + A[i + 8] * B[10] + A[i + 12] * B[11]
		result[i + 12] = A[i] * B[12] + A[i + 4] * B[13] + A[i + 8] * B[14] + A[i + 12] * B[15]
	  }

	  return result;
	}
	
	// 归一化函数
	function normalized(arr) {
	  let sum = 0;

	  for (let i = 0; i < arr.length; i++) {
		sum += arr[i] * arr[i]
	  }

	  const middle = Math.sqrt(sum);

	  for (let i = 0; i < arr.length; i++) {
		arr[i] = arr[i] / middle;
	  }
	}
	
	// 叉积函数 获取法向量
	function cross(a,b) {
	  return new Float32Array([
		a[1] * b[2] - a[2] * b[1],
		a[2] * b[0] - a[0] * b[2],
		a[0] * b[1] - a[1] * b[0],
	  ])
	}
	
	// 点积函数 获取投影长度
	function dot(a, b) {
	  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
	}


	// 向量差
	function minus(a, b) {
	  return new Float32Array([
		a[0] - b[0],
		a[1] - b[1],
		a[2] - b[2],
	  ])
	}
	
	// 获取透视投影矩阵
	function getPerspective(fov, aspect, far, near) {
	  fov = fov * Math.PI / 180;
	  return new Float32Array([
		1/(aspect*Math.tan(fov / 2)), 0, 0, 0,
		0, 1/(Math.tan(fov/2)),0,0,
		0,0,-(far+near)/(far-near),-(2*far*near)/(far-near),
		0,0,-1,0,
	  ])
	}

  const ctx = document.getElementById('canvas')

  const gl = ctx.getContext('webgl')

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

    uniform mat4 mat;
    void main() {
      gl_Position = mat * aPosition;
      vColor = (1.0, 0.0, 0.0, 1.0);
    }
  `; // 顶点着色器

  const FRAGMENT_SHADER_SOURCE = `
    precision lowp float;
    varying vec4 vColor;

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

  const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)

  const aPosition = gl.getAttribLocation(program, 'aPosition');
  const aColor = gl.getAttribLocation(program, 'aColor');
  const mat = gl.getUniformLocation(program, 'mat');


  let eyex = 0.0;
  let eyey = -0.1;
  let eyez = 0.2;

  function draw() {
    const vm = getViewMatrix(eyex,eyey,eyez,0.0,0.0,0.0,0.0,0.6,0.0);
    const perspective = getPerspective(150, ctx.width / ctx.height, 100, 1);
   
    gl.uniformMatrix4fv(mat, false, mixMatrix(perspective, vm));
    gl.drawArrays(gl.TRIANGLES, 0, 3 * 6);
	
		requestAnimationFrame(draw);
  }

  draw()
</script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194

# 创建出需要的8个顶点数据

 // 顶点
  const v0 = [1,1,1];
  const v1 = [-1,1,1];
  const v2 = [-1,-1,1];
  const v3 = [1,-1,1];
  const v4 = [1,-1,-1];
  const v5 = [1,1,-1];
  const v6 = [-1,1,-1];
  const v7 = [-1,-1,-1];
// 通过结构顶点数据,组成的面
  const points = new Float32Array([
    ...v0,...v1,...v2, ...v0,...v2, ...v3, // 前
    ...v0,...v3,...v4, ...v0,...v4, ...v5, // 右
    ...v0,...v5,...v6, ...v0,...v6, ...v1, // 上面
    ...v1,...v6,...v7, ...v1,...v7, ...v2, // 左
    ...v7,...v4,...v3, ...v7,...v3, ...v2, // 底
    ...v4,...v7,...v6, ...v4,...v6, ...v5, // 后
  ])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

完整绘制代码 jcode (opens new window) 绘制出来一个红色正方形,现在还看不出来立方体的形状,可以通过改变视角,进行查看

image.png 通过调整 eyeX、eyeY、eyeZ的值,可以看到透视的正方形

jcode (opens new window)

# 添加旋转动画

image.png

jcode (opens new window)

# 给每个面添加自定义颜色

主要是重新定义个颜色的缓冲对象buffer;

将bufferData数据进行绑定,然后进行colorData赋值;

之后通过gl.vertexAttribPointer给aColor设置值;

jcode (opens new window)

# 索引法

# 创建顶点数据信息

// 创建顶点数据信息vertices
const vertices = new Float32Array([
   1, 1, 1,
  -1, 1, 1,
  -1,-1, 1,
   1,-1, 1,
   1,-1,-1,
   1, 1,-1,
  -1, 1,-1,
  -1,-1,-1,
])

const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosition)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 通过索引进行设置面

const indeces = new Uint8Array([
  // 3点组成一个3角形,2个三角形组成一个面
  0,1,2,0,2,3, 
  0,3,4,0,4,5,
  0,5,6,0,6,1,
  1,6,7,1,7,2,
  7,4,3,7,3,2,
  4,6,7,4,6,5,
])
const indexBuffer = gl.createBuffer();
// 绑定索引的buffer数据,需要使用 ELEMENT_ARRAY_BUFFER 方法
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indeces, gl.STATIC_DRAW);
1
2
3
4
5
6
7
8
9
10
11
12
13

通过索引绘制面,需要使用drawElements函数;

# drawElements方法函数的使用

用了绘制面信息,包含4个参数

  • mode:同drawArrays时的参数值
  • count: 要绘制的顶点数量
  • type: 顶点数据类型,值为gl.UNSIGNED_BYTE 或者 gl.UNSIGNED_SHORT
  • offset: 索引数组开始绘制的位置

image.png

# 完整的索引创建立方体代码

jcode (opens new window)

# 给每个面添加不同颜色

# 创建顶点缓存数据
// 顶点数据,组成面
const vertices = new Float32Array([
    // 0123,组成前面
    1, 1, 1,
    -1, 1, 1,
    -1,-1, 1,
    1,-1, 1,
    // 0345,组成右面
    1, 1, 1,
    1,-1, 1,
    1,-1,-1,
    1, 1,-1,
    // 0156,组成上面
    1, 1, 1,
    1, 1, -1,
    -1, 1,-1,
    -1, 1,1,
    // 1267,组成左面
    -1, 1, 1,
    -1,1, -1,
    -1, -1,-1,
    -1,-1,1,
    // 2347组成下面
    -1,-1, 1,
    1,-1, 1,
    1,-1,-1,
    -1,-1,-1,
    // 4567组成后面
    1,-1,-1,
    1, 1,-1,
    -1, 1,-1,
    -1,-1,-1,
])
// 顶点位置缓存区
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosition)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

创建颜色缓存数据

// 创建颜色数据
const colors = new Float32Array([
  0.4,0.4,1.0, 0.4,0.4,1.0, 0.4,0.4,1.0, 0.4,0.4,1.0,
  0.4,1.0,0.6, 0.4,1.0,0.6, 0.4,1.0,0.6, 0.4,1.0,0.6,
  1.0,0.4,0.4, 1.0,0.4,0.4, 1.0,0.4,0.4, 1.0,0.4,0.4,
  1.0,0.8,0.4, 1.0,0.8,0.4, 1.0,0.8,0.4, 1.0,0.8,0.4,
  1.0,0.0,1.0, 1.0,0.0,1.0, 1.0,0.0,1.0, 1.0,0.0,1.0,
  0.0,1.0,1.0, 0.0,1.0,1.0, 0.0,1.0,1.0, 0.0,1.0,1.0,
])
//  创建颜色缓存区
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aColor)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

jcode (opens new window)

内容参考 《webgl编程指南》

更多资源文档和代码下载地址 (opens new window)

编辑 (opens new window)
上次更新: 2025/04/19, 14:22:11
webgl基础2-颜色、纹理
webgl基础4-灯光

← webgl基础2-颜色、纹理 webgl基础4-灯光→

最近更新
01
色戒2007
04-19
02
真实real
04-19
03
Home
更多文章>
Theme by Vdoing | Copyright © 2018-2025 北鸟南游
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式