首页
学习
活动
专区
圈层
工具
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

js webgl绘制图片

WebGL(全称:Web Graphics Library)是一种在不需要任何插件的情况下,在任何兼容的Web浏览器中呈现3D图形和2D图形的JavaScript API。WebGL通过HTML5的<canvas>元素与DOM进行交互,允许开发者在网页上渲染复杂的2D和3D图形。

基础概念

  1. Canvas元素:HTML5提供的一个画布,可以通过JavaScript脚本来绘制图形。
  2. 着色器(Shaders):WebGL程序的基本组成部分,包括顶点着色器和片段着色器,用于处理图形渲染过程中的各个阶段。
  3. 缓冲区(Buffers):用于存储顶点数据的内存区域。
  4. 纹理(Textures):可以映射到3D模型表面的图像。

绘制图片的步骤

  1. 获取Canvas元素和WebGL上下文。
  2. 创建并编译着色器程序。
  3. 设置顶点和纹理坐标。
  4. 加载图片作为纹理。
  5. 渲染场景。

示例代码

代码语言:txt
复制
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebGL Image Drawing</title>
<style>
canvas { width: 100%; height: 100%; }
</style>
</head>
<body>
<canvas id="glcanvas"></canvas>
<script>
function loadTexture(gl, url) {
    const texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);

    // 因为图片还没有加载完成,所以我们先填充一个1x1的蓝色像素
    const level = 0;
    const internalFormat = gl.RGBA;
    const width = 1;
    const height = 1;
    const border = 0;
    const srcFormat = gl.RGBA;
    const srcType = gl.UNSIGNED_BYTE;
    const pixel = new Uint8Array([0, 0, 255, 255]); // 蓝色
    gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border, srcFormat, srcType, pixel);

    const image = new Image();
    image.onload = function() {
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, srcFormat, srcType, image);

        // WebGL1.0对纹理尺寸有限制,需要对大图片进行缩放
        gl.generateMipmap(gl.TEXTURE_2D);
    };
    image.src = url;

    return texture;
}

function main() {
    const canvas = document.getElementById('glcanvas');
    const gl = canvas.getContext('webgl');

    if (!gl) {
        console.error('WebGL not supported, falling back on experimental-webgl');
        gl = canvas.getContext('experimental-webgl');
    }

    if (!gl) {
        alert('Your browser does not support WebGL');
    }

    // 顶点着色器代码
    const vertexShaderSource = `
        attribute vec4 a_position;
        attribute vec2 a_texCoord;
        varying vec2 v_texCoord;
        void main() {
            gl_Position = a_position;
            v_texCoord = a_texCoord;
        }
    `;

    // 片段着色器代码
    const fragmentShaderSource = `
        precision mediump float;
        varying vec2 v_texCoord;
        uniform sampler2D u_image;
        void main() {
            gl_FragColor = texture2D(u_image, v_texCoord);
        }
    `;

    // 创建并编译着色器程序
    const shaderProgram = initShaderProgram(gl, vertexShaderSource, fragmentShaderSource);
    const programInfo = {
        program: shaderProgram,
        attribLocations: {
            position: gl.getAttribLocation(shaderProgram, 'a_position'),
            texCoord: gl.getAttribLocation(shaderProgram, 'a_texCoord'),
        },
        uniformLocations: {
            u_image: gl.getUniformLocation(shaderProgram, 'u_image'),
        },
    };

    // 设置顶点和纹理坐标
    const buffers = initBuffers(gl);

    // 加载图片作为纹理
    const texture = loadTexture(gl, 'path/to/your/image.jpg');

    // 渲染循环
    function render() {
        drawScene(gl, programInfo, buffers, texture);
        requestAnimationFrame(render);
    }
    requestAnimationFrame(render);
}

function initBuffers(gl) {
    const positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

    const positions = [
         1.0,  1.0,
        -1.0,  1.0,
         1.0, -1.0,
        -1.0, -1.0,
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

    const texCoordBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);

    const texCoords = [
        1.0, 1.0,
        0.0, 1.0,
        1.0, 0.0,
        0.0, 0.0,
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texCoords), gl.STATIC_DRAW);

    return {
        position: positionBuffer,
        texCoord: texCoordBuffer,
    };
}

