1、前提需要安装 Emscripten
安装部分参考:emcc.zcopy.site/
2、C语言部分
createImg方法返回给js的是一个结构体指针ImgData(test.c)。
2、Emscripten编译指令
emcc test.c -o test.js -s EXPORTED_FUNCTIONS=["_malloc","_free","_createImg","_main"] -s EXPORTED_RUNTIME_METHODS=["cwrap"] -s ALLOW_MEMORY_GROWTH=1
-s EXPORTED_FUNCTIONS里面是C暴露给js的各种方法(注意需要在C方法名称前加_)
-s EXPORTED_RUNTIME_METHODS里面是Emscripten暴露给js的各种方法
CMD进入test.c所在目录运行编译指令,将在本目录下生成test.js 和 test.wasm文件。
3、前端部分
项目目录结构
前端代码
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./test.js"></script>
<style>
canvas {
width: 1920px;
height: 1080px;
border: 1px solid;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="1920" height="1080"></canvas>
<br />
<button id="getImg">获取图片</button>
<button id="stopImg">暂停</button>
<script>
let webgl = null;
let num = 0;//记录绘制的图片数量
//顶点着色器
//attribute变量,用来表示和顶点相关的数据,只可以在顶点着色器中使用。
//varying变量,作用是从顶点着色器向片元着色器传值,只要在片元着色器中也声明同名varying变量,顶点着色器赋给该变量的值就会自动传入片元着色器。
let vertexstring = `
attribute vec2 a_position;
attribute vec2 outUV;
varying vec2 inUV;
void main(void){
gl_Position = vec4(a_position, 0, 1.0);
inUV = outUV;
}
`;
//片元着色器
let fragmentstring = `
precision mediump float;
uniform sampler2D texture;
varying vec2 inUV;
void main(void){
gl_FragColor = texture2D(texture,inUV);
}
`;
Module.onRuntimeInitialized = function () {
createImg = Module["cwrap"]('createImg', "number", null);
}
window.onload = function () {
const myCanvas = document.querySelector("#myCanvas");
webgl = myCanvas.getContext("webgl");
webgl.viewport(0, 0, myCanvas.width, myCanvas.height);
initWebGL();
const getImgBtn = document.querySelector("#getImg");
const stopImgBtn = document.querySelector("#stopImg");
getImgBtn.onclick = getImgFun;
stopImgBtn.onclick = function () {
window.cancelAnimationFrame(thisTime);
}
};
function getImgFun(timestamp) {
console.log(num++);
console.log(timestamp);
let ptr = createImg();
let width = Module.HEAPU32[ptr / 4];
let height = Module.HEAPU32[ptr / 4 + 1];
let imgBufferPtr = Module.HEAPU32[ptr / 4 + 2];
let imageBuffer = Module.HEAPU8.subarray(imgBufferPtr, imgBufferPtr + width * height * 4);
if (!width || !height) {
console.log("获取图片失败,图片宽高为0");
return;
}
//console.log("ptr", width);
//console.log("ptr", height);
//console.log("imageBuffer", imageBuffer);
drawImage(width, height, imageBuffer);
Module._free(ptr);
Module._free(imgBufferPtr);
thisTime = window.requestAnimationFrame(getImgFun);
}
//canvas 2d 绘制RGBA图片数据
// function drawImage(width, height, buffer) {
// let imageData = ctx.createImageData(width, height);
// imageData.data.set(buffer);
// //myCanvas.width = width;
// //myCanvas.height = height;
// ctx.putImageData(imageData, 0, 0, 0, 0, width, height);
// }
function drawImage(width, height, buffer) {
webgl.texImage2D(webgl.TEXTURE_2D, 0, webgl.RGBA, 1920,1080,0,webgl.RGBA, webgl.UNSIGNED_BYTE, buffer);
//webgl.clearColor(0.0, 1.0, 0.0, 1.0);
//webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
webgl.drawArrays(webgl.TRIANGLE_STRIP, 0, 4);
}
function initWebGL() {
//创建顶点着色器和片元着色器
let vsshader = webgl.createShader(webgl.VERTEX_SHADER);
let fsshader = webgl.createShader(webgl.FRAGMENT_SHADER);
//关联GLSL源代码
webgl.shaderSource(vsshader, vertexstring);
webgl.shaderSource(fsshader, fragmentstring);
//编译源代码
webgl.compileShader(vsshader);
webgl.compileShader(fsshader);
if (!webgl.getShaderParameter(vsshader, webgl.COMPILE_STATUS)) {
var err = webgl.getShaderInfoLog(vsshader);
alert(err);
return;
}
if (!webgl.getShaderParameter(fsshader, webgl.COMPILE_STATUS)) {
var err = webgl.getShaderInfoLog(fsshader);
alert(err);
return;
}
let program = webgl.createProgram();
webgl.attachShader(program, vsshader);
webgl.attachShader(program, fsshader)
webgl.linkProgram(program);
webgl.useProgram(program);
//给顶点着色器设置顶点坐标(和canvas一样大)
const vertexData = [
-1.0,
-1.0,
1.0,
-1.0,
-1.0,
1.0,
1.0,
1.0,
];
let triangleBuffer = webgl.createBuffer();
webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);
webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(vertexData), webgl.STATIC_DRAW);
let aPsotion = webgl.getAttribLocation(program, "a_position");
webgl.enableVertexAttribArray(aPsotion);
webgl.vertexAttribPointer(aPsotion, 2, webgl.FLOAT, false, 0, 0);
//设置纹理坐标
//js不能直接给varying变量赋值,只能先给attribute变量赋值,
const txtCoordData = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0];
let triangleBuffers = webgl.createBuffer();
webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffers);
webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(txtCoordData), webgl.STATIC_DRAW);
let aPsotions = webgl.getAttribLocation(program, "outUV");
webgl.enableVertexAttribArray(aPsotions);
webgl.vertexAttribPointer(aPsotions, 2, webgl.FLOAT, false, 0, 0);
webgl.activeTexture(webgl.TEXTURE0); // 激活
let texture = webgl.createTexture(); // 创建纹理对象
webgl.bindTexture(webgl.TEXTURE_2D, texture);
const sampler = webgl.getUniformLocation(program, "u_image");// 获取纹理采样器索引
webgl.uniform1i(sampler, 0);// 绑定纹理单元
//纹理参数设置
webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_S, webgl.CLAMP_TO_EDGE);
webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_T, webgl.CLAMP_TO_EDGE);
webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MIN_FILTER, webgl.LINEAR);
webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MAG_FILTER, webgl.LINEAR);
webgl.pixelStorei(webgl.UNPACK_FLIP_Y_WEBGL, true);
}
</script>
</body>
</html>
4、运行结果展示
5、结束语
以上是本案例的全部代码,此案例适合像我一样刚刚接触相关知识的小白参考学习(C、Wasm、webGL),其中涉及到的大多数知识我也是一知半解,代码中有需要优化的部分希望大神可以指出。