计算机图形学进阶探索与实践

计算机图形学进阶探索与实践

计算机图形学的世界丰富而复杂,除了基础概念与操作,还有许多进阶内容值得深入钻研。接下来,我们将围绕曲线绘制、三维模型渲染、动画实现等方面,结合 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 变量,最后重新绘制立方体,实现立方体的旋转动画效果。

通过对曲线绘制、三维模型渲染和动画实现等进阶内容的学习与实践,相信你对计算机图形学有了更深入的理解。如果还想了解更多高级主题,比如物理模拟、实时渲染优化等,欢迎随时告诉我 。

相关推荐
Mike_jia36 分钟前
Memos:知识工作者的理想开源笔记系统
前端
前端大白话37 分钟前
前端崩溃瞬间救星!10 个 JavaScript 实战技巧大揭秘
前端·javascript
loveoobaby38 分钟前
Shadertoy着色器移植到Three.js经验总结
前端
蓝易云41 分钟前
在Linux、CentOS7中设置shell脚本开机自启动服务
前端·后端·centos
浩龙不eMo41 分钟前
前端获取环境变量方式区分(Vite)
前端·vite
一千柯橘1 小时前
Nestjs 解决 request entity too large
javascript·后端
土豆骑士1 小时前
monorepo 实战练习
前端
土豆骑士1 小时前
monorepo最佳实践
前端
见青..1 小时前
【学习笔记】文件包含漏洞--本地远程包含、伪协议、加密编码
前端·笔记·学习·web安全·文件包含
举个栗子dhy1 小时前
如何处理动态地址栏参数,以及Object.entries() 、Object.fromEntries()和URLSearchParams.entries()使用
javascript