当前位置: 首页 > article >正文

JavaScript系列(46)-- WebGL图形编程详解

JavaScript WebGL图形编程详解 🎨

今天,让我们深入探讨JavaScript的WebGL图形编程。WebGL是一种基于OpenGL ES的JavaScript API,它允许我们在浏览器中渲染高性能的2D和3D图形。

WebGL基础概念 🌟

💡 小知识:WebGL直接与GPU通信,使用GLSL着色器语言编写顶点和片段着色器。它提供了底层的图形API,让我们能够充分利用硬件加速进行图形渲染。

基本实现 📊

// 1. WebGL上下文初始化
class WebGLContext {
    constructor(canvas) {
        this.canvas = canvas;
        this.gl = canvas.getContext('webgl2') || canvas.getContext('webgl');
        
        if (!this.gl) {
            throw new Error('WebGL not supported');
        }
        
        // 初始化基本设置
        this.gl.clearColor(0.0, 0.0, 0.0, 1.0);
        this.gl.enable(this.gl.DEPTH_TEST);
        this.gl.enable(this.gl.CULL_FACE);
        this.gl.viewport(0, 0, canvas.width, canvas.height);
    }
    
    // 清除画布
    clear() {
        this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
    }
    
    // 设置视口
    setViewport(width, height) {
        this.canvas.width = width;
        this.canvas.height = height;
        this.gl.viewport(0, 0, width, height);
    }
}

// 2. 着色器程序管理
class ShaderProgram {
    constructor(gl, vertexSource, fragmentSource) {
        this.gl = gl;
        this.program = this.createProgram(vertexSource, fragmentSource);
        this.attributes = this.getAttributes();
        this.uniforms = this.getUniforms();
    }
    
    // 创建着色器程序
    createProgram(vertexSource, fragmentSource) {
        const vertexShader = this.createShader(this.gl.VERTEX_SHADER, vertexSource);
        const fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, fragmentSource);
        
        const program = this.gl.createProgram();
        this.gl.attachShader(program, vertexShader);
        this.gl.attachShader(program, fragmentShader);
        this.gl.linkProgram(program);
        
        if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {
            throw new Error('Failed to link shader program');
        }
        
        return program;
    }
    
    // 创建着色器
    createShader(type, source) {
        const shader = this.gl.createShader(type);
        this.gl.shaderSource(shader, source);
        this.gl.compileShader(shader);
        
        if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
            throw new Error(
                `Failed to compile shader: ${this.gl.getShaderInfoLog(shader)}`
            );
        }
        
        return shader;
    }
    
    // 获取所有属性位置
    getAttributes() {
        const attributes = {};
        const count = this.gl.getProgramParameter(this.program, this.gl.ACTIVE_ATTRIBUTES);
        
        for (let i = 0; i < count; i++) {
            const info = this.gl.getActiveAttrib(this.program, i);
            attributes[info.name] = this.gl.getAttribLocation(this.program, info.name);
        }
        
        return attributes;
    }
    
    // 获取所有统一变量位置
    getUniforms() {
        const uniforms = {};
        const count = this.gl.getProgramParameter(this.program, this.gl.ACTIVE_UNIFORMS);
        
        for (let i = 0; i < count; i++) {
            const info = this.gl.getActiveUniform(this.program, i);
            uniforms[info.name] = this.gl.getUniformLocation(this.program, info.name);
        }
        
        return uniforms;
    }
    
    // 使用程序
    use() {
        this.gl.useProgram(this.program);
    }
}

// 3. 几何体管理
class Geometry {
    constructor(gl) {
        this.gl = gl;
        this.vao = this.gl.createVertexArray();
        this.buffers = new Map();
    }
    
    // 创建缓冲区
    createBuffer(name, data, target = this.gl.ARRAY_BUFFER) {
        const buffer = this.gl.createBuffer();
        this.gl.bindBuffer(target, buffer);
        this.gl.bufferData(target, data, this.gl.STATIC_DRAW);
        this.buffers.set(name, { buffer, target });
    }
    
    // 设置顶点属性
    setAttribute(location, size, type, normalized = false, stride = 0, offset = 0) {
        this.gl.vertexAttribPointer(
            location, size, type, normalized, stride, offset
        );
        this.gl.enableVertexAttribArray(location);
    }
    
