I am creating a little browser game to learn OpenGL and trying to instance draw calls for textures. I have a texture that is an 85x44 png with transparency. I am having trouble getting the texture to display. I had it showing up previously when using single draw calls, but something started going wrong when I switched to drawArraysInstanced. If I mess with the alphas in the fragment shader I can get white blocks to appear on the canvas in the correct spots as seen below in the image.
I have been messing with the order of gl bind texture which makes the draws go from white to black. I don't know if I was on to something doing that.
Here is my code:
FS
// #version 300 es
// #pragma vscode_glsllint_stage: frag
precision mediump float;
// our texture
uniform sampler2D u_image;
// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;
void main() {
gl_FragColor = texture2D(u_image, v_texCoord);
if(gl_FragColor.a < 0.9) discard;
}
VS
// #version 300 es
// #pragma vscode_glsllint_stage: vert
attribute vec2 a_Position;
uniform mat3 u_matrix;
varying vec2 v_texCoord;
void main() {
gl_Position = vec4(u_matrix * vec3(a_Position, 1), 1);
// because we're using a unit quad we can just use
// the same data for our texcoords.
v_texCoord = a_Position;
}
initVertexBuffer.ts
import { ProgramInfo } from "../../../compileShaders";
export function initVertexBuffers({
gl,
programInfo,
array,
rgba, // note not in use in this file
texture, // note not in use in this file
}: {
gl: WebGL2RenderingContext;
programInfo: ProgramInfo;
array: number[];
rgba: [number, number, number, number] | number[];
texture: WebGLTexture;
}) {
const dim = 2;
const vertices = new Float32Array(array);
// Assign the vertices in buffer object to a_Position variable
// let positionLocation = gl.getAttribLocation(program, "a_position");
const a_Position = gl.getAttribLocation(programInfo.program, "a_Position");
const u_imageLoc = gl.getUniformLocation(programInfo.program, "u_image");
const u_matrixLoc = gl.getUniformLocation(programInfo.program, "u_matrix");
const vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log("Failed to create the buffer object");
return { n: -1 };
}
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
if (a_Position < 0) {
console.log("Failed to get the storage location of a_Position");
return { n: -2 };
}
gl.enableVertexAttribArray(a_Position);
gl.vertexAttribPointer(a_Position, dim, gl.FLOAT, false, 0, 0);
// use texture
gl.activeTexture(gl.TEXTURE0);
// Return number of vertices
return { n: vertices.length / dim, u_imageLoc, u_matrixLoc };
}
createRect.ts
export const createRect = ({
array,
gl,
position,
size,
cameraLoc,
color = [],
}: {
gl: WebGL2RenderingContext;
array: number[];
position: XY;
size: Dimensions;
cameraLoc: XY;
color?: number[] | [number, number, number, number];
}) => {
const { width: canvasWidth, height: canvasHeight } = gl.canvas;
const { x, y } = position;
const { width, height } = size;
const { x: cameraX, y: cameraY } = cameraLoc;
const clipX = ((x - cameraX) / canvasWidth) * 2 - 1;
const clipY = ((y - cameraY) / canvasHeight) * -2 + 1;
const clipWidth = width / canvasWidth;
const clipHeight = height / canvasHeight;
// minimum of 12 numbers per rect
array.push(
// first triangle
clipX,
clipY,
clipX + clipWidth,
clipY,
clipX,
clipY - clipHeight,
// second triangle
clipX + clipWidth,
clipY,
clipX,
clipY - clipHeight,
clipX + clipWidth,
clipY - clipHeight,
...color
);
};
draw.ts
import { RootState } from "../../../../store";
import { glBindTexture } from "../../shaders/textures/terrain/grass1/glBindTexture";
import { ProgramInfo } from "../../shaders/compileShaders";
import { initVertexBuffers } from "../../shaders/textures/terrain/grass1/initVertexBuffers";
import { gray } from "../../colors";
import { createRect } from "../../shaders/textures/terrain/grass1/createRect";
interface Props {
gl: WebGL2RenderingContext;
state: RootState;
shaders: {
grass1Shader: ProgramInfo;
};
}
const images = {} as Record<string, HTMLImageElement>;
export const terrain = ({ gl, state, shaders: { grass1Shader } }: Props) => {
if (!grass1Shader) return;
const cameras = state.game.cameras["main"].position;
if (!images["grass1.png"]) {
const img = document.getElementById("grass1.png") as HTMLImageElement;
if (!img) return;
images["grass1.png"] = img;
}
images["grass1.png"].width = 85;
images["grass1.png"].height = 40;
if (!images["grass1.png"]) return;
const array = [] as number[];
createRect({
array,
gl,
position: {
x: 0,
y: 0,
},
cameraLoc: cameras,
size: {
width: 85,
height: 44,
},
});
createRect({
array,
gl,
position: {
x: 100,
y: 100,
},
cameraLoc: cameras,
size: {
width: 85,
height: 44,
},
});
gl.useProgram(grass1Shader.program);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
const texture = glBindTexture(gl, grass1Shader, images["grass1.png"]);
const { n, u_matrixLoc } = initVertexBuffers({
gl,
programInfo: grass1Shader,
array,
rgba: gray,
texture,
});
if (n < 0) {
console.log(n);
console.log("Failed to set the positions of the vertices");
return;
}
// identity matrix spanning full screen
gl.uniformMatrix3fv(u_matrixLoc, false, [1, 0, 0, 0, 1, 0, 0, 0, 1]);
// console.log(n, array.length / 12);
gl.drawArraysInstanced(gl.TRIANGLES, 0, n, array.length / 12);
// gl.drawElementsInstanced(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0, 1);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
};
I solved this by using properly, 2^n, sized textures. Something must have been wrong with my code allowing for any sized textures. once I started using 64x64 pixel images it all began working.