计算机图形高频面试题解析

218 阅读7分钟

在计算机图形领域的面试中,面试官常通过各类问题考察候选人的专业知识和实践能力。以下整理了一些高频面试题,并结合 JavaScript 代码进行详细解析,帮助你更好地应对面试挑战。

一、图形渲染基础概念

1. 什么是光栅化?

光栅化是将矢量图形(如点、线、多边形)转换为像素的过程,这些像素最终会显示在屏幕上。想象一下,我们在纸上画一个三角形,光栅化就像是把这个三角形用无数个小方格(像素)填充出来,让它能在计算机屏幕上显示。

在 JavaScript 中,如果使用 HTML5 的 Canvas 2D 上下文来简单模拟光栅化绘制一个矩形,可以这样写:

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 400;
canvas.height = 400;
document.body.appendChild(canvas);
ctx.fillStyle ='red';
ctx.fillRect(50, 50, 100, 100); // 在(50, 50)坐标处绘制一个宽100、高100的红色矩形

上述代码创建了一个 Canvas 画布,通过fillRect方法将一个矩形区域填充上颜色,这背后就涉及到光栅化将矩形转换为像素显示在屏幕上的过程。

2. 简述图形渲染管线

图形渲染管线就像是一条自动化的生产线,将 3D 场景数据最终转化为屏幕上的 2D 图像。它主要包含以下几个阶段:

  • 应用阶段:这是起始阶段,在这个阶段,开发者会设置场景、定义物体的位置、旋转、缩放等变换,还会处理碰撞检测等逻辑。例如,在一个 3D 游戏中,确定每个角色在场景中的初始位置就发生在这个阶段。
  • 几何阶段:在这个阶段,会对物体进行各种几何变换,比如将物体从模型坐标系转换到世界坐标系,再转换到视图坐标系,最后进行投影变换,将 3D 物体转换为 2D 图形。还会进行裁剪操作,去除那些不在视图范围内的物体部分。
  • 光栅化阶段:如前面提到的,将几何阶段输出的图元(点、线、多边形)转换为像素,并且会进行像素着色,确定每个像素的颜色、透明度等属性 。

虽然 JavaScript 本身没有直接实现完整的图形渲染管线,但在使用 Three.js 等 3D 图形库时,内部就遵循了这样的渲染管线流程。以 Three.js 创建一个简单场景为例:

import * as THREE from 'three';
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建几何体和材质
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
function animate() {
    requestAnimationFrame(animate);
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    renderer.render(scene, camera);
}
animate();

在这段代码中,我们先设置场景、相机和渲染器(应用阶段相关操作),创建几何体和材质(也属于应用阶段对物体的定义),在渲染过程中,Three.js 内部会按照图形渲染管线的流程,对物体进行几何变换、光栅化等操作,最终将 3D 立方体显示在屏幕上。

二、几何变换

1. 如何实现 2D 图形的平移、旋转和缩放?

在 2D 图形中,平移是将图形沿着 x 轴和 y 轴移动一定的距离;旋转是围绕一个中心点将图形转动一定的角度;缩放则是按照一定的比例放大或缩小图形。

在 JavaScript 的 Canvas 2D 中实现这些变换:

  • 平移
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 400;
canvas.height = 400;
document.body.appendChild(canvas);
ctx.fillStyle = 'blue';
ctx.fillRect(50, 50, 50, 50); // 原始矩形
ctx.translate(100, 100); // 将坐标系向右平移100像素,向下平移100像素
ctx.fillStyle ='red';
ctx.fillRect(50, 50, 50, 50); // 平移后的矩形

translate方法改变了后续绘图操作的坐标系原点,从而实现图形的平移效果。

  • 旋转
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 400;
canvas.height = 400;
document.body.appendChild(canvas);
ctx.translate(200, 200); // 将原点移动到画布中心
ctx.rotate(Math.PI / 4); // 顺时针旋转45度(因为参数是弧度制,45度转换为弧度是Math.PI / 4)
ctx.fillStyle = 'green';
ctx.fillRect(-25, -25, 50, 50); // 旋转后的矩形