    // 绑定几何体
    bind() {
        this.gl.bindVertexArray(this.vao);
    }
    
    // 解绑几何体
    unbind() {
        this.gl.bindVertexArray(null);
    }
}

高级功能实现 🚀

// 1. 矩阵变换
class Transform {
    constructor() {
        this.position = vec3.create();
        this.rotation = quat.create();
        this.scale = vec3.fromValues(1, 1, 1);
        this.matrix = mat4.create();
    }
    
    // 更新变换矩阵
    updateMatrix() {
        mat4.fromRotationTranslationScale(
            this.matrix,
            this.rotation,
            this.position,
            this.scale
        );
        return this.matrix;
    }
    
    // 设置位置
    setPosition(x, y, z) {
        vec3.set(this.position, x, y, z);
        return this;
    }
    
    // 设置旋转
    setRotation(x, y, z) {
        quat.fromEuler(this.rotation, x, y, z);
        return this;
    }
    
    // 设置缩放
    setScale(x, y, z) {
        vec3.set(this.scale, x, y, z);
        return this;
    }
}

// 2. 相机系统
class Camera {
    constructor() {
        this.position = vec3.create();
        this.target = vec3.create();
        this.up = vec3.fromValues(0, 1, 0);
        
        this.viewMatrix = mat4.create();
        this.projectionMatrix = mat4.create();
    }
    
    // 更新视图矩阵
    updateViewMatrix() {
        mat4.lookAt(this.viewMatrix, this.position, this.target, this.up);
        return this.viewMatrix;
    }
    
    // 设置透视投影
    setPerspective(fov, aspect, near, far) {
        mat4.perspective(this.projectionMatrix, fov, aspect, near, far);
        return this;
    }
    
    // 设置正交投影
    setOrthographic(left, right, bottom, top, near, far) {
        mat4.ortho(this.projectionMatrix, left, right, bottom, top, near, far);
        return this;
    }
}

// 3. 材质系统
class Material {
    constructor(gl, shader) {
        this.gl = gl;
        this.shader = shader;
        this.uniforms = new Map();
    }
    
    // 设置统一变量
    setUniform(name, value) {
        this.uniforms.set(name, value);
        return this;
    }
    
    // 应用材质
    apply() {
        this.shader.use();
        
        for (const [name, value] of this.uniforms) {
            const location = this.shader.uniforms[name];
            if (location) {
                this.setUniformValue(location, value);
            }
        }
    }
    
    // 设置统一变量值
    setUniformValue(location, value) {
        if (Array.isArray(value)) {
            switch (value.length) {
                case 2:
                    this.gl.uniform2fv(location, value);
                    break;
                case 3:
                    this.gl.uniform3fv(location, value);
                    break;
                case 4:
                    this.gl.uniform4fv(location, value);
                    break;
                case 16:
                    this.gl.uniformMatrix4fv(location, false, value);
                    break;
            }
        } else if (typeof value === 'number') {
            this.gl.uniform1f(location, value);
        } else if (value instanceof WebGLTexture) {
            this.gl.uniform1i(location, 0);
            this.gl.activeTexture(this.gl.TEXTURE0);
            this.gl.bindTexture(this.gl.TEXTURE_2D, value);
        }
    }
}

实际应用场景 💼

// 1. 3D场景渲染
class Scene {
    constructor(gl) {
        this.gl = gl;
        this.objects = new Set();
        this.camera = new Camera();
    }
    
    // 添加对象
    add(object) {
        this.objects.add(object);
    }
    
    // 移除对象
    remove(object) {
        this.objects.delete(object);
    }
    
    // 渲染场景
    render() {
        this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
        
        const viewProjection = mat4.create();
        mat4.multiply(
            viewProjection,
            this.camera.projectionMatrix,
            this.camera.viewMatrix
        );
        
        for (const object of this.objects) {
            object.render(viewProjection);
        }
    }
}

// 2. 粒子系统
class ParticleSystem {
    constructor(gl, maxParticles) {
        this.gl = gl;
        this.maxParticles = maxParticles;
        this.particles = [];
        this.geometry = this.createParticleGeometry();
        this.shader = this.createParticleShader();
    }
    
