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

大白话react第十七章React 与 WebGL 项目进阶优化及拓展

大白话react第十七章React 与 WebGL 项目进阶优化及拓展

1. 引入物理引擎

在 React 和 WebGL 结合的项目里,加入物理引擎能让 3D 场景更真实,就像在现实世界里物体有重力、碰撞等效果一样。这里我们用 cannon-es 这个物理引擎库。

// 引入 React 的 useEffect 和 useRef 钩子
import React, { useEffect, useRef } from'react';
// 引入 three.js 用于创建 3D 场景
import * as THREE from 'three';
// 引入 cannon-es 物理引擎库
import * as CANNON from 'cannon-es';

const PhysicsComponent = () => {
    // 创建一个 ref 用于引用存放 3D 场景的 DOM 元素
    const containerRef = useRef(null);

    useEffect(() => {
        // 创建 three.js 的场景
        const scene = new THREE.Scene();
        // 创建透视相机,设置视角、宽高比、近裁剪面和远裁剪面
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        // 创建 WebGL 渲染器
        const renderer = new THREE.WebGLRenderer();
        // 设置渲染器的大小为窗口大小
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 将渲染器的 DOM 元素添加到 ref 对应的 DOM 元素中
        containerRef.current.appendChild(renderer.domElement);

        // 创建 cannon-es 的物理世界
        const world = new CANNON.World();
        // 设置物理世界的重力,这里模拟向下的重力
        world.gravity.set(0, -9.82, 0);

        // 创建一个地面的物理材质
        const groundMaterial = new CANNON.Material('groundMaterial');
        // 创建地面的形状,这里是平面
        const groundShape = new CANNON.Plane();
        // 创建地面的刚体
        const groundBody = new CANNON.Body({
            mass: 0, // 质量为 0 表示静止物体
            shape: groundShape,
            material: groundMaterial
        });
        // 旋转地面使其水平
        groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0);
        // 将地面刚体添加到物理世界
        world.addBody(groundBody);

        // 创建一个立方体的物理材质
        const boxMaterial = new CANNON.Material('boxMaterial');
        // 创建立方体的形状
        const boxShape = new CANNON.Box(new CANNON.Vec3(1, 1, 1));
        // 创建立方体的刚体,设置质量和位置
        const boxBody = new CANNON.Body({
            mass: 1,
            position: new CANNON.Vec3(0, 10, 0),
            shape: boxShape,
            material: boxMaterial
        });
        // 将立方体刚体添加到物理世界
        world.addBody(boxBody);

        // 创建地面的 3D 模型
        const groundGeometry = new THREE.PlaneGeometry(10, 10);
        const groundMaterialThree = new THREE.MeshBasicMaterial({ color: 0x808080, side: THREE.DoubleSide });
        const groundMesh = new THREE.Mesh(groundGeometry, groundMaterialThree);
        groundMesh.rotation.x = -Math.PI / 2;
        scene.add(groundMesh);

        // 创建立方体的 3D 模型
        const boxGeometry = new THREE.BoxGeometry(2, 2, 2);
        const boxMaterialThree = new THREE.MeshBasicMaterial({ color: 0xff0000 });
        const boxMesh = new THREE.Mesh(boxGeometry, boxMaterialThree);
        scene.add(boxMesh);

        // 设置相机位置
        camera.position.z = 5;

        // 定义渲染函数
        const animate = () => {
            // 请求下一帧动画
            requestAnimationFrame(animate);
            // 更新物理世界
            world.step(1 / 60);
            // 将立方体刚体的位置和旋转同步到 3D 模型上
            boxMesh.position.copy(boxBody.position);
            boxMesh.quaternion.copy(boxBody.quaternion);
            // 渲染场景
            renderer.render(scene, camera);
        };

        // 开始动画循环
        animate();

        // 组件卸载时清理资源
        return () => {
            containerRef.current.removeChild(renderer.domElement);
        };
    }, []);

    return (
        // 创建一个 div 用于存放 3D 场景
        <div ref={containerRef} />
    );
};

export default PhysicsComponent;
2. 实现光照与阴影效果