function drawScene(gl, programInfo, buffers, texture) {
    gl.clearColor(0.0, 0.0, 0.0, 1.0);  // Clear to black, fully opaque
    gl.clearDepth(1.0);                 // Clear everything
    gl.enable(gl.DEPTH_TEST);           // Enable depth testing
    gl.depthFunc(gl.LEQUAL);            // Near things obscure far things

    // Clear the canvas before we start drawing on it.
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    // Create a perspective matrix, a special matrix that is
    // used to simulate the distortion of perspective in a camera.
    // Our field of view is 45 degrees, with a width/height
    // ratio that matches the display size of the canvas
    // and we only want to see objects between 0.1 units
    // and 100 units away from the camera.
    const fieldOfView = 45 * Math.PI / 180;   // in radians
    const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
    const zNear = 0.1;
    const zFar = 100.0;
    const projectionMatrix = mat4.create();

    // note: glmatrix.js always has the first argument
    // as the destination to receive the result.
    mat4.perspective(projectionMatrix,
                     fieldOfView,
                     aspect,
                     zNear,
                     zFar);

    // Set the drawing position to the "identity" point, which is
    // the center of the scene.
    const modelViewMatrix = mat4.create();

    // Now move the drawing position a bit to where we want to
    // start drawing the square.
    mat4.translate(modelViewMatrix,     // destination matrix
                   modelViewMatrix,     // matrix to translate
                   [-0.0, 0.0, -6.0]);  // amount to translate

    // Tell WebGL how to pull out the positions from the position
    // buffer into the vertexPosition attribute.
    {
        const numComponents = 2;  // pull out 2 values per iteration
        const type = gl.FLOAT;    // the data in the buffer is 32bit floats
        const normalize = false;  // don't normalize
        const stride = 0;         // how many bytes to get from one set of values to the next
                                  // 0 = use type and numComponents above
        const offset = 0;         // how many bytes inside the buffer to start from
        gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
        gl.vertexAttribPointer(
            programInfo.attribLocations.position,
            numComponents,
            type,
            normalize,
            stride,
            offset);
        gl.enableVertexAttribArray(
            programInfo.attribLocations.position);
    }

    // Tell WebGL how to pull out the texture coordinates from
    // the texture coordinate buffer into the texCoord attribute.
    {
        const numComponents = 2;
        const type = gl.FLOAT;
        const normalize = false;
        const stride = 0;
        const offset = 0;
        gl.bindBuffer(gl.ARRAY_BUFFER, buffers.texCoord);
        gl.vertexAttribPointer(
            programInfo.attribLocations.texCoord,
            numComponents,
            type,
            normalize,
            stride,
            offset);
        gl.enableVertexAttribArray(
            programInfo.attribLocations.texCoord);
    }

    // Tell WebGL to use our program when drawing
    gl.useProgram(programInfo.program);

    // Set the shader uniforms
    gl.uniformMatrix4fv(
        programInfo.uniformLocations.projectionMatrix,
        false,
        projectionMatrix);
    gl.uniformMatrix4fv(
        programInfo.uniformLocations.modelViewMatrix,
        false,
        modelViewMatrix);

    // Bind the texture to texture unit 0
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, texture);

    // Tell the shader we bound the texture to texture unit 0
    gl.uniform1i(programInfo.uniformLocations.u_image, 0);

    {
        const offset = 0;
        const vertexCount = 4;
        gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount);
    }
}

function initShaderProgram(gl, vsSource, fsSource) {
    const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
    const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);

    // Create the shader program
    const shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);
    gl.linkProgram(shaderProgram);

    // If creating the shader program failed, alert
    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
        alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shader(self.program)));
        return null;
    }

    return shaderProgram;
}

function loadShader(gl, type, source) {
    const shader = gl.createShader(type);

    // Send the source to the shader object
    gl.shaderSource(shader, source);

    // Compile the shader program
    gl.compileShader(shader);

    // See if it compiled successfully
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
        return null;
    }

    return shader;
}
</script>
</body>
</html>

应用场景

WebGL广泛应用于:

  • 游戏开发:实时渲染3D游戏场景。
  • 数据可视化:以3D形式展示复杂数据。
  • 虚拟现实和增强现实:创建沉浸式体验。
  • 艺术创作:数字艺术和互动媒体。

可能遇到的问题及解决方法

  1. 纹理加载失败:确保图片路径正确,且图片格式受WebGL支持(如PNG、JPEG)。
  2. 性能问题:优化着色器代码,减少不必要的计算;使用纹理压缩格式减少内存占用。
  3. 兼容性问题:检测浏览器对WebGL的支持情况,提供回退方案。

优势

  • 性能:直接在GPU上运行,适合处理大量图形数据。
  • 灵活性:开发者可以完全控制渲染过程,实现复杂的视觉效果。
  • 跨平台:无需插件即可在任何现代浏览器中运行。