rotate方法以当前坐标系原点为中心进行旋转,所以先通过translate将原点移动到合适位置,再进行旋转操作。

  • 缩放
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 400;
canvas.height = 400;
document.body.appendChild(canvas);
ctx.fillStyle = 'yellow';
ctx.fillRect(50, 50, 50, 50); // 原始矩形
ctx.scale(2, 2); // 水平和垂直方向都放大2倍
ctx.fillStyle = 'orange';
ctx.fillRect(50, 50, 50, 50); // 缩放后的矩形

scale方法通过传入水平和垂直方向的缩放因子,实现图形的缩放效果。

2. 3D 图形的变换矩阵是如何工作的?

3D 图形的变换矩阵是一个 4x4 的矩阵,它可以将点或向量从一个坐标系转换到另一个坐标系,实现平移、旋转、缩放等变换。简单来说,变换矩阵就像是一个 “魔法盒子”,把物体的坐标放进去,经过一系列计算,就能得到变换后的坐标。

在 JavaScript 中使用 Three.js 进行 3D 变换时,虽然不需要直接操作底层的变换矩阵,但了解其原理很重要。例如,当我们创建一个 3D 物体并对其进行旋转时:

import * as THREE from 'three';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const geometry = new THREE.SphereGeometry(1, 32, 32);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
function animate() {
    requestAnimationFrame(animate);
    sphere.rotation.x += 0.01;
    sphere.rotation.y += 0.01;
    renderer.render(scene, camera);
}
animate();

这里对球体进行的旋转操作,Three.js 内部会通过变换矩阵来计算每个顶点在旋转后的新位置,从而实现球体的动态旋转效果展示在屏幕上。

三、光照模型

1. 简述 Lambert 光照模型

Lambert 光照模型是一种简单而常用的漫反射光照模型。它认为物体表面的亮度取决于表面法线方向和光线方向的夹角。夹角越小,表面接收到的光线越多,看起来就越亮;夹角越大,接收到的光线越少,看起来就越暗。

在 JavaScript 中使用 Three.js 实现 Lambert 光照效果:

import * as THREE from 'three';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const geometry = new THREE.BoxGeometry();
// 使用Lambert材质
const material = new THREE.MeshLambertMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(1, 1, 1).normalize();
scene.add(light);
function animate() {
    requestAnimationFrame(animate);
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    renderer.render(scene, camera);
}
animate();

在这段代码中,MeshLambertMaterial材质就应用了 Lambert 光照模型,结合添加的方向光DirectionalLight,使得立方体表面能够根据光线方向和表面法线方向的关系,呈现出不同的亮度,实现漫反射光照效果。

2. 什么是 Phong 光照模型?

Phong 光照模型比 Lambert 光照模型更复杂和真实,它除了考虑漫反射,还考虑了镜面反射。镜面反射模拟了光线在光滑表面像镜子一样反射的效果,使得物体表面看起来有高光。它通过计算观察者方向、反射光线方向等因素,来确定高光的强度和位置。

同样在 Three.js 中可以使用 Phong 材质来实现 Phong 光照模型效果:

import * as THREE from 'three';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const geometry = new THREE.SphereGeometry(1, 32, 32);
// 使用Phong材质
const material = new THREE.MeshPhongMaterial({ color: 0xffff00, shininess: 100 });
const sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
const light = new THREE.PointLight(0xffffff, 1);
light.position.set(2, 2, 2);
scene.add(light);
function animate() {
    requestAnimationFrame(animate);
    sphere.rotation.x += 0.01;
    sphere.rotation.y += 0.01;
    renderer.render(scene, camera);
}
animate();

这里通过MeshPhongMaterial材质,并设置shininess(光泽度)属性,结合点光源PointLight,使得球体表面能够呈现出镜面反射的高光效果,相比 Lambert 光照模型,看起来更加真实。

以上就是计算机图形领域一些高频面试题的详细解析,希望能帮助你加深理解,在面试中脱颖而出。如果还想了解其他相关问题或更深入的内容,欢迎随时和我交流。