光照和阴影能让 3D 场景更有立体感和真实感。在 three.js 里可以很方便地实现不同类型的光照和阴影效果。

// 引入 React 的 useEffect 和 useRef 钩子
import React, { useEffect, useRef } from'react';
// 引入 three.js 用于创建 3D 场景
import * as THREE from 'three';

const LightingAndShadowsComponent = () => {
    // 创建一个 ref 用于引用存放 3D 场景的 DOM 元素
    const containerRef = useRef(null);

    useEffect(() => {
        // 创建 three.js 的场景
        const scene = new THREE.Scene();
        // 创建透视相机,设置视角、宽高比、近裁剪面和远裁剪面
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        // 创建 WebGL 渲染器
        const renderer = new THREE.WebGLRenderer();
        // 设置渲染器的大小为窗口大小
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 开启渲染器的阴影支持
        renderer.shadowMap.enabled = true;
        // 将渲染器的 DOM 元素添加到 ref 对应的 DOM 元素中
        containerRef.current.appendChild(renderer.domElement);

        // 创建一个平面几何体
        const planeGeometry = new THREE.PlaneGeometry(10, 10);
        // 创建平面的材质
        const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x808080 });
        // 创建平面的 3D 模型
        const plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.rotation.x = -Math.PI / 2;
        // 设置平面接收阴影
        plane.receiveShadow = true;
        scene.add(plane);

        // 创建一个立方体几何体
        const cubeGeometry = new THREE.BoxGeometry(2, 2, 2);
        // 创建立方体的材质
        const cubeMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
        // 创建立方体的 3D 模型
        const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
        cube.position.y = 1;
        // 设置立方体投射阴影
        cube.castShadow = true;
        scene.add(cube);

        // 创建一个点光源,设置颜色和强度
        const pointLight = new THREE.PointLight(0xffffff, 1);
        pointLight.position.set(2, 5, 2);
        // 开启点光源的阴影投射
        pointLight.castShadow = true;
        scene.add(pointLight);

        // 设置相机位置
        camera.position.z = 5;

        // 定义渲染函数
        const animate = () => {
            // 请求下一帧动画
            requestAnimationFrame(animate);
            // 旋转立方体
            cube.rotation.x += 0.01;
            cube.rotation.y += 0.01;
            // 渲染场景
            renderer.render(scene, camera);
        };

        // 开始动画循环
        animate();

        // 组件卸载时清理资源
        return () => {
            containerRef.current.removeChild(renderer.domElement);
        };
    }, []);

    return (
        // 创建一个 div 用于存放 3D 场景
        <div ref={containerRef} />
    );
};

export default LightingAndShadowsComponent;
3. 支持用户交互

让用户可以和 3D 场景进行交互,比如点击、拖动等操作,能提升用户体验。这里我们用 three.jsRaycaster 来实现点击检测。

// 引入 React 的 useEffect、useRef 和 useState 钩子
import React, { useEffect, useRef, useState } from'react';
// 引入 three.js 用于创建 3D 场景
import * as THREE from 'three';