    // 创建粒子几何体
    createParticleGeometry() {
        const positions = new Float32Array(this.maxParticles * 3);
        const colors = new Float32Array(this.maxParticles * 4);
        const sizes = new Float32Array(this.maxParticles);
        
        const geometry = new Geometry(this.gl);
        geometry.createBuffer('position', positions);
        geometry.createBuffer('color', colors);
        geometry.createBuffer('size', sizes);
        
        return geometry;
    }
    
    // 更新粒子
    update(deltaTime) {
        for (const particle of this.particles) {
            particle.life -= deltaTime;
            if (particle.life <= 0) {
                this.resetParticle(particle);
            } else {
                particle.position[0] += particle.velocity[0] * deltaTime;
                particle.position[1] += particle.velocity[1] * deltaTime;
                particle.position[2] += particle.velocity[2] * deltaTime;
            }
        }
        
        this.updateGeometry();
    }
    
    // 渲染粒子
    render(viewProjection) {
        this.shader.use();
        this.shader.setUniform('viewProjection', viewProjection);
        
        this.geometry.bind();
        this.gl.drawArrays(this.gl.POINTS, 0, this.particles.length);
        this.geometry.unbind();
    }
}

// 3. 后处理效果
class PostProcessor {
    constructor(gl) {
        this.gl = gl;
        this.framebuffer = this.createFramebuffer();
        this.shader = this.createPostProcessShader();
        this.quad = this.createScreenQuad();
    }
    
    // 创建帧缓冲区
    createFramebuffer() {
        const framebuffer = this.gl.createFramebuffer();
        this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebuffer);
        
        // 创建纹理附件
        const texture = this.gl.createTexture();
        this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
        this.gl.texImage2D(
            this.gl.TEXTURE_2D, 0, this.gl.RGBA,
            this.gl.canvas.width, this.gl.canvas.height,
            0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null
        );
        this.gl.texParameteri(
            this.gl.TEXTURE_2D,
            this.gl.TEXTURE_MIN_FILTER,
            this.gl.LINEAR
        );
        this.gl.texParameteri(
            this.gl.TEXTURE_2D,
            this.gl.TEXTURE_MAG_FILTER,
            this.gl.LINEAR
        );
        
        // 附加纹理
        this.gl.framebufferTexture2D(
            this.gl.FRAMEBUFFER,
            this.gl.COLOR_ATTACHMENT0,
            this.gl.TEXTURE_2D,
            texture,
            0
        );
        
        return {
            framebuffer,
            texture
        };
    }
    
    // 应用后处理效果
    apply(scene) {
        // 渲染场景到帧缓冲区
        this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer.framebuffer);
        scene.render();
        
        // 渲染后处理效果到屏幕
        this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
        this.shader.use();
        this.gl.activeTexture(this.gl.TEXTURE0);
        this.gl.bindTexture(this.gl.TEXTURE_2D, this.framebuffer.texture);
        
        this.quad.bind();
        this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);
        this.quad.unbind();
    }
}

性能优化技巧 ⚡

// 1. 实例化渲染
class InstancedRenderer {
    constructor(gl, geometry, maxInstances) {
        this.gl = gl;
        this.geometry = geometry;
        this.maxInstances = maxInstances;
        
        this.setupInstancedBuffers();
    }
    
    // 设置实例化缓冲区
    setupInstancedBuffers() {
        const matrices = new Float32Array(this.maxInstances * 16);
        const colors = new Float32Array(this.maxInstances * 4);
        
        // 创建矩阵缓冲区
        const matrixBuffer = this.gl.createBuffer();
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, matrixBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, matrices, this.gl.DYNAMIC_DRAW);
        
        // 设置矩阵属性
        for (let i = 0; i < 4; i++) {
            const location = this.geometry.shader.attributes[`instanceMatrix${i}`];
            this.gl.enableVertexAttribArray(location);
            this.gl.vertexAttribPointer(
                location, 4, this.gl.FLOAT, false,
                64, i * 16
            );
            this.gl.vertexAttribDivisor(location, 1);
        }
        
