JavaScript系列(48)-- 3D渲染引擎实现详解
JavaScript 3D渲染引擎实现详解 🎮
今天,让我们深入探讨JavaScript的3D渲染引擎实现。通过WebGL和现代JavaScript技术,我们可以构建一个功能完整的3D渲染系统。
3D渲染基础概念 🌟
💡 小知识:3D渲染引擎的核心是将三维场景转换为二维图像的过程。这涉及到场景图管理、几何变换、光照计算、材质渲染等多个方面。通过WebGL,我们可以直接访问GPU,实现高性能的3D渲染。
基本实现 📊
// 1. 渲染引擎核心
class Engine3D {
constructor(canvas) {
this.canvas = canvas;
this.gl = canvas.getContext('webgl2');
if (!this.gl) {
throw new Error('WebGL2 not supported');
}
this.scene = new Scene();
this.camera = new Camera();
this.renderer = new Renderer(this.gl);
this.initGL();
}
// 初始化WebGL上下文
initGL() {
this.gl.enable(this.gl.DEPTH_TEST);
this.gl.enable(this.gl.CULL_FACE);
this.gl.cullFace(this.gl.BACK);
}
// 设置视口大小
setViewport(width, height) {
this.canvas.width = width;
this.canvas.height = height;
this.gl.viewport(0, 0, width, height);
this.camera.updateAspect(width / height);
}
// 渲染循环
render() {
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
this.renderer.render(this.scene, this.camera);
}
}
// 2. 场景管理
class Scene {
constructor() {
this.objects = [];
this.lights = [];
}
// 添加对象
add(object) {
if (object.isLight) {
this.lights.push(object);
} else {
this.objects.push(object);
}
object.parent = this;
}
// 移除对象
remove(object) {
const array = object.isLight ? this.lights : this.objects;
const index = array.indexOf(object);
if (index !== -1) {
array.splice(index, 1);
object.parent = null;
}
}
// 遍历场景
traverse(callback) {
this.objects.forEach(object => {
callback(object);
if (object.children) {
object.traverse(callback);
}
});
}
}
// 3. 相机系统
class Camera {
constructor() {
this.position = new Vector3(0, 0, 5);
this.target = new Vector3(0, 0, 0);
this.up = new Vector3(0, 1, 0);
this.fov = 45;
this.aspect = 1;
this.near = 0.1;
this.far = 1000;
this.updateProjectionMatrix();
this.updateViewMatrix();
}
// 更新投影矩阵
updateProjectionMatrix() {
this.projectionMatrix = Matrix4.perspective(
this.fov,
this.aspect,
this.near,
this.far
);
}
// 更新视图矩阵
updateViewMatrix() {
this.viewMatrix = Matrix4.lookAt(
this.position,
this.target,
this.up
);
}
// 更新相机位置
lookAt(target) {
this.target.copy(target);
this.updateViewMatrix();
}
}
高级功能实现 🚀
// 1. 几何体系统
class Geometry {
constructor() {
this.vertices = [];
this.indices = [];
this.normals = [];
this.uvs = [];
this.vertexBuffer = null;
this.indexBuffer = null;
this.normalBuffer = null;
this.uvBuffer = null;
}
// 设置顶点数据
setVertices(vertices) {
this.vertices = vertices;
this.updateVertexBuffer();
}
// 设置索引数据
setIndices(indices) {
this.indices = indices;
this.updateIndexBuffer();
}
// 计算法线
computeNormals() {
this.normals = new Array(this.vertices.length);
for (let i = 0; i < this.indices.length; i += 3) {
const i1 = this.indices[i];
const i2 = this.indices[i + 1];
const i3 = this.indices[i + 2];
const v1 = new Vector3().fromArray(this.vertices, i1 * 3);
const v2 = new Vector3().fromArray(this.vertices, i2 * 3);
const v3 = new Vector3().fromArray(this.vertices, i3 * 3);
const normal = Vector3.cross(
Vector3.subtract(v2, v1),
Vector3.subtract(v3, v1)
).normalize();
this.normals[i1] = normal;
this.normals[i2] = normal;
this.normals[i3] = normal;
}
this.updateNormalBuffer();
}
}
// 2. 材质系统
class Material {
constructor() {
this.uniforms = {
diffuseColor: new Vector3(1, 1, 1),
specularColor: new Vector3(1, 1, 1),
shininess: 32.0,
opacity: 1.0
};
this.vertexShader = null;
this.fragmentShader = null;
this.program = null;
}
// 编译着色器
compile(gl) {
const vertexShader = this.compileShader(
gl, gl.VERTEX_SHADER, this.vertexShader
);
const fragmentShader = this.compileShader(
gl, gl.FRAGMENT_SHADER, this.fragmentShader
);
this.program = gl.createProgram();
gl.attachShader(this.program, vertexShader);
gl.attachShader(this.program, fragmentShader);
gl.linkProgram(this.program);
if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {
throw new Error('Program linking failed: ' +
gl.getProgramInfoLog(this.program));
}
}
// 编译单个着色器
compileShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw new Error('Shader compilation failed: ' +
gl.getShaderInfoLog(shader));
}
return shader;
}
}
// 3. 光照系统
class Light {
constructor() {
this.isLight = true;
this.color = new Vector3(1, 1, 1);
this.intensity = 1.0;
}
}
class DirectionalLight extends Light {
constructor() {
super();
this.direction = new Vector3(0, -1, 0);
}
}
class PointLight extends Light {
constructor() {
super();
this.position = new Vector3();
this.distance = 0;
this.decay = 1;
}
}
实际应用场景 💼
// 1. 3D模型加载器
class ModelLoader {
constructor(engine) {
this.engine = engine;
}
// 加载OBJ格式模型
async loadOBJ(url) {
const response = await fetch(url);
const text = await response.text();
const vertices = [];
const normals = [];
const uvs = [];
const indices = [];
const lines = text.split('\n');
for (const line of lines) {
const parts = line.trim().split(/\s+/);
switch (parts[0]) {
case 'v': // 顶点
vertices.push(
parseFloat(parts[1]),
parseFloat(parts[2]),
parseFloat(parts[3])
);
break;
case 'vn': // 法线
normals.push(
parseFloat(parts[1]),
parseFloat(parts[2]),
parseFloat(parts[3])
);
break;
case 'vt': // 纹理坐标
uvs.push(
parseFloat(parts[1]),
parseFloat(parts[2])
);
break;
case 'f': // 面
for (let i = 1; i <= 3; i++) {
const vertexData = parts[i].split('/');
indices.push(parseInt(vertexData[0]) - 1);
}
break;
}
}
const geometry = new Geometry();
geometry.setVertices(vertices);
geometry.setIndices(indices);
if (normals.length > 0) {
geometry.normals = normals;
} else {
geometry.computeNormals();
}
if (uvs.length > 0) {
geometry.uvs = uvs;
}
return geometry;
}
}
// 2. 动画系统
class AnimationSystem {
constructor() {
this.animations = new Map();
this.currentTime = 0;
}
// 添加关键帧动画
addKeyframeAnimation(name, keyframes) {
this.animations.set(name, {
keyframes,
duration: keyframes[keyframes.length - 1].time
});
}
// 更新动画
update(deltaTime) {
this.currentTime += deltaTime;
for (const [name, animation] of this.animations) {
const time = this.currentTime % animation.duration;
// 查找当前关键帧
let frame1, frame2;
for (let i = 0; i < animation.keyframes.length - 1; i++) {
if (time >= animation.keyframes[i].time &&
time < animation.keyframes[i + 1].time) {
frame1 = animation.keyframes[i];
frame2 = animation.keyframes[i + 1];
break;
}
}
if (frame1 && frame2) {
const t = (time - frame1.time) /
(frame2.time - frame1.time);
this.interpolate(frame1, frame2, t);
}
}
}
// 插值计算
interpolate(frame1, frame2, t) {
// 实现关键帧插值
return {
position: Vector3.lerp(frame1.position, frame2.position, t),
rotation: Quaternion.slerp(frame1.rotation, frame2.rotation, t),
scale: Vector3.lerp(frame1.scale, frame2.scale, t)
};
}
}
// 3. 物理系统
class PhysicsSystem {
constructor() {
this.objects = [];
this.gravity = new Vector3(0, -9.81, 0);
}
// 添加物理对象
addObject(object, mass = 1) {
this.objects.push({
object,
mass,
velocity: new Vector3(),
acceleration: new Vector3()
});
}
// 更新物理
update(deltaTime) {
for (const obj of this.objects) {
// 应用重力
obj.acceleration.add(this.gravity);
// 更新速度
obj.velocity.add(
Vector3.multiply(obj.acceleration, deltaTime)
);
// 更新位置
obj.object.position.add(
Vector3.multiply(obj.velocity, deltaTime)
);
// 重置加速度
obj.acceleration.set(0, 0, 0);
}
// 碰撞检测
this.detectCollisions();
}
// 碰撞检测
detectCollisions() {
for (let i = 0; i < this.objects.length; i++) {
for (let j = i + 1; j < this.objects.length; j++) {
const obj1 = this.objects[i];
const obj2 = this.objects[j];
if (this.checkCollision(obj1, obj2)) {
this.resolveCollision(obj1, obj2);
}
}
}
}
}
性能优化技巧 ⚡
// 1. 渲染优化
class RenderOptimizer {
constructor(engine) {
this.engine = engine;
this.frustumCuller = new FrustumCuller();
this.occlusionCuller = new OcclusionCuller();
}
// 视锥体剔除
cullFrustum(scene, camera) {
this.frustumCuller.updateFrustum(camera);
return scene.objects.filter(object =>
this.frustumCuller.isVisible(object)
);
}
// 遮挡剔除
cullOcclusion(objects) {
return this.occlusionCuller.getVisibleObjects(objects);
}
// LOD管理
updateLOD(objects, camera) {
for (const object of objects) {
if (object.lod) {
const distance = Vector3.distance(
object.position,
camera.position
);
object.updateLOD(distance);
}
}
}
}
// 2. 内存管理
class ResourceManager {
constructor() {
this.geometries = new Map();
this.textures = new Map();
this.materials = new Map();
}
// 加载几何体
async loadGeometry(url) {
if (this.geometries.has(url)) {
return this.geometries.get(url);
}
const loader = new ModelLoader();
const geometry = await loader.loadOBJ(url);
this.geometries.set(url, geometry);
return geometry;
}
// 加载纹理
async loadTexture(url) {
if (this.textures.has(url)) {
return this.textures.get(url);
}
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = () => {
const texture = new Texture(image);
this.textures.set(url, texture);
resolve(texture);
};
image.onerror = reject;
image.src = url;
});
}
// 释放资源
unload(url) {
if (this.geometries.has(url)) {
const geometry = this.geometries.get(url);
geometry.dispose();
this.geometries.delete(url);
}
if (this.textures.has(url)) {
const texture = this.textures.get(url);
texture.dispose();
this.textures.delete(url);
}
}
}
// 3. 渲染批处理
class BatchRenderer {
constructor(gl) {
this.gl = gl;
this.batches = new Map();
}
// 添加到批次
addToBatch(object) {
const key = this.getBatchKey(object);
if (!this.batches.has(key)) {
this.batches.set(key, {
objects: [],
vertices: [],
indices: []
});
}
const batch = this.batches.get(key);
batch.objects.push(object);
// 合并几何数据
this.mergeGeometry(batch, object);
}
// 获取批次键
getBatchKey(object) {
return `${object.material.id}_${object.geometry.id}`;
}
// 合并几何数据
mergeGeometry(batch, object) {
const geometry = object.geometry;
const baseVertex = batch.vertices.length / 3;
// 添加顶点
batch.vertices.push(...geometry.vertices);
// 添加索引
for (const index of geometry.indices) {
batch.indices.push(index + baseVertex);
}
}
// 渲染批次
render() {
for (const batch of this.batches.values()) {
if (batch.objects.length === 0) continue;
// 使用第一个对象的材质
const material = batch.objects[0].material;
material.use(this.gl);
// 渲染合并后的几何体
this.renderBatch(batch);
}
}
}
最佳实践建议 💡
- 性能优化策略
// 1. 渲染状态管理
class RenderStateManager {
constructor(gl) {
this.gl = gl;
this.currentState = {
program: null,
texture: null,
blending: false
};
}
// 设置着色器程序
useProgram(program) {
if (this.currentState.program !== program) {
this.gl.useProgram(program);
this.currentState.program = program;
}
}
// 设置纹理
bindTexture(texture) {
if (this.currentState.texture !== texture) {
this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
this.currentState.texture = texture;
}
}
// 设置混合
setBlending(enable) {
if (this.currentState.blending !== enable) {
if (enable) {
this.gl.enable(this.gl.BLEND);
} else {
this.gl.disable(this.gl.BLEND);
}
this.currentState.blending = enable;
}
}
}
// 2. 着色器管理
class ShaderManager {
constructor() {
this.shaders = new Map();
}
// 注册着色器
register(name, vertexSource, fragmentSource) {
this.shaders.set(name, {
vertex: vertexSource,
fragment: fragmentSource
});
}
// 获取着色器
get(name) {
return this.shaders.get(name);
}
// 编译着色器
compile(gl, name) {
const shader = this.get(name);
if (!shader) return null;
const program = new ShaderProgram(gl);
program.compile(shader.vertex, shader.fragment);
return program;
}
}
// 3. 调试工具
class DebugTools {
constructor(engine) {
this.engine = engine;
this.stats = {
drawCalls: 0,
vertices: 0,
triangles: 0
};
}
// 开始性能分析
beginProfile() {
this.stats.drawCalls = 0;
this.stats.vertices = 0;
this.stats.triangles = 0;
}
// 记录绘制调用
recordDrawCall(vertices, triangles) {
this.stats.drawCalls++;
this.stats.vertices += vertices;
this.stats.triangles += triangles;
}
// 获取性能报告
getStats() {
return {
...this.stats,
fps: this.calculateFPS()
};
}
// 显示调试信息
showDebugInfo() {
const stats = this.getStats();
console.log(`
FPS: ${stats.fps}
Draw Calls: ${stats.drawCalls}
Vertices: ${stats.vertices}
Triangles: ${stats.triangles}
`);
}
}
结语 📝
JavaScript的3D渲染引擎实现是一个复杂但有趣的主题。通过本文,我们学习了:
- 3D渲染引擎的基本架构和实现
- 场景管理和相机系统
- 几何体、材质和光照系统
- 动画和物理系统
- 性能优化技巧和最佳实践
💡 学习建议:在实现3D渲染引擎时,要注意性能优化和内存管理。合理使用批处理、剔除和LOD等技术可以显著提升渲染性能。同时,要充分利用WebGL的特性,避免不必要的状态切换。
如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇
终身学习,共同成长。
咱们下一期见
💻