const InteractionComponent = () => {
    // 创建一个 ref 用于引用存放 3D 场景的 DOM 元素
    const containerRef = useRef(null);
    // 创建一个状态来存储点击的物体
    const [clickedObject, setClickedObject] = useState(null);

    useEffect(() => {
        // 创建 three.js 的场景
        const scene = new THREE.Scene();
        // 创建透视相机,设置视角、宽高比、近裁剪面和远裁剪面
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        // 创建 WebGL 渲染器
        const renderer = new THREE.WebGLRenderer();
        // 设置渲染器的大小为窗口大小
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 将渲染器的 DOM 元素添加到 ref 对应的 DOM 元素中
        containerRef.current.appendChild(renderer.domElement);

        // 创建一个立方体几何体
        const cubeGeometry = new THREE.BoxGeometry(2, 2, 2);
        // 创建立方体的材质
        const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
        // 创建立方体的 3D 模型
        const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
        scene.add(cube);

        // 设置相机位置
        camera.position.z = 5;

        // 创建一个射线投射器,用于检测点击
        const raycaster = new THREE.Raycaster();
        // 创建一个二维向量,用于存储鼠标位置
        const mouse = new THREE.Vector2();

        // 定义鼠标点击事件处理函数
        const onMouseClick = (event) => {
            // 计算鼠标在标准化设备坐标中的位置
            mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
            mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
            // 通过鼠标位置更新射线投射器
            raycaster.setFromCamera(mouse, camera);
            // 获取射线与场景中物体的交点
            const intersects = raycaster.intersectObjects(scene.children);
            if (intersects.length > 0) {
                // 如果有交点,设置点击的物体状态
                setClickedObject(intersects[0].object);
            } else {
                // 没有交点则清空点击的物体状态
                setClickedObject(null);
            }
        };

        // 监听鼠标点击事件
        window.addEventListener('click', onMouseClick);

        // 定义渲染函数
        const animate = () => {
            // 请求下一帧动画
            requestAnimationFrame(animate);
            // 渲染场景
            renderer.render(scene, camera);
        };

        // 开始动画循环
        animate();

        // 组件卸载时清理资源
        return () => {
            containerRef.current.removeChild(renderer.domElement);
            // 移除鼠标点击事件监听
            window.removeEventListener('click', onMouseClick);
        };
    }, []);

    return (
        <div>
            {/* 创建一个 div 用于存放 3D 场景 */}
            <div ref={containerRef} />
            {clickedObject && (
                // 如果有点击的物体,显示提示信息
                <p>你点击了一个物体!</p>
            )}
        </div>
    );
};

export default InteractionComponent;

通过这些进阶的优化和拓展,你的 React 与 WebGL 项目会变得更加丰富、真实和有趣,用户体验也会大大提升。

如何优化WebGL的渲染性能?

优化 WebGL 渲染性能的方法

1. 减少绘制调用次数

在 WebGL 里,每次绘制调用都需要一些额外的开销。如果能把多次绘制合并成一次,就能减少这些开销,提高性能。就好像你去超市买东西,一次多买点,少跑几趟,效率就高了。

// 初始化 WebGL 上下文
function initWebGL(canvas) {
    try {
        // 尝试获取 WebGL 上下文
        return canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
    } catch (e) {
        // 如果获取失败,输出错误信息
        console.error("无法获取 WebGL 上下文", e);
        return null;
    }
}

// 合并几何体的函数
function mergeGeometries(geometries) {
    let positions = [];
    let indices = [];
    let indexOffset = 0;

    // 遍历所有几何体
    geometries.forEach(geometry => {
        // 将当前几何体的顶点位置添加到总的顶点位置数组中
        positions = positions.concat(geometry.positions);
        // 将当前几何体的索引添加到总的索引数组中,并根据偏移量调整索引值
        indices = indices.concat(geometry.indices.map(index => index + indexOffset));
        // 更新索引偏移量
        indexOffset += geometry.positions.length / 3;
    });

    return {
        positions: positions,
        indices: indices
    };
}

// 创建一个简单的几何体
function createGeometry() {
    // 定义顶点位置
    const positions = [
        -0.5, -0.5, 0,
        0.5, -0.5, 0,
        0.5, 0.5, 0,
        -0.5, 0.5, 0
    ];
    // 定义索引
    const indices = [
        0, 1, 2,
        2, 3, 0
    ];

    return {
        positions: positions,
        indices: indices
    };
}

