接下来是创建buffer,设置顶点位置,法线,顶点索引等。
const cubeVertexPositions = 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,
-1, 1, 1,
1, 1, 1,
1, 1, -1,
-1, 1, -1,
-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,
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 cubeVertexNormals = new Float32Array([
1, 0, 0,
1, 0, 0,
1, 0, 0,
1, 0, 0,
-1, 0, 0,
-1, 0, 0,
-1, 0, 0,
-1, 0, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, -1, 0,
0, -1, 0,
0, -1, 0,
0, -1, 0,
0, 0, 1,
0, 0, 1,
0, 0, 1,
0, 0, 1,
0, 0, -1,
0, 0, -1,
0, 0, -1,
0, 0, -1,
]);
const cubeVertexTexcoords = new Float32Array([
1, 0,
0, 0,
0, 1,
1, 1,
1, 0,
0, 0,
0, 1,
1, 1,
1, 0,
0, 0,
0, 1,
1, 1,
1, 0,
0, 0,
0, 1,
1, 1,
1, 0,
0, 0,
0, 1,
1, 1,
1, 0,
0, 0,
0, 1,
1, 1,
]);
const cubeVertexIndices = new Uint16Array([
0, 1, 2,
0, 2, 3,
4, 5, 6,
4, 6, 7,
8, 9, 10,
8, 10, 11,
12, 13, 14,
12, 14, 15,
16, 17, 18,
16, 18, 19,
20, 21, 22,
20, 22, 23,
],);
cubeVertexPositions
-
const cubeVertexPositions = new Float32Array([])
-
创建一个
Float32Array
类型的数组,用于存储立方体的八个顶点在三维空间中的位置坐标。每个顶点由三个浮点数表示,分别对应 X、Y、Z 轴的坐标。这个数组中依次列出了立方体八个顶点的坐标,按照一定的顺序排列,以便在后续的渲染过程中能够正确地构建立方体的几何形状。例如1,1,-1 表示一个顶点的坐标为X轴为1,Y轴为1,Z轴为-1。通过这种方式,定义立方体的位置。 -
每个顶点坐标包涵X,Y,Z三个分量,每个面有4个顶点,立方体有6个面,因此需要3x4x6 = 72个分量
-
此图用于理解
-
cubeVertexNormals
- 创建一个
Float32Array
类型的数组,存储了立方体每个顶点的法线向量。 -
法线向量是垂直于物体表面的向量,对于光照计算非常重要。在这个数组中,每个顶点都有一个对应的法线向量,所以是有24个组成,每个分别由三个浮点数表示,分别对应 X、Y、Z 轴的方向分量。例如,
1, 0, 0
表示一个法线向量在 X 轴方向上为 1,Y 轴和 Z 轴方向为 0,即指向 X 轴正方向。 -
法线分量也是一样,每个顶点三个分量,一个面有4个顶点,总共6个面,也是3x4x6=72个分量
cubeVertexTexcoords
-
这是一个
Float32Array
类型的数组,用于存储立方体每个顶点的纹理坐标纹理坐标。用于确定如何从纹理图像中采样颜色值,并将其应用到对应的顶点上。每个顶点由两个浮点数表示,通常在范围 [0, 1] 之间,分别对应纹理图像的 U(水平方向)和 V(垂直方向)坐标。(我们通常理解的UV坐标)例如1, 0
表示纹理坐标在纹理图像的右上角。 -
立方体有6个面,每个面由4个顶点,每个顶点需要2个纹理坐标分量,因此需要6 x 4 x 2 = 48个纹理坐标数据。
cubeVertexIndices
- 创建一个
Uint16Array
类型的数组,存储了绘制立方体所需的顶点索引。 -
由于立方体有八个顶点,但在绘制时通常使用三角形来构建表面,每个三角形需要三个顶点索引。这个数组中依次列出了构成立方体十二个面(每个面由两个三角形组成)的顶点索引。例如,
0, 1, 2
表示第一个三角形的三个顶点索引分别为 0、1、2,对应 cubeVertexPositions 数组中的三个顶点。通过这种方式,可以使用较少的数据量来定义复杂的几何形状,避免重复存储顶点数据。 -
立方体每个面由2个三角形绘制而成,每个三角形需要3个顶点索引,因此每个面需要6个索引,则总共需要2x3x6=36个分量
创建buffer缓冲区
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexPositions, gl.STATIC_DRAW);
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexNormals, gl.STATIC_DRAW);
const texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexTexcoords, gl.STATIC_DRAW);
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndices, gl.STATIC_DRAW);
- const positionBuffer = gl.createBuffer();
- 创建一个新的缓冲区对象,用于存储立方体顶点位置数据
- 创建的buffer缓冲区对象
- gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
- 将创建的缓冲区绑定到ARRAY_BUFFER上。后续对bufferData等方法的调用将操作这个特定的缓冲区
- 绑定到ARRAY_BUFFER上。
- gl.bufferData(gl.ARRAY_BUFFER, cubeVertexPositions, gl.STATIC_DRAW);
- 将存储在cubeVertexPositions数组中的立方体顶点位置数据上传到当前绑定的缓冲区。
- gl.STATIC_DRAW参数表示这些数据不会经常改变,适合一次性上传并多次绘制;
- 绑定数据
其他缓冲区也是一个道理,这里不做赘述
通过缓冲区,可以减少数据传输的开销,提高图形渲染的性能
创建并设置纹理
onst checkerTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, checkerTexture);
gl.texImage2D(
gl.TEXTURE_2D,
0, // mip level
gl.LUMINANCE, // internal format
4, // width
4, // height
0, // border
gl.LUMINANCE, // format
gl.UNSIGNED_BYTE, // type
new Uint8Array([ // data
192, 128, 192, 128,
128, 192, 128, 192,
192, 128, 192, 128,
128, 192, 128, 192,
]));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
const decalTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, decalTexture);
gl.texImage2D(
gl.TEXTURE_2D,
0, // mip level
gl.RGBA, // internal format
gl.RGBA, // format
gl.UNSIGNED_BYTE, // type
makeTextCanvas('F', 32, 32, 'red'));
gl.generateMipmap(gl.TEXTURE_2D);
- const checkerTexture = gl.createTexture();
- 使用Webgl的createTexture方法创建一个纹理对象,用于存储纹理;
- gl.bindTexture(gl.TEXTURE_2D, checkerTexture);
- 将创建的纹理对象绑定到TEXTURE_2D目标上,以便后续对该纹理进行操作;
- gl.texImage2D(gl.TEXTURE_2D,0, gl.LUMINANCE,4, 4,0, gl.LUMINANCE, gl.UNSIGNED_BYTE,new Uint8Array([...]));
- 定义纹理的图形数据。创建一个4x4的灰度纹理,数据由一个Unit8Array提供,其中包涵了不同灰度值的像素数据,用于形成棋盘格图案;
- gl.LUMINANCE表示内部格式和外部格式都是灰度
- gl.UNSIGNED_BYTE表示数据类型为无符号字节
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
- 设置纹理的缩小过滤器为最近邻过滤。当纹理缩小显示时,会选择最接近的像素,不进行差值。
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
- 设置纹理的方法过滤器也为最近邻过滤,当纹理被放大显示时,同样选择最接近的像素。
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- 设置纹理在水平方向(S轴)的环绕模式为CLAMP_TO_EDGE,当超出纹理范围的坐标会被截断到纹理边缘颜色;
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- 设置纹理在垂直方向(T轴)的环绕模式也为CLAMP_TO_EDGE
创建并设置贴花纹理
- const decalTexture = gl.createTexture();
- 创建另一个纹理对象,用于存储贴花纹理
- gl.bindTexture(gl.TEXTURE_2D,decalTexture);
- 绑定贴花纹理对象
- gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
gl.RGBA,
gl.UNSIGNED_BYTE,
makeTextCanvas('F', 32, 32, 'red'));- 定义贴花纹理的图像数据。自定义创建了一个包涵字符F的红色32x32的画布,并将其作为纹理数据。内部格式和外部格式以及数据类型分别为gl.RGBA和gl.UNSIGNED_BYTE,表示一个包涵红、绿、蓝和透明通达的纹理。
-
const makeTextCanvas = (text, width, height, color) => { const ctx = document.createElement('canvas').getContext('2d') ctx.canvas.width = width ctx.canvas.height = height ctx.font = `bold ${height * 5 / 6 | 0}px sans-serif` ctx.textAlign = 'center' ctx.textBaseline = 'middle' ctx.fillStyle = color ctx.fillText(text, width / 2, height / 2) return ctx.canvas };
-
gl.generateMipmap(gl.TEXTURE_2D);
-
生成纹理的多级渐远纹理(mipmap)。多级渐远纹理可以提高纹理在不同距离下的渲染质量和性能。
-
- 图片上的内容只是辅助理解
- 到这里,我们就完成了所有的初始化代码。