UNPKG

@flyskywhy/react-native-gcanvas

Version:

A C++ native canvas 2D/WebGL component based on gpu opengl glsl shader GCanvas

327 lines (286 loc) 8.65 kB
function compile({vShader, fShader, gl, options}) { let textureCount = 0; function createShaderProgram(vShaderSource, fShaderSource, gl) { function loadShader(gl, type, source) { var shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { throw ( 'An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader) ); } return shader; } const fShader = loadShader(gl, gl.FRAGMENT_SHADER, fShaderSource); const vShader = loadShader(gl, gl.VERTEX_SHADER, vShaderSource); const program = gl.createProgram(); gl.attachShader(program, vShader); gl.attachShader(program, fShader); gl.linkProgram(program); if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { throw ( 'Unable to initialize the shader program: ' + gl.getProgramInfoLog(program) ); } const uniforms = []; const attributes = []; const attributeCount = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES, ); for (let i = 0; i < attributeCount; i++) { const attribute = gl.getActiveAttrib(program, i); attributes.push(attribute); } const uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (let i = 0; i < uniformCount; i++) { const uniform = gl.getActiveUniform(program, i); uniforms.push(uniform); } return { program: program, uniforms, attributes, }; } function getType(value, gl) { const types = [ 'FLOAT', 'FLOAT_VEC2', 'FLOAT_VEC3', 'FLOAT_VEC4', 'FLOAT_MAT2', 'FLOAT_MAT3', 'FLOAT_MAT4', 'INT', 'INT_VEC2', 'INT_VEC3', 'INT_VEC4', 'BOOL', 'BOOL_VEC2', 'BOOL_VEC3', 'BOOL_VEC4', 'SAMPLER_2D', 'SAMPLER_CUBE', ]; for (let i = 0; i < types.length; i++) { if (gl[types[i]] === value) { return types[i]; } } throw 'get type failed ' + value; } function attributeManager(attribute, program, gl) { const manager = {}; const type = getType(attribute.type, gl); const position = gl.getAttribLocation(program, attribute.name); const baseType = type.split('_')[0]; const vecType = type.split('_').length > 1 ? type.split('_')[1] : 'VEC1'; const vecSize = Number(vecType[3]); const arrayTypeMap = { FLOAT: Float32Array, INT: Int16Array, BOOL: Uint8Array, }; const ArrayType = arrayTypeMap[baseType]; manager.fill = function (buffer) { gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.vertexAttribPointer(position, vecSize, gl[baseType], false, 0, 0); gl.enableVertexAttribArray(position); }; manager.createBuffer = function (value) { const buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, new ArrayType(value), gl.STATIC_DRAW); gl.bindBuffer(gl.ARRAY_BUFFER, null); return buffer; }; return { type, ...manager, }; } function uniformManager(uniform, program, gl) { const manager = {}; const type = getType(uniform.type, gl); const position = gl.getUniformLocation(program, uniform.name); const baseType = type.split('_')[0]; const vecType = type.split('_').length > 1 ? type.split('_')[1] : 'VEC1'; const baseVecType = vecType.substr(0, 3); const vecSize = Number(vecType[3]); const arrayTypeMap = { FLOAT: Float32Array, INT: Int32Array, BOOL: Uint8Array, }; const ArrayType = arrayTypeMap[baseType]; let uniformMethodName; switch (baseVecType) { case 'VEC': uniformMethodName = [ 'uniform', vecSize, baseType === 'FLOAT' ? 'f' : 'i', 'v', ].join(''); manager.fill = function (data) { gl[uniformMethodName](position, new ArrayType(data)); }; break; case 'MAT': uniformMethodName = ['uniform', 'Matrix', vecSize, 'fv'].join(''); manager.fill = function (data) { gl[uniformMethodName](position, false, new ArrayType(data)); }; break; case '2D': { const textureUnitIndex = textureCount; const textureUnitName = `TEXTURE${textureUnitIndex}`; if (textureCount === maxTextureImageUnits) { throw 'texture size exceed max texture image units'; } else { textureCount++; } manager.createTexture = function (image, flipY = true) { const texture = gl.createTexture(); gl.activeTexture(gl[textureUnitName]); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 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); if (flipY) { gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); } if (image instanceof Uint8Array) { gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, image, ); } else { gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image, ); } if (flipY) { gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); } gl.bindTexture(gl.TEXTURE_2D, null); return texture; }; manager.fill = function (texture) { gl.activeTexture(gl[textureUnitName]); gl.bindTexture(gl.TEXTURE_2D, texture); gl.uniform1i(position, textureUnitIndex); }; manager.update = function (texture, image) { gl.activeTexture(gl[textureUnitName]); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image, ); }; break; } default: throw 'baseVecType invalid'; } return {type, ...manager}; } const maxTextureImageUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); const { program, attributes: attributeList, uniforms: uniformList, } = createShaderProgram(vShader, fShader, gl); const attributes = {}; const uniforms = {}; attributeList.forEach(function (attribute) { attributes[attribute.name] = attributeManager(attribute, program, gl); }); uniformList.forEach(function (uniform) { let name = uniform.name; if (name.endsWith('[0]')) { name = name.replace('[0]', ''); } uniforms[name] = uniformManager(uniform, program, gl); }); gl.useProgram(program); return { program, uniforms, attributes, drawArrays: function (count) { gl.drawArrays(gl.TRIANGLES, 0, count); }, drawElements: function (count) { gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, 0); }, createElementsBuffer: function (value) { const buffer = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(value), gl.STATIC_DRAW, ); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); return buffer; }, fillElements: function (buffer) { gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); return buffer; }, createFramebuffer: function (width, height) { const framebuffer = gl.createFramebuffer(); const tex = gl.createTexture(); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, tex); gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null, ); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0, ); gl.bindFramebuffer(gl.FRAMEBUFFER, null); return { framebuffer, texture: tex, }; }, }; } export {compile};