// 主函数
function main() {
    // 获取 canvas 元素
    const canvas = document.getElementById("glCanvas");
    // 初始化 WebGL 上下文
    const gl = initWebGL(canvas);

    if (!gl) {
        // 如果上下文获取失败,输出错误信息并返回
        console.error("无法初始化 WebGL");
        return;
    }

    // 创建两个几何体
    const geometry1 = createGeometry();
    const geometry2 = createGeometry();
    // 合并这两个几何体
    const mergedGeometry = mergeGeometries([geometry1, geometry2]);

    // 创建顶点缓冲区
    const positionBuffer = gl.createBuffer();
    // 绑定顶点缓冲区
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    // 将合并后的顶点位置数据写入缓冲区
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(mergedGeometry.positions), gl.STATIC_DRAW);

    // 创建索引缓冲区
    const indexBuffer = gl.createBuffer();
    // 绑定索引缓冲区
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    // 将合并后的索引数据写入缓冲区
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(mergedGeometry.indices), gl.STATIC_DRAW);

    // 顶点着色器代码
    const vertexShaderSource = `
        attribute vec3 a_position;
        void main() {
            gl_Position = vec4(a_position, 1.0);
        }
    `;
    // 片段着色器代码
    const fragmentShaderSource = `
        precision mediump float;
        void main() {
            gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
        }
    `;

    // 创建顶点着色器对象
    const vertexShader = gl.createShader(gl.VERTEX_SHADER);
    // 将顶点着色器代码放入着色器对象
    gl.shaderSource(vertexShader, vertexShaderSource);
    // 编译顶点着色器
    gl.compileShader(vertexShader);

    // 创建片段着色器对象
    const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    // 将片段着色器代码放入着色器对象
    gl.shaderSource(fragmentShader, fragmentShaderSource);
    // 编译片段着色器
    gl.compileShader(fragmentShader);

    // 创建着色器程序对象
    const shaderProgram = gl.createProgram();
    // 将顶点着色器附加到着色器程序
    gl.attachShader(shaderProgram, vertexShader);
    // 将片段着色器附加到着色器程序
    gl.attachShader(shaderProgram, fragmentShader);
    // 链接着色器程序
    gl.linkProgram(shaderProgram);
    // 使用着色器程序
    gl.useProgram(shaderProgram);

    // 获取顶点属性的位置
    const positionAttributeLocation = gl.getAttribLocation(shaderProgram, "a_position");
    // 启用顶点属性
    gl.enableVertexAttribArray(positionAttributeLocation);
    // 绑定顶点缓冲区
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    // 设置顶点属性指针
    gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);

    // 设置视口大小
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    // 清空颜色缓冲区
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);

    // 绘制合并后的几何体
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    gl.drawElements(gl.TRIANGLES, mergedGeometry.indices.length, gl.UNSIGNED_SHORT, 0);
}

// 调用主函数
main();
2. 优化纹理使用

纹理是影响性能的一个重要因素。大尺寸的纹理会占用很多内存,加载和处理起来也慢。所以要尽量压缩纹理,并且按需加载。就像你搬家,东西能压缩就压缩,需要用的时候再拿出来。

// 初始化 WebGL 上下文
function initWebGL(canvas) {
    try {
        // 尝试获取 WebGL 上下文
        return canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
    } catch (e) {
        // 如果获取失败,输出错误信息
        console.error("无法获取 WebGL 上下文", e);
        return null;
    }
}

// 加载纹理的函数
function loadTexture(gl, url) {
    // 创建纹理对象
    const texture = gl.createTexture();
    // 绑定纹理对象
    gl.bindTexture(gl.TEXTURE_2D, texture);

    // 设置纹理的初始参数
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
        new Uint8Array([0, 0, 255, 255]));

    // 创建图像对象
    const image = new Image();
    image.onload = function () {
        // 图像加载完成后,绑定纹理对象
        gl.bindTexture(gl.TEXTURE_2D, texture);
        // 将图像数据上传到纹理
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);

        // 检查图像尺寸是否是 2 的幂次方
        if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
            // 如果是 2 的幂次方,生成多级纹理
            gl.generateMipmap(gl.TEXTURE_2D);
        } else {
            // 如果不是 2 的幂次方,设置纹理过滤模式
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        }
    };
    // 设置图像的源地址
    image.src = url;

    return texture;
}

// 判断一个数是否是 2 的幂次方
function isPowerOf2(value) {
    return (value & (value - 1)) === 0;
}

