效果图

完整代码
<!DOCTYPE html>
<html>
<head>
<title>3D旋转柱状图</title>
<style>
body {
margin: 0
}
canvas {
display: block
}
</style>
</head>
<body>
<canvas id="canvas" width="800" height="800"></canvas>
<!-- 引入 gl-matrix 库 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.4.0/gl-matrix.js"></script>
<script>
const canvas = document.getElementById("canvas")
const gl = canvas.getContext("webgl")
if (!gl) {
alert("WebGL不可用,请更换支持WebGL的浏览器。")
}
// 顶点着色器程序
const vsSource = `
attribute vec4 aPosition
attribute vec4 aColor
uniform mat4 uModelViewMatrix
uniform mat4 uProjectionMatrix
varying lowp vec4 vColor
void main(void) {
gl_Position = uProjectionMatrix * uModelViewMatrix * aPosition
vColor = aColor
}
`
// 片元着色器程序
const fsSource = `
varying lowp vec4 vColor
void main(void) {
gl_FragColor = vColor
}
`
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
gl.shaderSource(vertexShader, vsSource)
gl.compileShader(vertexShader)
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
console.error("顶点着色器编译失败:", gl.getShaderInfoLog(vertexShader))
}
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(fragmentShader, fsSource)
gl.compileShader(fragmentShader)
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
console.error(
"片元着色器编译失败:",
gl.getShaderInfoLog(fragmentShader)
)
}
const shaderProgram = gl.createProgram()
gl.attachShader(shaderProgram, vertexShader)
gl.attachShader(shaderProgram, fragmentShader)
gl.linkProgram(shaderProgram)
gl.useProgram(shaderProgram)
const positionAttributeLocation = gl.getAttribLocation(
shaderProgram,
"aPosition"
)
const colorAttributeLocation = gl.getAttribLocation(
shaderProgram,
"aColor"
)
const modelViewMatrixUniformLocation = gl.getUniformLocation(
shaderProgram,
"uModelViewMatrix"
)
const projectionMatrixUniformLocation = gl.getUniformLocation(
shaderProgram,
"uProjectionMatrix"
)
gl.enableVertexAttribArray(positionAttributeLocation)
gl.enableVertexAttribArray(colorAttributeLocation)
const vertices = new Float32Array([
// 前面
-1.0, -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0,
1.0, 0.0, 0.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 0.0,
// 后面
-1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 1.0,
1.0, -1.0, 0.0, 0.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 0.0,
])
const indices = new Uint16Array([
0,
1,
2,
0,
2,
3, // 前面
4,
5,
6,
4,
6,
7, // 后面
0,
3,
7,
0,
7,
4, // 左侧
1,
2,
6,
1,
6,
5, // 右侧
0,
1,
5,
0,
5,
4, // 底部
2,
3,
7,
2,
7,
6, // 顶部
])
const vertexBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)
const indexBuffer = gl.createBuffer()
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW)
const projectionMatrix = mat4.create()
const modelViewMatrix = mat4.create()
const modelViewProjectionMatrix = mat4.create()
const fieldOfView = Math.PI / 4
const aspect = canvas.width / canvas.height
const zNear = 0.1
const zFar = 100.0
mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar)
const translation = [0, 0, -5]
const rotation = [0, 0, 0]
const scale = [1, 1, 1]
const identityMatrix = mat4.create()
let isDragging = false
let prevMouseX
let prevMouseY
canvas.addEventListener("mousedown", (e) => {
isDragging = true
prevMouseX = e.clientX
prevMouseY = e.clientY
})
canvas.addEventListener("mouseup", () => {
isDragging = false
})
canvas.addEventListener("mousemove", (e) => {
if (isDragging) {
const deltaX = e.clientX - prevMouseX
const deltaY = e.clientY - prevMouseY
prevMouseX = e.clientX
prevMouseY = e.clientY
rotation[0] += deltaY * 0.01
rotation[1] += deltaX * 0.01
}
})
function render() {
gl.clearColor(0.0, 0.0, 0.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
mat4.identity(modelViewMatrix)
mat4.translate(modelViewMatrix, modelViewMatrix, translation)
mat4.rotateX(modelViewMatrix, modelViewMatrix, rotation[0])
mat4.rotateY(modelViewMatrix, modelViewMatrix, rotation[1])
mat4.scale(modelViewMatrix, modelViewMatrix, scale)
mat4.multiply(
modelViewProjectionMatrix,
projectionMatrix,
modelViewMatrix
)
gl.uniformMatrix4fv(
modelViewMatrixUniformLocation,
false,
modelViewMatrix
)
gl.uniformMatrix4fv(
projectionMatrixUniformLocation,
false,
projectionMatrix
)
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
gl.vertexAttribPointer(
positionAttributeLocation,
3,
gl.FLOAT,
false,
24,
0
)
gl.vertexAttribPointer(
colorAttributeLocation,
3,
gl.FLOAT,
false,
24,
12
)
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0)
requestAnimationFrame(render)
}
render()
</script>
</body>
</html>