function makeShader(gl, type, source) { const shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { throw Error(`Shader compiler error: ${gl.getShaderInfoLog(shader)}`); } return shader; } export function makeProgram(gl, vertexShader, fragmentShader) { const vshader = makeShader(gl, gl.VERTEX_SHADER, vertexShader); const fshader = makeShader(gl, gl.FRAGMENT_SHADER, fragmentShader); const shaderProg = gl.createProgram(); gl.attachShader(shaderProg, vshader); gl.attachShader(shaderProg, fshader); gl.linkProgram(shaderProg); if (!gl.getProgramParameter(shaderProg, gl.LINK_STATUS)) { throw Error(`Shader linker error: ${gl.getProgramInfoLog(shaderProg)}`); } return shaderProg; } function getImage(url) { return new Promise(resolve => { const image = new Image(); image.onload = () => resolve(image); image.src = url; }); } export async function loadTexture(gl, url) { const image = await getImage(url); const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 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.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); gl.generateMipmap(gl.TEXTURE_2D); return texture; }