// 主函数
function main() {
    // 获取 canvas 元素
    const canvas = document.getElementById("glCanvas");
    // 初始化 WebGL 上下文
    const gl = initWebGL(canvas);

    if (!gl) {
        // 如果上下文获取失败,输出错误信息并返回
        console.error("无法初始化 WebGL");
        return;
    }

    // 加载纹理
    const texture = loadTexture(gl, "compressed_texture.jpg");

    // 顶点着色器代码
    const vertexShaderSource = `
        attribute vec3 a_position;
        attribute vec2 a_texCoord;
        varying vec2 v_texCoord;
        void main() {
            gl_Position = vec4(a_position, 1.0);
            v_texCoord = a_texCoord;
        }
    `;
    // 片段着色器代码
    const fragmentShaderSource = `
        precision mediump float;
        uniform sampler2D u_texture;
        varying vec2 v_texCoord;
        void main() {
            gl_FragColor = texture2D(u_texture, v_texCoord);
        }
    `;

    // 创建顶点着色器对象
    const vertexShader = gl.createShader(gl.VERTEX_SHADER);
    // 将顶点着色器代码放入着色器对象
    gl.shaderSource(vertexShader, vertexShaderSource);
    // 编译顶点着色器
    gl.compileShader(vertexShader);

    // 创建片段着色器对象
    const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    // 将片段着色器代码放入着色器对象
    gl.shaderSource(fragmentShader, fragmentShaderSource);
    // 编译片段着色器
    gl.compileShader(fragmentShader);

    // 创建着色器程序对象
    const shaderProgram = gl.createProgram();
    // 将顶点着色器附加到着色器程序
    gl.attachShader(shaderProgram, vertexShader);
    // 将片段着色器附加到着色器程序
    gl.attachShader(shaderProgram, fragmentShader);
    // 链接着色器程序
    gl.linkProgram(shaderProgram);
    // 使用着色器程序
    gl.useProgram(shaderProgram);

    // 顶点位置数据
    const positions = [
        -0.5, -0.5, 0,
        0.5, -0.5, 0,
        0.5, 0.5, 0,
        -0.5, 0.5, 0
    ];
    // 纹理坐标数据
    const texCoords = [
        0, 0,
        1, 0,
        1, 1,
        0, 1
    ];
    // 索引数据
    const indices = [
        0, 1, 2,
        2, 3, 0
    ];

    // 创建顶点缓冲区
    const positionBuffer = gl.createBuffer();
    // 绑定顶点缓冲区
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    // 将顶点位置数据写入缓冲区
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

    // 获取顶点属性的位置
    const positionAttributeLocation = gl.getAttribLocation(shaderProgram, "a_position");
    // 启用顶点属性
    gl.enableVertexAttribArray(positionAttributeLocation);
    // 设置顶点属性指针
    gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);

    // 创建纹理坐标缓冲区
    const texCoordBuffer = gl.createBuffer();
    // 绑定纹理坐标缓冲区
    gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
    // 将纹理坐标数据写入缓冲区
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texCoords), gl.STATIC_DRAW);

    // 获取纹理坐标属性的位置
    const texCoordAttributeLocation = gl.getAttribLocation(shaderProgram, "a_texCoord");
    // 启用纹理坐标属性
    gl.enableVertexAttribArray(texCoordAttributeLocation);
    // 设置纹理坐标属性指针
    gl.vertexAttribPointer(texCoordAttributeLocation, 2, gl.FLOAT, false, 0, 0);

    // 创建索引缓冲区
    const indexBuffer = gl.createBuffer();
    // 绑定索引缓冲区
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    // 将索引数据写入缓冲区
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

    // 获取纹理统一变量的位置
    const textureUniformLocation = gl.getUniformLocation(shaderProgram, "u_texture");
    // 激活纹理单元 0
    gl.activeTexture(gl.TEXTURE0);
    // 绑定纹理对象
    gl.bindTexture(gl.TEXTURE_2D, texture);
    // 设置纹理统一变量的值
    gl.uniform1i(textureUniformLocation, 0);

    // 设置视口大小
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    // 清空颜色缓冲区
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);

    // 绘制几何体
    gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
}

// 调用主函数
main();
3. 控制渲染循环

渲染循环就是不停地更新和绘制场景。如果帧率太高,会浪费性能;如果场景没变化,还一直渲染也没必要。所以要合理控制渲染循环,就像你跑步,不需要一直全速跑,该慢就慢,该停就停。

// 初始化 WebGL 上下文
function initWebGL(canvas) {
    try {
        // 尝试获取 WebGL 上下文
        return canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
    } catch (e) {
        // 如果获取失败,输出错误信息
        console.error("无法获取 WebGL 上下文", e);
        return null;
    }
}

