WebGL显示图片
简述
WebGL显示图片相对来说还是比较简单,跟Android,Python,IOS,C++等的用法都是差不多的,具体的opengl概念可以自己搜索。下面的介绍主要是基于Vue框架来设计做的Demo,不用vue的也可以简单的把代码移植过去。
开始做一个Demo
首先需要Canvas,opengl都是基于Canvas来操作的, 宽高可以自己设定。
<canvas id="canvas" height="600" width="600">canvas>
获取gl对象,用于后面的gl相关操作
this.gl = document.getElementById('canvas').getContext("webgl");
初始化着色器,opengl显示需要通过着色器脚本来计算显示
编写顶点着色器脚本
vertexShaderSource: ` attribute vec4 a_Position; attribute vec2 a_TexCoord; varying vec2 v_TexCoord; void main(){ gl_Position = a_Position; v_TexCoord = a_TexCoord; }`
编写片元着色器脚本
fragmentShaderSource: ` precision mediump float; uniform sampler2D u_Sampler; varying vec2 v_TexCoord; void main(){ gl_FragColor = texture2D(u_Sampler, v_TexCoord); }`
绑定和使用着色器
let vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);// 创建顶点着色器this.gl.shaderSource(vertexShader, this.vertexShaderSource);// 绑定顶点着色器源码this.gl.compileShader(vertexShader);// 编译定点着色器let fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);// 创建片元着色器this.gl.shaderSource(fragmentShader, this.fragmentShaderSource);// 绑定片元着色器源码this.gl.compileShader(fragmentShader);// 编译片元着色器let shaderProgram = this.gl.createProgram();// 创建着色器程序this.gl.attachShader(shaderProgram, vertexShader);// 指定顶点着色器this.gl.attachShader(shaderProgram, fragmentShader);// 指定片元着色色器this.gl.linkProgram(shaderProgram);// 链接程序this.gl.useProgram(shaderProgram);//使用着色器this.gl.program = shaderProgram;
设置顶点坐标
定义顶点坐标和纹理坐标
vertexs: new Float32Array([ -1, 1, 0.0, 0.0, 1.0, //左边三位表示顶点坐标的x、y、z,右边两位表示顶点对应的纹理坐标 -1, -1, 0.0, 0.0, 0.0, 1, 1, 0.0, 1.0, 1.0, 1, -1, 0.0, 1.0, 0.0])
设置顶点坐标和纹理坐标
let vertexsBuffer = this.gl.createBuffer();if (vertexsBuffer === null) {console.log("vertexsBuffer is null");return false;}this.gl.bindBuffer(this.gl.ARRAY_BUFFER, vertexsBuffer);this.gl.bufferData(this.gl.ARRAY_BUFFER, this.vertexs, this.gl.STATIC_DRAW);let a_Position = this.gl.getAttribLocation(this.gl.program, "a_Position");if (a_Position < 0) {console.log("a_Position < 0");return false;}let a_TexCoord = this.gl.getAttribLocation(this.gl.program, "a_TexCoord");if (a_TexCoord < 0) {console.log("a_TexCoord < 0");return false;}//将顶点坐标的位置赋值this.gl.vertexAttribPointer(a_Position, 3, this.gl.FLOAT, false, this.vertexs.BYTES_PER_ELEMENT * 5, 0);this.gl.enableVertexAttribArray(a_Position);//将纹理坐标赋值this.gl.vertexAttribPointer(a_TexCoord, 2, this.gl.FLOAT, false, this.vertexs.BYTES_PER_ELEMENT * 5, this.vertexs.BYTES_PER_ELEMENT * 3);this.gl.enableVertexAttribArray(a_TexCoord);
加载图片
let image = new Image();image.src = require('@/assets/qw.png');this.image = image;
设置纹理
let textureId = this.gl.createTexture(); //创建纹理对象 if (textureId === null) { console.log("textureId is null"); return false; } this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, 1); // 对纹理图像进行y轴反转 this.gl.activeTexture(this.gl.TEXTURE0); // 开启0号纹理单元 this.gl.bindTexture(this.gl.TEXTURE_2D, textureId); // 向target绑定纹理对象 this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);// 配置纹理参数 this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE); this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST); this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST); this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, this.image); // 配置纹理图像 let u_Sampler = this.gl.getUniformLocation(this.gl.program, "u_Sampler"); if (u_Sampler < 0) { console.log("u_Sampler < 0"); return false; } this.gl.uniform1i(u_Sampler, 0); // 将0号纹理传递给着色器
画出图片
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
渲染出来的结果如图下图:
总结
opengl对于初学者可能会难一些,可以多了解下着色器和使用流程,就比较容易上手了。
直接上源码
<template> <div> <canvas id="canvas" height="600" width="600">canvas> div>template><script>export default { name: "GLImage", data() { return { gl: null, vertexs: new Float32Array([ -1, 1, 0.0, 0.0, 1.0, -1, -1, 0.0, 0.0, 0.0, 1, 1, 0.0, 1.0, 1.0, 1, -1, 0.0, 1.0, 0.0]), vertexShaderSource: ` attribute vec4 a_Position; attribute vec2 a_TexCoord; varying vec2 v_TexCoord; void main(){ gl_Position = a_Position; v_TexCoord = a_TexCoord; }`, fragmentShaderSource: ` precision mediump float; uniform sampler2D u_Sampler; varying vec2 v_TexCoord; void main(){ gl_FragColor = texture2D(u_Sampler, v_TexCoord); }` } }, methods: { initShader() { let vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);// 创建顶点着色器 this.gl.shaderSource(vertexShader, this.vertexShaderSource);// 绑定顶点着色器源码 this.gl.compileShader(vertexShader);// 编译定点着色器 let fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);// 创建片元着色器 this.gl.shaderSource(fragmentShader, this.fragmentShaderSource);// 绑定片元着色器源码 this.gl.compileShader(fragmentShader);// 编译片元着色器 let shaderProgram = this.gl.createProgram();// 创建着色器程序 this.gl.attachShader(shaderProgram, vertexShader);// 指定顶点着色器 this.gl.attachShader(shaderProgram, fragmentShader);// 指定片元着色色器 this.gl.linkProgram(shaderProgram);// 链接程序 this.gl.useProgram(shaderProgram);//使用着色器 this.gl.program = shaderProgram; return true }, initVertexs() { let vertexsBuffer = this.gl.createBuffer(); if (vertexsBuffer === null) { console.log("vertexsBuffer is null"); return false; } this.gl.bindBuffer(this.gl.ARRAY_BUFFER, vertexsBuffer); this.gl.bufferData(this.gl.ARRAY_BUFFER, this.vertexs, this.gl.STATIC_DRAW); let a_Position = this.gl.getAttribLocation(this.gl.program, "a_Position"); if (a_Position < 0) { console.log("a_Position < 0"); return false; } let a_TexCoord = this.gl.getAttribLocation(this.gl.program, "a_TexCoord"); if (a_TexCoord < 0) { console.log("a_TexCoord < 0"); return false; } //将顶点坐标的位置赋值 this.gl.vertexAttribPointer(a_Position, 3, this.gl.FLOAT, false, this.vertexs.BYTES_PER_ELEMENT * 5, 0); this.gl.enableVertexAttribArray(a_Position); //将纹理坐标赋值 this.gl.vertexAttribPointer(a_TexCoord, 2, this.gl.FLOAT, false, this.vertexs.BYTES_PER_ELEMENT * 5, this.vertexs.BYTES_PER_ELEMENT * 3); this.gl.enableVertexAttribArray(a_TexCoord); return true; }, initTextures() { let textureId = this.gl.createTexture(); //创建纹理对象 if (textureId === null) { console.log("textureId is null"); return false; } this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, 1); // 对纹理图像进行y轴反转 this.gl.activeTexture(this.gl.TEXTURE0); // 开启0号纹理单元 this.gl.bindTexture(this.gl.TEXTURE_2D, textureId); // 向target绑定纹理对象 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_WRAP_S, this.gl.CLAMP_TO_EDGE); this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE); this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST); this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST); this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, this.image); // 配置纹理图像 let u_Sampler = this.gl.getUniformLocation(this.gl.program, "u_Sampler"); if (u_Sampler < 0) { console.log("u_Sampler < 0"); return false; } this.gl.uniform1i(u_Sampler, 0); // 将0号纹理传递给着色器 return true }, renderImage() { if (!this.initShader()) { console.log('initShader is failed') return; } if (!this.initVertexs()) { console.log('drawVertexs is failed') return; } if (!this.initTextures()) { console.log('drawTextures is failed') return; } this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4); } }, mounted() { this.gl = document.getElementById('canvas').getContext("webgl"); let image = new Image(); image.src = require('@/assets/qw.png'); this.image = image; image.onload = this.renderImage; }}script><style scoped>style>