代码是 VUE3 ,里面包含了空间变换(矩阵运算)和灯光效果(向量运算)的示例代码,可以帮助理解前面三节文章的知识点。
<template>
<div>
使用矩阵进行三维空间的位移、旋转、缩放变换,灯光效果。原理与二维类似。
聚光灯实例中会出现黑色,可能是计算出现undefined的原因,要专门处理之后才可以消除。
</div>
<canvas id="canvas"></canvas>
<a-tabs style="width: 300px; float: right">
<a-tab-pane key="1" tab="模型参数">
X 方向平移:{{ transform.tx }}
<a-slider class="slider" v-model:value="transform.tx" :step="1" :min="-300" :max="300" />
Y 方向平移:{{ transform.ty }}
<a-slider class="slider" v-model:value="transform.ty" :step="1" :min="-300" :max="300" />
Z 方向平移:{{ transform.tz }}
<a-slider class="slider" v-model:value="transform.tz" :step="1" :min="-300" :max="300" />
绕 X 轴旋转:{{ transform.rx }}
<a-slider class="slider" v-model:value="transform.rx" :step="1" :min="0" :max="360" />
绕 Y 轴旋转:{{ transform.ry }}
<a-slider class="slider" v-model:value="transform.ry" :step="1" :min="0" :max="360" />
绕 Z 轴旋转:{{ transform.rz }}
<a-slider class="slider" v-model:value="transform.rz" :step="1" :min="0" :max="360" />
X 方向缩放:{{ transform.sx }}
<a-slider class="slider" v-model:value="transform.sx" :step="1" :min="1" :max="10" />
Y 方向缩放:{{ transform.sy }}
<a-slider class="slider" v-model:value="transform.sy" :step="1" :min="1" :max="10" />
Z 方向缩放:{{ transform.sz }}
<a-slider class="slider" v-model:value="transform.sz" :step="1" :min="1" :max="10" />
</a-tab-pane>
<a-tab-pane key="3" tab="相机参数">
相机 X 坐标:{{ transform.cameraX }}
<a-slider class="slider" v-model:value="transform.cameraX" :step="1" :min="-300" :max="300" />
相机 Y 坐标:{{ transform.cameraY }}
<a-slider class="slider" v-model:value="transform.cameraY" :step="1" :min="-300" :max="300" />
相机 Z 坐标:{{ transform.cameraZ }}
<a-slider class="slider" v-model:value="transform.cameraZ" :step="1" :min="-300" :max="300" />
目标点 X 坐标:{{ transform.targetX }}
<a-slider class="slider" v-model:value="transform.targetX" :step="1" :min="-300" :max="300" />
目标点 Y 坐标:{{ transform.targetY }}
<a-slider class="slider" v-model:value="transform.targetY" :step="1" :min="-300" :max="300" />
目标点 Z 坐标:{{ transform.targetZ }}
<a-slider class="slider" v-model:value="transform.targetZ" :step="1" :min="-300" :max="300" />
</a-tab-pane>
<a-tab-pane key="2" tab="灯光参数">
灯光 X 坐标:{{ transform.lightX }}
<a-slider class="slider" v-model:value="transform.lightX" :step="1" :min="-300" :max="300" />
灯光 Y 坐标:{{ transform.lightY }}
<a-slider class="slider" v-model:value="transform.lightY" :step="1" :min="-300" :max="300" />
灯光 Z 坐标:{{ transform.lightZ }}
<a-slider class="slider" v-model:value="transform.lightZ" :step="1" :min="-300" :max="300" />
聚光灯内径:{{ transform.spotLightR1 }}
<a-slider class="slider" v-model:value="transform.spotLightR1" :step="1" :min="0" :max="360" />
聚光灯外径:{{ transform.spotLightR2 }}
<a-slider class="slider" v-model:value="transform.spotLightR2" :step="1" :min="0" :max="360" />
</a-tab-pane>
</a-tabs>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { M4 } from '../../utils/m4';
// 变换参数
let transform = ref({
tx: 0,
ty: 0,
tz: -10,
rx: 0,
ry: 10,
rz: 0,
sx: 1.0,
sy: 1.0,
sz: 1.0,
cameraX: 100,
cameraY: 150,
cameraZ: 200,
targetX: 0,
targetY: 35,
targetZ: 0,
lightX: 20,
lightY: 30,
lightZ: 60,
spotLightR1: 10,
spotLightR2: 20,
});
let gl: WebGLRenderingContext;
let program: WebGLProgram;
onMounted(async () => {
// 准备环境
gl = createWebGL();
// 创建着色器
program = createProgram(gl)!;
render();
});
/** 从 canvas 获取 gl */
function createWebGL(): WebGLRenderingContext {
let canvas = document.getElementById('canvas') as HTMLCanvasElement;
let gl = canvas.getContext('webgl2') as WebGLRenderingContext;
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
gl.viewport(0, 0, canvas.width, canvas.height);
return gl;
}
/** 创建着色器 */
function createShader(gl: WebGLRenderingContext, type: number, source: string): WebGLShader | undefined {
let shader: WebGLShader = gl.createShader(type)!; // 创建着色器对象
gl.shaderSource(shader, source); // 提供数据源
gl.compileShader(shader); // 编译 -> 生成着色器
let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.log(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
/** 创建着色器程序 */
function createProgram(gl: WebGLRenderingContext): WebGLProgram | undefined {
let vertexShaderSource = `
attribute vec4 a_position; // 模型局部坐标系下的顶点(这里的顶点数据已经是世界坐标系了)
attribute vec3 a_color; // 模型各个面的颜色
attribute vec3 a_normal; // 模型局部坐标系下的法向量(这里的法向量数据已经是世界坐标系了)
uniform mat4 u_mvpMatrix;
uniform mat4 u_mMatrix;
uniform mat4 u_wMatrix;
uniform mat4 u_nMatrix;
uniform vec3 u_pointLightPosition;
uniform vec3 u_cameraPosition;
varying vec3 v_normal; // 世界坐标下法向量,用于和平行光相乘
varying vec3 v_surfaceToLight; // 世界坐标下点光源方向,用于计算光照
varying vec3 v_surfaceToCamera; // 世界坐标下点视线方向,用于计算光照
varying vec3 v_color;
void main() {
mat4 matrix = u_mvpMatrix * u_wMatrix;
gl_Position = matrix * a_position;
v_normal = mat3(u_nMatrix) * a_normal; // 重新计算世界坐标的下法向量
vec3 surfacePosition = (u_wMatrix * a_position).xyz; // 变换之后的顶点的世界坐标
v_surfaceToLight = u_pointLightPosition - surfacePosition; // 计算世界坐标下每个顶点对应的点光源方向
v_surfaceToCamera = u_cameraPosition - surfacePosition; // 计算世界坐标下每个顶点对应的视线方向
v_color = a_color;
}
`;
let fragmentShaderSource = `
precision mediump float;
varying vec3 v_color;
varying vec3 v_normal; // 片元法向量
varying vec3 v_surfaceToCamera; // 视线方向
varying vec3 v_surfaceToLight; // 点光源光线方向
uniform vec3 u_parallelLightDirection; // 平行光的方向,传进来的就是归一化向量
uniform float u_spotLightAngleLimitIn; // 点光的范围(聚光大小)
uniform float u_spotLightAngleLimitOut; // 点光的范围(聚光大小)
uniform vec3 u_spotLightDirection; // 聚光方向
uniform vec3 v_ambientLightColor; // 环境光颜色
uniform vec3 u_pointLightColor; // 点光颜色
uniform vec3 u_specularColor; // 高光颜色
uniform float u_shininess; // 高光颜强度
void main() {
vec3 normal = normalize(v_normal);
// // 平行光
// float parallelLight = dot(normal, u_parallelLightDirection); // 计算平行光照
// vec3 parallel = parallelLight * v_color; // 将颜色rgb部分和平行光相乘
// vec3 ambientColor = v_ambientLightColor * v_color; // 环境光
// gl_FragColor = vec4(parallel + ambientColor, 1);
// // 点光
// vec3 surfaceToLightDirection = normalize(v_surfaceToLight); // 点光方向
// vec3 surfaceToCameraDirection = normalize(v_surfaceToCamera); // 视线方向
// vec3 halfVector = normalize(surfaceToLightDirection + surfaceToCameraDirection); // 视线和点光方向夹角的一半
// float pointLight = dot(normal, surfaceToLightDirection); // 计算点光照
// float specular = 0.0;
// if (pointLight > 0.0) {
// specular = pow(dot(normal, halfVector), u_shininess); // 高光强度(值越大亮斑越小)
// }
// // 颜色合成
// vec3 spotColor = pointLight * u_pointLightColor * v_color; // 将颜色rgb部分和点光、点光颜色相乘
// vec3 specularColor = specular * u_specularColor; // 加上高光强度和颜色
// vec3 ambientColor = v_ambientLightColor * v_color; // 环境光
// gl_FragColor = vec4(spotColor + specularColor + ambientColor, 1);
// 聚光
vec3 surfaceToLightDirection = normalize(v_surfaceToLight); // 点光方向
vec3 surfaceToCameraDirection = normalize(v_surfaceToCamera); // 视线方向
vec3 halfVector = normalize(surfaceToLightDirection + surfaceToCameraDirection); // 视线和点光方向夹角的一半
float dotFromDirection = dot(surfaceToLightDirection, -u_spotLightDirection);
float inLight = smoothstep(u_spotLightAngleLimitOut, u_spotLightAngleLimitIn, dotFromDirection);
float spotLight = inLight * dot(normal, surfaceToLightDirection);
float specular = inLight * pow(dot(normal, halfVector), u_shininess);
// 颜色合成
vec3 spotColor = spotLight * v_color; // 将颜色rgb部分和聚光相乘
vec3 specularColor = specular * u_specularColor; // 加上高光强度和颜色
vec3 ambientColor = v_ambientLightColor * v_color; // 环境光
gl_FragColor = vec4(spotColor + specularColor + ambientColor, 1);
}
`;
// 创建着色器方法,输入参数:渲染上下文,着色器类型,数据源
let vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource)!;
let fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource)!;
let program: WebGLProgram = gl.createProgram()!;
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
let success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (success) {
gl.useProgram(program);
return program;
} else {
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
}
/** 渲染 */
let zDeg = 0;
function render() {
zDeg += 0.5;
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // Clear the canvas AND the depth buffer.
gl.enable(gl.CULL_FACE); // 开启背面剔除
gl.enable(gl.DEPTH_TEST); // 开启深度检测
let positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
let colorAttributeLocation = gl.getAttribLocation(program, 'a_color');
let normalAttributeLocation = gl.getAttribLocation(program, 'a_normal');
let ambientLightColorLocation = gl.getUniformLocation(program, 'v_ambientLightColor');
let parallelLightDirectionLocation = gl.getUniformLocation(program, 'u_parallelLightDirection');
let pointLightPositionLocation = gl.getUniformLocation(program, 'u_pointLightPosition');
let pointLightColorLocation = gl.getUniformLocation(program, 'u_pointLightColor');
let spotLightAngleLimitInLocation = gl.getUniformLocation(program, 'u_spotLightAngleLimitIn');
let spotLightAngleLimitOutLocation = gl.getUniformLocation(program, 'u_spotLightAngleLimitOut');
let spotLightDirectionLocation = gl.getUniformLocation(program, 'u_spotLightDirection');
let shininessLocation = gl.getUniformLocation(program, 'u_shininess');
let specularColorLocation = gl.getUniformLocation(program, 'u_specularColor');
let cameraPositionLocation = gl.getUniformLocation(program, 'u_cameraPosition');
let mMatrixLocation = gl.getUniformLocation(program, 'u_mMatrix');
let mvpMatrixLocation = gl.getUniformLocation(program, 'u_mvpMatrix');
let wMatrixLocation = gl.getUniformLocation(program, 'u_wMatrix');
let nMatrixLocation = gl.getUniformLocation(program, 'u_nMatrix');
// 设置模型数据
setPosition(gl); // 顶点
gl.enableVertexAttribArray(positionAttributeLocation);
gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);
setColor(gl); // 颜色
gl.enableVertexAttribArray(colorAttributeLocation);
gl.vertexAttribPointer(colorAttributeLocation, 3, gl.UNSIGNED_BYTE, true, 0, 0);
setNormal(gl); // 法线,是固定的,如果要变,也要和矩阵相乘
gl.enableVertexAttribArray(normalAttributeLocation);
gl.vertexAttribPointer(normalAttributeLocation, 3, gl.FLOAT, true, 0, 0);
//#region 设置光照数据
// 设置环境光颜色
gl.uniform3fv(ambientLightColorLocation, new Float32Array([0.2, 0.2, 0.2]));
// 设置平行光线,只有一个方向
gl.uniform3fv(parallelLightDirectionLocation, M4.normalize([0.5, 0.7, 1]));
// 设置点光(只需要设置点光源的位置,然后在着色器中计算每个顶点上光的方向)
gl.uniform3fv(pointLightColorLocation, [1, 0.6, 0.6]);
gl.uniform3fv(pointLightPositionLocation, [transform.value.lightX, transform.value.lightY, transform.value.lightZ]);
gl.uniform1f(shininessLocation, 35);
gl.uniform1f(spotLightAngleLimitInLocation, Math.cos((transform.value.spotLightR1 * Math.PI) / 180));
gl.uniform1f(spotLightAngleLimitOutLocation, Math.cos((transform.value.spotLightR2 * Math.PI) / 180));
gl.uniform3fv(specularColorLocation, [1, 0.6, 0.6]);
//#endregion
// M 模型矩阵,这里的坐标已经是世界坐标,不需要变换了
let mMatrix = M4.identity();
gl.uniformMatrix4fv(mMatrixLocation, false, mMatrix);
// V 视图矩阵(相机在100,150,200),目标在(0,35,0)
let vMatrix = M4.identity();
let cameraPosition = [transform.value.cameraX, transform.value.cameraY, transform.value.cameraZ];
let targetPosition = [transform.value.targetX, transform.value.targetY, transform.value.targetZ];
vMatrix = M4.inverse(M4.lookAt(cameraPosition, targetPosition, [0, 1, 0]));
// 设置相机的位置
gl.uniform3fv(cameraPositionLocation, cameraPosition);
gl.uniform3fv(spotLightDirectionLocation, [-vMatrix[8], -vMatrix[9], -vMatrix[10]]);
// P 投影矩阵
// 正交投影,长宽是容器大小,深度是6000(大点,不容易被裁减)
// let pMatrix = M4.orthographic(-gl.canvas.width / 2, gl.canvas.width / 2, -gl.canvas.height / 2, gl.canvas.height / 2, -3000, 3000);
// 透视投影,90度视场角,深度是6000(大点,不容易被裁减)
let pMatrix = M4.perspective((60 * Math.PI) / 180, gl.canvas.width / gl.canvas.width, 1, 6000);
// 计算最终矩阵,注意是左乘PMV顺序,满足乘法结合律,不满足交换律
let mvpMatrix = M4.multiply(pMatrix, M4.multiply(vMatrix, mMatrix));
gl.uniformMatrix4fv(mvpMatrixLocation, false, mvpMatrix);
//#endregion
//#region W 世界矩阵:
let wMatrix = M4.identity();
wMatrix = M4.translate(wMatrix, transform.value.tx, transform.value.ty, transform.value.tz); // 平移矩阵
// wMatrix = M4.xRotate(wMatrix, (zDeg * Math.PI) / 180); // 旋转矩阵(自动旋转动画)
// wMatrix = M4.yRotate(wMatrix, (zDeg * Math.PI) / 180); // 旋转矩阵(自动旋转动画)
// wMatrix = M4.zRotate(wMatrix, (zDeg * Math.PI) / 180); // 旋转矩阵(自动旋转动画)
wMatrix = M4.xRotate(wMatrix, (transform.value.rx * Math.PI) / 180); // 旋转矩阵
wMatrix = M4.yRotate(wMatrix, (transform.value.ry * Math.PI) / 180); // 旋转矩阵
wMatrix = M4.zRotate(wMatrix, (transform.value.rz * Math.PI) / 180); // 旋转矩阵
wMatrix = M4.scale(wMatrix, transform.value.sx, transform.value.sy, transform.value.sz); // 缩放矩阵
gl.uniformMatrix4fv(wMatrixLocation, false, wMatrix);
//#endregion
// 设置法向矩阵
let nMatrix = M4.transpose(M4.inverse(M4.multiply(mMatrix, wMatrix)));
gl.uniformMatrix4fv(nMatrixLocation, false, nMatrix);
// 16 个矩形(每个矩形 2 个三角形,每个三角形 3 个点)
gl.drawArrays(gl.TRIANGLES, 0, 16 * 2 * 3);
requestAnimationFrame(render);
}
const setPosition = (gl: WebGLRenderingContext) => {
// 除了Z方向的面逆时针,其他方向(+-X、+-Y、-Z)都要顺时针
// prettier-ignore
let positions = [
-0.8, 0.8, 0.1, // F前面-1(是逆时针)
-0.8, -0.8, 0.1,
-0.5, -0.8, 0.1,
-0.8, 0.8, 0.1, // F前面-2(是逆时针)
-0.5, -0.8, 0.1,
-0.5, 0.8, 0.1,
-0.5, 0.8, 0.1, // F前面-3(是逆时针)
-0.5, 0.6, 0.1,
0.0, 0.6, 0.1,
-0.5, 0.8, 0.1, // F前面-4(是逆时针)
0.0, 0.6, 0.1,
0.0, 0.8, 0.1,
-0.5, 0.1, 0.1, // F前面-5(是逆时针)
-0.5, -0.1, 0.1,
-0.1, -0.1, 0.1,
-0.1, -0.1, 0.1, // F前面-6(是逆时针)
-0.1, 0.1, 0.1,
-0.5, 0.1, 0.1,
//#region 侧面
-0.8, 0.8, 0.1, // F顶面-1(是顺时针)
0.0, 0.8, -0.1,
-0.8, 0.8, -0.1,
-0.8, 0.8, 0.1, // F顶面-2(是顺时针)
0.0, 0.8, 0.1,
0.0, 0.8, -0.1,
-0.8, 0.8, -0.1, // F左面-1(是顺时针)
-0.8, -0.8, -0.1,
-0.8, 0.8, 0.1,
-0.8, -0.8, -0.1, // F左面-2(是顺时针)
-0.8, -0.8, 0.1,
-0.8, 0.8, 0.1,
-0.8, -0.8, 0.1, // F面-1(是顺时针)
-0.5, -0.8, -0.1,
-0.5, -0.8, 0.1,
-0.8, -0.8, 0.1, // F面-2(是顺时针)
-0.8, -0.8, -0.1,
-0.5, -0.8, -0.1,
//#endregion
-0.8, 0.8, -0.1, // F后面-1(是顺时针)
-0.5, -0.8, -0.1,
-0.8, -0.8, -0.1,
-0.8, 0.8, -0.1, // F后面-2(是顺时针)
-0.5, 0.8, -0.1,
-0.5, -0.8, -0.1,
-0.5, 0.8, -0.1, // F后面-3(是顺时针)
0.0, 0.6, -0.1,
-0.5, 0.6, -0.1,
-0.5, 0.8, -0.1, // F后面-4(是顺时针)
0.0, 0.8, -0.1,
0.0, 0.6, -0.1,
-0.5, 0.1, -0.1, // F后面-5(是顺时针)
-0.1, -0.1, -0.1,
-0.5, -0.1, -0.1,
-0.5, 0.1, -0.1, // F后面-6(是顺时针)
-0.1, 0.1, -0.1,
-0.1, -0.1, -0.1,
// 右侧还有几个面
];
// 使用教程里的数据,倒立的 F,Z 值都大于 0,后面会“扶正”
positions = [
// left column front
0, 0, 0, 0, 150, 0, 30, 0, 0, 0, 150, 0, 30, 150, 0, 30, 0, 0,
// top rung front
30, 0, 0, 30, 30, 0, 100, 0, 0, 30, 30, 0, 100, 30, 0, 100, 0, 0,
// middle rung front
30, 60, 0, 30, 90, 0, 67, 60, 0, 30, 90, 0, 67, 90, 0, 67, 60, 0,
// left column back
0, 0, 30, 30, 0, 30, 0, 150, 30, 0, 150, 30, 30, 0, 30, 30, 150, 30,
// top rung back
30, 0, 30, 100, 0, 30, 30, 30, 30, 30, 30, 30, 100, 0, 30, 100, 30, 30,
// middle rung back
30, 60, 30, 67, 60, 30, 30, 90, 30, 30, 90, 30, 67, 60, 30, 67, 90, 30,
// top
0, 0, 0, 100, 0, 0, 100, 0, 30, 0, 0, 0, 100, 0, 30, 0, 0, 30,
// top rung right
100, 0, 0, 100, 30, 0, 100, 30, 30, 100, 0, 0, 100, 30, 30, 100, 0, 30,
// under top rung
30, 30, 0, 30, 30, 30, 100, 30, 30, 30, 30, 0, 100, 30, 30, 100, 30, 0,
// between top rung and middle
30, 30, 0, 30, 60, 30, 30, 30, 30, 30, 30, 0, 30, 60, 0, 30, 60, 30,
// top of middle rung
30, 60, 0, 67, 60, 30, 30, 60, 30, 30, 60, 0, 67, 60, 0, 67, 60, 30,
// right of middle rung
67, 60, 0, 67, 90, 30, 67, 60, 30, 67, 60, 0, 67, 90, 0, 67, 90, 30,
// bottom of middle rung.
30, 90, 0, 30, 90, 30, 67, 90, 30, 30, 90, 0, 67, 90, 30, 67, 90, 0,
// right of bottom
30, 90, 0, 30, 150, 30, 30, 90, 30, 30, 90, 0, 30, 150, 0, 30, 150, 30,
// bottom
0, 150, 0, 0, 150, 30, 30, 150, 30, 0, 150, 0, 30, 150, 30, 30, 150, 0,
// left side
0, 0, 0, 0, 0, 30, 0, 150, 30, 0, 0, 0, 0, 150, 30, 0, 150, 0,
];
// 把 F 扶正,中心放到 0 0 0 位置
let matrix = M4.xRotation(Math.PI); // 反转倒立的 F
matrix = M4.translate(matrix, -50, -75, -15); // 移动到中心位置
for (let i = 0; i < positions.length; i += 3) {
// 3个一步长,依次转换每一个点的坐标
let vector = M4.transformPoint(matrix, [positions[i + 0], positions[i + 1], positions[i + 2], 1]);
positions[i + 0] = vector[0];
positions[i + 1] = vector[1];
positions[i + 2] = vector[2];
}
let positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
};
const setColor = (gl: WebGLRenderingContext) => {
// prettier-ignore
let colors = [
100, 0, 0, // F前面-1
100, 0, 0,
100, 0, 0,
0, 100, 0, // F前面-2
0, 100, 0,
0, 100, 0,
100, 20, 0, // F前面-3
100, 20, 0,
100, 20, 0,
100, 60, 0, // F前面-4
100, 60, 0,
100, 60, 0,
100, 80, 0, // F前面-5
100, 80, 0,
100, 80, 0,
100, 0, 0, // F前面-6
100, 0, 0,
100, 0, 0,
//#region 侧面
0, 100, 0, // 侧顶面-1
0, 100, 0,
0, 100, 0,
130, 10, 0, // 侧顶面-2
130, 10, 0,
130, 10, 0,
0, 100, 0, // 侧左面-1
0, 100, 0,
0, 100, 0,
60, 100, 0, // 侧左面-2
60, 100, 0,
60, 100, 0,
0,100, 100, // 侧下面-1
0,100, 100,
0,100, 100,
60, 100, 0, // 侧下面-2
60, 100, 0,
60, 100, 0,
//#endregion
0, 0, 100, // F后面-1
0, 0, 100,
0, 0, 100,
80, 0, 100, // F后面-2
80, 0, 100,
80, 0, 100,
20, 0, 100, // F后面-3
20, 0, 100,
20, 0, 100,
40, 0, 100, // F后面-4
40, 0, 100,
40, 0, 100,
0, 0, 100, // F后面-5
0, 0, 100,
0, 0, 100,
60, 0, 100, // F后面-6
60, 0, 100,
60, 0, 100,
];
// 使用 教程里的数据
colors = [
// left column front
200, 70, 120, 200, 70, 120, 200, 70, 120, 200, 70, 120, 200, 70, 120, 200, 70, 120,
// top rung front
200, 70, 120, 200, 70, 120, 200, 70, 120, 200, 70, 120, 200, 70, 120, 200, 70, 120,
// middle rung front
200, 70, 120, 200, 70, 120, 200, 70, 120, 200, 70, 120, 200, 70, 120, 200, 70, 120,
// left column back
80, 70, 200, 80, 70, 200, 80, 70, 200, 80, 70, 200, 80, 70, 200, 80, 70, 200,
// top rung back
80, 70, 200, 80, 70, 200, 80, 70, 200, 80, 70, 200, 80, 70, 200, 80, 70, 200,
// middle rung back
80, 70, 200, 80, 70, 200, 80, 70, 200, 80, 70, 200, 80, 70, 200, 80, 70, 200,
// top
70, 200, 210, 70, 200, 210, 70, 200, 210, 70, 200, 210, 70, 200, 210, 70, 200, 210,
// top rung right
200, 200, 70, 200, 200, 70, 200, 200, 70, 200, 200, 70, 200, 200, 70, 200, 200, 70,
// under top rung
210, 100, 70, 210, 100, 70, 210, 100, 70, 210, 100, 70, 210, 100, 70, 210, 100, 70,
// between top rung and middle
210, 160, 70, 210, 160, 70, 210, 160, 70, 210, 160, 70, 210, 160, 70, 210, 160, 70,
// top of middle rung
70, 180, 210, 70, 180, 210, 70, 180, 210, 70, 180, 210, 70, 180, 210, 70, 180, 210,
// right of middle rung
100, 70, 210, 100, 70, 210, 100, 70, 210, 100, 70, 210, 100, 70, 210, 100, 70, 210,
// bottom of middle rung.
76, 210, 100, 76, 210, 100, 76, 210, 100, 76, 210, 100, 76, 210, 100, 76, 210, 100,
// right of bottom
140, 210, 80, 140, 210, 80, 140, 210, 80, 140, 210, 80, 140, 210, 80, 140, 210, 80,
// bottom
90, 130, 110, 90, 130, 110, 90, 130, 110, 90, 130, 110, 90, 130, 110, 90, 130, 110,
// left side
160, 160, 220, 160, 160, 220, 160, 160, 220, 160, 160, 220, 160, 160, 220, 160, 160, 220,
];
let colorsBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(colors), gl.STATIC_DRAW);
};
const setNormal = (gl: WebGLRenderingContext) => {
let normals = [
// 正面左竖
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, 1, 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, 1,
// 背面左竖
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, -1, 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, -1,
// 顶部
0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 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,
// 上横和中横之间
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,
// 中横右面
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,
// 底部右侧
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,
// 左面
-1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0,
];
let normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
};
</script>
<style>
#canvas {
width: 600px;
height: 600px;
border: 1px solid orange;
}
</style>
这是WebGL 系列的入门文章,免费订阅,如有帮助请点赞收藏,纰漏之处欢迎指正!
也欢迎关注公众号交流知识哇😄