// 主函数
function main() {
    // 获取 canvas 元素
    const canvas = document.getElementById("glCanvas");
    // 初始化 WebGL 上下文
    const gl = initWebGL(canvas);

    if (!gl) {
        // 如果上下文获取失败,输出错误信息并返回
        console.error("无法初始化 WebGL");
        return;
    }

    // 顶点着色器代码
    const vertexShaderSource = `
        attribute vec3 a_position;
        void main() {
            gl_Position = vec4(a_position, 1.0);
        }
    `;
    // 片段着色器代码
    const fragmentShaderSource = `
        precision mediump float;
        void main() {
            gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
        }
    `;

    // 创建顶点着色器对象
    const vertexShader = gl.createShader(gl.VERTEX_SHADER);
    // 将顶点着色器代码放入着色器对象
    gl.shaderSource(vertexShader, vertexShaderSource);
    // 编译顶点着色器
    gl.compileShader(vertexShader);

    // 创建片段着色器对象
    const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    // 将片段着色器代码放入着色器对象
    gl.shaderSource(fragmentShader, fragmentShaderSource);
    // 编译片段着色器
    gl.compileShader(fragmentShader);

    // 创建着色器程序对象
    const shaderProgram = gl.createProgram();
    // 将顶点着色器附加到着色器程序
    gl.attachShader(shaderProgram, vertexShader);
    // 将片段着色器附加到着色器程序
    gl.attachShader(shaderProgram, fragmentShader);
    // 链接着色器程序
    gl.linkProgram(shaderProgram);
    // 使用着色器程序
    gl.useProgram(shaderProgram);

    // 顶点位置数据
    const positions = [
        -0.5, -0.5, 0,
        0.5, -0.5, 0,
        0.5, 0.5, 0,
        -0.5, 0.5, 0
    ];
    // 索引数据
    const indices = [
        0, 1, 2,
        2, 3, 0
    ];

    // 创建顶点缓冲区
    const positionBuffer = gl.createBuffer();
    // 绑定顶点缓冲区
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    // 将顶点位置数据写入缓冲区
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

    // 获取顶点属性的位置
    const positionAttributeLocation = gl.getAttribLocation(shaderProgram, "a_position");
    // 启用顶点属性
    gl.enableVertexAttribArray(positionAttributeLocation);
    // 设置顶点属性指针
    gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);

    // 创建索引缓冲区
    const indexBuffer = gl.createBuffer();
    // 绑定

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

相关文章:

  • GStreamer —— 2.13、Windows下Qt加载GStreamer库后运行 - “教程13:播放控制“(附:完整源码)
  • BUUCTF——[GYCTF2020]FlaskApp1 SSTI模板注入/PIN学习
  • 无人机避障——XTDrone中运行VINS-Fusion+Ego-planner进行路径规划
  • 构建功能齐全的JavaScript计算器:从基础到高级功能的全面实现
  • 深入解析 BitBake 日志机制:任务调度、日志记录与调试方法
  • UE5中UBlueprintFunctionLibrary类详解
  • MySQL表空间碎片原理和解决方案
  • Ubuntu-docker安装mysql
  • 语言模型作为零样本规划者:提取可执行知识以供具身代理使用
  • 在Linux系统上集成OpenSlide与SpringBoot
  • AR配置静态IP双链路负载分担示例
  • 【技海登峰】Kafka漫谈系列(五)Java客户端之生产者KafkaProducer核心组件与实现原理剖析
  • Collections.addAll与List实例对象addAll方法的比较
  • 【从零开始学习计算机科学】硬件设计与FPGA原理
  • uni-app基础拓展
  • 【无人机三维路径规划】基于CPO冠豪猪优化算法的无人机三维路径规划Maltab
  • 生物电阻抗技术:精准洞察人体营养的“智能窗口”
  • 如何设计评论系统
  • 使用Arduino和ESP8266进行基于物联网的垃圾箱监控
  • 【UI自动化技术思路分析】【总纲】UI自动化代码完整设计思路