通过上述代码示例和应用场景的介绍,你可以开始尝试在WebGL中绘制图片,并根据需要进行进一步的探索和优化。

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

  • 解剖 WebGL & Three.js 工作原理

    我们讲两个东西: 1、WebGL背后的工作原理是什么? 2、以Three.js为例,讲述框架在背后扮演什么样的角色? 二、我们为什么要了解原理?...于是,我们看了看WebGL绘图API,发现: 也就是说,再复杂的3D图形,也是通过顶点,绘制出一个个三角形来表示的: 4.2、WebGL绘制流程 简单说来,WebGL绘制过程包括以下三步: 1、获取顶点坐标...这段代码什么也没做,如果是绘制2d图形,没问题,但如果是绘制3d图形,即传入的顶点坐标是一个三维坐标,我们则需要转换成屏幕坐标。...4.3、WebGL的完整工作流程 至此,实质上,WebGL经历了如下处理流程: 1、准备数据阶段 在这个阶段,我们需要提供顶点坐标、索引(三角形绘制顺序)、uv(决定贴图坐标)、法线(决定光照效果),以及各种矩阵...5.1、three.js顶点处理流程 从WebGL工作原理的章节中,我们已经知道了顶点着色器会将三维世界坐标转换成屏幕坐标,但实际上,坐标转换不限于投影矩阵。

    9.8K21

    JS 图片压缩

    前言 说起图片压缩,大家想到的或者平时用到的很多工具都可以实现,例如,客户端类的有图片压缩工具 PPDuck3, JS 实现类的有插件 compression.js ,亦或是在线处理类的 OSS 上传,...文件上传后,在访问文件时中也有图片的压缩配置选项,不过,能不能自己撸一套 JS 实现的图片压缩代码呢?...压缩思路 涉及到 JS 的图片压缩,我的想法是需要用到 Canvas 的绘图能力,通过调整图片的分辨率或者绘图质量来达到图片压缩的效果,实现思路如下: 获取上传 Input 中的图片对象 File 将图片转换成...,调用 drawImage 方法在 canvas 中绘制上传的图片 let image = new Image(); //新建一个img标签 image.src = e.target.result; let...Canvas 元素上绘制图像的宽度和高度(如果不说明, 在绘制时图片的宽度和高度不会缩放)。

    25.8K21

    WPF 使用 Skia 绘制 WriteableBitmap 图片

    本文告诉大家如何在 WPF 中使用 SkiaSharp 调用 Skia 这个全平台底层渲染框架,使用绘制命令在 WriteableBitmap 图片上绘制内容 谷歌提出了 Skia 全平台渲染框架,这是一个很底层的框架...其实 WriteableBitmap 是将一个数组里面的像素在屏幕显示,而 SKSurface 可以从一个像素数组开始创建,创建的时候需要规定这个数组对应的图片的格式,包括图片的大小以及 RGB 像素格式...使用下面代码创建一个简单的界面,在这个界面里面点击按钮将会给 Image 控件赋值使用 Skia 创建的图片 ...Skia 绘制到 Surface 上,而绘制内容将会作为像素数组放在传入的数组里面 小伙伴是否还记得 WPF 使用不安全代码快速从数组转 WriteableBitmap 的方法,其实 Skia 在 WriteableBitmap...绘制的本质就是这样 在开始绘制之前需要调用 WriteableBitmap 的 Lock 方法,接着在绘制完成之后,需要调用 AddDirtyRect 和 Unlock 方法 大概的绘制代码如下

    2.3K20

    你知道几种前端动画的实现方式?

    那前端实现动画效果的方式有哪些呢,大致有如下几种: 一、GIF图片 GIF图就直接贴上图片就好了,使用非常简单,但GIF图有时出现问题是没办法控制的,比如常见的GIF图的闪烁现象。...参数说明: 五、WebGL与Canvas 当页面动画复杂性较高时,使用dom进行绘制可能会出现性能问题,画面会出现卡顿,此时可以考虑WebGL或Canvas进行渲染。...1、性能对比 从结果中可见,当需要执行大量绘制任务时,WebGL的性能远远超越了Canvas 2D Api,达到了后者的数10倍。...canvas.getContext(“experimental-webgl”) 若返回结果为undefined则表示不支持,否则便可以使用WebGL 3、调用方式 绘制一个简单的矩形,内部填充颜色为红色...(2)Pixi.js 一般来说,WebGL 的渲染速度都会比 Canvas 快,这是由俩者的绘制路径决定的。

    3.9K20

    WPF 通过 DrawingContext DrawImage 绘制图片 裁剪图片

    本文告诉大家如何通过 DrawingContext 绘制图片,同时指定绘制图片在画布的某个区域和绘制出来的图片大小,如何裁剪图片 在 WPF 中可以使用 DrawingVisual 进行底层的绘制,底层的绘制的效率是比较高的...,这时需要拖动一张图片进入解决方案 public MainWindow() { InitializeComponent();...)); } Element.ContainerVisual.Children.Add(drawingVisual); } 现在可以看到图片在...100,100 的坐标画出,此时图片为被缩放到 50x50 也就是缩放画图片到指定的 Rect 上 裁剪图片 如果只是需要画出被裁剪的图片,可以使用 CroppedBitmap 进行裁剪 在 CroppedBitmap...的构造可以传入需要裁剪的图片和如何裁剪,裁剪是进行矩形的裁剪 如下面代码是裁剪矩形从图片的左上角 50x50 范围 var croppedBitmap = new CroppedBitmap

    2.9K20
    领券