        // 创建颜色缓冲区
        const colorBuffer = this.gl.createBuffer();
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, colorBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, colors, this.gl.DYNAMIC_DRAW);
        
        const colorLocation = this.geometry.shader.attributes.instanceColor;
        this.gl.enableVertexAttribArray(colorLocation);
        this.gl.vertexAttribPointer(
            colorLocation, 4, this.gl.FLOAT, false,
            0, 0
        );
        this.gl.vertexAttribDivisor(colorLocation, 1);
    }
    
    // 更新实例数据
    updateInstances(matrices, colors) {
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.matrixBuffer);
        this.gl.bufferSubData(this.gl.ARRAY_BUFFER, 0, matrices);
        
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.colorBuffer);
        this.gl.bufferSubData(this.gl.ARRAY_BUFFER, 0, colors);
    }
    
    // 渲染实例
    render(instanceCount) {
        this.geometry.bind();
        this.gl.drawArraysInstanced(
            this.gl.TRIANGLES,
            0,
            this.geometry.vertexCount,
            instanceCount
        );
        this.geometry.unbind();
    }
}

// 2. 批处理渲染
class BatchRenderer {
    constructor(gl, maxBatchSize) {
        this.gl = gl;
        this.maxBatchSize = maxBatchSize;
        this.batch = [];
    }
    
    // 添加到批处理
    add(object) {
        if (this.batch.length >= this.maxBatchSize) {
            this.flush();
        }
        this.batch.push(object);
    }
    
    // 刷新批处理
    flush() {
        if (this.batch.length === 0) return;
        
        // 合并几何体数据
        const vertices = [];
        const indices = [];
        let indexOffset = 0;
        
        for (const object of this.batch) {
            vertices.push(...object.vertices);
            indices.push(...object.indices.map(i => i + indexOffset));
            indexOffset += object.vertices.length / 3;
        }
        
        // 更新缓冲区
        this.updateBuffers(vertices, indices);
        
        // 渲染批处理
        this.render();
        
        // 清空批处理
        this.batch.length = 0;
    }
}

// 3. 视锥体剔除
class Frustum {
    constructor() {
        this.planes = new Array(6);
        for (let i = 0; i < 6; i++) {
            this.planes[i] = vec4.create();
        }
    }
    
    // 从投影视图矩阵更新视锥体
    updateFromMatrix(matrix) {
        // 提取平面
        for (let i = 0; i < 6; i++) {
            const plane = this.planes[i];
            const row = Math.floor(i / 2);
            const sign = i % 2 === 0 ? 1 : -1;
            
            vec4.set(
                plane,
                matrix[3] + sign * matrix[row],
                matrix[7] + sign * matrix[row + 4],
                matrix[11] + sign * matrix[row + 8],
                matrix[15] + sign * matrix[row + 12]
            );
            
            vec4.normalize(plane, plane);
        }
    }
    
    // 检查点是否在视锥体内
    containsPoint(point) {
        for (const plane of this.planes) {
            if (vec4.dot(plane, [...point, 1]) < 0) {
                return false;
            }
        }
        return true;
    }
    
    // 检查包围球是否在视锥体内
    containsSphere(center, radius) {
        for (const plane of this.planes) {
            if (vec4.dot(plane, [...center, 1]) < -radius) {
                return false;
            }
        }
        return true;
    }
}

最佳实践建议 💡

  1. 性能优化模式
// 1. 状态管理
class GLState {
    constructor(gl) {
        this.gl = gl;
        this.currentProgram = null;
        this.currentTexture = null;
        this.currentVAO = null;
    }
    
    // 使用着色器程序
    useProgram(program) {
        if (this.currentProgram !== program) {
            this.gl.useProgram(program);
            this.currentProgram = program;
        }
    }
    
    // 绑定纹理
    bindTexture(texture) {
        if (this.currentTexture !== texture) {
            this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
            this.currentTexture = texture;
        }
    }
    
    // 绑定VAO
    bindVAO(vao) {
        if (this.currentVAO !== vao) {
            this.gl.bindVertexArray(vao);
            this.currentVAO = vao;
        }
    }
}

// 2. 资源管理
class ResourceManager {
    constructor() {
        this.resources = new Map();
        this.loading = new Set();
    }
    
