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

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

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

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

相关推荐
koiy.cc38 分钟前
记录:echarts实现tooltip的某个数据常显和恢复
前端·echarts
一只专注api接口开发的技术猿1 小时前
企业级电商数据对接:1688 商品详情 API 接口开发与优化实践
大数据·前端·爬虫
GISer_Jing1 小时前
[前端高频]数组转树、数组扁平化、深拷贝、JSON.stringify&JSON.parse等手撕
前端·javascript·json
古拉拉明亮之神1 小时前
Spark处理过程-转换算子
javascript·ajax·spark
Yvonne爱编码1 小时前
CSS- 4.1 浮动(Float)
前端·css·html·github·html5·hbuilder
timeguys2 小时前
【前端】[vue3] [uni-app]使用 vantUI 框架
前端·uni-app
岁岁岁平安2 小时前
Vue3学习(组合式API——Watch侦听器、watchEffect()详解)
前端·javascript·vue.js·学习·watch侦听器·组合式api
uwvwko2 小时前
BUUCTF——web刷题第一页题解
android·前端·数据库·php·web·ctf
有事没事实验室3 小时前
CSS 浮动与定位以及定位中z-index的堆叠问题
前端·css·开源
Stringzhua3 小时前
JavaScript入门【3】面向对象
javascript