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

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

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

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

相关推荐
杨进军6 分钟前
React 创建根节点 createRoot
前端·react.js·前端框架
ModyQyW21 分钟前
用 AI 驱动 wot-design-uni 开发小程序
前端·uni-app
说码解字27 分钟前
Kotlin lazy 委托的底层实现原理
前端
gnip1 小时前
总结一期正则表达式
javascript·正则表达式
爱分享的程序员1 小时前
前端面试专栏-算法篇:18. 查找算法(二分查找、哈希查找)
前端·javascript·node.js
翻滚吧键盘1 小时前
vue 条件渲染(v-if v-else-if v-else v-show)
前端·javascript·vue.js
vim怎么退出1 小时前
万字长文带你了解微前端架构
前端·微服务·前端框架
你这个年龄怎么睡得着的1 小时前
为什么 JavaScript 中 'str' 不是对象,却能调用方法?
前端·javascript·面试
Java水解1 小时前
前端常用单位em/px/rem/vh/vm到底有什么区别?
前端
CAD老兵1 小时前
Vite 如何借助 esbuild 实现极速 Dev Server 体验,并支持无 source map 的源码调试
前端