    // 加载资源
    async load(url, type) {
        if (this.resources.has(url)) {
            return this.resources.get(url);
        }
        
        if (this.loading.has(url)) {
            return new Promise(resolve => {
                const check = () => {
                    if (this.resources.has(url)) {
                        resolve(this.resources.get(url));
                    } else {
                        requestAnimationFrame(check);
                    }
                };
                check();
            });
        }
        
        this.loading.add(url);
        
        try {
            const resource = await this.loadResource(url, type);
            this.resources.set(url, resource);
            this.loading.delete(url);
            return resource;
        } catch (error) {
            this.loading.delete(url);
            throw error;
        }
    }
    
    // 释放资源
    unload(url) {
        const resource = this.resources.get(url);
        if (resource) {
            if (resource.dispose) {
                resource.dispose();
            }
            this.resources.delete(url);
        }
    }
}

// 3. 渲染队列
class RenderQueue {
    constructor() {
        this.opaque = [];
        this.transparent = [];
    }
    
    // 添加渲染对象
    add(object) {
        if (object.material.transparent) {
            this.transparent.push(object);
        } else {
            this.opaque.push(object);
        }
    }
    
    // 排序渲染队列
    sort(cameraPosition) {
        // 不透明物体从前往后排序
        this.opaque.sort((a, b) => {
            return a.material.renderOrder - b.material.renderOrder;
        });
        
        // 透明物体从后往前排序
        this.transparent.sort((a, b) => {
            const distA = vec3.distance(a.position, cameraPosition);
            const distB = vec3.distance(b.position, cameraPosition);
            return distB - distA;
        });
    }
    
    // 执行渲染
    render(scene) {
        // 渲染不透明物体
        for (const object of this.opaque) {
            object.render(scene);
        }
        
        // 渲染透明物体
        this.gl.enable(this.gl.BLEND);
        this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
        
        for (const object of this.transparent) {
            object.render(scene);
        }
        
        this.gl.disable(this.gl.BLEND);
    }
}

结语 📝

WebGL为JavaScript提供了强大的图形渲染能力。通过本文,我们学习了:

  1. WebGL的基本概念和初始化
  2. 着色器程序和几何体管理
  3. 高级渲染技术
  4. 性能优化策略
  5. 最佳实践和设计模式

💡 学习建议:在使用WebGL时,要注意性能优化和内存管理。合理使用批处理和实例化渲染,避免频繁的状态切换。同时,要考虑跨平台兼容性和降级处理。


如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇

终身学习,共同成长。

咱们下一期见

💻


http://www.kler.cn/a/522327.html

相关文章:

  • Windows11无法打开Windows安全中心主界面
  • Baklib揭示内容中台与人工智能技术的创新协同效应
  • 使用CSS实现一个加载的进度条
  • PostgreSQL 约束
  • C++二叉树进阶
  • 出现 Error processing condition on org.springframework.cloud.openfeign 解决方法
  • 专为课堂打造:宏碁推出三款全新耐用型 Chromebook
  • 【实用技能】如何借助Excel处理控件Aspose.Cells,使用 C# 锁定 Excel 中的单元格
  • 获取加工视图下所有元素
  • java后端之事务管理
  • 【C++探索之路】STL---string
  • Day27-【13003】短文,单链表应用代码举例
  • 解决MySQL删除/var/lib/mysql下的所有文件后无法启动的问题
  • 未来五年高速线缆市场有望翻3倍!AEC凭借传输距离优势占比将更高
  • CentOS7非root用户离线安装Docker及常见问题总结、各种操作系统docker桌面程序下载地址
  • 非注意力模型崛起:LLM架构新突破
  • 【JavaEE】Spring(5):Mybatis(上)
  • 【单链表算法实战】解锁数据结构核心谜题——环形链表
  • 基于PostgreSQL的自然语义解析电子病历编程实践与探索(下)
  • vim多文件操作如何同屏开多个文件
  • 软件测试丨Airtest 游戏自动化测试框架
  • 电梯系统的UML文档12
  • LangChain:使用表达式语言优化提示词链
  • 论文阅读(三):微阵列数据的图形模型和多变量分析
  • UF_CAM常用函数
  • C++ - AVL平衡二叉树