计算机图形学进阶探索与实践
计算机图形学的世界丰富而复杂,除了基础概念与操作,还有许多进阶内容值得深入钻研。接下来,我们将围绕曲线绘制、三维模型渲染、动画实现等方面,结合 JavaScript 代码,开启一场新的计算机图形学学习之旅。
一、曲线绘制
在图形设计与建模中,曲线的绘制至关重要,它能让图形更加流畅和自然。贝塞尔曲线是计算机图形学中常用的曲线类型,通过控制点可以灵活地调整曲线形状。
(一)二次贝塞尔曲线
二次贝塞尔曲线由一个起点、一个终点和一个控制点确定。在 JavaScript 中,我们可以通过以下代码实现绘制二次贝塞尔曲线:
html
<!DOCTYPE html>
<html>
<body>
<canvas id="curveCanvas" width="400" height="400"></canvas>
<script>
const canvas = document.getElementById('curveCanvas');
const ctx = canvas.getContext('2d');
// 绘制二次贝塞尔曲线
function drawQuadraticBezierCurve() {
ctx.beginPath();
ctx.moveTo(50, 200); // 起点
ctx.quadraticCurveTo(200, 50, 350, 200); // 控制点,终点
ctx.strokeStyle = 'blue';
ctx.lineWidth = 3;
ctx.stroke();
}
drawQuadraticBezierCurve();
</script>
</body>
</html>
在上述代码中,quadraticCurveTo方法接收控制点的坐标和终点坐标,从而绘制出二次贝塞尔曲线。通过改变控制点和起点、终点的坐标,就能得到不同形状的曲线。
(二)三次贝塞尔曲线
三次贝塞尔曲线由一个起点、一个终点和两个控制点确定,相比二次贝塞尔曲线,它能表达更复杂的形状。在 JavaScript 的 canvas 中绘制三次贝塞尔曲线的代码如下:
html
<!DOCTYPE html>
<html>
<body>
<canvas id="cubicCurveCanvas" width="400" height="400"></canvas>
<script>
const canvas = document.getElementById('cubicCurveCanvas');
const ctx = canvas.getContext('2d');
// 绘制三次贝塞尔曲线
function drawCubicBezierCurve() {
ctx.beginPath();
ctx.moveTo(50, 50); // 起点
ctx.bezierCurveTo(100, 200, 300, 0, 350, 350); // 控制点1,控制点2,终点
ctx.strokeStyle ='red';
ctx.lineWidth = 3;
ctx.stroke();
}
drawCubicBezierCurve();
</script>
</body>
</html>
bezierCurveTo方法依次接收两个控制点的坐标和终点坐标,利用这些参数绘制出三次贝塞尔曲线。通过巧妙调整控制点,能够创造出各种独特的曲线形态,在 UI 设计、图标绘制等场景中广泛应用。
二、三维模型渲染
三维模型的渲染是计算机图形学的核心领域之一,它能让虚拟的三维物体以逼真的效果呈现在屏幕上。WebGL 是在网页上进行三维图形渲染的标准接口,下面我们使用 JavaScript 结合 WebGL 来实现简单的三维模型渲染。
(一)创建三维场景基础
首先,我们需要创建一个 WebGL 上下文,并初始化三维场景的基本设置,如清空画布、设置视口等。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>3D Model Rendering</title>
</head>
<body>
<canvas id="webglCanvas" width="600" height="600"></canvas>
<script>
const canvas = document.getElementById('webglCanvas');
const gl = canvas.getContext('webgl');
if (!gl) {
alert('WebGL is not supported in your browser.');
}
// 清空画布
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// 设置视口
gl.viewport(0, 0, canvas.width, canvas.height);
</script>
</body>
</html>
上述代码获取 WebGL 上下文,如果浏览器不支持 WebGL 则给出提示。接着设置画布的清空颜色为黑色,并清空画布,最后设置视口大小与画布一致。
(二)绘制三维几何体
以绘制一个简单的立方体为例,我们需要定义立方体的顶点数据,并通过 WebGL 将其渲染到场景中。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>3D Cube Rendering</title>
</head>
<body>
<canvas id="webglCanvas" width="600" height="600"></canvas>
<script>
const canvas = document.getElementById('webglCanvas');
const gl = canvas.getContext('webgl');
if (!gl) {
alert('WebGL is not supported in your browser.');
}
// 定义立方体顶点数据
const vertices = new Float32Array([
// 前面
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
// 后面
-1.0, -1.0, -1.0,
-1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
// 上面
-1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
// 下面
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0, 1.0,
-1.0, -1.0, 1.0,
// 左面
-1.0, -1.0, -1.0,
-1.0, -1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, 1.0, -1.0,
// 右面
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
1.0, 1.0, 1.0,
1.0, -1.0, 1.0
]);
// 创建顶点缓冲区对象
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// 设置顶点着色器
const vertexShaderSource = `
attribute vec3 aVertexPosition;
void main() {
gl_Position = vec4(aVertexPosition, 1.0);
}
`;
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
// 设置片段着色器
const fragmentShaderSource = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
// 创建程序对象并链接着色器
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
// 获取顶点位置属性的存储位置
const vertexPositionAttribute = gl.getAttribLocation(program, 'aVertexPosition');
gl.enableVertexAttribArray(vertexPositionAttribute);
gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
// 绘制立方体
gl.drawArrays(gl.TRIANGLE_FAN, 0, vertices.length / 3);
gl.disableVertexAttribArray(vertexPositionAttribute);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
</script>
</body>
</html>
上述代码先定义了立方体的顶点数据,然后创建顶点缓冲区对象存储这些数据。接着分别编写顶点着色器和片段着色器,创建程序对象并链接两个着色器。获取顶点位置属性的存储位置后,启用并设置顶点属性指针,最后使用drawArrays方法绘制出立方体。
三、动画实现
在计算机图形学中,动画能让静态图形 "动" 起来,为用户带来更生动的视觉体验。基于 JavaScript 的 canvas 和 WebGL 都可以实现动画效果,下面我们通过两种方式来展示动画的实现。
(一)基于 canvas 的动画
以一个简单的小球移动动画为例,我们可以通过不断更新小球的位置,并重新绘制来实现动画效果。
html
<!DOCTYPE html>
<html>
<body>
<canvas id="animationCanvas" width="400" height="400"></canvas>
<script>
const canvas = document.getElementById('animationCanvas');
const ctx = canvas.getContext('2d');
let x = 50;
let y = 50;
let dx = 2;
let dy = 2;
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, canvas.width, canvas.height);
x += dx;
y += dy;
if (x > canvas.width - 50 || x < 0) {
dx = -dx;
}
if (y > canvas.height - 50 || y < 0) {
dy = -dy;
}
ctx.beginPath();
ctx.arc(x, y, 25, 0, Math.PI * 2);
ctx.fillStyle = 'green';
ctx.fill();
}
animate();
</script>
</body>
</html>
在这段代码中,requestAnimationFrame函数用于在浏览器下次重绘之前调用指定的函数,实现流畅的动画效果。每次循环中,先清空画布,然后更新小球的位置,根据小球是否碰到边界来改变移动方向,最后绘制小球。
(二)基于 WebGL 的动画
在 WebGL 中实现三维物体的旋转动画,我们可以通过不断更新物体的旋转角度,并重新渲染场景来达成。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>3D Animation</title>
</head>
<body>
<canvas id="webglAnimationCanvas" width="600" height="600"></canvas>
<script>
const canvas = document.getElementById('webglAnimationCanvas');
const gl = canvas.getContext('webgl');
if (!gl) {
alert('WebGL is not supported in your browser.');
}
// 定义立方体顶点数据(同上述立方体示例)
const vertices = new Float32Array([...]);
// 创建顶点缓冲区对象(同上述立方体示例)
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// 设置顶点着色器
const vertexShaderSource = `
attribute vec3 aVertexPosition;
uniform mat4 uModelMatrix;
void main() {
gl_Position = uModelMatrix * vec4(aVertexPosition, 1.0);
}
`;
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
// 设置片段着色器
const fragmentShaderSource = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
// 创建程序对象并链接着色器
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
// 获取顶点位置属性和模型矩阵uniform变量的存储位置
const vertexPositionAttribute = gl.getAttribLocation(program, 'aVertexPosition');
const modelMatrixUniform = gl.getUniformLocation(program, 'uModelMatrix');
gl.enableVertexAttribArray(vertexPositionAttribute);
gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
let angle = 0;
function animate() {
requestAnimationFrame(animate);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// 计算旋转矩阵
const cosA = Math.cos(angle);
const sinA = Math.sin(angle);
const modelMatrix = new Float32Array([
cosA, 0, sinA, 0,
0, 1, 0, 0,
-sinA, 0, cosA, 0,
0, 0, 0, 1
]);
gl.uniformMatrix4fv(modelMatrixUniform, false, modelMatrix);
gl.drawArrays(gl.TRIANGLE_FAN, 0, vertices.length / 3);
angle += 0.01;
}
animate();
gl.disableVertexAttribArray(vertexPositionAttribute);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
</script>
</body>
</html>
在这段 WebGL 动画代码中,通过requestAnimationFrame实现动画循环。每次循环时,先清空画布,然后根据当前角度计算旋转矩阵,并将其传递给顶点着色器中的uModelMatrix uniform 变量,最后重新绘制立方体,实现立方体的旋转动画效果。
通过对曲线绘制、三维模型渲染和动画实现等进阶内容的学习与实践,相信你对计算机图形学有了更深入的理解。如果还想了解更多高级主题,比如物理模拟、实时渲染优化等,欢迎随时告诉我 。